Как стать автором
Обновить

Введение в эксплоитинг и реверсинг с использованием IDA FREE и других бесплатных инструментов. Глава 2

Информационная безопасность *Assembler *Реверс-инжиниринг *
Перевод
Tutorial
Автор оригинала: Ricardo Narvaja
В первой части мы установили несколько инструментов, которые будут полезны для нас для прохождения этого курса. Их особенность в том, что они все бесплатны. Мы не будем использовать какой-либо платный инструмент, а из тех, у которых есть платная версия, таких как IDA или PYCHARM, мы будем использовать версию FREE или COMMUNITY.

Давайте посмотрим на некоторые концепции, прежде чем мы приступим к упражнениям.

Что такое БАГ?

БАГ — это результат сбоя или недостатка в процессе создания компьютерных программ (программного обеспечения) или компьютера. Указанный сбой может произойти на любом из этапов жизненного цикла программного обеспечения, хотя наиболее очевидный сбой происходит на этапе разработки и программирования.

Как я всегда говорю, программист может ошибаться, и эти ошибки могут вызвать сбои в программе или баги. Пока что ничего нового я не сказал.

Вопрос заключается в том, чтобы знать разницу между БАГОМ и УЯЗВИМОСТЬЮ, поэтому давайте посмотрим, что такое УЯЗВИМОСТЬ.

Что такое УЯЗВИМОСТЬ?

УЯЗВИМОСТЬ — это определенный тип бага в программе, который позволяет, используя ее, нарушать безопасность компьютерной системы.

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

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



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

Что такое ЭКСПЛОИТ?


ЭКСПЛОИТ — это компьютерная программа, которая пытается использовать некоторую уязвимость другой программы. Конечной целью эксплойта может быть злонамеренной, например, как уничтожение или отключение атакуемой системы, хотя обычно речь идет о нарушении мер безопасности, чтобы получить доступ к информации несанкционированным способом и использовать ее в своих собственных интересах или в качестве источника других атак на третьи стороны.

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

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

Вначале у нас не будут активированы средства защиты системы, но постепенно мы будем активировать их, чтобы узнать, как мы можем справиться с ними и в каких ситуациях.

Что такое БУФЕР?


БУФЕР — это пространство памяти определенного размера, зарезервированное для хранения данных и управления ими.

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

Что такое ПЕРЕПОЛНЕНИЕ БУФЕРА?


ПЕРЕПОЛНЕНИЕ БУФЕРА происходит, когда компьютерная программа превышает объем памяти, зарезервированный для нее, записывая данные в непрерывный блок памяти.

https://www.welivesecurity.com/la-es/tag/buffer-overflow-la-es

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

Наиболее распространенные типы переполнений буфера — переполнения буфера стека и переполнения буфера кучи.

Здесь мы видим определение переполнения буфера, и в нашем предыдущем примере, если я попытаюсь налить 40 литров в 20-ти литровый бак, он переполнится, как мы поняли. Это переполнение, которое производит переполнение буфера, т.е. переполнение моего бака, когда превышается его максимальная емкость.

Теперь объясним разницу между стеком и кучей.

Что такое СТЕК?


СТЕК используется для хранения локальных переменных функции, которые необходимы только до тех пор, пока выполняется функция. В большинстве языков программирования важно, чтобы мы знали во время компиляции, насколько велика переменная, если мы хотим сохранить ее в стеке.

Что такое КУЧА?


КУЧА используется для резервирования динамической памяти, срок полезного использования которой заранее неизвестен, но ожидается, что он продлится некоторое время. Если мы не знаем её размер или он определяется во время выполнения, размер должен быть рассчитан и зарезервирован в куче.

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

В нашей компании я работаю уже более 13 лет как автор эксплойтов, и первое, что мы делаем со всеми нанимаемыми людьми, даже делали со мной, когда я присоединялся, — это попытаться разгадать стеки и кучи знаменитого ГЕРАРДО РИЧАРТЕ. Это один из основателей CORE SECURITY и гуру анализа эксплойтов.

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

Давайте посмотрим на исходный код для задачи STACK1.

https://drive.google.com/open?id=16btJAetpa1V5yHDZE2bnnFWTQNpsUR4H

Мы видим папку с упражнениями, а внутри находится исходный код STACK1, называемый STACK1_VS_2017.CPP.

#define _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_DEPRECATE

#include <stdlib.h>
#include  <stdio.h> 
#include "Windows.h"


int main(int argc, char **argv) 
{


	MessageBoxA((HWND)-0, (LPCSTR) "Imprimir You win..\n", (LPCSTR)"Vamosss", (UINT)0);

	int cookie;
	char buf[80];

	printf("buf: %08x cookie: %08x\n", &buf, &cookie);
	gets(buf);

	if (cookie == 0x41424344) printf("you win!\n");

}


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

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

Кому нужна информация по этой функции, её можно получить здесь

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

Таким образом, мы остались с этим исходным кодом, созданным ГЕРАРДО РИЧАРТЕ.

int main(int argc, char **argv) 
{

	int cookie;
	char buf[80];

	printf("buf: %08x cookie: %08x\n", &buf, &cookie);
	gets(buf);

	if (cookie == 0x41424344) printf("you win!\n");

}

Мы видим красным цветом первую часть программы, где резервируется место для локальных переменных. В этом случае есть две локальные переменные, COOKIE и BUF.

Вы можете увидеть в таблице типы данных. Там же там находятся и другие типы переменных.

Код будет скомпилирован в 32 бита.

Мы видим, что переменная COOKIE будет типа INT, поэтому для этой переменной будет зарезервировано 4 байта памяти.



В случае переменной BUF мы видим, что это массив или цепочка символов (размер символа = 1 байт).



Т.е. это будет массив из 80 символов, т.е. его длина будет 80x1 = 80 байт.

Тот, кто не знает, что такое массив, может почитать про него здесь:

https://www.programiz.com/c-programming/c-arrays

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



В первом примере это массив целых чисел, т.е. он будет равен 100 байт, а поскольку каждое целое занимает 4 байта, длина массива будет равна 100 x 4 = 400 байт.

Во втором примере FLOAT занимает 4 байта, поэтому это будет массив из 5 FLOAT, поэтому его длина будет 5 x 4 = 20 байт.

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

Возвращаясь к нашему упражнению:

char buf[80];

Это массив символов длиной 80 x 1 = 80 байт, т.е. это похоже на нашу 20-ти литровую банку. Если мы попытаемся сохранить более 80 байт, банка переполнится.

Теперь давайте посмотрим, где используется буфер BUF.



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

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

"buf: %08x cookie: %08x\n"

Но функция PRINTF не только печатает строку в кавычках, но она также печатает строку в заданном формате. Проценты внутри говорят нам, что будет создаваться выходная строка. Мы видим, что строка является только первым аргументом функции. Формат вывода и другие аргументов может быть несколько (будет один на каждый аргумент % в формате). В нашем случае их два.



В этом случае у нас два формата %X, поэтому, если я обращаюсь к таблице формата PRINTF:



Мы видим, что функция возьмет эти целые числа (INT) и вставит их в выходную строку с основанием системы счисления 16, т.е. в шестнадцатеричном формате. 08 указывает на то, что если число содержит менее 8 цифр, функция заполнит его пробелами.

Вывод для «buf: %31x”,&buf будет таким

buf:             19FED4 

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

Все возможные случаи перечислены здесь:

http://www.cplusplus.com/reference/cstdio/printf/

Наш же случай такой:





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

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

printf("buf: %08x cookie: %08x\n", &buf, &cookie);

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

На это указывает символ &. Он возвращает адрес или местоположение банки, а не ее содержимое или значение.

Определение АМПЕРСАНДА


АМПЕРСАНД используется для указания адреса памяти переменной, в которой будут храниться данные.

Поэтому, если я запущу исполняемый файл в консоли, я увижу, например, что при выполнении функции PRINTF она напечатает:



На ваших ПК адреса могут меняться, но поскольку нижний адрес обоих адресов совпадает с адресом BUF, мы видим, что они расположены таким образом:



Адрес BUF меньше адреса COOKIE, поэтому он будет увеличиваться.

А что нам говорят эти адреса переменных? (В моем случае &BUF=0x19FED4 и &COOKIE=0x19FF24)



Оба представлены в шестнадцатеричном формате. Помните, что это был формат %X? Поэтому я поставил 0x вперед, чтобы отличать десятичные числа, которые мы будем представлять без каких-либо дополнений.

Если в консоли PYTHON или в PYCHARM я сделаю вычитание:



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

Часто, когда мы делаем такой тип переменных основанных на исходного кода, может случиться так, что компилятор даст нам больший размер, чем тот, который зарезервирован в исходном коде. Компилятор гарантирует, что он зарезервирует не менее 80 байт, т.е. он может зарезервировать больше, а не меньше.

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

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

int main(int argc, char **argv) 
{

	int cookie;
	char buf[80];

	printf("buf: %08x cookie: %08x\n", &buf, &cookie);
	gets(buf);

	if (cookie == 0x41424344) printf("you win!\n");

}

Здесь, на красной строчке, GET — это функция для ввода данных с клавиатуры. Данные будут вводиться пока я не нажму клавишу ВВОД.

Программа не может ограничить объем данных, вводимых пользователем, и также нет способа проверить эти данные. Все, что вводится до нажатия клавиши ВВОД, скопируется в буфер BUF.

В этом есть проблема. Мы сказали, что BUF может хранить только 80 байт максимум, поэтому, если мы введем больше, мы создадим переполнение буфера, и здесь приведены все условия для этого, потому что, если пользователь записывает более 80 байтов, то переполнит наш бак и жидкость закапает вниз.



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

Например, если тот, кто печатает, пишет 80*A и 4*B, 80*A заполнят BUF, а 4*B заполнят COOKIE, и, как мы знаем, когда кто-то печатает символ в консоли, на низком уровне сохраниться значение ASCII.



Поскольку COOKIE будет заполнена четырьмя буквами B, которые эквивалентны значению 0x42, мы можем гарантировать, что значение COOKIE будет 0x42424242, т.е. на моем компьютере адрес COOKIE 0x19FF24 будет иметь 0x42424242 в качестве содержимого.

0x19FF24=>42424242



Дело в том, что мы уже видели, как переполнить и контролировать значение COOKIE.

int main(int argc, char **argv) 
{

	int cookie;
	char buf[80];

	printf("buf: %08x cookie: %08x\n", &buf, &cookie);
	gets(buf);

	if (cookie == 0x41424344) printf("you win!\n");

}

Для выполнения упражнения вы должны распечатать „you win“. Для этого COOKIE должен быть равен значению 0x41424344, и если бы не было переполнения, это было бы невозможным, так как значение COOKIE никогда не изменялось с самого начала программы. Мы не сможем напечатать „you win“, и для этого мы используем переполнение буфера, в котором говорится, что это может заставить программу выполнить какое-то действие, отличное от того, что было запрограммировано.

В этом случае вы никогда не сможете напечатать “you win”, только переполнение позволит вам это сделать.

AAAAAAAA

Другими словами, вместо того, чтобы передать, например, 80*A и 4*B, для печати „you win“, вы должны передать 80*A, а затем буквы DCBA, поскольку это приведет к тому, что в COOKIE сохранятся значения ASCII.

44434241

https://www.arumeinformatica.es/blog/los-formatos-big-endian-y-little-endian/

Формат, в котором хранятся данные является LITTLE ENDIAN. Другими словами, данные в памяти хранятся в обратном порядке, если говорить просто.



И если сохраняется последовательность 0x41424344, система сохранит её в памяти как:

44 43 42 41

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

Мы можем запустить исполняемый файл в консоли.



И курсор будет мигать, так как функция GET просит меня ввести входные данные. Осторожно наберите 80 символов A и затем DCBA.

В консоли PYTHON или PYCHARM я могу напечатать строку, скопировать ее без кавычек и вставить ее в консоль, чтобы не печатать её как сумасшедший, а затем нажать клавишу ENTER, чтобы ввести ее.





Мы видим, что мы получили „you win“.

Мы можем увидеть это в отладчике. Для этого мы будем использовать X64DBG.



Я выбираю 32-х битную версию.





Если мы остановимся на библиотеке NTDLL.DLL, мы снова нажмем RUN с помощью F9.

Мы видим, что отладчик останавливается на первой инструкции модуля STACK1, которая называется ENTRY POINT или первой инструкции, выполняемой модулем.



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



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



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



Теперь видно намного больше. Мы видим вызов функции MessageBoxA, PRINTF, GETS и сравнение со значением 0x41424344.

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





Мы видим, что это не идеально, но это лучше чем то, что было.

Я собираюсь поставить BP в начале функции и нажимать F9, пока отладчик не остановится.



Для тех, кто не знает, что такое аргумент функции.



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

int main(int argc, char **argv) 
{

	int cookie;
	char buf[80];

	printf("buf: %08x cookie: %08x\n", &buf, &cookie);
	gets(buf);

	if (cookie == 0x41424344) printf("you win!\n");

}

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

Непосредственно перед вызовом функции, аргументы будут сохранены в стеке.

Когда мы остановимся в начале функции, первое значение в стеке будет равно RETURN ADDRESS, т.е., куда функция вернется после завершения выполнения функции, и ниже этого значения будут аргументы этой функции.

Если я щелкну правой кнопкой мыши по RETURN ADDRESS и выберу FOLLOW DWORD IN DISASSEMBLER, я увижу, куда отладчик должен вернуться после завершения функции.



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



Я помещу BP немного раньше и сделаю перезагрузку программы.



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



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

Ниже ссылка, для тех кто хочет больше знать про аргументы функции:

https://publications.gbdirect.co.uk/c_book/chapter10/arguments_to_main.html

Это не очень сложно. Первый аргумент ARGC — это INT, который указывает число параметров консоли, используемых при выполнении программы, включая путь к исполняемому файлу, а ARGV — это массив указателей на строки.

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





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

Я трассирую и каждая инструкция PUSH сохраняет аргументы.



Здесь я могу видеть аргументы функции. Выше приведен первый аргумент ARGC. Он равен 3, так как он отмечает количество аргументов, которые передаются на консоль.



Здесь это 3 аргумента.

Теперь мы нажимаем F7, чтобы сделать STEP INTO и войти в функцию.

Здесь мы видим, что при входе в CALL отладчик сохраняет АДРЕС ВОЗВРАТА в стек.



Итак, как мы уже говорили при входе в функцию, первое, что будет в сохранено в стеке это RETURN ADDRESS (в 32-битной компиляции), а ниже будут аргументы функции, сначала первый аргумент, а затем последовательно остальные.



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



Здесь мы остановились в начале функции, чуть ниже у нас находится АДРЕСА ВОЗВРАТА и АРГУМЕНТЫ.

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

Затем функция начинает выполняться. Первая вещь — это так называемое ПРОЛОГ, в котором хранится значение EBP функции, которая вызвала нашу.



Это приведет к сохранению значения EBP чуть выше адреса возврата.



Если я выполню инструкцию с помощью F7.

Я вижу, что значение EBP GUARDADO находится в стеке над адресом возврата.



Следующая инструкция в ПРОЛОГЕ:

MOV EBP, ESP

Она устанавливает значение EBP для текущей функции, который был сохранен и был родительской функцией, которая вызывает нашу (В этом случае моей основной функцией является функция основанная на EBP, в других случаях она может отличаться, и мы увидим их позже)

Помещая в EBP текущее значение ESP, мы добиваемся того, что мы создаем фрейм для нашей текущей функции.



Теперь, поскольку это функция основана на EBP или EBP BASED, внутри функции будет сохранено значение EBP, и оно будет принято в качестве эталонного, а ESP будет меняться.



Это значение EBP берется как базовое.

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

Мы можем видеть в списке несколько переменных, которые упоминаются как EBP-4 или EBP-54, относящиеся к значению EBP, которое он принимает в данный момент.

Можно сказать, что как только EBP примет свое значение после ПРОЛОГА, он будет похож на водосток, поэтому аргументы всегда будут идти в этом направлении, поэтому EBP + XXX ссылается на аргументы (сохраненный EBP и АДРЕС ВОЗВРАТА также находится ниже, но не будет иметь ссылок в коде), в то время как переменные, как мы увидим, будут выше этого адреса, поэтому ссылка на EBP-XXX относится к некоторой локальной переменной.

Таким образом в функциях ОСНОВАННЫХ НА EBP:

EBP + XXXX = аргументы переданные функции
EBP — XXXX = локальные переменные функции

После ПРОЛОГА будет некоторый способ зарезервировать пространство для переменных. В нашем случае это делается путем перемещения ESP вверх, чтобы оставшееся пространство внизу было зарезервировано для суммы всех длин переменных а, иногда, немного больше на всякий случай, который зависит от компилятора.

00401043 | 83EC 54 | SUB ESP,54

Мы видим, что ESP расположен над EBP, который останется ссылкой, и что 0x54, переведенное в десятичное число, равно 84, что является суммой длины BUF и COOKIE. Помните, что они были 80 и 4 соответственно.







При выполнении создается пространство для переменных BUF и COOKIE размером 84 байта. Вы можете щелкнуть первый столбец в направлении горизонта, и посмотреть значение EBP и найти это значение в стеке. Очевидно, теперь оно будет ниже.



Я делаю двойной щелчок здесь.



Таким образом, мы будем иметь значения относительно EBP также в стеке.

Например, EBP-4 совпадает в листинге, -4 отображается в первом столбце стека, а в пояснении также отображается как EBP-4.



Если я трассирую, мы видим, что от места, где ESP был расположен для резервирования переменных, он всегда будет двигаться вверх, потому что он должен учитывать пространство, выделенное для переменных. При выполнении 4 PUSH для MessageBoxA отладчик помещает переменные выше зарезервированного пространства и увеличвает ESP.



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



При входе в функцию MessageBoxA АДРЕС ВОЗВРАТА этой функции сохраняется в стеке.



Здесь это адрес возврата MessageBoxA. Когда я добираюсь до RET этой функции путем трассировки с помощью F8, и я исполняю MessageBoxA.



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



И те значения PUSH, которые вы передали для функции MessageBoxA и АДРЕС ВОЗВРАТА этой функции, уже использовались, и ESP снова находится чуть выше зарезервированной области, как до вызова любой функции. То же самое произойдет с вызовом функции PRINTF.

После того, как вы пройдете функцию PRINTF, будут напечатаны адреса BUF и COOKIE.



Адрес BUF на моей машине будет 0x19FED4, а адрес COOKIE — 0x19FF24.

Здесь программа читает адрес BUF, чтобы передать его функции GETS и заполнить BUF. Мы можем проверить, совпадает ли адрес с тем, что показывает консоль 0x19FED4.





Здесь мы видим, что это EBP-54. Если я дважды щелкну по стеку, где он показывает -54, он покажет, что адрес BUF=0x19FED4 на моей машине.



Теперь, когда на этом адресе я буду сохранять введенные данные, я могу поместить их в дамп, чтобы посмотреть, как там сохраняются байты.





Вот они. Более того, ниже ничего не отображается, поскольку там нет данных.

Когда я вызову функцию GETS с помощью F8, мне нужно будет перейти к консоли, набрать и нажать клавишу ВВОД, чтобы заполнить буфер BUF и переписать COOKIE.



Мы видим, что переменная COOKIE была по адресу 19FF24 на моей машине.

Здесь программа сравнивает COOKIE с 0x41424344.



Мы видим, что EBP-4 говорит, что это COOKIE, в дополнение к адресу, если мы установим ГОРИЗОНТ на значение EBP, как и раньше.



Я делаю двойной щелчок здесь.

Мы видим, что EBP-4 — это COOKIE, поскольку переменная находится на уровне -4 стека, обнуляя ГОРИЗОНТ.



Мы видим, что программа не будет переходить и покажет нам you win!





Так мы вручную достигаем цели, которая говорит you win!..

Мы динамически проанализировали STACK1, используя X64DBG, который является отладчиком и не позволяет нам анализировать программу без запуска. Для этого мы должны использовать другие инструменты, такие как IDA PRO, GHIDRA или RADARE.

Я могу сделать модель скрипта для эксплуатации упражнения из PYTHON.

import sys
from subprocess import Popen, PIPE

payload = b"A" * 80 + b"\x44\x43\x42\x41"

p1 = Popen(r"C:\Users\ricardo\Desktop\abos y stack nuevos\STACK1_VS_2017.exe", stdin=PIPE)
print ("PID: %s" % hex(p1.pid))
print ("Enter para continuar")
p1.communicate(payload)
p1.wait()
input()

В случае с PYTHON 3, я должен поставить круглые скобки в функции PRINT и быть осторожным при добавлении строк, которые должны быть байтами (поместите b перед строками в PYTHON 2).

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



Хорошо. У нас уже есть модель скрипта для PYTHON 3 для эксплуатации STACK1. В следующей части мы продолжим статический анализ в IDA, RADARE и GHIDRA.


Обратите внимание, что помимо задачи STACK1 есть ещё версии 2, 3 и 4. Вы можете попытаться решить их. Они очень простые и похожи на STACK1, так что не скучайте.

В следующей части мы увидим IDA FREE, RADARE и GHIDRA.

Увидимся в следующей 3-ей части.

Рикардо Нарваха
25/10/2019

P.S.#1
Красивый PDF можно скачать на моей домашней страничке — yasha.su

P.S.#2
Уже скоро я напишу продожение статьи https://habr.com/ru/post/464117/ про то, как я собирал помощью для отца Криса Касперски и что из этого получилось.

Не скучайте.
Теги:
Хабы:
Всего голосов 5: ↑4 и ↓1 +3
Просмотры 6.1K
Комментарии Комментировать