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

Дата публикации 27 окт 2017 | Редактировалось 28 окт 2017
Уязвимости.

В этой части, мы поговорим об уязвимоcтях и как анализировать самые простые из них.

Что же такое уязвимость ?

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

Уязвимости - это результат ошибок или просчётов в дизайне системы. Хотя, в более широком смысле, они также могут быть результатом самих технологических ограничений, потому что, в принципе, не существует 100% безопасной системы. Таким образом, существуют теоретические и настоящие уязвимости.

То же самое правило применяется и к программам. Уязвимая программа - это такая программа, которая имеет ошибки или изъяны в программном коде и в зависимости от типа ошибок, эти ошибки могут быть эксплуатированы. Из-за этих ошибок может быть запущен вредоносный код в вышеупомянутой программе, но также и аутентификация может завершиться ошибкой и позволит совершать действия, которые не позволены пользователю, провоцировать сбои, поднимать привилегии, и т.д.

Конечно, на уровне ошибок повреждения памяти самые простые - это переполняющиеся буфера.

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

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

В исходном коде простой программы на C, буфер может быть представлен так:

char buf[xxx];

Где XXX - это размер буфера. В нашем случае, это буфер в стеке длиной 0x300 байт.

1.png

Очевидно, эта программа ничего не делает, но мы всё равно компилируем её и загружаем в IDA.

Она приложена к этому туториалу, как файл COMPILADO_1.EXE.

2.png

Уже знаем, что нам нужно искать ссылки на аргументы ARGC или ARGV. Так мы можем попасть в функцию MAIN в консольной программе.

Делая двойной щелчок на них, я прибываю сюда:

3.png

Теперь поищем перекрёстные ссылки с помощью клавиши X.

4.png

Ища перекрёстные ссылки, попадаем в известный блок, который вызывает функцию MAIN. В нашем случае, давайте дадим ему другое имя. Может быть такое, потому что блок ничего не делает.

5.png

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

6.png

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

7.png

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

8.png

Видим, что вышеупомянутая функция имеет два аргумента - буфер и максимальный размер того, что Вы можете ввести, для того, чтобы буфер не переполнился. Очевидно, в примере нет переполнения, потому что размер, который копируется, не превышает размера созданного буфера в 0x300 байт.

9.png

Видно, что не существует возможности переполнить буфер, так как то, что я буду вводить будет меньше или равно 0x300 байт.

Давайте посмотрим на эту программу в IDA. Файл приложен к данному туториалу с именем COMPILADO_2.EXE.

10.png

Я компилировал файл с символами, и IDA также нашла символы на моей машине.

Так выглядит намного лучше. Уже появилась функция MAIN c её аргументами и переменными.

Давайте немного проанализируем этот код в IDA.

11.png

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

Здесь у нас есть три аргумента - ENVP, ARGV и ARGC, которые программа не использует внутри функции MAIN.

Они помещаются в стек перед вызовом MAIN.

12.png

Программа помещает три аргумента в стек перед тем как сделать ВЫЗОВ функции MAIN.

Программа сохраняет АДРЕС ВОЗВРАТА, который является адресом, который знает куда нужно вернуться программе после выхода из CALL. В нашем случае, адрес возврата имеет значение 0x401200, если не будет рандомизации.

13.png

Программа будет возвращаться сюда, после выполнения функции MAIN. Поэтому она должна сохранить значение 0x401200 в стеке чуть выше 3-х аргументов.

14.png

Затем программа начинает выполнять функцию MAIN. Первое, что делает программа — это исполняет инструкцию PUSH EBP.

15.png

Эта инструкция сохраняет в стеке значение EBP, которое использовалось функцией, которая вызвала функцию MAIN, чуть выше АДРЕСА ВОЗВРАТА 0x401200. Мы не знаем, какое значение оно может иметь, потому что оно меняется при каждом запуске программы, но сохраненный EBP или STORED EBP - это отец этой функции.

16.png

Здесь, EBP будет сохраняться в стек, выше адреса возврата.

Следующая инструкция, которая исполняется - такая.

17.png

Она устанавливает EBP как базу в этой функции и выравнивает его с ESP. Это одинокая инструкция MOV. Она меняет значение EBP, а не стек.

Затем, следующая инструкция SUB ESP, 0x304 передвигает указатель ESP вверх памяти, резервируя место для локальных переменных и буферов в стеке, которые расположены выше STORED EBP и ESP будет работать в функции основанной на EBP. Чуть выше резервируется место.

18.png

Здесь, мы видим зарезервированное пространство для переменных и буферов. Чуть ниже находится S (STORED EBP).

Первая переменная, которая почти всегда находится сразу после S - это CANARY, которая нужна для защиты стека. В нашем случае переменная называется VAR_4.

19.png

Здесь видим, что программа читает значение __SECURITY_COOKIE, которое является случайным значением, которое создаётся каждый раз, когда запускается программа. Это значение XORится с помощью EBP и сохраняется в переменную VAR_4 как мы уже видели ранее. Переименовываем её в CANARY.

20.png

Выше CANARY находится буфер BUF. Давайте посмотрим его в статическом представлении стека.

21.png

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

22.png

Видно, что его размер равен 768 * 1 байт, т.к. это длина каждого элемента. Следовательно, размер буфера равен 768, который в HEX представлении равен 0x300.

23.png

Поэтому, мы соглашаемся с IDA и получается буфер BUF, который уже определен как буфер из 0x300 байт в HEX или 768 в десятичном виде.

Здесь, есть вызов функции GETS_S и два её аргумента: максимальный размер 0x300 и другой её аргумент - адрес буфера, который получается через инструкцию LEA.

24.png

Поэтому, мы проверяем, что размер буфера BUF равен 0x300 байт и он вмещает максимум 0x300 байт, который мы вводим через функцию GETS_S.

Очевидно, что если бы мы могли переполнить буфер, копируя больше чем 0x300 байт, мы переписали бы переменные CANARY, STORED EBP и АДРЕС ВОЗВРАТА, которые находятся чуть ниже БУФЕРА.

25.png

Но это не тот случай. Это пример хорошего буфера, который записывается правильно.

26.png

Очевидно, часто пользователь не знает размер данных, которые будут скопированы. Если это так, размер должен быть хорошо проверен, что этот SIZE не будет больше, чем размер буфера.

27.png

Здесь видим буфер из 0x10 байт или 16 байт в десятичном виде и пользователь имеет возможность ввести размер буфера через функцию GETS_S. Очевидно, не существует никакой проверки этого максимального значения, поэтому, если я компилирую и запускаю этот файл (COMPILADO_3.EXE).

28.png

Видно, что теперь в программе есть БАГ и она стала уязвима. Если откроем её в IDA, даже если мы не имеем исходного кода, то увидим.

29.png

Как и раньше это переменная CANARY. Чуть выше есть буфер BUF. Давайте посмотри его длину в статическом представление стека.

30.png

Размер буфера равен 16 байт * 1, так как это размер одного элемента. Поэтому размер буфера в HEX равен 0x10.

31.png

Другими словами, если бы мы смогли скопировать более 16 байтов в буфер, произошло бы переполнение и перезапись переменных CANARY, STORED EBP и АДРЕС ВОЗВРАТА.

Посмотрим размер переменной.

32.png

Видно, что после вывода с помощью функции PRINTF сообщения PLEASE ENTER YOUR NUMBER, вызывается функция SCANF_S для ввода значений с клавиатуры, которые сохраняется в переменной, размер которой равен DWORD и передает размер переменной с помощью инструкции LEA в регистр EAX.

Давайте посмотрим описание функции SCANF_S.

33.png

Функция scanf_s читает данные из стандартного потока stdin, и пишет данные по адресу, который указан в аргументе. Каждый аргумент должен быть указателем на переменную типа, который соответствует спецификатору типа в формате. Если копирование происходит между строками, которые перекрываются, поведение не определено.

Другими словами, это как противоположность функции PRINTF. Только вместо печати с форматом, формат вводится с консоли с форматом в буфер. В нашем случае, формат %d интерпретирует данные как десятичное число.

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

34.png

Возможным решением этой проблемы будет проверять размер введённых данных перед тем как копировать ввод пользователя в буфер.

35.png

Было бы неплохо проанализировать программу, чтобы убедиться, что это решение делает её неуязвимой или оставляет её такой же уязвимой. Эта программа называется VULNERABLE_O_NO.EXE, и мы будем обсуждать её в следующей части.

До встрече в следующей 21-той части друзья.


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

19 8.324
yashechka

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

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

Комментарии


      1. Sloneny 7 янв 2018
        Очевидно, Рикардо плохой учитель, который давно забыл что курс называется "Введине в реверсинг с нуля".
        К тому же слишком часто отвлекается на свои размышления, от которых так и хочется кричать "Карина!":)
        Но тем кому этот курс как и мне, из за нехватки знаний дается не легко, хочу сказать: НЕ ЗДАВАЙТЕСЬ!
        Перечитывайте статьи по несколько раз, пропускайте "бла-бла-бла" не относящиеся к теме, не ограничивайтесь только одним источником обучения, и ЯРОСТНО гуглите. И тогда у нас все получиться!
        yashechka нравится это.
      2. yashechka 5 ноя 2017
        Класс, спасибо большое!!!
        Но смотрите, когда я дойду до последней главы, я начну с первой, чтобы исправить все ошибки, переписать все чему я научился и оформить в ПДФ.
        DzenGURU нравится это.
      3. jkmjkmjkm 5 ноя 2017
        yashechka нравится это.
      4. fontbyself 5 ноя 2017
        Именно интересны уязвимости в этом курсе и разбор DEP\ASLR. На мой взгляд это самая сладкая часть курса. =)
        yashechka нравится это.
      5. fontbyself 3 ноя 2017
        Самое интересное началось... Жду продолжения.
        yashechka нравится это.
      6. m1nu78p 31 окт 2017
        спасиб за перевод :popcorm2:
        yashechka нравится это.
      7. yashechka 10 янв 2018
        Имеется ввиду авторитет с испанской сцены
      8. yashechka 9 янв 2018
        Простите, а кто тогда авторитет?
      9. TermoSINteZ 9 янв 2018
        SadKo

        Ну - тут уже претензии не к Яшечке. "Я поэт зовут Незнайка, от меня вам балалайка" - в общем этот Риккардо никогда не был авторитетом, что тут удивляться.
      10. SadKo 9 янв 2018
        Что-то объяснено всё через пятую точку. Я студентам ошибку переполнения буфера за две минуты рассказываю, достаточно для этого только содержимое стека в динамике показать, что и когда с ним происходит.
        Нет, надо обязательно десять раз дизасмить сишный код зачем-то, когда в любой современной IDE есть дебаггер, который можно, в том числе, запустить в режиме трассировки машинного кода.
      11. yashechka 7 янв 2018
        Так что в примере есть переполнение? Может это Яша так перевел? В агнл. версию смотрели?
      12. yashechka 5 ноя 2017
        Сегодня завтра выложу 21, и пойдем дальше :girl_witch::girl_witch::girl_witch:
      13. yashechka 5 ноя 2017
        USE AFTER FREE, DOUBLE FREE:acute::acute::acute:
      14. yashechka 4 ноя 2017
        Да, я знаю. Поступили жалобы и предложения, поэтому, так как видео уроки есть в хоть каком-то качестве, я возобновляю перевод, будем идти до конца.
      15. yashechka 31 окт 2017
        Т.к. я не могу делать всё сразу, но переводы будет задерживаться, нужно подготовить уроки в оригинальном качестве как я и обещал и сделать статью в е-зин. На всё про всё меньше 2х месяцев!!!
      16. yashechka 30 окт 2017
        :preved:Спасибо, Холи.
      17. yashechka 28 окт 2017
        0 [​IMG]2 [​IMG]49
      18. yashechka 28 окт 2017
        Т.к. я ещё подписался на Энтэрсэпшн, то следующий перевод будет задерживаться, сами понимаете, что я не могу делать всё сразу.
      19. yashechka 28 окт 2017
        Увидел ошибку - пиши:acute:, не молчи.