Археологические раскопки ядра vista-longhorn

Атаки на переполняющиеся буфера


Microsoft продолжила наступление на переполняющееся буфера, начатое еще в w2k, вводя в действие все новые и новые защитные механизмы, существенно (якобы) затрудняющие реализацию удаленных атак. "Новые", естественно, только для Windows. В остальных операционных системах они были реализованы задолго до появления висты и в гораздо более полным объеме.

С большим опозданием в висте появилась поддержка рандомизации адресного пространства Address Space Layout Randomization — ASLR. По умолчанию все системные библиотеки теперь загружаются по одному из 256 возможных адресов, а в заголовке исполняемых файлов появился специальный бит, указывающий должен ли он загружаться по случайному адресу или нет.

Подробности о реализации ASLR от Microsoft можно почерпнуть из blog'а Michael'я Howard'а: http://blogs.msdn.com/michael_howard/archive/2006/05/26/608315.aspx. Там даже приводится конкретный пример расположения системных библиотек его ноутбука до и после перезагрузки:

wsock32.dll  (0x73ad0000)

winhttp.dll  (0x74020000)

user32.dll   (0x779b0000)

kernel32.dll (0x77c10000)

gdi32.dll    (0x77a50000)

Листинг 1 базовые адреса некоторых системных библиотек в висте

wsock32.dll  (0x73200000)

winhttp.dll  (0x73760000)

user32.dll   (0x770f0000)

kernel32.dll (0x77350000)



gdi32.dll    (0x77190000)

Листинг 2 базовые адреса тех же самых библиотек после перезагрузки системы

Насколько этот трюк усложняет атаку? И каким боком системные библиотеки относятся к переполняющимся буферам? Все просто — первым действием атакующего обычно становится подмена адреса возврата из функции на адрес машинной команды jmp esp (FFh E4h), находящейся в доступной оперативной памяти. На машинах с задействованным аппаратным DEP'ом (блокирующим исполнение кода в стеке) приходится предварительно вызывать API-функции, присваивающие данному региону статус исполняемого или выделяющие блок памяти с нужными атрибутами и копирующие туда shell-код.
Такие атаки получили называние return-to-libc, поскольку впервые были реализованы на UNIX-системах, где основным "поставщиком" API-функций становится библиотека libc.lib, ну а в Windows хакеры используют прямой вызов KERNEL32.DLL, которая теперь загружается по случайному адресу и атакующий имеет 1/256 шанс на удачу, в противном случае, произойдет исключение и работа уязвимого приложения будет завершена в аварийном режиме, что не есть хорошо.

Да… не слишком-то надежная защита. К тому же 1/256 — это _гигантская_ цифра. Если в сети находится 1.000 уязвимых машин, то в первой же итерации атаки заражаются ~4 из них. Фактически, "тасовка" системных библиотек лишь увеличивает количество попыток, которые необходимо предпринять атакующему, но не препятствует самой атаке в принципе. К тому же, если уязвимый исполняемый файл (или принадлежащие ему динамические библиотеки) не используют флаг рандомизации, то вместо прямых вызов KERNEL32.DLL атакующий может использовать таблицу импорта или RTL файла-жертвы. Теоретически, взвести бит рандомизации можно и в hiew'е, но практически все старое программное обеспечение останется уязвимым и продолжит им оставаться до тех пор, пока не появятся линкеры, поддерживающие данную фичу и разработчики не пересоберут ими свои проекты, что случится не скоро, к тому же, загрузка исполняемого файла по случайному адресу требует наличия fixup'ов (так же называемых перемещаемыми элементами), что увеличивает размер exe-файла и замедляет его загрузку. Беглый опрос нескольких знакомых разработчиков показал, что никто из них не собирается задействовать рандомизацию ни сейчас, ни даже когда она будет поддерживается VC и другими компиляторами.

К тому же, рандомизация не затрагивает стек, и лишь частично воздействует на кучу, оставляя атакующему достаточно предсказуемой информации для успешной реализации атаки, неизбежность которой очевидна всем, кроме парней из Microsoft. В частности, пакет PaX, созданный для UNIX и успешно перенесенный на NT (где он известен под именем Wehnus, см. — http://www.wehnus.com) рандомизует все, что только можно рандомизовать, успешно работая от w2k до Server 2003 с минимальными издержками производительности.


Подробнее о "настоящей" рандомизации можно прочитать на en.wikipedia.org/wiki/Address_Space_Layout_Randomization.

Другим, не менее "выдающимся" шагом вперед стало помещение списка обработчиков структурных исключений (SEH) в специальную секцию PE-файла (.pdata), доступную только на чтение. До этого список обработчиков располагался в стеке и был свободно доступен для модификации. На самом деле, это (достаточно широко разрекламированное решение) не имеет никакого отношения к операционной системе и обуславливается исключительно одной лишь политикой компилятора. _Никто_ не запрещает вытворять подобные трюки даже на 9x, не говоря уже про w2k или XP. Вот только подходящих компиляторов пока что нет. Ах да, маленькая деталь. Даже если приложение не устанавливает никаких своих обработчиков, то за обработку исключений отвечает обработчик, назначаемый операционной системой, и вплоть до Server 2003 SP1 размещающийся в стеке. Теперь же, с приходом висты к власти, его там нет.

Препятствует ли это удаленным атакам? Хм. Навряд ли. Обработчики структурных исключений очень широко распространены в приплюснутом си, где они устанавливаются компилятором неявно, не говоря уже о специальных средствах вроде секций try/except. До тех пор, пока разработчики не перекомпилируют все свои приложения новыми версиями компиляторов (поддерживающих эту фишку), в стеке по прежнему будет болтаться куча SEH'ов, спасающих хакеров от голодной смерти. Но даже после перекомпиляции, в секцию .pdata попадут лишь статические обработчики (адрес которых известен еще не стадии трансляции), а с учетом активного внедрения парадигм метапрограммирования, таковых окажется не так уж и много. Конечно, если засунуть указатели на динамически назначаемые обработчики в секцию .data (или локальную память потока), это существенно усложнит хакерам жизнь, но… опять-таки все упирается в вопрос: "когда появятся соответствующие компиляторы".

Аналогичным образом обстоят дела и с защитой кучи.


Шифрование блока метаданных по XOR со случайно сгенерированным ключом, проверка целостности заголовков блоков, pseudo-рандомизация кучи (деликатно именуемая random heap rebasing) — все это, конечно, позволяет обнаружить разрушение кучи перевод выполнением функции VirtualFree, действующей в таких условиях как оператор Бейсика POKE, т. е. записывающей ячейку памяти размером в двойное слово (на 64-битных редакция — четвертное) по произвольному адресу, но… весь фокус в том, что прямые вызовы API-функций, работающих с кучей, используются довольно редко и приложения предпочитают полагаться на RTL компилятора, реализующую свою собственную кучу, остающуюся незащищенной.

Комментарии, как говорится, излишне. Microsoft утверждает, что предпринятые меры убивает не только ранее написанные exploit'ы, но и препятствуют появлению новых. Однако, на самом деле, даже старые exploit'ы в большинстве своем остаются в строю. Некоторые лоси уже опубликовали внушительный список exploit'ов, неработающих на висте. Ну и что с того? На w2k/XP со всеми установленными заплатками они (не)работают с тем же успехом. Если exploit проникал через дыру в Горящем Лисе или любом другом приложении, пользователь которого не удосужился скачать последнюю версию, то с большой степенью вероятности, exploit продолжит поражать Лиса и на висте.


Содержание раздела