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

Дата публикации 4 фев 2018 | Редактировалось 25 фев 2018
Прежде чем делать упражнение из предыдущей части, я расскажу о некоторых моментах более подробно, которые остались в тени из-за моего быстрого объяснения и затем мы создадим POC.

Один из моментов, который остаётся неясным заключается в том, что иногда мы принимаем размер массива, который предлагает нам IDA при выполнении правого щелчка и выбора пункта ARRAY. Иногда мы ставим предлагаемый нам размер под сомнение как в предыдущем примере и устанавливаем значение длины массива немного больше.

Очевидно, что всё приходит с опытом, но мы попытаемся объяснить это на нескольких примерах.

Давайте посмотрим на этот простой пример.

1.png

Программа имеет приглашение для ввода размера(переменная беззнаковая), который проверяется на то, чтобы он не был равен или был больше 0x200 байт.

Затем идёт цикл, который повторяется количество раз равное размеру, который мы ввели. Он читает символы, которые мы вводим через функцию GETCHAR.

Программа не будет уязвима для переполнения буфера, потому что переменная SIZE - беззнаковая. Таким образом никаких проблем со знаком при сравнении со значением 0x200 не будет.

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

2.png

Если я посмотрю на буфер в IDA, то поскольку я скомпилировал программу с символами, IDA уже обнаружила буфер корректно (512 десятичных байт = 0x200 hex. байт). То же самое произойдёт, если я снова скомпилирую пример, но без символов.

3.png

Давайте посмотрим статическое представление стека.

4.png

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

5.png

Здесь видна четкая ссылка, когда программа печатает получившийся буфер. Обычно, когда идёт ссылка вместе с инструкцией LEA - почти всегда это будет буфер. Кроме того, функция по адресу 0x401190 является функцией PRINTF.

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

6.png

Сейчас, давайте пройдём в статическое представление стека и посмотрим размер буфера.

7.png

Мы видим, что IDA говорит нам, что буфер равен 512 байт и будет права. Но почему 512?

Потому что IDA смотрит на пустое пространство, которое есть до следующей переменной VAR_4. Сейчас наш метод такой - увидеть где программа сохраняет в первый раз значение в эту переменную и где эта переменная VAR_4 используется позже. Давайте посмотрим.

8.png

Мы видим, что есть два места где используется эта переменная. Первое место - где переменная инициализируется значением (сохраняется SECURITY COOKIE) и второе место, где это значение считывается.

Давайте перейдём к первому месту.

9.png

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

Я сохраняю этот пример как BUFFER1.EXE и здесь IDA не ошибается.

Сейчас я буду делать другой пример и буду называть его BUFFER2.EXE.

10.png

Мы видим, что размер буфера по-прежнему равен 0x200 байт. Всё аналогично предыдущему примеру, за исключением того, что здесь программа печатает ЧЕТВЁРТЫЙ БАЙТ буфера, и сравнивает ПЯТЫЙ БАЙТ с нулём. Если этот байт не равен нулю, то печатается наш буфер.

Давайте посмотрим, что случится если я скомпилирую этот пример с символами, а потом без символов.

11.png

Мы видим, что когда у нас есть символы, то нет никаких проблем. IDA продолжает обнаруживать буфер как переменную из 0x200 байт и ЧЕТВЕРТЫЙ БАЙТ, который печатается.

12.png

Мы видим, что IDA не восприняла это как независимую переменную, как я предположил. Программа считывает четвертый байт буфера к которому она обращается, прибавляет значение 4 (инструкция SHL EDX, 2 равносильна умножению EDX на 4) и затем складывает регистр EDX с начальным адресом буфера, для того чтобы указывать на значение четвертого байта.

13.png

Здесь программа умножает регистр EDX на 5, и складывает EDX с началом буфера, и помещает результат в регистр EAX с помощью инструкции MOVSX. Если результат отличается от нуля, программа производит печать

Мы ясно видим, что буфер не был затронут и что с символами IDA продолжает обнаруживать его длину правильно. Давайте теперь посмотрим пример без символов.

Я открываю пример и вижу, что IDA не ошибается. Она не присваивает отдельную переменную четвёртому и пятому значению буфера (хотя бывают случаи, когда это происходит). IDA по прежнему предлагает мне размер буфера 512 байт, потому что следующая переменная VAR_4 - это защита CANARY. Если по каким либо причинам IDA примет эти четвертый и пятый байт за переменные, очевидно они не будут независимыми. Потому что они заполняются, когда с буфером идет работа. Иначе здесь нет другого места для сохранения переменных. Следовательно, я должен рассматривать эти байты как часть буфера.

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

Давайте посмотрим буфер нашего упражнения.

14.png

Эта инструкция LEA говорит о том, что аргумент очень возможно является буфером в стеке, который передаётся функцию STREAM_READ. Давайте переименуем эту переменную в буфер.

Давайте посмотрим на переменные ниже, чтобы увидеть первую независимую переменную буфера.

15.png

Я нажимаю X на каждой переменной.

16.png

Здесь я вижу как я оставляю курсор на инструкции LEA(???MOVZX) где программа будет читать буфер, который использует эту переменную ниже (DOWN), и нет смысла использовать её если нет никакой другой ссылки, где программа сохраняет какое-нибудь начальное значение. Единственное возможное место - когда программа заполняет буфер. То же самое случится со всеми следующими переменными.

17.png

Здесь тот же случай. Переменная принадлежит буферу.

А это первая переменная, которая имеет другую перекрёстную ссылку:

18.png

Эта переменная похожа на буфер, поскольку программа использует инструкцию LEA. Давайте посмотрим.

19.png

Мы видим, что это действительно буфер, но он является частью предыдущего буфера, потому что у него нет никакого независимого места для заполнения. Первая ссылка передаётся как источник в инструкцию REPS MOVS которая должна сохранить значения, чтобы скопировать их в другое место. IDA также указывает направление DOWN, другими словами переменная используется после заполнения исходного буфера. Так что ничего важного здесь нет. Давайте продолжим опускаться вниз.

Программа продолжает чтение всех следующих переменных ниже того места, где заполняется буфер до этого момента.

20.png

Здесь нам показывается инструкция LEA и направление UP. Другими словами этот буфер находится выше того места где заполняется первоначальный буфер.

Мы видим, что это другой буфер, независимый от исходного.

21.png

Буфер передаётся как аргумент в функцию STREAM_CONTROL и заполняется там. Поэтому мы нашли первую независимую переменную, и поэтому буфер заполняется непосредственно перед этой переменной.

Поскольку я делал всё это быстро, я не увидел, что есть другой вызов STREAM_READ, который находится выше с тем же буфером.

22.png

В любом случае, анализ будет точно таким же. Нет никаких переменных вплоть до переменной VAR_1C, которые имеют перекрестные ссылки, до некоторого места где заполняется буфер.

23.png

Я оставляю буфер равным 32 байта, и мы можем проверить, что все переменные, внутри буфера являются внутренними по отношению к этому буферу, и они не являются независимыми.

Другая вещь, про которую я спрашил - это то, как я понял, что функция STREAM_READ может писать количество байт, которое мы передали в буфер. Само название функции говорит об этом. Кроме того, есть патч, который ограничивает значение, которое передаётся туда. Если оно больше чем 8, это даёт мне подозревать, что это максимальное значение, которое программа копирует.

24.png

Здесь мы видим, что программа идёт в вызов STREAM_READ. Давайте здесь нажмём ENTER.

25.png

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

26.png

Я вижу, что функция зависит от аргумента ARG_0, который является константой. Он приходит из вызова CALL. От этой константы зависит куда программа совершит переход используя инструкцию [EAX+2C].

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

Для тех, кто спрашивал, что такое POC. POC - это PROOF OF CONCEPT, который не является полноценным эксплоитом, но он показывает уязвимость, создающий файл TY, в этом случае, который переполняет буфер из 32 байт.

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

27.png

Во-первых, я увижу откуда приходит это значение ESI, которое имеет размер для копирования и похоже, что оно приходит из переменной VAR_58. Переименуем эту переменную в SIZE_A_COPIAR.

28.png

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

29.png

Мы видим, что это регистр EDI, который программа сохраняет в переменную SIZE_A_COPIAR и он приходит из другой переменной VAR_5C и к этому значению прибавляется ещё значение 8. Давайте переименуем эту переменную.

30.png

Мы видим, что все исходит от первого вызова STREAM_READ. Программа берет байты из 14-15-16-17 позиции и будет создавать значение DWORD с использованием инструкций SHL и OR, оставляя его в переменной SIZE_A_COPIAR_MENOS_8.

Давайте установим BP на инструкцию LEA.

31.png

Мы присоединяем WIN32_REMOTE, перетаскиваем и бросаем файл .TY в уязвимый VLC.

32.png

У меня есть VLC и WIN32_REMOTE.EXE, открытые на моей виртуальной машине.

33.png

Я присоединяюсь к этому процессу.

images.jpg

1 5.686
yashechka

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

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