• XSS.stack #1 – первый литературный журнал от юзеров форума

PWN [write-up] No-return - Hack The Box

swagcat228

X-pert
Эксперт
Регистрация
23.12.2019
Сообщения
284
Реакции
232
Депозит
300
Вступление: хочу поблагодарить искренне и от всего кошачего сердца этого человека:
1587901751500.png

Дядюшка Фазз, если бы не ты - я бы никогда не смог стать хакером.

кароче говоря там из гаджетов только безусловные jop-ы и всего один единственный ret;
я подумал, что этот таск - это жопа.
а жопа состоит из двух полужопий.
и между ними нужно вставить ret;

а пиндосы по другому проходят
они сначала ret; кладут

Предварительные ласки
Сам бинарник весит 4.4кб. В нем нету не plt ни got. не залинкована не одна либа. Нету не единой функции, нету даже названия у main().
a_015.png

мда, не густо. стек с защитой от выполнения. ну и aslr на сервере на уровне системы. посмотрим что внутри....

Прилюдия
a_011.png

а что вы знаете про минимализм?
start
vmmap
i fu

a_016.png

жесть. не исполняемый стек, и 4096 байт инструкций. и всё.
сперва появилась идея использовать vdso. такие функции, как time и gettimeofday имеют в себе огромное множество полезных вещей. Но, но на удалёной системе aslr.

задумавшись про ядро я вспомни про dmesg:
a_010.png

мда. исходя из увиденного, можно предположить, что у нас есть буффер. что программа крашится в любом случае. и что если буффер переполнить - то крашится она чуть иначе.
a_007.png

а ещё, что буффер не большой, так как излишки паттерна выплюнуло обратно в терминал.

Раздевайся
a_012.png

Пролог (преамбула) - кладём в стек указатель стекфрейма - $RSP. далее буквально капля кода. есть syscall, это уже хорошо. в конце, вместо эпилога, идёт безусловный переход по адресу, который находится в ячейке памяти на 8 байт меньше, чем ячейка памяти, которую занимает $RSP - указатель стекового фрейма.
зато над основным телом программы есть мёртвая зона с кодом.
a_020.png

тут-то мы и будем искать гаджеты.
но сперва посмотрим на краш под дебагером. и на память в процессе краша.

Экшен!
a_008.png

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

C-подобный:
pwndbg> p 12*8*2
$2 = 192
pwndbg> p 12*8*2 - 16
$3 = 176
и так, у нас есть 176 байт буффера и 2 ячейки памяти по 8 байт за ним. в сумме 192 байта.
Последняя ячейка принадлежит текущему указателю стека - $RSP.
Джамп указывает на предыдущую ячейку памяти, как бы намекая нам на жоп-чейн. Забегая вперёд скажу, что JOP-чейн - это альтернатива привычным ret; чейнам. Используется в ситуациях когда нормальных гаджетов нету, либо когда нужно обойти файрвол или какой-либо модуль защиты на удалённом сервере. Так как про 0x41414141 все знают, то существуют комплексные решения для защиты серверов, которые тут же улавливают подобные вещи и сообщают куда надо. Но это не наш случай. Пока что не наш :3

А в нашем случае нужно делать stack pivoting.
Это методика, которая позволяет создать фейковый стекфрейм (вернее он настоящий, но не предусмотрен самой программой) и создать его в таком месте, где у нас есть возможность управлять памятью. То бишь - расширить себе стек.
It seems that the easiest way to set the stack pointer to a specific value is by using the xchg gadget. It exchanges the EAX and ESP register values. The plan is therefore:
что ещё мы знаем про ассемблер?
mov esp, edx; ret; == push edx; pop esp; ret

пока всё. у нас есть аж 3 мненоники которые позволят нам это осуществить. (вообще их значительно больше, но я знаю пока только эти)

Жоп-Чейн
Я специально оттягивал это на по-позже.
a_021.png

0x\ue\n\n0

a_013.png

эээээээээ..... всё что с сисколлом - сразу нет.
остаётся 4 гаджета. первый - нет, т.к. новый указатель будет в младших байтах от старого (выше).
второй тоже нет, т.к. он не влияет на указатель.
и 3 и 4 тоже не влияют. и шо делать?

a_014.png

ноу-ретурну ret; приделать. :)

и так, логика. у нас в стекфрейме 2 ячейки одна за одной, в первую($psp-8) прыгает каретка ($rip), во вторую ($rsp текущего стекфрейма, основного) мы можем записать данные.
Важное уточнение: от того, что мы туда что-то запишем - стекфрейм не обновится сам по себе. просто данная ячейка будет содержать ререзаписанные данные. Что бы он обновился - его оттуда нужн счиать.
мы берем и записываем в первую ячейку адрес гаджета, а во вторую ячейку - адрес начала нашего нового стекфрейма. каретка попадает на pop rsp, берет значение из текущего стекфрейма (тоесть из следуещей ячейки в данном случае, так как она у нас по совместительству ячейка текущего стекфрейма), и дублирует его в регистр $rsp. после этого стекфрейм сдвигается, а то, что находится в пределах нового стекфрейма - попадает в стек соответственно. И по скольку в ASM инструкции выполняются поочередно и результат их выполнения материализуется мгновенно - дальше, все остальные инструкциии pop, будут брать значения из уже нового стефрейма. Хотя гаджет - один и тот же. Причем цельный. Наверное, это один из самых важных моментов которые я хотел разжевать. Себе и другим :)

a_018.png

это какая-то просто 3/14 + 0x3da
реально, человеческому мозгу сложно думать снизу вверх и справа на лево, ещё и в 16-ричной системе думанья. Но, это эксплойтинг, чёрная магия. А это значит, что вопрос только желания и усилий.
Я решил себе эту задачу с помощью подключения в дело ассоциативного мышления, так как мой мозг не работает в 16-ричной, и ему сложно было счтитать. по этому я расширил регистры своего мозга метамфетамином, подключив мощьность абстрактного мышления.

C-подобный:
( (p64(0x1) + p64(0x2) + p64(0x3) + p64(0x4) + p64(0x5) + p64(0x6) + p64(0x7)) + p64(0x8) + p64(0x9) + p64(0xa) + p64(0xb) + p64(0xc) + p64(0xd) + p64(0xe) + p64(0xf) + p64(0x20) + p64(0x21) + p64(0x22) + p64(0x23) + p64(0x24) + p64(0x25) + p64(0x26) + ) ((( p64(0x00401000)+p64(0x7fffffffe4a8) )))

сперва я сделал вот так. пошёл на уступки всё-так процессору, проявил ссолидарность, и начал говорить с ним на его языке.
a_017.png

a_006.png

он ответил мне взаимностью, и пояснил, что в стеке лежат не данные, а указатели на данные и данные.
то бишь, jmp [rdi] == переход не на инструкцию, которая лежит в $rdi, а на инструкцию, которая лежит по адресу, который лежит в $rdi.

a_019.png

гениальная хрень. как в шахматах или в шашках. всё ради одного региста - rdx
я специально не буду углубляться в подробности самого жоп-чейна, так как эти знания можно взять только в гугле. просто гуглим мнемоники и по немногу понимаем что происходит.

а потом - джамп через rcx. куда?? нам осталось сделать 2 вещи:
1 - положить в $RAX 0x3b (rax - это регистр, в котором хранится номер syscall, который передаётся в syscall() в качестве аргумента и отвечает за то, какой именно сискол будет вызван)
2 - вызваать syscall

ну в rdx мы можем записать 0x3b, сделать exchange, в раксе окажется нужный номер сискола, а в рдх, который нам по сути безразличен, 0 из rax
a_004.png

ага ага. только мы с rcx (#4) прыгаем в жоп.(#5) а с жопа(#5) - в rcx(#5). привет, infloop.

сутки где-то я думал что-же делать.
a_003.png

оказалось просто. вставить ret;
а за ret; положить в память то, что нам нужно. я так до конца и не понял опираясь на что процессор выбирает ячейку из которой ему вздумается взять следующий адрес для rsp (после ret фрейм так же сдвигается), но поскольку мы с ним договорились - он обьяснил мне на пальцах.


a.png

ну и для красаты чисто
Эпилог
В начале бинарник выплёвывает нам лик. это адрес последней ячейки из 192.
a_002.png


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



a_005.png



Это был Кот. Если шо - на связи.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх