• Открыта запись на вторую часть курса по анонимности и безопасности в сети интернет "Paranoid II" от команды codeby. Анонимные роутеры, Подъём, настройка и администрирование Tor-ноды, Работа с железом ПК, Удаление аппаратных закладок, Минимизация рисков, Авторские разработки и многое другое. Подробнее ...

Статья Переполнение буфера и перезапись адреса возврата - разработка эксплойтов, часть 5

fuzzz

fuzzz

Grey Team
03.02.2019
171
298
Все части переполнение буфера
Предыдущая часть Переполнение буфера и перезапись указателя на функцию - разработка эксплойтов, часть 4
Следующая часть Переполнение буфера и размещение шеллкода в памяти - разработка эксплойтов, часть 6

Привет codeby =) Продолжу тему по разработке эксплоитов. В предыдущей статье мы научились перезаписывать указатель на функцию используя уязвимость переполнения буфера. В этой статье мы продолжим изучать переполнение буфера, рассмотрим еще один вариант как можно перезаписать указатель на функцию, а так же поговорим с вами о памяти компьютера и освоим с вами отладчик GDB. И так...

Память, локальные переменные, аргументы и стек
До сегодняшнего момента я вам наглым образом ничего не рассказывал о памяти и работе процессора, настало время истины... Когда запускается программа, она загружается в память компьютера со всеми данными, всеми библиотеками и со всеми командами которые выполняет сама программа. И когда это произошло нет не какого отличия по сути между данными и командами. Если открыть к примеру программу в отладчике, то можно в этом убедится. Мы увидим там не код программы на языке высокого уровня, а код на ассемблере, который приближён к машинному коду 101110 единицам и нуля. К слову вся память она делится на регионы - области. В какой то части хранятся, команды, данные, переменные... И в низу самом низу памяти находится куча и стек.

Это две основные области памяти для работы программы.

Куча (heap) используется для динамического выделения памяти, например под какой нибудь большой массив...
А стек (stack) для хранения в основном аргументов функций и локальных переменных.

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

Есть три основных регистра которые используются при работе.
* Регистр EIP - указывает на адрес в памяти, какая инструкция (команда) должна выполнится следующей.
* Регистр ESP - указывает на вершину стека (адрес в памяти).
* Регистр EBP - указывает на стековый фрейм (как переменные и аргументы располагаются в памяти).

Эти регистры надо запомнить!!! Надо еще сказать, что это регистры x86-архитектуры процессора, так, как образ Protostar - 32 битная система. Теперь поговорим о стеке и именно на нем очень многое завязано как и на куче.

Как я и сказал выше стек это область памяти и он обрабатывается особым образом.

К примеру у нас есть такой код
C:
#include <stdio.h>
#include <string.h>

int main(){
    char overflow[] = "AAAAAAAAAA";
    char buffer[8];
    strcpy(buffer,overflow);
    return 0;
}
И когда это всё переходит в машинный код, вызов функций выглядит следующим образом, т.е. когда программа дает возможность действовать подпрограмме. Простым языком, когда главная функция main() разрешает выполнится функции strcpy() или любой другой функции (подпрограмме). Так вот вызов функций будет выглядеть следующим образом.

В стек с начало попадают аргументы функций, аргументы справа налево.

В нашем случае в программе выше с начало в стек попадет overflow затем, buffer.

Далее... Сохраняется место для адреса инструкции (команды), или вернее сказать адрес инструкции (команды) куда вернется работа программы, после того как функция выполнится. Простым языком это когда функция отработала она передает управление от куда ее вызвали. (Return address).

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

Локальные переменные и аргументы функций записываются относительно регистра EBP, локальная переменная которая лежит выше это EBP-4 еще выше это EBP-8 (уменьшается еще на 4) Если обращаться к аргументам это +8 +16 зависимости от размера... Дополнительно почитать о стеке можно .

Теперь говорим об отладчике GDB.

GDB - основные команды для отладки программ
Когда вы запускается программу под отладчиком, первое, что надо сделать это установить вывод ассемблерных команд в формате Intel, по умолчанию GDB использует синтаксис AT&T.

Для того чтобы это сделать надо выполнить в отладчике следующее...

set disassemly intel

Чем отличается синтаксис AT&T от Intel'a ?

Разработка эксплойтов


А отличается AT&T от Intel'a, тем, что в синтаксисе AT&T есть такие знаки, как процент и знак доллара. По этим признакам вы его всегда узнаете, в то время, как синтаксисе Intel этого нет.

Рассмотрим не большой пример на основе команды mov. Эта команда пересылает данные.
К тому же синтаксис AT&T для команды mov будет следующий

mov <источник>, <назначение>

В то время как у Intel'a
mov <назначение>, <источник>

Вы можете использовать любой из них, но я советую вам использовать синтаксис Intel, хоть команды наоборот, но он более понятный.
Если вы закроете отладчик, и заново его потом запустите, то вы опять увидите синтаксис AT&T и та настройка синтаксиса действует только на время отладки текущей программы. Если вы не хотите постоянно вводить данную опцию для отображения, то можно поместить в корневой каталог такой файл.

Значит последовательность действий будет следующей
nano ~/.gdbinit
пишем текст: set disassembly-flavor intel
Затем нажимаем F2, Y, Enter.

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

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

run - запуск программы под отладчиком (сокр. команды - r)

stepi - команда выполнит ровно одну инструкцию процессора и остановится на следующей.

nexti выполнит тоже одну инструкцию но перепрыгнет, если следующая инструкция будет подпрограмой - функцией (call)

Т.е. зайти внутрь функции (stepi) или же перейти к следующей команде (nexti)

break main - команда break устанавливает точку останова на указанном месте, и при выполнение программы под отладчиком, программа остановится в указанном месте (сокр. команды - b), в данном примере точка останова установлена на функции main().

b main.c:7 - точка останова установлена используя исходник программы на строчке 7. Удобно в том, случае когда ваша программа скомпилирована с флагом -g. (gcc -g main.c -o main)

b *0x40056d - точка останова по указанному адресу в памяти, с начало пишется звездочка потом сразу адрес в памяти.

continue - команда для продолжения выполнения программы после того, как вы установили точку останова (сокр. команды - с)

info breakpoints - покажет информацию об всех установленных точках останова (сокр. команды - i b)

disable 1 - команда убирает первую точку останова

info registers - команда покажет информацию об регистрах и их значениях в текущем их времени (сокр. команды - i r)

info register $eip -команда покажет информацию об конкретном регистре, в данном случае о EIP (сокр. команды - i r $eip)

disassemble - команда дизассемблирует инструкции в текущей функции, например если мы находимся main то получим листинг ассемблерных команд этой функции. (сокр. команды - disas). Но обычно так не используют, а пишут эту команду с конкретным названием функции.

например
disas win, как мы это делали в предыдущей статье.

Теперь рассмотрим как посмотреть значения ячеек памяти
x 0x400634 команда х позволяет заглянуть в ячейку памяти по указанному адресу.
И по умолчанию выводит 4 байтовое значение, т.е. 4 байта памяти, а значение ячейки выводится в шестнадцатеричной системе счисления.
*** 0x400634: 0x000a6425

Если надо вывести большее значение памяти не 4 , а 8 байт, то тогда
x/2 0x400634
*** 0x400634: 0x000a6425 0x3b031b01

Если не устраивает вывод значения в шестнадцатеричном формате, можно вывести значение по адресу в десятичном форме.
x/2d 0x400634 - что означает покажи мне по такому адресу два целых 4 байтовых числа

вывод вещественных чисел (чисел с запятой )
x/2f 0x400634

Собственно формат вывода может любым
x/o <адрес или $регистр> - восьмеричная
x/x - шестнадцатеричная
x/d - десятичная
x/u - десятичная без знаковая
x/t - двоичная
x/c - символьная

Выводит один байт в шестнадцатеричном значение по этому адресу
x/1xb 0x400634

Вывести 4 однобайтовых символа по адресу
x/4cb 0x400634

b байт
h полуслово (два байта)
w слово (четыре байта)
g двойное слово (восемь байтов)

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

finish - команда завершения отладки

quit - выход из отладчика

Теперь приступим к решению нашего ExploitMe!!!

Описание ExploitMe
Stack4 рассматривает перезапись сохраненного EIP и стандартное переполнение буфера.

Этот уровень находится в / opt / protostar / bin / stack4

,

Советы
  • Может помочь множество вводных статей о переполнении буфера.
  • GDB позволяет вам «run < input»
  • EIP находится не сразу после конца буфера, заполнение компилятором также может увеличить размер.
Исходный код
C:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void win()
{
  printf("code flow successfully changed\n");
}

int main(int argc, char **argv)
{
  char buffer[64];

  gets(buffer);
}
Решение
Здесь всё практически тоже самое аналогично предыдущему уровню, за исключением того, что тут нет указателя на не существующую функцию. Возникает вопрос каким образом теперь мы будем вызывать функцию win() ? Ведь у нас нету локальной переменной, в том же кадре стека, как это было с указателем в прошлый раз. Посмотрим еще раз описание нашего ExploitMe и подсказку... Там говорится о регистре EIP...

Исходя из этого будем значит работать с отладчиком GDB и регистром EIP.

К слову о том, что представляет собой этот регистр.

Регистр EIP — служебный регистр. Указывает на текущую исполняемую инструкцию процессора.

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

Регистр EIP имеет разрядность 32 бита. К 16-ти младшим битам регистра можно обратиться по имени IP.

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

Из текста выше можно понять, что для того чтобы подсунуть в регистр EIP нужный нам адрес, а именно адрес функции win(), нам надо переполнить буфер таким образом, чтобы перезаписать адрес возврата (RET) основной функции, т.е. main(). Когда программа или функция завершает выполнение, она должна вернуться к тому, что ее вызвало. Этот адрес хранится в стеке в начале кадра. Пожалуй приступим.

Запустим программу под GDB

gdb -q ./stack4

Вычислим адрес функции win()

disas win

Переполнение буфера


Функция win() находится по адресу 0x080483f4, теперь нам надо переполнить буфер, таким образом, чтобы получить ошибку сегментации памяти - «Segmentation fault». Эта ошибка будет обозначать, то, что мы перезаписали регистр EIP. Поэтому мы будем увеличивать на 4 байта каждый раз, поскольку EIP занимает четыре байта (32 бита). Метод не очень хорош но воспользуемся именно им, затем попробуем другой метод.

Значит выходим из отладчика (quit) и запускаем несколько команд...

python -c "print '\x41' * 64" | ./stack4
python -c "print '\x41' * 68" | ./stack4
python -c "print '\x41' * 72" | ./stack4
python -c "print '\x41' * 76" | ./stack4

Отлично мы получили ошибку «Segmentation fault».

Рассмотрим способ номер два. Создадим файл
nano ~/offset_eip.txt
Далее запишем в него такую последовательность букв алфавита
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ
Выйдем и сохраним F2,y,enter.

Теперь откроем нашу программу под отладчиком.
gdb -q ./stack4
затем запустить нашу программу и перенаправим вывод из созданного нами файла в нашу программу.
run < ~/offset_eip.txt

Переполнение буфера и перезапись адреса возврата - разработка эксплойтов, часть 5


И видим ошибку сегментации. Что программа пыталась обратиться к несуществующей функции по адресу 0x54545454.
Теперь посмотрим значение регистров
info registers

Переполнение буфера и перезапись адреса возврата - разработка эксплойтов, часть 5


И видим, что по мимо регистра EIP так же перезаписалось значение регистра EBP. Теперь узнаем и посмотрим чему равно значение "0x54". В это значение равняется букве "T", стало быть 4 байта из букв "T", это то значение которое перезаписывает EIP. А так, как в нашем текстовом документе строка составляет алфавит, буква T это 20 буква по счету, то нам нужна 19 буква алфавита. Буква "S" как раз таки и ровняется значению 0x53, о чем свидетельствует регистр EBP. Ну , а по сколько в строке было по 4 одинаковых буквы, то смещение это 19 * 4 = 76 байт. Вот и все таким образом мы опять вычислили смещение.

Теперь рассмотрим третий вариант, на этом варианте мы по сути должны воспользоваться Metasploit'ом, а точнее говоря двумя модулями pattern_create.rb и pattern_offset.rb. Первый модуль нужен для генерации уникальной мусорной строки, а второй для вычисления адреса смещения. И весь алгоритм действий сводится к тому, что мы с генерировали бы уникальную мусорную строку для буфера превышающую его в два раза. Т.е. 64 * 2 = 128 , скормили бы нашу строку под отладчиком для нашей программы, получили бы адрес, а затем с помощью модуля оффесетов подставили бы полученный адрес, который выплюнул нам отладчик, а на выходе мы бы получили число на ск байт произошло смещение. Вот к примеру он делает тоже самое, что и данные модули msf.

По суте даже метасплойт не нужен...

Переполнение буфера и перезапись адреса возврата - разработка эксплойтов, часть 5


Теперь мы можем сказать, что 80 байт это та область, которая перезаписывает EIP, поэтому нам понадобится 76 байтов мусора и адрес функции win(), который мы уже знаем.

Перейдем к эксплуатации уязвимости.

python -c "print '\x41' * 76 + '\x08\x04\x83\xf4'[::-1]" | ./stack4

Вот и всё на экране отобразились две строки «code flow successfully changed» и «Segmentation fault» можно переходить на следующий уровень .
 
deadroot

deadroot

Well-known member
06.01.2019
60
134
Интересные статьи. А вы будете рассматривать фаззинг с такими проектами как или , например?
 
  • Нравится
Реакции: Vander и fuzzz
fuzzz

fuzzz

Grey Team
03.02.2019
171
298
Интересные статьи. А вы будете рассматривать фаззинг с такими проектами как или , например?
Спасибо, очень радует положительный отзыв.

Обязательно расскажу о фаззинге с практическими примерами, но до этого еще далеко многие темы не затронуты. Надо как минимум рассказать хотя бы о SEH\DEP\ASLR =)
 
ryppp

ryppp

Member
12.03.2019
5
5
Есть три основных регистра которые используются при работе.
* Регистр EIP - указывает на адрес в памяти, какая инструкция (команда) должна выполнится следующей.
EIP - текущая инструкция (которая исполняется)
по моему так, если не ошибаюсь...
Опечатка, поправьте пожалуйста...
 
U

ulan75

New member
17.02.2019
1
0
Хорошие статьи, понятно и в то же время достаточно глубоко.
Скажите вы планируете рассмотреть решение всех задач реализованных в ВМ protostar и с какой периодичностью будут выходить статьи?
 
Bringer_the_Light

Bringer_the_Light

Премиум
02.03.2017
109
121
Привет codeby =) Продолжу тему по разработке эксплоитов. В предыдущей статье мы научились перезаписывать указатель на функцию используя уязвимость переполнения буфера. В этой статье мы продолжим изучать переполнение буфера, рассмотрим еще один вариант как можно перезаписать указатель на функцию, а так же поговорим с вами о памяти компьютера и освоим с вами отладчик GDB. И так...

Память, локальные переменные, аргументы и стек
До сегодняшнего момента я вам наглым образом ничего не рассказывал о памяти и работе процессора, настало время истины... Когда запускается программа, она загружается в память компьютера со всеми данными, всеми библиотеками и со всеми командами которые выполняет сама программа. И когда это произошло нет не какого отличия по сути между данными и командами. Если открыть к примеру программу в отладчике, то можно в этом убедится. Мы увидим там не код программы на языке высокого уровня, а код на ассемблере, который приближён к машинному коду 101110 единицам и нуля. К слову вся память она делится на регионы - области. В какой то части хранятся, команды, данные, переменные... И в низу самом низу памяти находится куча и стек.

Это две основные области памяти для работы программы.

Куча (heap) используется для динамического выделения памяти, например под какой нибудь большой массив...
А стек (stack) для хранения в основном аргументов функций и локальных переменных.

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

Есть три основных регистра которые используются при работе.
* Регистр EIP - указывает на адрес в памяти, какая инструкция (команда) должна выполнится следующей.
* Регистр ESP - указывает на вершину стека (адрес в памяти).
* Регистр EBP - указывает на стековый фрейм (как переменные и аргументы располагаются в памяти).

Эти регистры надо запомнить!!! Надо еще сказать, что это регистры x86-архитектуры процессора, так, как образ Protostar - 32 битная система. Теперь поговорим о стеке и именно на нем очень многое завязано как и на куче.

Как я и сказал выше стек это область памяти и он обрабатывается особым образом.

К примеру у нас есть такой код
C:
#include <stdio.h>
#include <string.h>

int main(){
    char overflow[] = "AAAAAAAAAA";
    char buffer[8];
    strcpy(buffer,overflow);
    return 0;
}
И когда это всё переходит в машинный код, вызов функций выглядит следующим образом, т.е. когда программа дает возможность действовать подпрограмме. Простым языком, когда главная функция main() разрешает выполнится функции strcpy() или любой другой функции (подпрограмме). Так вот вызов функций будет выглядеть следующим образом.

В стек с начало попадают аргументы функций, аргументы справа налево.

В нашем случае в программе выше с начало в стек попадет overflow затем, buffer.

Далее... Сохраняется место для адреса инструкции (команды), или вернее сказать адрес инструкции (команды) куда вернется работа программы, после того как функция выполнится. Простым языком это когда функция отработала она передает управление от куда ее вызвали. (Return address).

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

Локальные переменные и аргументы функций записываются относительно регистра EBP, локальная переменная которая лежит выше это EBP-4 еще выше это EBP-8 (уменьшается еще на 4) Если обращаться к аргументам это +8 +16 зависимости от размера... Дополнительно почитать о стеке можно .

Теперь говорим об отладчике GDB.

GDB - основные команды для отладки программ
Когда вы запускается программу под отладчиком, первое, что надо сделать это установить вывод ассемблерных команд в формате Intel, по умолчанию GDB использует синтаксис AT&T.

Для того чтобы это сделать надо выполнить в отладчике следующее...

set disassemly intel

Чем отличается синтаксис AT&T от Intel'a ?

Посмотреть вложение 27333

А отличается AT&T от Intel'a, тем, что в синтаксисе AT&T есть такие знаки, как процент и знак доллара. По этим признакам вы его всегда узнаете, в то время, как синтаксисе Intel этого нет.

Рассмотрим не большой пример на основе команды mov. Эта команда пересылает данные.
К тому же синтаксис AT&T для команды mov будет следующий

mov <источник>, <назначение>

В то время как у Intel'a
mov <назначение>, <источник>

Вы можете использовать любой из них, но я советую вам использовать синтаксис Intel, хоть команды наоборот, но он более понятный.
Если вы закроете отладчик, и заново его потом запустите, то вы опять увидите синтаксис AT&T и та настройка синтаксиса действует только на время отладки текущей программы. Если вы не хотите постоянно вводить данную опцию для отображения, то можно поместить в корневой каталог такой файл.

Значит последовательность действий будет следующей
nano ~/.gdbinit
пишем текст: set disassembly-flavor intel
Затем нажимаем F2, Y, Enter.

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

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

run - запуск программы под отладчиком (сокр. команды - r)

stepi - команда выполнит ровно одну инструкцию процессора и остановится на следующей.

nexti выполнит тоже одну инструкцию но перепрыгнет, если следующая инструкция будет подпрограмой - функцией (call)

Т.е. зайти внутрь функции (stepi) или же перейти к следующей команде (nexti)

break main - команда break устанавливает точку останова на указанном месте, и при выполнение программы под отладчиком, программа остановится в указанном месте (сокр. команды - b), в данном примере точка останова установлена на функции main().

b main.c:7 - точка останова установлена используя исходник программы на строчке 7. Удобно в том, случае когда ваша программа скомпилирована с флагом -g. (gcc -g main.c -o main)

b *0x40056d - точка останова по указанному адресу в памяти, с начало пишется звездочка потом сразу адрес в памяти.

continue - команда для продолжения выполнения программы после того, как вы установили точку останова (сокр. команды - с)

info breakpoints - покажет информацию об всех установленных точках останова (сокр. команды - i b)

disable 1 - команда убирает первую точку останова

info registers - команда покажет информацию об регистрах и их значениях в текущем их времени (сокр. команды - i r)

info register $eip -команда покажет информацию об конкретном регистре, в данном случае о EIP (сокр. команды - i r $eip)

disassemble - команда дизассемблирует инструкции в текущей функции, например если мы находимся main то получим листинг ассемблерных команд этой функции. (сокр. команды - disas). Но обычно так не используют, а пишут эту команду с конкретным названием функции.

например
disas win, как мы это делали в предыдущей статье.

Теперь рассмотрим как посмотреть значения ячеек памяти
x 0x400634 команда х позволяет заглянуть в ячейку памяти по указанному адресу.
И по умолчанию выводит 4 байтовое значение, т.е. 4 байта памяти, а значение ячейки выводится в шестнадцатеричной системе счисления.
*** 0x400634: 0x000a6425

Если надо вывести большее значение памяти не 4 , а 8 байт, то тогда
x/2 0x400634
*** 0x400634: 0x000a6425 0x3b031b01

Если не устраивает вывод значения в шестнадцатеричном формате, можно вывести значение по адресу в десятичном форме.
x/2d 0x400634 - что означает покажи мне по такому адресу два целых 4 байтовых числа

вывод вещественных чисел (чисел с запятой )
x/2f 0x400634

Собственно формат вывода может любым
x/o <адрес или $регистр> - восьмеричная
x/x - шестнадцатеричная
x/d - десятичная
x/u - десятичная без знаковая
x/t - двоичная
x/c - символьная

Выводит один байт в шестнадцатеричном значение по этому адресу
x/1xb 0x400634

Вывести 4 однобайтовых символа по адресу
x/4cb 0x400634

b байт
h полуслово (два байта)
w слово (четыре байта)
g двойное слово (восемь байтов)

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

finish - команда завершения отладки

quit - выход из отладчика

Теперь приступим к решению нашего ExploitMe!!!

Описание ExploitMe
Stack4 рассматривает перезапись сохраненного EIP и стандартное переполнение буфера.

Этот уровень находится в / opt / protostar / bin / stack4

,

Советы
  • Может помочь множество вводных статей о переполнении буфера.
  • GDB позволяет вам «run < input»
  • EIP находится не сразу после конца буфера, заполнение компилятором также может увеличить размер.
Исходный код
C:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void win()
{
  printf("code flow successfully changed\n");
}

int main(int argc, char **argv)
{
  char buffer[64];

  gets(buffer);
}
Решение
Здесь всё практически тоже самое аналогично предыдущему уровню, за исключением того, что тут нет указателя на не существующую функцию. Возникает вопрос каким образом теперь мы будем вызывать функцию win() ? Ведь у нас нету локальной переменной, в том же кадре стека, как это было с указателем в прошлый раз. Посмотрим еще раз описание нашего ExploitMe и подсказку... Там говорится о регистре EIP...

Исходя из этого будем значит работать с отладчиком GDB и регистром EIP.

К слову о том, что представляет собой этот регистр.

Регистр EIP — служебный регистр. Указывает на текущую исполняемую инструкцию процессора.

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

Регистр EIP имеет разрядность 32 бита. К 16-ти младшим битам регистра можно обратиться по имени IP.

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

Из текста выше можно понять, что для того чтобы подсунуть в регистр EIP нужный нам адрес, а именно адрес функции win(), нам надо переполнить буфер таким образом, чтобы перезаписать адрес возврата (RET) основной функции, т.е. main(). Когда программа или функция завершает выполнение, она должна вернуться к тому, что ее вызвало. Этот адрес хранится в стеке в начале кадра. Пожалуй приступим.

Запустим программу под GDB

gdb -q ./stack4

Вычислим адрес функции win()

disas win

Посмотреть вложение 27366

Функция win() находится по адресу 0x080483f4, теперь нам надо переполнить буфер, таким образом, чтобы получить ошибку сегментации памяти - «Segmentation fault». Эта ошибка будет обозначать, то, что мы перезаписали регистр EIP. Поэтому мы будем увеличивать на 4 байта каждый раз, поскольку EIP занимает четыре байта (32 бита). Метод не очень хорош но воспользуемся именно им, затем попробуем другой метод.

Значит выходим из отладчика (quit) и запускаем несколько команд...

python -c "print '\x41' * 64" | ./stack4
python -c "print '\x41' * 68" | ./stack4
python -c "print '\x41' * 72" | ./stack4
python -c "print '\x41' * 76" | ./stack4

Отлично мы получили ошибку «Segmentation fault».

Рассмотрим способ номер два. Создадим файл
nano ~/offset_eip.txt
Далее запишем в него такую последовательность букв алфавита
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ
Выйдем и сохраним F2,y,enter.

Теперь откроем нашу программу под отладчиком.
gdb -q ./stack4
затем запустить нашу программу и перенаправим вывод из созданного нами файла в нашу программу.
run < ~/offset_eip.txt

Посмотреть вложение 27398

И видим ошибку сегментации. Что программа пыталась обратиться к несуществующей функции по адресу 0x54545454.
Теперь посмотрим значение регистров
info registers

Посмотреть вложение 27399

И видим, что по мимо регистра EIP так же перезаписалось значение регистра EBP. Теперь узнаем и посмотрим чему равно значение "0x54". В это значение равняется букве "T", стало быть 4 байта из букв "T", это то значение которое перезаписывает EIP. А так, как в нашем текстовом документе строка составляет алфавит, буква T это 20 буква по счету, то нам нужна 19 буква алфавита. Буква "S" как раз таки и ровняется значению 0x53, о чем свидетельствует регистр EBP. Ну , а по сколько в строке было по 4 одинаковых буквы, то смещение это 19 * 4 = 76 байт. Вот и все таким образом мы опять вычислили смещение.

Теперь рассмотрим третий вариант, на этом варианте мы по сути должны воспользоваться Metasploit'ом, а точнее говоря двумя модулями pattern_create.rb и pattern_offset.rb. Первый модуль нужен для генерации уникальной мусорной строки, а второй для вычисления адреса смещения. И весь алгоритм действий сводится к тому, что мы с генерировали бы уникальную мусорную строку для буфера превышающую его в два раза. Т.е. 64 * 2 = 128 , скормили бы нашу строку под отладчиком для нашей программы, получили бы адрес, а затем с помощью модуля оффесетов подставили бы полученный адрес, который выплюнул нам отладчик, а на выходе мы бы получили число на ск байт произошло смещение. Вот к примеру он делает тоже самое, что и данные модули msf.

По суте даже метасплойт не нужен...

Посмотреть вложение 27400

Теперь мы можем сказать, что 80 байт это та область, которая перезаписывает EIP, поэтому нам понадобится 76 байтов мусора и адрес функции win(), который мы уже знаем.

Перейдем к эксплуатации уязвимости.

python -c "print '\x41' * 76 + '\x08\x04\x83\xf4'[::-1]" | ./stack4

Вот и всё на экране отобразились две строки «code flow successfully changed» и «Segmentation fault» можно переходить на следующий уровень .
Спасибо за граммотный подход к важной теме,так держать и не сбавлять обороты,я думаю не долог час и место в команде борда будет обеспечено!))Мое уважение.
 
fuzzz

fuzzz

Grey Team
03.02.2019
171
298
Хорошие статьи, понятно и в то же время достаточно глубоко.
Скажите вы планируете рассмотреть решение всех задач реализованных в ВМ protostar и с какой периодичностью будут выходить статьи?
Да все задачи будут рассмотрены и не только эта VM. Статьи будут выходить не часто, но будут =) Сейчас загружен по полной, поэтому будет пауза...
 
Последнее редактирование:
  • Нравится
Реакции: Bringer_the_Light
P

Pavel Shuhray

Happy New Year
14.12.2016
5
0
Прошу прощения, а как скопировать мусорную строку в редактор nano? Или как перенести текстовый файл на виртуальную машину?
 
fuzzz

fuzzz

Grey Team
03.02.2019
171
298
Прошу прощения, а как скопировать мусорную строку в редактор nano? Или как перенести текстовый файл на виртуальную машину?
Всё нормально )) Моя ошибка надо было чуть раньше об этом написать. В шестой части я написал не большую ремарку.
Вот выдержка из статьи

Ремарка

Начиная с этого момента нам будет не очень удобно работать напрямую с VM, т.е. взаимодействовать с ней. Особенно это касается копирование данных. Или когда нам надо запустить два окна сразу... Чтобы это исправить теперь мы будем не просто запускать VM и работать с ней, а теперь мы будем подключаться к VM по SSH используя клиент Putty. Для того, чтобы это сделать, надо в настройках VM в разделе "Сеть" включить виртуальный хост адаптера. После чего запустить VM, дальше выполнить команду
Код:
Код:
/sbin/ifconfig
Затем посмотрев IP-адрес машины (192.168.56.101) подключиться с помощью клиента Putty.

Просто надо подключатся через клиент Putty. Что-то копируешь вне ВМ. Дальше наводишь курсор мыши уже на открытый терминал Putty и нажимаешь правую кнопку мыши. И текст вставится, после копирования.
 
Aleks Binakril

Aleks Binakril

Happy New Year
04.12.2019
25
4
а вашем цикле статей планируется практическое освещение такого инструмента как Метасплоит ?
 
fuzzz

fuzzz

Grey Team
03.02.2019
171
298
а вашем цикле статей планируется практическое освещение такого инструмента как Метасплоит ?
Интересная задумка. Не думал об этом. Использовать его разве, что для генерации шеллкода - полезной нагрузки и для подсчета оффсетов скрипты - pattern_create, pattern_offset... А потом разве что написать свой вариант сплойта в виде модуля. Вы про это имеете введу ? В плане практики ?
 
K

krk

New member
07.12.2019
2
0
Может немного глупый вопрос, но почему массив на 64 байта, а eip начинается с 77?
 
fuzzz

fuzzz

Grey Team
03.02.2019
171
298
М
Может немного глупый вопрос, но почему массив на 64 байта, а eip начинается с 77?
Мне не совсем понятен твой вопрос. Но попробую на него ответить.

Если я правильно вас понял, вопрос звучит так. Почему 77 байт нужно для того чтобы дотянутся до EIP ? чтобы его перезаписать ? Когда буфер всего лишь имеет 64 байта.

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

Компилятор сам решает каждый раз по разному как ему поступить. И ск выделить места дополнительного. Такая уж у него прихоть.
 
R

Rizor

Member
24.06.2019
7
1
Я создал файл offset.txt, затем открыл gdb и прописал r < offset.txt, на что он выдал
"Program received signal SIGSEGV, Segmentation fault.
__strcpy_ssse3 () at ../sysdeps/i386/i686/multiarch/strcpy-ssse3.S:84
84 ../sysdeps/i386/i686/multiarch/strcpy-ssse3.S: No such file or directory."(я думал что что-то с кодом но все нормально и аргументы принимает и запускается)
что делать?
Код ниже к вопросу
Переполнение буфера и перезапись адреса возврата - разработка эксплойтов, часть 5
 
fuzzz

fuzzz

Grey Team
03.02.2019
171
298
Я создал файл offset.txt, затем открыл gdb и прописал r < offset.txt, на что он выдал
"Program received signal SIGSEGV, Segmentation fault.
__strcpy_ssse3 () at ../sysdeps/i386/i686/multiarch/strcpy-ssse3.S:84
84 ../sysdeps/i386/i686/multiarch/strcpy-ssse3.S: No such file or directory."(я думал что что-то с кодом но все нормально и аргументы принимает и запускается)
что делать?
Код ниже к вопросу
Посмотреть вложение 35973
Ошибку "Program received signal SIGSEGV, Segmentation fault" ты получил. Что означает память повреждена. Это уже хорошо!

По идее у тебя должен быть адрес с ошибкой. А что ты передаешь в файле offset.txt программе?

код программы еще знакомый это вроде как Gera Secure Programming
 
Последнее редактирование:
  • Нравится
Реакции: Rizor
R

Rizor

Member
24.06.2019
7
1
Ошибку "Program received signal SIGSEGV, Segmentation fault" ты получил. Что означает память повреждена. Это уже хорошо!

По идее у тебя должен быть адрес с ошибкой. А что ты передаешь в файле offset.txt программе?

код программы еще знакомый это вроде как Gera Secure Programming
в оффсете я передаю множество значений от А до Z, код из курса по реверсу на платформе юдеми
 
fuzzz

fuzzz

Grey Team
03.02.2019
171
298
в оффсете я передаю множество значений от А до Z, код из курса по реверсу на платформе юдеми
Скинь ссылку на курс посмотрю хоть, что там...

Короче смотри... Вот тебе сайт для паттерна креэйта и подсчета

Без файла попробуй сделать то что ты там задумал... Ошибка поэтому и вылезает у тебя потому как запуск с файлом тут не совсем верный. И проще тут сделать передать строку без файла.

Смотри

Переполнение буфера и перезапись адреса возврата - разработка эксплойтов, часть 5


Переполнение буфера и перезапись адреса возврата - разработка эксплойтов, часть 5
 
Последнее редактирование:
  • Нравится
Реакции: Rizor
R

Rizor

Member
24.06.2019
7
1
Скинь ссылку на курс посмотрю хоть, что там...

Короче смотри... Вот тебе сайт для паттерна креэйта и подсчета

Без файла попробуй сделать то что ты там задумал... Ошибка поэтому и вылезает у тебя потому как запуск с файлом тут не совсем верный. И проще тут сделать передать строку без файла.

Смотри

Посмотреть вложение 35979

Посмотреть вложение 35980
Благодарю!
Курс:
 
Мы в соцсетях: