1. Если вы только начинаете программировать на ассемблере и не знаете с чего начать, тогда попробуйте среду разработки ASM Visual IDE
    (c) на правах рекламы
    Скрыть объявление

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

Дата публикации 6 янв 2019 | Редактировалось 17 май 2019
Реверсинг задачи по SCADA

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

CVE-2013-0657

https://ics-cert.us-cert.gov/advisories/ICSA-13-018-01

Здесь есть ссылка. Все версии и 9 и 10 являются уязвимыми, за исключением тех версий, к которым был применен патч.

1.png

Уязвимую версию 9 можно загрузить отсюда:

HTTPS://DRIVE.GOOGLE.COM/OPEN?ID=1V4DAVXWD6FYSMVK4UKRG-SHCJNXI55XN

А патч отсюда:

HTTP://IGSS.SCHNEIDER-ELECTRIC.COM/IGSS/IGSSUPDATES/V90/PROGUPDATESV90.ZIP

Это исполняемый файл, который прослушивает порт TCP 12397. Если мы установим программу, мы увидим, какой исполняемый файл прослушивает этот порт.

Когда я устанавливаю программу, я сначала выбираю опцию DEMO, а затем FREE FOR 50. С этими настройками я могу запустить программу нормально. И когда я нажимаю на кнопку START, программа начинает работать. Если она не запускается, то можно удалить и создать новый проект с меньшим количеством элементов, так как программа даёт до 50 бесплатных элементов. Но здесь я её запускаю.

2.png

Мы видим, что процесс на моей машине имеет PID равный 3116.

3.png

Для того, чтобы все хорошо работало нужно перейти на DESIGN AND SETUP, удалить старый проект и создать новый с 50 элементами или меньше и запустить программу. С этими настройками будет то, что нужно.

4.png

Мы уже примерно понимаем, что происходит. Давайте сделаем DIFF между двумя DC.EXE. Оригинальный в моем случае это версия:

5.png

И та версия, к которой применен патч:

6.png

При терпеливом осмотре видно, что в DC.EXE есть много измененных функций. Я не собираюсь анализировать их одну за другой, но здесь есть переполнение стека. Стрелка указывает на патч.

7.png

Я ищу среди импортируемых функций RECV. Мы видим, что она находится здесь, и видно, что она вызывается из одного места.

8.png

9.png

Я ищу 32-х битный удаленный сервер IDA и копирую его на машину, на которую я установил программу, и запускаю его как администратор.

10.png

В IDA я меняю отладчик на REMOTE WINDOWS DEBUGGER.

11.png

И настраиваю опции процесса (PROCESS OPTIONS) указывая IP адрес машины и её порт.

12.png

13.png

Когда мы идем в ATTACH PROCESS, мы получаем список процессов на целевой машине если соединение между ними возможно (нет брандмауэров или блокировок и т.д.).

14.png

Здесь находится DC.EXE. Мы выбираем его и присоединяемся к нему.
Чтобы проверить, что это действительно функция RECV, которую мы нашли, я поставил на эту функцию BP.

15.png

Я создаю скрипт PYTHON, который отправляет данные на порт 12397, который прослушивает DC.EXE. Я отправляю в порт вручную 300 букв A просто для проверки, проверить произойдет ли остановка.

16.png

Мы видим, что отладчик останавливается.

17.png

Мы видим, что в первый раз, отладчик останавливается. Программа получает только восемь байтов, поэтому очевидно, что будет ещё остановка на функции.

Если я нажимаю RUN без реверсинга много раз, я вижу, что пакет доходит до уязвимой функции.

18.png

Возможно только с этим программа не будет проходить по уязвимому пути. Программа постоянно останавливается на уязвимой функции. Это место нужно хорошо проанализировать.

У нас нет символом. У нас нет ничего. Посмотрим куда мы сможем с этим добраться.

19.png

Мы видим, что есть указатель на структуру в этой переменной, которая называется P_STRUCT.
Каждый раз, когда значение читается, поле в структуре всегда доступно путем добавления смещения к этому адресу.

Мы видим, что программа имеет большое смещение. Мы видим значения 0x64, 0x3A4, 0x37C. Поэтому мы создадим структуру длиной 0x500 байт. Если нам понадобится размер больше, то мы увеличим ее. Если она меньше, то ничего страшного.

Я создаю структуру с одним DWORD и расширяю её до 0x500.

20.png

Это выглядит так. Поскольку уже было 4 байта плюс 0x500 в общей сложности мы получим 0x504 байт.

21.png

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

22.png

Мы видим, что VAR_4 это флаг, который может быть 0 или 1. Есть место, где в это значение помещается значение 1.

23.png

И в зависимости от 0 или 1 прежде чем вызвать RECV программа идет на правый блок или на левый.

24.png

Мы видим, что программа сравнивает значение с 0. Если значение равно нулю, программа изменяет его на 1, и в следующий раз программа проходит по пути 1. Поэтому мы предполагаем, что начальное состояние флага равно 0, и программа проходит через блок и устанавливается флаг в 1.

Таким образом, при первом пакете программа пройдет здесь. Перед RECV. Она поместит в регистр ECX значение 8, что равно длине. Перед этим вычитается значение поля 0x3A4 из структуры, которое в начале равно 0, так как мы уже увидели, что длина первого пакета должна быть 8.

25.png

26.png

Если я поищу константу 0x3A4, я вижу, что здесь сохраняется сумма байтов, поэтому я переименую её.

Я иду по структурам до смещения 0x3A4 той структуры которую я создал и нажимаю D до тех пор пока не появится DWORD и переименовываю поле.

27.png

Я могу искать смещение.

28.png

И затем с помощью CTRL+I я ищу все вхождения этого смещения и переименовываю в то же самое имя.

29.png

Я могу начать заново с начала функции.

30.png

Теперь выглядит лучше. По крайней мере, в той области функции, над которой я работаю.

31.png

Также мы видим, что во второй раз, когда программа пройдет через первый блок предыдущего изображения, уже прочитано 8 байт и программа пойдет по правому пути.

Мы видим также, что в поле 0x64 есть указатель на вторую структуру, которая имеет буфер внутри со смещением 0x7008 и который отчитывается от EDX, который на данный момент равен нулю, поскольку это сумма прочитанных байтов. В начале она равна нулю.

32.png

Я создам следующую структуру как минимум 0x10000 байт. Не важно если будет больше.

33.png

По смещению 0x64 структуры MI_STRUCT есть указатель на STRUCT_2, поэтому я иду туда. Я нажимаю D до тех пор, пока не будет создан DWORD, а затем делаю смещение на структуру.

34.png

Я выбираю STRUCT_2 и оставляю всё так.

35.png

36.png

По смещению 0x7008 начинается буфер, где программа будет хранить данные RECV. Поскольку это инструкция LEA, это не указатель на буфер, но он находится внутри той же самой структуры STRUCT_2.

В структурах я иду по адресу 0x7008 структуры STRUCT_2 и создаю буфер.

Я предварительно создам 0x1000 байт.

37.png

38.png

39.png

Сейчас смотрится лучше.

Если мы сделаем SEARCH FOR INMEDIATE VALUE со значением 0x7008 по всей программе.

40.png

Мы видим, что есть больше двух мест, где программа использует это смещение. Давайте посмотрим.

41.png

Первое вхождение находится в начале той же функции, которая читает, указатель на STRUCT_2 и добавляет 0x7008, и это дает указатель на буфер, в котором программа сохраняет байты, поэтому я переименую переменную, в которой программа сохраняет, в P_BUFFER.

42.png

Здесь мы видим, как программа ищет содержимое P_BUFFER, т.е. первый DWORD его и это значение должно быть меньше 0x7000. Мы можем проверить это, поставив BP на сравнении.

43.png

Я изменяю пакет, который я отправляю чтобы первые 4 байта были разными. В этом случае у меня получается значение 0x41424344.

Таким образом, из 8 байтов, которые программа читает, она проверяет, если первые 4 байта меньше чем 0x7000.

44.png

Я поменяю значение в скрипте на 0x2000.

45.png

46.png

Сейчас то что нужно.

Поэтому мы продолжаем здесь.

47.png

Содержимое P_BUFFER это 0x2000.

48.png

В PRINTF это помогает нам увидеть, что есть три значения, которые будут напечатаны LENGHT, SOCKET и TCPRECVMSG, поэтому мы можем видеть, можем ли мы что-то переименовать с этими именами.

49.png

Давайте напомним, что в поле MI_STRUCT 0X37C находится сокет. Мы переименуем поле.

50.png

51.png

52.png

И здесь где я реверсил.

53.png

В другое поле я поместил TCPRECVMSG как мне говорил PRINTF. Я ещё не знаю для чего оно используется, но я его идентифицирую.

54.png

Последнее что делается в этом блоке это читается размер и сохраняется в переменную.

55.png

И программа очищает сумму прочитанных байтов и повторяет цикл, возвращаясь назад с размером 0x2000.

56.png

В конце концов программа читает размер 0x2000 и не вычитает ничего поскольку сумма равна нулю, поэтому программа сохраняет 0x2000 в LEN и вызывает заново RECV.

57.png

Затем программа приходит к сравнению флага, который сейчас равен 1.

58.png

Если программа прочитает 0x2000 байт, которые я ей прислал, поскольку пакет сейчас находится здесь.

59.png

Для того, чтобы программа смогла прочитать спокойно 0x2000 байт во втором вызове RECV.

Если программа прочитала нормально, она выходит из функции LEYENDO. В EAX помещается 0. Если всё в порядке.

60.png

61.png

62.png

Это другая функция, где читается буфер. Я переименую её в COPY. Я вижу, что программа вызывает её прямо под тем местом где я нахожусь.

63.png

64.png

Я вижу, что DESTINATION функции MEMCPY внутри COPY это буфер который я переименую как _DESTINATION. Туда скопируются мои данные.

65.png

Также мы видим, что здесь это P_SIZE который передаётся в MEMCPY.

66.png

И что буфер _DESTINATION состоит из 0x7004 байт, поэтому здесь не будет переполнения, потому что если при сравнении размер был меньше чем 0x7000, а если бы он был больше программа шла бы по другому пути где читается только 0x7000 как максимум.

Также мы видим, что есть указатель на _DESTINATION который сохраняется в другую переменную. Я переименовываю.

67.png

68.png

Здесь программа сравнивает один из 0x2000 байт, которые я прислал ей со значением 0x9C, но похоже, что путь слева, связанный с разжатием данных, указанных в строке. Другой путь, похоже, не выбрасывает вас, поэтому я могу подумать, что если байт равен 0x9C, это флаг сжатия, а пока давайте не будем туда заходить и пойдет по правой стороне.

69.png

Здесь снова программа сравнивает размер с 0x7000. Нет проблемы со знаком, несмотря на то, что используется JLE, потому что это зависит от количества прочитанных байтов и не может прочитаться столько, для того чтобы там было отрицательное значение, так что там ничего нет.

Мы видим что копируется мой буфер _DESTINATION в другой, которая вызывает DST, который также по длине равен 0x7004.

70.png

Мы видим, что-то, что называет SRC, на самом деле является указателем на DST, который является местом куда я копирую свои фрукты.

71.png

Также здесь есть структура.

72.png

Её поле 0x2A8, по-видимому, является номером сеанса. Поэтому я создам третью структуру и в неё добавлю поле SESIÓN_NUMBER.

73.png

Прежде чем мы продолжим, нужно объяснить одну вещь. Эта задача трудна не только потому, что у нас нет символов, но также и потому, что не существует верного пути между точкой RECV, куда приходит наша информация и уязвимой функцией.

Если я помещу BP на уязвимую функцию, я увижу, что программа постоянно каждые несколько секунд останавливается на ней. По-видимому, как если бы это был поток, отличный от RECV, и нам нужно посмотреть, как вы можете добраться до него, несмотря на это, поэтому без символов и с этим трудом нам придется выжать все возможности статического реверсинга и, наконец динамическое тоже, чтобы помочь нам в том, что на войне и в реверсинге все годится. Мы увидим, как это сделать.

Итак, мы добрались до SWITCH. Затем здесь есть несколько путей. Чтобы посмотреть, какой из них приведет нас к уязвимой функции, давайте немного разберем ее, чтобы попытаться выяснить, откуда взялась эта величина и можем ли мы на нее повлиять.

74.png

Мы видим, что есть буфер. Я пометил его как P_DST. Его первый WORD - это значение, которое фильтруется. Мы видели это в патче, если это значение меньше 20, программа вас выбрасываете.

75.png

Таким образом, программа оценивает, если значение меньше 0x800, например 0x10, программа идет вправо, затем перечитывает значение и вычитает 0x20, сохраняя результат и используя этот отрицательный размер в качестве размера MEMCPY для стека, который переполняет его.

Чтобы избежать этого, в патче проверяют перед вычитанием, чтобы значение было больше 0x20 перед переходом к процедуре.

76.png

Здесь в пропатченной функции, если она не ниже JNB программа переходит в зону вычитания с 0x20, чтобы она не была отрицательной.

77.png

=======================================================
Автор текста: Рикардо Нарваха - Ricardo Narvaja (@ricnar456)
Перевод на русский с испанского: Яша_Добрый_Хакер(Ростовский фанат Нарвахи).
Перевод специально для форума системного и низкоуровневого программирования — WASM.IN
12.04.2019
Версия 1.0

0 418
yashechka

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

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