Обзор эксплойтовПовышение привилегий в Microsoft WindowsTargets
BriefИнтересная уязвимость была опубликована 24 ноября на ресурсе The Code Project под видом статьи. Буквально через несколько часов статья была удалена, но информация уже распространялась по сети с огромной скоростью. Уязвимость представляет собой классическое переполнение буфера, однако есть несколько интересных нюансов. Для эксплуатации бага надо выполнить WinAPI-функцию EnableEUDC, что приведет к выполнению системного вызова NtGdiEnableEUDC. NtGdiEnableEUDC служит для включения или выключения шрифтов интерфейса, определенных пользователем, и работает следующим образом: функция считывает путь к файлу пользовательского шрифта из параметра SystemDefaultEUDCFont, ключа реестра HKEY_CURRENT_USER\EUDC\<Current_code_page>. Для получения значения из реестра используется функция RtlQueryRegistryValues:
В качестве одного из входных параметров функция получает указатель на структуру RTL_QUERY_REGISTRY_TABLE:
А теперь посмотрим, с какими параметрами вызывается данная функция в коде win32k.sys:
Следует обратить внимание на значение Flags: оно равно 0x24, что соответствует RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_ REGISTRY_DIRECT. Взведенный флаг RTL_QUERY_REGISTRY_DIRECT указывает на то, что при копировании функция QueryRoutine будет проигнорирована, а данные в EntryContext будут обрабатываться как нетипизированный буфер. В этом и заключается вся суть уязвимости! Атакующий может создать ключ в реестре: к примеру, HKEY_CURRENT_USER\EUDC\CP-1251 со значением SystemDefaultEUDCF ont любого типа (допустим, REG_BINARY) и большой длиной. Далее в стековый буфер будут без проверок скопированы данные, что приведет к переполнению.
Примечательно также, что проэксплуатировать данную уязвимость можно на Windows 2000, Vista, 2008 и 7. На XP и 2003-й максимум, чего можно добиться, это появление синего экрана смерти. SolutionОфициальное исправление для уязвимости на данный момент отсутствует, однако можно предотвратить возможность ее эксплуатации из-под ограниченной учетной записи, выполнив следующие шаги:
Повышение привилегий в ядре LinuxTargets
BriefОчень интересный эксплоит по повышению привилегий, задействующий сразу 3 различных уязвимости в ядре, опубликовал Дэн Розенберг. Это отличный пример того, когда неэксплуатирумые на первый взгляд уязвимости в связке дают возможность выполнить код. 1. Первая уязвимость (CVE-2010-4258), на мой взгляд, самая интересная. Она связана с системным вызовом clone(2). Если поток создается с помощью clone(2) с взведенным флагом CLONE_CHILD_CLEARTID, то при завершении потока два нулевых байта могут быть записаны по любому адресу в пользовательском адресном пространстве. Запись двух байтов осуществляется функцией put_user(), которая проверяет адрес на принадлежность пользовательскому API путем стандартной функции access_ok() — аналогом в ядре Windows служат ProbeForRead/ProbeForWrite. Нельсон Элхадж (автор данной уязвимости) обнаружил, что если ядро выполняет перестановку лимитов адресного пространства путем set_fs(KERNLE_DS), и поток вызовет OOPS (с помощью какоголибо исключения, например, разыменования нулевого указателя), то проверка access_ok() не выполнится. Это даст возможность переписать два байта по любому адресу уже в пространстве ядра, что ведет к достаточно простому пути повышения привилегий. Тем не менее, access_ok() имеет нюанс. Чтобы понять его, разберем подробнее внутренние функции ядра линукса: get_fs() и set_fs(). Иногда ядро изменяет правила, для каких адресов access_ok() даст разрешение. Этот функционал выполняется функцией set_fs(), которая меняет диапазоны для пользовательского и ядерного адресного пространства. После того, как будет выполнено set_fs(KERNEL_DS), access_ok() не выполнит никаких проверок на принадлежность пользовательскому адресному пространству. set_fs(KERNEL_DS) используется в основном тогда, когда функция принимает указатель на пользовательское адресное пространство, а нужно передать указатель на память ядра. Типичное использование представлено ниже:
2. Вторая уязвимость (CVE-2010-3849) — это простейшее разыменование нулевого указателя в обработке Econet-протокола, которая ведет лишь к панике ядра. 3. Третья уязвимость (CVE-2010-3850) — неправильная настройка прав на сетевые интерфейсы, с помощью которой можно присвоить Econet-адрес на любой сетевой интерфейс. Также следует рассмотреть некоторые аспекты работы ядра linux, чтобы стало понятно, как три уязвимости работают в связке. Механизм Kernel OOPS’ов: Когда в ядре происходит OOPS (какоелибо исключение) — к примеру, из-за вызова макроса BUG(), который используется разработчиками для контроля неудачи assert’a, ядро пытается очистить ресурсы, а также убить текущий процесс с помощью функции do_exit(). В этот момент у процесса тот же контекст, что и до выполнения OOPS'a, включая set_fs()-перезапись диапазонов адресных пространств. Из этого следует, что при выполнении access_ok() в коде do_exit() никаких проверок выполнено не будет! Опция CLONE_CHILD_CLEARTID в параметре flags функции clone() означает, что при выходе потока ядро запишет два нулевых байта в специальный адрес в адресном пространстве потока, для того чтобы уведомить остальные потоки о завершении текущего. Этот функционал реализован одной строчкой и обнуляет адрес в специальной структуре task_struct (которая описывает поток/процесс):
Обычно это не вызывает никаких исключений, однако если данный код исполняется с условием get_fs() == KERNEL_DS, то никаких проверок не будет выполнено, и мы можем подставить любой адрес ядра. Так как же заставить определенный код работать при условии get_fs() == KERNEL_DS? Дэн Розенберг нашел лазейку, используя функцию splice(). Системный вызов splice() не так давно появился в ядре, он используется для перемещения данных между пайпами и файловыми дескрипторами. Создаем Econet-сокет и далее вызываем splice() с параметром этого сокета, что приведет к вызову econet_sendsmg с set_fs(KERNEL_DS). Теперь разберем эксплойт по кирпичикам: Создание сокетов для использования в splice()
Сбор адресов ядерных функций и данных (credentials или прав процесса)
Создание потока
Функция, которая вызовет разыменование нулевого указателя в econet_sendmsg
SolutionОбновляем ядро до последнего Release Candidate'а. Удаленное исполнение кода в EximTargets
Небезызвестный Kingcope снова порадовал общественность serverside эксплоитом на популярный Open Source продукт — почтовый сервер Exim. Хотя эксплоит работает на достаточно старой версии Exim’a, релиз которой был популярен еще в далеком 2006 году (exim.org/lurker/message/20060731.142652.97e79ab1.en.html и seclists.org/fulldisclosure/2010/Dec/233), используемые техники актуальны и по сей день. BriefПосле проведения diff-анализа файла expand.c становится ясно, что это типичный integer overflow. Важно отметить, что Payload в эксплойте делает backconnect shell’а c правами root.
Его выполнение производится на скомпрометированной системе путем нехитрого манипулирования с файлами:
Забавно, но в эксплоите присутствует что-то наподобие help’a — наверное, чтобы никто не заблудился :).
Перейдем к формированию данных, которые приведут к выполнению шелл-кода. При манипуляциях с переменной сразу видно, что тут пахнет integer overflow:
Что и приводит к переполнению и эксплуатации shellcode. SolutionДля решения этой баги в exim 4.64 (lists.exim.org/lurker/message/20061220.105401.340f1c13.en.html) был добавлен индикатор на integer overflow. Обход ограничений безопасности в ядре LinuxTargets
BriefИ снова легендарный Тавис Орманди обнаружил замечательную уязвимость — обход защиты выделения памяти ниже значения, установленного в mmap_min_addr. С помощью этого становится реальностью эксплуатация уязвимостей класса «Разыменование нулевого указателя». Дело в том, что функция install_special_ mapping (используемая для установки vdso) не выполняет проверки безопасности до вызова insert_vm_struct, тем самым атакующий может обойти механизм mmap_min_addr с помощью лимитирования страниц для определенных проецирований.
К сожалению, уязвимость позволяет проецировать память лишь на 4096 байт за пределами значения mmap_min_addr. Однако стоит отметить, что на Linux Red Hat значение mmap_min_addr по умолчанию выставлено в 4096, соответственно, мы можем проецировать память по нулевому указателю! SolutionПрименить нехитрый патч:
|