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


 
english
обо мне
 
sysoev.ru
 
nginx
 
mod_accel
mod_realip
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) Игорь Сысоев
http://sysoev.ru