Введение в реверсинг с нуля, используя IDA PRO. Часть 66. Часть 1.

Дата публикации 19 апр 2019 | Редактировалось 17 май 2019
ТУТОРИАЛ ДЛЯ РЕШЕНИЯ ЗАДАНИЯ NICO ИЗ EKOPARTY 2018 - ЧАСТЬ 2.

Давайте сделаем скрипт на PYTHON для решения конкурсной задачи NICO, которую мы реверсировали в предыдущей части. Cкрипт основан на реверсинге, который мы делали, а также на решении, которое прислал мой дружбан LUCAS KOW, особенно это видно в части про ROP, и мы объясним, как это сделать.

1.png

Если я запускаю скрипт LUCAS, я вижу что останавливается счетчик de la fuga de capitales(сложно понять без запуска про что он говорит) и что запускается калькулятор. Поэтому давайте посмотрим на это всё.

Очевидно, что, во-первых, устанавливается соединение с сервером, который будет прослушивать порт 41414, как мы уже видели. В качестве IP-адреса я введу 127.0.0.1, так как запускаю его на той же машине. Если сервер находится на удаленной машине, придется ввести IP машины, на которой работает этот сервер.

2.png

Здесь импортируется сокет и устанавливается соединение с ним. Напомним, что первый пакет назывался HANDSHAKE. Вам нужно отправить слово HELLO, и если все в порядке, программа вернёт HI.

3.png

Давайте напомним, что это проверяется в функции, которую я переименовал в CHECK_HANDSHAKE после первого RECV. Мы установим BP на возврате из функции RECV.

Сейчас, чтобы это было более удобнее я изменяю отладчик на WINDBG, который будет использоваться как локальный отладчик внутри IDA.

4.png

5.png

Конечно, нужно удалить в PROCESS OPTIONS любую CONNECTION STRING которая будет.

6.png

Я помещаю BP и нажимаю на START.

Отладчик останавливается.

7.png

Кто хочет увидеть адрес в WINDBG, нужно выполнить эту команду.

8.png

WINDBG показывает адрес как IMAGEBASE + RVA.

RVA
это расстояние от IMAGEBASE.

В моём случае RVA = 0x1C59.

В IDA нет возможности показывать адреса как базу плюс RVA, но есть возможность показать как смещение от функции, которой оно принадлежит.

9.png

Если мы поставим эту галочку произойдут изменения.

10.png

Таким образом, если мы назовем эту функцию как MAIN, название будет совпадать со всеми именами, так как все здесь будут в MAIN + 189.

11.png

И это всё приведет нас на правильный адрес, конечно RIP будет продолжать показывать числовое значение.

Хотя сбоку будет показываться уточнение, которое равно MAIN+189.

12.png

Я думаю, что таким образом будет легче следовать по пунктам, которые я Вам показываю, потому что, если мы поставим одинаковые имена, они будут совпадать.

13.png

Если я посмотрю сейчас, и увижу что отладчик остановился, я вижу, что есть в BUF, наведя указатель мыши на строку HELLO.

Если я пойду сюда и нажму на клавишу A у меня появится строка ASCII.

14.png

В CHECK_HANDSHAKE+27, убедитесь, что вы переименовали функцию в то же имя, и вы можете перейти туда.

15.png

Она находится в STRNCMP. Если всё хорошо, функция вернет нуль, если обе строки одинаковые.

16.png

Поскольку строки одинаковые, в FLAG_OK помещается 1.

17.png

И затем программа пойдет на HANDSHAKE отвечая HI. И с этим мы понимаем, что у нас все в-порядке.

18.png

В скрипте я печатаю в ответе HI.

19.png

Затем я останавливаю сервер и обращаю внимание, что программа переходит на выполнение этой функции. Я переименовал её в RECV_AND_PARSE.

20.png

Если вы переименуете её также сможете добавить смещение через начало функции.

21.png

В RECV_AND_PARSE+46 мы вернемся на RECV, хотя мы знаем, что он был 16 байтов, из 4 DWORDS. Мы бы создали структуру и уже бы примерно знали сколько равна каждая.

22.png

23.png

Здесь мы видим четыре значения. Первое - это длина 3-го RECV, который будет вызываться позже. В этом случае он пропускает 0x200 байт, потому что, как мы видели, мы могли бы переполниться здесь, но передав больше данных, это приведет к сбою программы при перезаписи COOKIE.

24.png

Давайте вспомним, что буфер, в который вы записали 3RECV, был 0x200 т.е. 512 байт, поэтому, если мы передадим больше, программа сломается, чуть ниже есть COOKIE, поэтому значение будет 0x200.

Следующим значением будет код операции, который мы будем выполнять. Мы увидели, что 0x22222222 позволит нам лучше читать не только адрес возврата, но и COOKIE безопасности, что очень важно, поэтому второй DWORD равен 0x22222222.

25.png

Третьим DWORD был уровень (меньше или равный 0x200). Напомним, что он должен быть настолько большим, насколько это возможно, потому что в STRNCAT он использует его в как COUNT копируемой величины, и поскольку 0x200 - максимум. Мы будем использовать это значение.

26.png

Последнее это отрицательное смещение. Мы помним, что оно не может быть каким-либо значением, потому что оно должно быть добавлено к P_BUFFER_512_TERCER_RECV, и результирующий адрес будет там, где программа пишет OK в STRCPY.

27.png

Расстояние до CONST_0 это 0x48 поэтому мы перейдем как к смещению 0x48.

28.png

29.png

Что равно 0x48.

После этого мы напишем ОК в CONST_0, которая, как мы знали, является тем же аргументом функции OP_0x22222222.

Это позволит нам в первом повторе, когда программа пройдет через все обратные адреса, достичь функции STRNCAT, которая все сделает.

30.png
Здесь находится то, что мы будем засылать на данный момент.

31.png

Я поставлю BP на STRCPY.

32.png

Я запустил скрипт и отправил программе пакет, которую мы сделали.

Как только скрипт достигнет уровня 0, программа перейдет к STRCPY как мы видели, и остановится там. Затем программа выйдет из всех повторений, увеличивая уровень каждый раз, когда будет выход, пока не дойдет до первого повтора, равного уровню 0x200. Здесь мы видим, что мы перезаписываем CONST_0 отцовскую функцию и переходим на STRNCAT.

33.png

Программа скопирует 0x200 из SOURCE, что представляет собой буквы A, которые я отправил на 3-й RECV, и добавил его после OK, который является DESTINATION.

34.png

SOURCE

35.png

DESTINATION

36.png

Хорошо. С этим мы уже знаем, что мы перезапишем размер SEND. Давайте продолжим трассировать до нужного места.

37.png

Я вижу, что размер того, что программа собирается отправить переписан моими символами A. Проблема в том, что программа будет зависать, так как она не может читать так много и будет ломаться при чтении. Проблема в том, что нужно увидеть, какое значение передать, чтобы прочитать COOKIE, обратный адрес и ничего не поломать.

Я останавливаю программу.

Напомним, что мне начинает отправляться с начала BUFFER_512_TERCER_RECV сумма, которую я хочу.

38.png

Возможное количество будет 544 байта от начала буфера, так как программа отправит мне COOKIE и адрес возврата. Так же, может быть передано более большее значение, но главное чтобы не сломает стек. То, что передает LUCAS, это значение 0x2FF. Так как мы собираемся использовать его ROP давайте оценим это значение.

39.png

Как мы можем узнать какая из всех этих букв A это та которая перезаписывает размер?

Это нужная функция

import random, string

def randomword(length):
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(length))

Я её добавляю.

40.png

Чтобы прочитать размер SEND программа говорит какие символы попадают прямо здесь.

41.png

В моём случае это LOJW. Я ищу их в строке который распечатал скрипт.

42.png

Я копирую строку и обращаю внимание на её длину.

len("nvcrphoxkyxmbvxefyyfxuhexaldxq")

Она равна 30.

43.png

Поэтому я помещу 30 букв A, затем размер, который я хочу отправить. Я буду использовать тот же, который использовал LUCAS 0x2FF, а затем дополню 0x200 байтов пакета большим количеством букв A.

Давайте ещё раз запустим скрипт.

44.png

Мы видим, что размер подходит. Давайте дальше посмотрим, что мне вернется через SEND.

Мы видим, что после букв A добавляются символы.

45.png

Напомним, что буфер был из 0x200 байт (512 десятичных) поэтому сразу после этого идет COOKIE.

print "%r"%data[512:512+8]

Этим мы распечатаем значение COOKIE.

Я запускаю скрипт снова, и когда он останавливается на SEND, я обращаю внимание на значение COOKIE.

46.png

При этом запуске в этом снимке в моем случае это то, что мы увидим если я нажму RUN. Если я распечатаю в скрипте те же COOKIE.

47.png

Это и есть те самые COOKIE.

Если я добавлю к коду import binascii

И использую это
data = s.recv(1024)
cookie=data[512:512+8]
print "%r"%cookie
print binascii.hexlify(cookie)

И запускаю скрипт заново.

COOKIE покажутся в новом виде.

48.png

49.png

Я вижу, что совпадают значение. Если хочу распечатать с помощью \X.

Это никак не влияет, это необязательно, но я делю строку на два символа, а затем с помощью JOIN добавляю \X посередине и впереди.

50.png

И заново запускаю скрипт, посмотреть что получилось.

51.png

52.png

Уже лучше видно и у нас есть COOKIE.

Следующее значение которое нужно получить это адрес возврата.

53.png

Он находится между 536 и 536+8.
Для того, чтобы это всё выглядело красиво я определил функцию MY_PRINT, чтобы распечатать на свой вкус наши значения.

54.png

Когда программа достигает адреса возврата, я обращаю внимания куда она вернется. ESP будет указывать туда.

55.png

56.png

У нас уже есть два наиболее важных значения: COOKIE и место куда бы я хотел вернуться.

Очевидно, что при выходе из функции RECV_AND_PARSE программа бы вернулась туда, поэтому мы объединили этот адрес исполняемого файла.

57.png

Если я трассирую с помощью F7 отсюда.

58.png

0 2.821
yashechka

yashechka
Ростовский фанат Нарвахи

Регистрация:
2 янв 2012
Публикаций:
90