Обзор самых опасных, неоднозначных и дурацких уязвимостей в ядре Linux

14 мая этого года в ядре Linux была обнаружена серьезная локальная 0-day-уязвимость (CVE–2013–2094), затрагивающая практически все версии ядер, выпущенные за последние три года. Это довольно серьезный удар по репутации Linux, а также админов, серверы которых были взломаны. Является ли этот случай исключением из правил и как часто на самом деле в Linux находят серьезные проблемы безопасности?

CVE–2013–2094

Данная уязвимость была выявлена в ядрах с 2.6.37 по 3.8.8 и, по сути, распространялась на все актуальные версии дистрибутивов и даже на RHEL 6 и CentOS 6, которые хоть и основаны на ядре 2.6.32, но включают в себя бэкпортированную функциональность из более поздних ядер. Проблемный участок был найден в коде подсистемы PERF_EVENTS, предназначенной для трассировки, но активированной в большинстве дистрибутивов.

Вместе с известием об ошибке был опубликован и рабочий эксплойт, однако сама ошибка была исправлена еще в апреле вместе с выпуском ядра версии 3.8.9, без разглашения информации об уязвимости. Стоит отметить, что доступный сплоит не сработает, если значение параметра kernel.perf_event_paranoid механизма sysctl равно двум.

# sysctl -w kernel.perf_event_paranoid=2

Однако эта команда не решает проблему в принципе. Немного модифицировав исходный код сплоита, можно получить root даже при kernel.perf_event_paranoid=2.

Богатый 2013-й

Отматываем время всего на пару месяцев назад (13.03.2013) и видим еще один вполне осязаемый локальный root-эксплойт для всех ядер ветки 3.8. В этот раз проблемным оказался код, ответственный за реализацию пространств имен для непривилегированных пользователей. Уязвимость проявлялась при использовании комбинации флагов CLONE_NEWUSER и CLONE_FS во время клонирования процесса, в результате чего несколько процессов, работающих в разных пространствах имен, могли совместно использовать один корневой каталог. Ошибка была исправлена в 3.8.3.

Тремя неделями ранее (25.02.2013) исследователи выявили локальную уязвимость CVE–2013–1763, затрагивающую ядра Linux с 3.3 по 3.8. В этот раз проблема была в банальном переполнении буфера в подсистеме sock_diag. Проверка на выход за границы массива sock_diag_handlers не осуществлялась, поэтому, отправив специальным образом сформированное netlink-сообщение из пространства пользователя, можно было вызвать переполнение буфера и выполнить код с правами ядра.

Код эксплойта был опубликован и распространялся на такие дистрибутивы, как Fedora 17, Fedora 18, Ubuntu 12.04 и Ubuntu 12.10, а также на актуальную к тому моменту версию ArchLinux и другие дистрибутивы. Однако самое интересное, что, по некоторым сведениям, 0-day-эксплойт для данной уязвимости существовал еще с июля 2012 года, но распространялся только на Fedora 17 с ядрами 3.4.4–3.fc17.i686, 3.5.2–3.fc17.i686 и 3.5.5–2.fc17.i686. А это сотни тысяч серверов по всему миру.

Девятью днями ранее в Linux обнаружили любопытную уязвимость под индексом CVE–2013–0871, которая могла быть использована локальным злоумышленником для выполнения кода на уровне ядра. По заявлению исследователей, она существовала в подсистеме PTRACE, однако для ее эксплуатации требовался не просто эксплойт, а еще и модификация ядра таким образом, чтобы спровоцировать появление эффекта гонки при вызове ptrace с параметром PTRACE_SETREGS.

Справедливости ради стоит сказать, что реальность возникновения этого самого эффекта гонки так никто и не подтвердил, однако и не опроверг. На каких архитектурах может проявляться проблема, также осталось невыясненным. Реальным же остался только факт, что в последних на тот момент версиях ядра «уязвимости» уже не существовало.

Заголовок эксплойта clown-newuser.c
Заголовок эксплойта clown-newuser.c

История с багом в /dev/ /mem

От каскада проблем 2013 года перейдем к более ранним и опасным уязвимостям, найденным в предыдущие годы жизни пингвина. Начнем, как обычно, с конца, а именно с критической root-уязвимости, найденной в январе 2012 года. Исследователь Юри Аэдла в приватном порядке уведомил разработчиков ядра о проблеме, найденной им в интерфейсе /proc/ /mem, используемом для получения и модификации информации о памяти процесса. Как оказалось, проверка прав на запись делалась криво, в результате чего при определенных условиях любой процесс мог писать в память любого другого процесса (даже с SUID-правами) и, как следствие, внедрить в него shell-код, который, отработав, даст права root.

Однако интересно в этой истории вовсе не глупость ошибки, а то, как она была исправлена. Все дело в том, что разрабы отреагировали на сообщение очень быстро и сразу внесли необходимые исправления в общедоступный Git-репозиторий ядра Linux. А взломщики отреагировали еще быстрее и на основе внесенных изменений разобрали суть проблемы и выпустили сразу несколько эксплойтов (в том числе для получения root на Android). В результате, несмотря на внесенные исправления, эксплойты появились раньше официального обновления ядра и внесения изменений в дистрибутивы.

Впрочем, дистрибутивостроители также не заставили себя ждать, и багфиксы для Ubuntu 11.1 и RHEL 6 были внесены в репозитории в тот же день. Более того, как выяснилось, RHEL 6 и другие дистрибутивы с включенными по умолчанию технологиями рандомизации адресного пространства ASLR и PIE вообще оказались не подвержены данному типу атаки. Если быть точным, эксплойт не срабатывал только в отношении SUID-бинарников, поставляемых с дистрибутивом, но если админ самостоятельно собрал SUID-приложение без опции поддержки PIE и оставил его в системе, взломщик смог бы использовать его для атаки. Проблема была исправлена в ядрах 3.0.18 и 3.2.

Процесс получения root, используя уязвимость в /dev/<br /><br />
<pid>/mem
Процесс получения root, используя уязвимость в /dev/ /mem

В гостях хорошо, а дома…

В конце 2011 года в ядре был обнаружен другой тип бага, не относящийся к получению прав root, но опасный по своей природе. Речь идет о драйвере virtio, который предназначен для проброса реальных дисковых накопителей и разделов внутрь виртуальных окружений. Здесь проблема оказалась уж совсем банальной, так как выяснилось, что драйвер virtio пропускает SCSI-команды через ioctl-вызов SG_IO к блочному устройству вообще без каких-либо проверок. Поэтому, имея права root в гостевом окружении, куда проброшен один из разделов хост-системы, можно без проблем получить доступ ко всему диску как на чтение, так и на запись. Например, чтобы сделать дамп boot-сектора, достаточно отдать такую команду:

$ sg_dd if=/dev/vda blk_sgio=1 bs=512 count=1 of=output

В результате будет прочитан первый блок всего диска. Другими словами, если KVM-окружения используются для изоляции сетевых сервисов от хост-системы и одно из них будет поломано, под угрозой окажется вообще весь сервер. Прием отлично работает в KVM-окружениях, но неработоспособен в Xen, где вызов SG_IO не поддерживается. Чтобы решить проблему, в ядро был добавлен отдельный ioctl-вызов scsi_blk_cmd_ioctl, запрещающий проброс SCSI ioctl для разделов и вводящий белый список для ограничения области действия некоторых операций.

Угроза от USB-стика

Другая интересная уязвимость была найдена в марте 2011 года в драйвере для звуковых плат Native Instruments. Как выяснилось, драйвер содержит примитивнейшую из всех возможных ошибку, связанную с использованием небезопасной функции strcpy() для копирования имени устройства. При подключении USB-девайса с длиной имени, превышающей 80 символов, происходит переполнение буфера и, как следствие, затирание соседнего участка памяти. Для эксплуатации уязвимости злоумышленник может использовать специальное программируемое USB-устройство на базе микроконтроллеров серии AT90USB или ATMEGA32U4, которое будет прикидываться устройством Native Instruments и содержать в своем имени 80 случайных символов, за которыми следует shell-код. При втыкании такого устройства в USB-порт система автоматически загрузит в ядро уязвимый драйвер, в котором при чтении строки имени произойдет переполнение и будет выполнен shell-код, с помощью которого, например, может быть открыт бэкдор или выполнено любое другое действие. Ошибка была исправлена в версии ядра 2.6.37.2.

Примечательно, что с идеей подобного устройства еще за месяц до того выступил Джон Лаример из подразделения IBM X-Force. Однако его метод основывался на эксплуатации системы через функции автоматического запуска и индексации программ файловым менеджером десктоп-окружений, и он высказал сомнение, что то же самое можно сделать, используя ошибку в драйвере устройства.

Через DoS к root

7 декабря 2010 года Ден Розенберг, консультант по безопасности компании Virtual Security Research, опубликовал в мейл-листе Full Disclosure весьма интересный концепт эксплойта, который использует в своей основе не классические переполнение, срыв стека или ненадлежащую проверку прав доступа, а, как это ни странно, три различные и на вид совсем не страшные DoS-уязвимости в ядре.

В качестве базы для эксплойта использована уязвимость CVE–2010–4258, которая позволяет при генерации сбойного события, такого как OOPS в ядре, поместить в нужный участок ядра символ NULL. Уязвимости CVE–2010–3849 и CVE–2010–3850, в свою очередь, используются для создания события OOPS и последующей передачи управления по нужному адресу в ядре с помощью разыменования NULL-указателя, предварительно размещенного с помощью первой уязвимости.

Интересно так же, что, несмотря на актуальность первой уязвимости, к моменту публикации эксплойта две другие уже были давно закрыты. Однако, по словам автора, это не проблема, так как ошибки, приводящие ядро к состоянию OOPS, находятся постоянно. Тем более что эксплойт был успешно протестирован в Ubuntu 10.04 и 10.10.

Фрагмент кода эксплойта для получения root с помощью трех DoS-уязвимостей
Фрагмент кода эксплойта для получения root с помощью трех DoS-уязвимостей

Иногда они возвращаются

Один из самых курьезных случаев с обнаружением уязвимости в ядре произошел немного раньше — в октябре 2010 года, когда в 64-битных сборках Linux была обнаружена проблема с трансляцией 32-битных вызовов, которая могла привести к получению root. Проблема связана с некорректной работой с регистрами и сама по себе не столь интересна. Примечательнее то, что та же самая дыра уже была найдена аж три года назад.

Бен Хоукс, обнаруживший проблему в 2007 году, спустя почти три года заметил, что фикс, изначально наложенный на ядро для исправления уязвимости, на самом деле был позднее исправлен в связи с падением производительности. А новая версия фикса, как оказалось, легко обходится. Исправив изначальный эксплойт, Бен вновь добился его работоспособности на всех ядрах, выпущенных за последние три года, доказав таким образом уязвимость огромного количества машин.

Но история не была бы столь интересной, если бы не появившийся чуть позже в рассылке Full Disclosure эксплойт ABftw.c, предназначенный для проверки систем на уязвимость. Дело в том, что при запуске эксплойт позволял-таки получить root, но после этого внедрял в память ядра черный ход, через который злоумышленники могли без всяких проблем влезть на машину.

 

Нерасторопная NVIDIA

Уязвимость могут найти не только в самом ядре, но и, например, в проприетарных драйверах. Так, в августе 2012 года появилась информация о наличии в драйвере NVIDIA незакрытой уязвимости, которую можно легко использовать для получения root с помощью опубликованного эксплойта.

Интересно в этой ситуации то, что анонимный автор эксплойта сообщил NVIDIA о наличии дыры еще за месяц до его публикации, но, так и не дождавшись ответа, отдал эксплойт разработчику ядра из Intel. Сам принцип работы эксплойта также оказался не совсем тривиальным. Используя интерфейс /dev/nvidia0, предназначенный для изменения параметров VGA-окна (область, отведенная под видеопамять), эксплот начинает передвигать окно в памяти до тех пор, пока оно не окажется в районе памяти ядра, после чего перезаписывает этот участок shell-кодом.

Угроза Икс

Немногим ранее, в сентябре 2010 года, была обнаружена другая, не так тесно связанная именно с ядром Linux уязвимость. По сути, проблемными оказались как X-сервер, так и ядро. Принцип атаки состоял в следующем: находилось непривилегированное иксовое приложение, содержащее уязвимость (а таких реально много), и доводилось до состояния бесконечного потребления памяти сервера через расширение MIT-SHM. Далее приложение создавало сегмент S, который оказывался прямо над стеком сервера, а сам сервер получал инструкцию выполнения рекурсивной функции, что приводило к расширению стека и смещению его указателя в адресное пространство того самого сегмента S. Далее в сегмент происходила запись, в результате чего содержимое стека менялось и злоумышленник получал возможность исполнить произвольный код.

Интересно, что как таковой уязвимости X-сервера тут, по сути, не было, а проблему вызывало именно ядро, которое по каким-то причинам реагировало на столь дикие извращения с сегментами уже после того, как происходила атака. Другими словами, сначала злоумышленник получал root, а через мгновение уже падал X-сервер по исключению SIGSEGV, которое в нормальной ситуации должно происходить сразу. Проблему исправили в ядрах версий 2.6.32.19, 2.6.34.4, 2.6.35.2 (оказалось, разработчики SUSE Linux присылали точно такой же патч в список рассылки ядра аж в 2004 году, но его почему-то отвергли).

 

Linux, убивающий ноутбуки

Если мифическая уязвимость в PTRACE не кажется тебе чем-то абсурдным, то как насчет ошибки, которая в прямом смысле выводит из строя ноутбук? Если попробовать загрузить Linux с USB Flash на ноутбуках Samsung NP300E5C, NP700Z5C, NP700Z7C, NP530U3C или NP900X4C, то устройство превратится в кирпич, и спасение только одно — обратиться в сервисный центр.

Как оказалось, причиной ошибки стало поведение драйвера samsung-laptop, для которого вскоре был выпущен патч, вошедший в следующие версии ядра Linux (3.0.62, 3.4.29 и 3.7.6). И хотя позже сама компания Samsung заявила, что проблема не в драйвере, а в UEFI-прошивке, на других системах проблема все-таки не проявлялась.

Не только ядро

Серьезные уязвимости, угрожающие большому количеству систем, находят не только в ядре. Опасность заключается также в библиотеке GNU Libc, которая, как и ядро, присутствует в любом дистрибутиве. В разное время в ней было найдено достаточно большое количество проблем, а в октябре 2010 года так и вообще целая куча.

Сначала был найден способ убить любой сетевой FTP-сервер через дурацкую уязвимость в функции glob(), которая используется для сравнения списка файлов с шаблоном. Как оказалось, проверка на длину пути в функции осуществляется только для реально существующих файлов, но если передать функции строку, содержащую шаблон, адресующий заведомо несуществующие файлы (например, “/..//../строка”), то можно запросто исчерпать всю доступную процессу память и уронить его. Проблема затронула почти все FTP-серверы, работающие под управлением таких систем, как OpenBSD 4.7, NetBSD 5.0.2, FreeBSD 7.3 / 8.1, Oracle Sun Solaris 10 и Linux (проблема была найдена почти во всех реализациях библиотеки libc).

Через пару недель была доказана возможность эксплуатации известной ранее недоработки в коде библиотеки, которая до этого считалась просто неосуществимой. Библиотека glibc игнорировала требования спецификации ELF по запрещению использования текущего пути к исполняемому файлу ($ORIGIN) в процессе динамического связывания программ с идентификатором смены владельца или группы (SUID/SGID). Поэтому, используя хитрую последовательность действий, можно подсунуть SUID-приложению подставную библиотеку, в коде которой происходит открытие root-шелла. С другой стороны, воспользоваться уязвимостью можно лишь в системе, где непривилегированному пользователю доступен на запись каталог, расположенный на том же разделе, что и SUID-бинарник (например, когда /tmp и /sbin в одной файловой системе).

Данная уязвимость была выявлена только в системах RHEL, Fedora и CentOS, однако спустя несколько дней был предложен несколько измененный метод атаки, который срабатывал также в Debian/Ubuntu и других Linux-дистрибутивах, использующих glibc 2.4 или более позднюю версию. При этом OpenWall и ALT Linux устояли за счет использования повышающего безопасность патча sanitize-env.

Мудреный процесс получения root через дыру в glibc
Мудреный процесс получения root через дыру в glibc
 

Баг в системе udev

Одна из самых глупых уязвимостей была найдена не в ядре Linux, а в системе udev. Выяснилось, что до версии 1.4.1 udev не проверял отправителя сообщения, которым должно было быть ядро, и поэтому любой юзер мог сгенерировать любые системные события, связанные с подключением/отключением устройств. Забавно, что та же проблема обнаружилась и в Android, хотя udev там нет и в помине.

Код эксплойта с внедренным бэкдором действительно выглядит нарочно запутанным
Код эксплойта с внедренным бэкдором действительно выглядит нарочно запутанным

Так ли все плохо?

Как и в любой сложной системе, дыры в Linux находят постоянно, а различные DoS-уязвимости так и вообще открываются целыми пачками. Запредельно страшного в этом ничего нет, так как практически все уязвимости носят локальный характер, поэтому, если регулярно ставить обновки для ядра и сетевых сервисов, риск оказаться поломанным будет минимальным. А пользователям домашних систем, 90% которых вообще находятся за NAT’ом, так и волноваться даже нечего.

Оставить мнение

Check Also

Как Apple обходит стандарты, заставляя тебя платить. Колонка Олега Афонина

Иногда сложные вещи начинаются с простых: планшет iPad Pro 10.5 вдруг перестал заряжаться …