Анализ message queuing exploit'а
Одного лишь знания ассемблера и API-операционной системы для исследования shell-кода будет явно недостаточно. Здесь все сложнее и… интереснее. Продемонстрируем технику дизассемблирования shell-кода (со всеми сопутствующими ей приемами и трюками) на примере анализа "Message Queuing Buffer Overflow Vulnerability Universal" exploit'а (http://milw0rm.com/exploits/1075), прилагаемого к статье в файле 1075. Базовый код, написанный на языке Си, рассматривать не будем, а сразу перейдем к shell'у.
Самое сложное — это определить точку входа в shell-код, то есть ту точку, которой будет передано управление при переполнении. В данном случае нам повезло и создатель exploit'а структурировал листинг, разбив двоичные данные на шесть массивов, первые четыре из которых (dce_rpc_header1, tag_private, dce_rpc_header2 и dce_rpc_header3) представляют собой заголовки RPC-пакетов, в которых для нас нет ничего интересного.
А вот массив offsets включает в себя ключевые структуры данных, передающие управление на shell-код. Способ передачи основан на подмене SEH-фреймов по усовершенствованной методике, обходящей защиту от переполнения, появившуюся в Windows 2000, Windows XP и Server 2003. И хотя это не отражено в комментариях явным образом (создатели shell-кодов традиционно неразговорчивы), опытные кодокопатели распознают подложные фреймы с первого взгляда (тем более, что кое-какие комментарии там все-таки присутствуют, но даже если бы их и не было, это не сильно бы затруднило анализ).
Основная часть shell-кода (расположенная в массиве bind_shellcode) системно независима (ну, почти), а все фиксированные адреса вынесены в массив offset, который при тестировании под различными версиями операционных систем имеет наглость требовать "ручной" коррекции. Даже при наличии не залатанной дыры exploit может не работать только потому, что по фиксированным адресам расположено не то, что ожидалось. Но, прежде, чем приступать к анализу массива offsets, необходимо определить его местоположение в пакете-убийце, вызывающим переполнение. Приведенный ниже фрагмент базового кода собирает все массивы в непрерывный буфер, передаваемый на атакуемый сервер:
// выделяем память
buff = (char *) malloc(4172); memset(buff, NOP, 4172); ptr = buff;
// RPC-заголовки
memcpy(ptr,dce_rpc_header1,sizeof(dce_rpc_header1)-1);ptr+=sizeof(dce_rpc_header1)-1;
memcpy(ptr, tag_private, sizeof(tag_private)-1); ptr+=sizeof(tag_private)-1;
memcpy(buff+1048, dce_rpc_header2, sizeof(dce_rpc_header2)-1);
memcpy(buff+1048*2, dce_rpc_header2, sizeof(dce_rpc_header2)-1);
memcpy(buff+1048*3, dce_rpc_header3, sizeof(dce_rpc_header3)-1);
// offsets
ptr=buff;ptr+=438;
memcpy(ptr, offsets, sizeof(offsets)-1);ptr += sizeof(offsets)-1;
// shellcode
memcpy(ptr, bind_shellcode, sizeof(bind_shellcode)-1);