Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2021-43837: Merge pull request #198 from peopledoc/templates · peopledoc/vault-cli@3ba3955

vault-cli is a configurable command-line interface tool (and python library) to interact with Hashicorp Vault. In versions before 3.0.0 vault-cli features the ability for rendering templated values. When a secret starts with the prefix !template!, vault-cli interprets the rest of the contents of the secret as a Jinja2 template. Jinja2 is a powerful templating engine and is not designed to safely render arbitrary templates. An attacker controlling a jinja2 template rendered on a machine can trigger arbitrary code, making this a Remote Code Execution (RCE) risk. If the content of the vault can be completely trusted, then this is not a problem. Otherwise, if your threat model includes cases where an attacker can manipulate a secret value read from the vault using vault-cli, then this vulnerability may impact you. In 3.0.0, the code related to interpreting vault templated secrets has been removed entirely. Users are advised to upgrade as soon as possible. For users unable to upgrade a workaround does exist. Using the environment variable VAULT_CLI_RENDER=false or the flag --no-render (placed between vault-cli and the subcommand, e.g. vault-cli --no-render get-all) or adding render: false to the vault-cli configuration yaml file disables rendering and removes the vulnerability. Using the python library, you can use: vault_cli.get_client(render=False) when creating your client to get a client that will not render templated secrets and thus operates securely.

CVE
#vulnerability#mac#rce

@@ -1,8 +1,6 @@ import itertools
import pytest
from vault_cli import client, exceptions, testing from vault_cli import client, exceptions

def test_get_client(mocker): @@ -476,44 +474,6 @@ def test_vault_client_base_render_template_path_not_found(vault, template): ({"a": {"value": "b"}}, {"value": "b"}), # Secret not a string ({"a": {"value": [“yay”]}}, {"value": [“yay”]}), # Secret is a template without variable expansion ({"a": {"value": "!template!b"}, "b": {"value": "c"}}, {"value": "b"}), # Secret is a template ( {"a": {"value": "!template!{{ vault(‘b’).value }}"}, "b": {"value": "c"}}, {"value": "c"}, ), # Secret is a dict with containing a template ( { "a": {"x": "!template!{{ vault(‘b’).value }}", "y": "yay"}, "b": {"value": "c"}, }, {"x": "c", "y": "yay"}, ), # Finite recursion ( { "a": {"value": "!template!{{ vault(‘b’).value }}"}, "b": {"value": "!template!{{ vault(‘c’).value }}"}, "c": {"value": "d"}, }, {"value": "d"}, ), # Infinite Recursion ( { "a": {"value": "!template!{{ vault(‘b’).value }}"}, "b": {"value": "!template!{{ vault(‘c’).value }}"}, "c": {"value": "!template!{{ vault(‘a’).value }}"}, }, {"value": '<recursive value "a">’}, ), # Direct Recursion ( {"a": {"value": "!template!{{ vault(‘a’).value }}"}}, {"value": '<recursive value "a">’}, ), ], ) def test_vault_client_base_get_secret(vault, vault_contents, expected): @@ -522,23 +482,6 @@ def test_vault_client_base_get_secret(vault, vault_contents, expected): assert vault.get_secret(“a”) == expected

def test_vault_client_base_get_secret_deprecation_warning(vault, caplog): vault.db = {"a": {"value": "!template!b"}} caplog.set_level(“WARNING”)
vault.get_secret(“a”) assert “Templated values are deprecated” in caplog.records[0].message

def test_vault_client_base_get_secret_template_root(vault): vault.base_path = “base” vault.db = {"/base/a": {"value": '!template!{{ vault(“a”).value }} yay’}}
# In case of erroneous caching, e.g. a different cache entry # for /base/a and base/a, we would find ‘<recursive value "a"> yay yay’ assert vault.get_secret(“/base/a”) == {"value": '<recursive value "a"> yay’}

def test_vault_client_base_get_secret_multiple_keys(vault): vault.db = {"rabbitmq/creds/role": {"username": "foo", "password": "bar"}} assert vault.get_secret(“rabbitmq/creds/role”) == { @@ -547,22 +490,11 @@ def test_vault_client_base_get_secret_multiple_keys(vault): }

def test_vault_client_base_get_secret_with_dict(vault): vault.db = { "credentials": {"value": {"username": "foo", "password": "bar"}}, "dsn": { "value": “!template!proto://{{ vault(‘credentials’)[‘value’][‘username’] }}:{{ vault(‘credentials’).value.password }}@host” }, }
assert vault.get_secret(“dsn”) == {"value": "proto://foo:bar@host"}

def test_vault_client_base_get_secret_not_found(vault): vault.db = {}
with pytest.raises(exceptions.VaultSecretNotFound): vault.get_secret(“not-exiting”) vault.get_secret(“not-existing”)

def test_vault_client_base_get_secret_missing_key(vault): @@ -572,20 +504,6 @@ def test_vault_client_base_get_secret_missing_key(vault): vault.get_secret(“a", key="username”)

def test_vault_client_base_get_secret_template_error(vault, caplog): vault.db = {"a": {"key": "!template!{{"}}
with pytest.raises(exceptions.VaultRenderTemplateError) as exc_info: vault.get_secret(“a”)
assert str(exc_info.value) == ‘Error while rendering secret at path "a"’ assert ( str(exc_info.value.__cause__) == ‘Error while rendering secret value for key "key"’ ) assert str(exc_info.value.__cause__.__cause__) == “Jinja2 template syntax error”

def test_vault_client_base_lookup_token(vault): assert vault.lookup_token() == {"data": {"expire_time": "2100-01-01T00:00:00"}}
@@ -672,56 +590,6 @@ def test_vault_client_base_get_secret_implicit_cache(vault): assert vault.get_secret(“a”) == {"value": "b"}

class RaceConditionTestVaultClient(testing.TestVaultClient): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.counter = itertools.count()
def _get_secret(self, path): if path == "a": val = next(self.counter) return {"b": f"b{val}", "c": f"c{val}"} return super()._get_secret(path)

def test_vault_client_base_get_secret_implicit_cache_no_race_condition(): # In this test we check that if a value is read several times by # a template, implicit caching makes sure we have the same value # every time.
# Values returned by this client keep changing
vault = RaceConditionTestVaultClient()
with vault: assert vault.get_secret(“a”) == {"b": "b0", "c": "c0"} with vault: assert vault.get_secret(“a”) == {"b": "b1", "c": "c1"}
vault.db = {"d": {"value": """!template!{{ vault(“a”).b }}-{{ vault(“a”).c }}"""}}
# b2-c3 would be the value if caching didn’t work. with vault: assert vault.get_secret(“d”) == {"value": "b2-c2"}

def test_vault_client_base_get_secrets_implicit_cache_no_race_condition(): # In this test, the same value is read twice by get-all and template # We check that 2 values are consistent
vault = RaceConditionTestVaultClient()
vault.db = { "a": {}, "d": {"value": """!template!{{ vault(“a”).b }}-{{ vault(“a”).c }}"""}, }
assert vault.get_secrets(“”) == { "a": {"b": "b0", "c": "c0"}, "d": {"value": "b0-c0"}, }

def test_vault_client_base_get_secret_explicit_cache(vault): vault.db = {"a": {"value": "b"}} with vault:

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda
CVE-2023-6905
CVE-2023-6903
CVE-2023-6904
CVE-2023-3907