Bash после Shellshock: три CVE за три дня и мониторинг версии пакета
Первый патч Shellshock оказался неполным - за три дня вышло ещё три CVE для того же механизма. Настроили мониторинг версии bash через Zabbix с триггером на несоответствие.
Серия патчей Shellshock (CVE-2014-6271, CVE-2014-6277, CVE-2014-6278) потребовала многократного обновления bash в течение нескольких дней - первый патч оказался неполным
Когда мы закончили первый раунд патчинга по Shellshock, появилось ощущение, что всё - закрыли и можно выдохнуть. Это ощущение продержалось примерно сутки. Потом пошла CVE-2014-7169, потом CVE-2014-6277 и CVE-2014-6278. За несколько дней получилось четыре CVE по одному механизму.
Это не обычная история «нашли уязвимость, выпустили патч». Это история о том, как парсер bash чинили на ходу, и каждый патч открывал соседнюю дыру в том же коде.
Что произошло с патчами
CVE-2014-6271 - исходный Shellshock. Bash не останавливался после закрывающей скобки в определении функции через переменную окружения и выполнял произвольный код хвоста. Первые патчи от Red Hat и Debian вышли 24-25 сентября.
Но уже через день исследователи показали, что исправление неполное:
- CVE-2014-7169 - другой вариант той же техники; с пропатченным bash первого патча всё равно можно было создать или перезаписать файл через хитрый синтаксис с перенаправлением. Та же корневая причина - парсер функций.
- CVE-2014-6277 и CVE-2014-6278 - ещё два вектора в том же механизме разбора функций, с потенциальным выполнением кода.
Итого: патч первого дня закрыл CVE-2014-6271, но не закрыл CVE-2014-7169. Второй патч закрыл CVE-2014-7169, пришли CVE-2014-6277 и CVE-2014-6278, нужен третий патч. На разных дистрибутивах хронология немного отличалась, но суть одна - bash обновляли несколько раз подряд за короткий промежуток.
На CentOS 6 версии выглядели примерно так:
bash-4.1.2-15.el6_5.1 # первый патч (CVE-2014-6271)
bash-4.1.2-15.el6_5.2 # второй патч (CVE-2014-7169 + CVE-2014-6277/6278)
На Ubuntu 12.04:
bash 4.2-2ubuntu2.1 # первый патч
bash 4.2-2ubuntu2.2 # второй патч
bash 4.2-2ubuntu2.3 # третий патч (для закрытия оставшихся CVE)
Пакеты у разных дистрибутивов выходили в разное время, темп у RedHat и Debian/Ubuntu отличался. Несколько серверов со старыми ветками Debian пришлось обновлять вручную - backport шёл с задержкой.
Почему это создало операционную проблему
Первый патч мы откатали методично: прошлись по всем серверам managed-клиентов, проверили версии, убедились что уязвимость закрыта. Это разовая операция - сделал, отметил, успокоился.
Серия патчей ломает эту логику. «Я проверил bash вчера» теперь не значит ничего, потому что вчера актуальной была другая версия. Нужно либо каждый день перепроверять вручную, либо сделать так, чтобы это проверялось автоматически.
Вручную на нескольких десятках серверов - это выглядит как:
ssh server01 rpm -q bash
ssh server02 rpm -q bash
...
Нудно, легко ошибиться, не масштабируется. Сервер без свежего патча легко пропустить.
Как настроили мониторинг через Zabbix
У нас уже стоит Zabbix с агентами на серверах. Zabbix умеет запускать произвольные команды через агент и сравнивать результат с ожидаемым значением. Это именно то, что нужно.
Шаг первый - UserParameter на агенте. Добавили в /etc/zabbix/zabbix_agentd.conf (или в отдельный файл в conf.d/):
UserParameter=pkg.version[*],rpm -q --queryformat '%{VERSION}-%{RELEASE}' $1 2>/dev/null || echo "not-installed"
Для Debian/Ubuntu аналогично через dpkg:
UserParameter=pkg.version[*],dpkg-query -W -f='${Version}' $1 2>/dev/null || echo "not-installed"
После изменения конфига - перезапуск агента.
Шаг второй - item в Zabbix. На нужных хостах (или через шаблон) добавили item:
- Key:
pkg.version[bash] - Type: Zabbix agent
- Value type: Character
- Update interval: 3600 (раз в час достаточно)
Шаг третий - триггер. Это главное. Триггер сравнивает собранное значение с ожидаемой версией:
{Template_Pkg_Version:pkg.version[bash].str("4.1.2-15.el6_5.2")}<>1
Логика: если строка с ожидаемой версией не содержится в значении - триггер срабатывает. Это значит severity «Average» и уведомление дежурному инженеру.
Когда выходит новый патч, мы обновляем строку в триггере на актуальную версию. Сами серверы обновляем через yum/apt, Zabbix через час проверяет и закрывает алерт.
Что ещё сделали параллельно. Для серверов на разных дистрибутивах - разные шаблоны с разными ожидаемыми версиями. CentOS 6, CentOS 7, Ubuntu 12.04, Ubuntu 14.04 - у каждого своя актуальная версия bash. Немного муторно поддерживать, но иначе никак.
Про парсер bash и суть проблемы
Пока шли патчи, несколько исследователей опубликовали разборы того, что именно не так с парсером bash. Суть в архитектурном решении: bash при старте обходит все переменные окружения и пытается интерпретировать те, которые выглядят как функции. При этом после разбора тела функции парсер продолжает читать входной поток - и первый патч просто добавил проверку наличия функции, но не исправил базовую логику продолжения разбора.
Это объясняет, почему вариантов оказалось несколько: одну точку входа прикрыли, но соседние остались. Полное исправление - переписать механизм импорта функций через переменные окружения. Что в итоге и сделали в поздних патчах.
Что сейчас
На момент написания этого поста актуальные версии для основных дистрибутивов - bash-4.1.2-15.el6_5.2 для CentOS 6 и аналоги для остальных. Zabbix-триггеры стоят на всех managed-серверах, несоответствие версии подсвечивается алертом в течение часа после сбора метрики.
Неприятный вывод из этой недели: «мы пропатчили» - это утверждение с датой истечения. Особенно когда исследователи активно копают тот же класс проблем. Мониторинг версий пакетов - не параноя, а просто следующий шаг после патч-менеджмента.
- Патч-менеджмент: Shellshock показал, где у нас прорехи · 18 сентября 2014