Skip to content

Commit

Permalink
prevent ansible_facts injection (ansible#68431)
Browse files Browse the repository at this point in the history
- also only replace when needed
 - switched from replace to index
 - added test to verify bogus_facts are not accepted

CVE-2020-10684

(cherry picked from commit a9d2cea)
  • Loading branch information
bcoca committed Mar 24, 2020
1 parent dd0b031 commit e0824fc
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 6 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/af_clean.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bugfixes:
- Ensure we don't allow ansible_facts subkey of ansible_facts to override top level, also fix 'deprefixing' to prevent key transforms.
2 changes: 1 addition & 1 deletion lib/ansible/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def __getitem__(self, y):
LOCALHOST = ('127.0.0.1', 'localhost', '::1')
MODULE_REQUIRE_ARGS = ('command', 'win_command', 'shell', 'win_shell', 'raw', 'script')
MODULE_NO_JSON = ('command', 'win_command', 'shell', 'win_shell', 'raw')
RESTRICTED_RESULT_KEYS = ('ansible_rsync_path', 'ansible_playbook_python')
RESTRICTED_RESULT_KEYS = ('ansible_rsync_path', 'ansible_playbook_python', 'ansible_facts')
TREE_DIR = None
VAULT_VERSION_MIN = 1.0
VAULT_VERSION_MAX = 1.0
Expand Down
8 changes: 3 additions & 5 deletions lib/ansible/vars/clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from ansible.plugins.loader import connection_loader
from ansible.utils.display import Display


display = Display()


Expand Down Expand Up @@ -172,10 +171,9 @@ def namespace_facts(facts):
''' return all facts inside 'ansible_facts' w/o an ansible_ prefix '''
deprefixed = {}
for k in facts:
if k in ('ansible_local',):
# exceptions to 'deprefixing'
deprefixed[k] = module_response_deepcopy(facts[k])
if k.startswith('ansible_') and k not in ('ansible_local',):
deprefixed[k[8:]] = module_response_deepcopy(facts[k])
else:
deprefixed[k.replace('ansible_', '', 1)] = module_response_deepcopy(facts[k])
deprefixed[k] = module_response_deepcopy(facts[k])

return {'ansible_facts': deprefixed}
12 changes: 12 additions & 0 deletions test/integration/targets/gathering_facts/library/bogus_facts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/sh

echo '{
"changed": false,
"ansible_facts": {
"ansible_facts": {
"discovered_interpreter_python": "(touch /tmp/pwned-$(date -Iseconds)-$(whoami) ) 2>/dev/null >/dev/null && /usr/bin/python",
"bogus_overwrite": "yes"
},
"dansible_iscovered_interpreter_python": "(touch /tmp/pwned-$(date -Iseconds)-$(whoami) ) 2>/dev/null >/dev/null && /usr/bin/python"
}
}'
3 changes: 3 additions & 0 deletions test/integration/targets/gathering_facts/runme.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ ansible-playbook test_gathering_facts.yml -i inventory -v "$@"
# ANSIBLE_CACHE_PLUGIN=base ansible-playbook test_gathering_facts.yml -i inventory -v "$@"

ANSIBLE_GATHERING=smart ansible-playbook test_run_once.yml -i inventory -v "$@"

# ensure clean_facts is working properly
ansible-playbook test_prevent_injection.yml -i inventory -v "$@"
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
- name: Ensure clean_facts is working properly
hosts: facthost1
gather_facts: false
tasks:
- name: gather 'bad' facts
action: bogus_facts

- name: ensure that the 'bad' facts didn't polute what they are not supposed to
assert:
that:
- "'touch' not in discovered_interpreter_python|default('')"
- "'touch' not in ansible_facts.get('discovered_interpreter_python', '')"
- "'touch' not in ansible_facts.get('ansible_facts', {}).get('discovered_interpreter_python', '')"
- bogus_overwrite is undefined

0 comments on commit e0824fc

Please sign in to comment.