Персональный
сайт
Игоря
Сысоева



sysoev.ru
mod_accel
mod_deflate
программирование
windows
freebsd
apache
pppd
unix
web

для писем

Немного об chunked encoding exploit

 

30.06.2002

Exploit, надо сказать, красивый. Работает он следующим образом.

ap_discard_request_body() выделяет на стеке буфер, размером в 8K. Допустим, он располагается по адресу 0xBFBFDD00. Адрес и размер буфера передаётся функции ap_get_client_block(). Она читает размер чанка, предположим, 0xFFFFFF02. Затем размер чанка сравнивается с размером буфера, но так сравнение выполняется со знаком, то размер чанка становится отрицательным числом -254 и оказывается меньше размера буфера, равного 8192. Поэтому в функцию ap_bread() передаётся 0xFFFFFF02. ap_bread() вызывает memcpy(), чтобы скопировать часть уже прочитанных данных в переданный буфер. Адрес буфера уже 0xBFBFDD0A, поскольку 10 байт заняты размером чанка. И опять -254 сравнивается с числом уже прочитанных байт. И опять -254 оказывается меньше и memcpy() передаётся 0xFFFFFF02, а не число прочитанных байт.

Казалось бы, подобный размер блока должен в дальнейшем вызвать ошибку защиты, поскольку скопировать в 32-битном адресном пространстве такой объём невозможно, но при удачном стечении параметров ошибки не происходит. Всё дело в реализации memcpy(). Для того, что бы копирование выполнялось быстрее, memcpy() копирует по 4 байта за раз. Однако размеры блоков могут быть некратны четырём, поэтому сначала копируется небольшой кусок из 1, 2 или 3 байт. Важно отметить, что после копирования этих байт функция memcpy() опять загружает длину блока в один из регистров со стека. Итак, прочитанные данные хранятся в одном из буферов Apache, предположим, они начинаются с адреса 0x80A00102. Функция memcpy() определяет, что 0xBFBFDD0A - 0x80A00102 меньше, чем 0xFFFFFF02, поэтому для того, что бы данные не перекрылись, копирование начинается с конца, то есть, из 0x80A0003 (0x80A00102+0xFFFFFF02-1) в 0xBFBFDC0B (0xBFBFDD0A+0xFFFFFF02-1). Но так как длина блока некратна четырём, то сначала копируются 2 байта из 0x80A0002 в 0xBFBFDC0A. Теперь представьте себе ситуацию, что на стеке по адресу 0xBFBFDC08 хранится длина нашего блока – 0xFFFFFF02, которую мы передали функции memcpy(). Если по адресу 0xBFBFDC08 скопировать два нулевых байта, то это число превратится в 0x0000FF02, то есть, во вполне безобидное 65282. memcpy() повторно загружает длину и копирует 65280 байт, изменяя при этом адрес возврата, который находится чуть ниже нашей длины. Вернуться теперь можно или на перезаписанный стек, или же в буфер Apache, из которого всё копировалось – в обоих местах лежит наш код.

Заметьте, что первоначальный буфер, переданный из функции ap_discard_request_body(), вообще не используется, участвует только его адрес. Основная задача exploit'а – подобрать размер чанка таким образом, чтобы он равнялся разнице между адресом буфера, переданным из функции ap_discard_request_body() и адресом, по которому размер чанка будет передан функции memcpy().

Кстати, недавно во FreeBSD 4.6-STABLE была изменена функция memcpy(). Теперь она не читает повторно со стека длину копируемого блока.

На днях появился Apache worm, подобный RedCode worm для IIS. Червяк создан на основе этого expolit'а, однако он вряд ли получит широкое распространение в России, так как на большинстве серверов стоит Russian Apache, который, к счастью, в большинстве случаев не приемлет chunked encoding.

(C) Igor Sysoev
http://sysoev.ru