Привет, бандиты! Это Кот.
Вступление
Прошлый мой райт-ап собрал как и положительную критику, так и отрицательную. Среди отрицательной бытует такое мнение, что не гоже писать в паблик рай-апы по активным квестам. Я специально старался писать именно о техниках и особенностях с которыми мне пришлось столкнуться и изучить в процессе прохождения квеста. А не о самом квесте. Дело в том, что в сложных челленджах по PWN собраны реально сильные и наглядные примеры того, с чем можно столкнуться и что нужно знать. По этому я решил писать No-write-апы. Тут не будет мануала по прохождению челленджа, зато я постараюсь рассмотреть с вами те вещи, которые мне показались очень интересными.
Погнали :3
Канарейка должна быть в клетке.
Stack canary. В гугле полно инфы на эту тему. Но я себе в конспекте обозначил это так:
Канарейка всегда начинается с нульбайта. Зачем?
В эпилоге стекфрейма идёт проверка целостности канарейки. А в прологе - она записывается. Если хоть одно пёрышко упадёт с канарейки - процесс аварийно завершается не давая нам возможности исполнить код. Выглядит это примерно вот так:
из спец-регистра в $rax
из $rax в ячейку памяти $rbp-8 (то есть перед адресом дна стека)
и чистим $rax
Это был пролог канарейки.
Ниже - эпилог.
Из ячейки памяти перед $rbp в $rcx
потом ксорим то что есть на то что должно было бы быть
и условный переход (je)
если всё ок - в leave и в ret - в родительский стекфрейм.
если нет - вылетаем с грохотом.
То есть. Какие есть варианты перезаписать что-то, что находится за канарейкой? Да тут только один вариант (на самом деле не один, но с UAF я ещё не разобрался до конца) - положить её на место. Окей, капитан очевидность, а как же ее узнать-то? И тут мы плавно переходим к форкам.
Форки
Раньше были треды, их я не застал, на смену им пришли форки.
Форк - это дочерний процесс. Нужно это для многопоточности. Что бы к серверу могли подключаться и работать сразу несколько клиентов паралельно, а не в порядке живой очереди.
Слева в вернем углу я запустил сервер. Он слушает порт в бесконечном цикле while (true){} - это 6й процесс снизу на фото. После этого я открыл дебаггер, потом открыл ещё одну консоль справа вверху, и подключился неткатом к серверу. В этот момент сервак создаёт форк - свой клон. Вернее часть себя. Ну и последний процесс - это команда ps a.
Так вот. Сервак загружен в память. Абсолютно в разных рандомных сегментах памяти. И он форкает новый процесс.
Дочерний форк - наследует от родительского и канарейку, и пространства памяти. Если бы это был бы syscall(execve) возможно дело бы обстаяло и иначе.
Но в случае с форками - все новые форки - будут абсолютно такими же, как и первый форк.
Все помнять слепую SQL injection? Помните там была техника, когда чар делился на пополам и сравнивался > или <. таким обазом время перебора уменьшалось.
Тоже самое, только по символьно
Зато не придётся брутить сразу все 8 байт. Лично я не мог сначала понять как же так-то?
А потом написал сплоит почти на 300 строк питона. Вот кусочек:
*2 - это не касается канарейки, но касается дальнейшего брута $rbp и ret;
Особенно этому подвержен ret; так как во время брута мы просто на просто можем запустить программу заново. Это не будет являться true для нашего символа.
Но и false мы не дождёмся от сервера. Так что если мы ждём дольше 10 секунд - это false
*1 - дело в том, что питон очень пахабно относится к целостности данных. и может просто отработать быстрее чем прийдёт ответ от сервера. и забить на то, что бы этот ответ получить. учитываем это.
В принципе, я думаю должно быть +- понятно как оно работает. На эту тему много инфы в инете.
Вывод: если у нас есть переполнение буффера в приложжении, где используется fork(), и это приложение реагирует на легетимное завершение и не легетимное завершение по разному (или хотя бы с разным промежутком времени) - это значит, что мы можем посимвольно подобрать все байты трёх значений подряд (canary, $rbp, ret
за довольно короткий промежуток времени. Получить секретную канарейку, да и ещё 2 лика в придачу!!! На стек - $rbp и на сам файл (либо libc) - ret;
Фух. Едем далее.
nano-stack-pivoting
В прошлый раз за адресом возврата у нас была 1 ячейка памяти, если я не ошибаюсь. Мы могли положить туда адрес для ret; и сделать стек пайвотинг. Кстати, можно записать в конспект: стек пайвотинг - это создание фейкового стекфрейма в контроллируемой нами области памяти для того, что бы вместить туда наш эксплоит. Применяется в том случае, когда для нашего эксплоита места не хватает.
Ок, вроде бы понятно. Меняем значение указателя верхушки стека - меняется сам стекфрейм - меняется его содержимое. Считай - прыгаем в свою функцию и там уже делаем что хотим.
Но, для этого нам нужно как миниму ret; гаджет и 1 ячейка памяти для того, что бы туда прыгнуть. А что делать если её нету? А?
Вот представь, есть у тебя буффер, а за ним 8+8+8 байт которые ты можешь перезаписать. канарейка, дно-стека, адрес возврата. И всё. И не байтом больше.
Где-то с вечера и почти до утра я возился с этой задачкой. А потом открыл для себя
получается следующий трюк: мы контролируем канарейку, $rbp и ret;
находим JOP-чейн, знакомый нам с прошлого но-врайт-апа, который прыгает на код который указан по адресу, который лежит в $rbp.
ещё раз: JMP [rbp] == выполнить инструкцию, адрес которой лежит в ячейке памяти, адрес которой лежит в регистре $RBP --- это надо понимать!
Сам буффер мы контролируем. ячейки памяти в нем - тоже. оффсеты мы знаем. лики у нас есть. то есть, мы смело можем сказать что в перезаписанном $RBP будет лежать стековый адрес где-то в начале нашего буффера, а в нём будет лежать уже бинарный (из файла ELF) адрес на гаджет LEAVE; RET;
Который просто, мля, твроит чудеса, и детает нам новый стекфрейм прям там, где выполняется.
Магия.
ну а дальше уже обычный роп-чейн.
FD
Файловые дескрипторы системы linux. их три: ввод, вывод, ошибка --- 0, 1, 2 сответственно
Почитай за них в инете подробнее, дорогой читатель, если с ними ещё не знаком. это удобная и полезная вещь в повседневной жизни.
А вот в нашем контексте - это головная боль. Почему?
потому, что у форков есть один недостаток. Файловые дескрипторы тоже наследуются. Получается, что ввод, вывод и ошибки дочернего форка принадлежат тому терминалу, в котором запущен сервер. А работаем мы с ним - через сокет. (на скрине под номером 6). И прикол в том, что после брутфорса ret; у тебя будет примерно так:
и каждый раз этих мёртвых душ будет разное количество. и среди них выжевших сокетов - тоже будет разное количество.
Ты наверное спросишь, при чём тут файловые дескрипторы ввода-вывода к количеству подвисший дочерних форков?
Смотри: есть процесс сервера, у него всегда есть 4 файловых дескриптора: ввод вывод ошибка и сокет.
железобетонно. Далее, в зависимотси от техники эксплуатации, количества обычных подключенных к серверу людей и тд - количество сокетов ДО твоего сокета - будет разным.
Если ты проэксплуатируешь сервер и запустишь там новый как-бы процесс - он унаследует иерархию файловых дескрипторов. И твой /bin/sh откроется. Но откроется он на сервере. Непоредственно в /dev/pts/1 в данном случае. Так вот, есть такая функция в табличке syscall линкса (ссыль на нее будет в конце статьи) - называется dup2(). Она перенаправляет потоки между файловых дескрипторов, если грубо. Хавает она два параметра. int fd и int fd. И это хреново. Хреново потому, что в стеке обычно есть заветный номер файлового дескриптора твоего твоего сокета. Но, он явно за перделами твоего буффера. То есть ты бы мог на него сослаться, мол, лежит интовое значение по адресу, скажем, $RBP + 0x44.
Но мог бы ты это сделать, если бы у тебя функция dup2() кушала int* fd, int* fd. А кушает, она зараза, исключительно целочисленное значение в чистом виде. и ничего более.
Ты конечно же пойдёшь надёшь какой-нибудь гаджет, скопируешь в регистр это значение и его скормишь уже в dup2() - но что же делать, если нету такого гаджета?
Два варианта. 1 вариант - брутфорс. Причем не просто брутфорс. А брутфорс от меньшего к большему. Дело в том, что если ты попытаешься перенаправить:
dup(6, 0) - из нулевого FD в 6-й FD
dup(6, 1) - из первого FD в 6-й FD
( дескриптор #2 с ошибками мы не трогаем, он нам не к чему)
- то у тебя всё сработает и ты получишь шелл.
если ты сделаешь то же самое, но с 5м - то шелл получит не о чём не подозревающая соседка.
А если ты попробуешь перенаправить из нулевого в седьмой - не существующий (или на тот, на который нету прав) - то ничего не произойдёт. привязка дескрипторов останется на том же месте, где и была до вызова функции с не валидными значениями.
Отсюда следует, что если ты будет проделывать эту махинацию со всеми FD от 4х и до +бесконечности - то шансов у тебя будет значительно больше. Кстати, все эти движения происходят в твоем форке, с которым ты работаешь. пока ты их осуществляешь - для внешнего мира ничего, практически, не меняется. Зато когда ты заспаунишь шелл - он унаследует у этого процесса те дескрипторы, которые привязаны к тем сокетам, которые были на момент вызова тобой шелла из этого процесса. Кароче, перед тем как брать шелл - дескрипторы ввода и вывода должны писать в твой сокет.
mprotect
а что делать, если нету нужных гаджетов. и не работает брутфорс. нету просто места у тебя в буффере перебрать столько значений гаджетами.
или просто ты хочешь сделать по красоте?
тогда будем делать стек исполняемым.
Для этого есть фукнция mprotect либо mmap. но с первой работать проще и подходит она нам больше.
Особенность её в том, что она принимает ссылку на адрес, размер и бит привелегий.
Бит - как и на файл rwx. 0 = ничего, 7 = полный доступ.
размер должен быть кратным. 100. 1000. 21000. так далее.
а вот адрес - адрес должен быть кратным размеру.
другими словами первый параметр долже заканчиваться на 000.
а это - головняк. почему? потому, что во первых оффсеты от базы стека до начала буффера в стеке, да и вообще всего стекфрейма в стеке - могут отличаться в форках.
Во вторых - стек может менять свой размер в ходе работы. И если ты слил адрес со стека когда он был размером 0x21000 - это не гарантирует, что он будет такого же размера, когда ты будешь вызывать mprotect.
решение было простым до безобразия:
во время брутфорса мы получаем адрес дна стека - $rbp
а потом берём, и тупо отрезаем у него 2 последний байта, потом добовляем единицу к крайнему, что бы точно попасть, а вместо отрезанных - ставим нули. и поподаем примерно в наш стек. Не обязательно в начало, но в стек.
А дельше - магия. чёрная магия эксплойтинга.
Вот как было:
Ты только зацени как красиво.
В-ж-ж-жух!
Класс!
После этого есть один не маловажный момент: хоть стек и стал исполняемым - наша каретка - $RIP едет по возвратам из стека. По значениям которые лежат на стеке.
А нам надо ее перевести на код, который лежит в стеке. Поможет нам снова JOP-rop. Джопу - плевать что там на стеке лежит. ему важно что бы у него в адресе был указатель на адрес инструкции, хоть в стеке, хоть в куче. Глваное что бы там бит +X стоял.
Приземляемся мы на так называемый nop-sleed. Посадочная полоса. Просто ленивые хакеры не хотят заморачиваться с выссчитываением адреса, по этому заморачиваются потом нажимая SI SI SI в дебаггере
я зашёл на шелл-шторм и написал простой шелл-код на ASM который копирует значение из ячейки адрес которой лежит в r15 в rdi (в r15 я указатель на дескриптор положил заранее, так как в новом стекфрейме его уже будет найти сложнее)
ну и победный
Ссылки:
/usr/include/asm-generic/unistd.h
⇒ список вызовов syscall
https://filippo.io/linux-syscall-table/
⇒ список вызовов syscall
http://shell-storm.org/online/Online-Assembler-and-Disassembler/
Gr33t1ngzZz: fuzzz, Ralf, ex0dus, IntercepterNG, bilka00
Fucking: mrOkey, mrOkey, mrOkey
На связи :3
p.s. для прохождения квеста нужно ещё разобраться с арифметическим выражением XOR - вот тут можно почитать об этом
m.habr.com
Вступление
Прошлый мой райт-ап собрал как и положительную критику, так и отрицательную. Среди отрицательной бытует такое мнение, что не гоже писать в паблик рай-апы по активным квестам. Я специально старался писать именно о техниках и особенностях с которыми мне пришлось столкнуться и изучить в процессе прохождения квеста. А не о самом квесте. Дело в том, что в сложных челленджах по PWN собраны реально сильные и наглядные примеры того, с чем можно столкнуться и что нужно знать. По этому я решил писать No-write-апы. Тут не будет мануала по прохождению челленджа, зато я постараюсь рассмотреть с вами те вещи, которые мне показались очень интересными.
Погнали :3
Канарейка должна быть в клетке.
Stack canary. В гугле полно инфы на эту тему. Но я себе в конспекте обозначил это так:
прикол в том, что канарейка (не всегда, но за частую) есть в каждом стекфрейме, и она наследуется от родительского стекфрейма (это если грубо, вообще она берется со спец-регистра)Канарейка: способ защиты, обусловлен тем, что после преамбулы, при создании нового стек-фрейма, идёт код,
который копирует значение из регистра FS/GS в ячейку перед указателем дна стека (rbp-8\ebp-4).
Затем в памяти размещается сам RBP/EBP. А следующая ячейка содержит адрес возврата.
Канарейка всегда начинается с нульбайта. Зачем?
'\x00' терминирует строку для всяких считывающих функций, типа scanf().В эпилоге стекфрейма идёт проверка целостности канарейки. А в прологе - она записывается. Если хоть одно пёрышко упадёт с канарейки - процесс аварийно завершается не давая нам возможности исполнить код. Выглядит это примерно вот так:
из спец-регистра в $rax
из $rax в ячейку памяти $rbp-8 (то есть перед адресом дна стека)
и чистим $rax
Это был пролог канарейки.
Ниже - эпилог.
Из ячейки памяти перед $rbp в $rcx
потом ксорим то что есть на то что должно было бы быть
и условный переход (je)
если всё ок - в leave и в ret - в родительский стекфрейм.
если нет - вылетаем с грохотом.
То есть. Какие есть варианты перезаписать что-то, что находится за канарейкой? Да тут только один вариант (на самом деле не один, но с UAF я ещё не разобрался до конца) - положить её на место. Окей, капитан очевидность, а как же ее узнать-то? И тут мы плавно переходим к форкам.
Форки
Раньше были треды, их я не застал, на смену им пришли форки.
Форк - это дочерний процесс. Нужно это для многопоточности. Что бы к серверу могли подключаться и работать сразу несколько клиентов паралельно, а не в порядке живой очереди.
Слева в вернем углу я запустил сервер. Он слушает порт в бесконечном цикле while (true){} - это 6й процесс снизу на фото. После этого я открыл дебаггер, потом открыл ещё одну консоль справа вверху, и подключился неткатом к серверу. В этот момент сервак создаёт форк - свой клон. Вернее часть себя. Ну и последний процесс - это команда ps a.
Так вот. Сервак загружен в память. Абсолютно в разных рандомных сегментах памяти. И он форкает новый процесс.
Дочерний форк - наследует от родительского и канарейку, и пространства памяти. Если бы это был бы syscall(execve) возможно дело бы обстаяло и иначе.
Но в случае с форками - все новые форки - будут абсолютно такими же, как и первый форк.
Все помнять слепую SQL injection? Помните там была техника, когда чар делился на пополам и сравнивался > или <. таким обазом время перебора уменьшалось.
Тоже самое, только по символьно
А потом написал сплоит почти на 300 строк питона. Вот кусочек:
*2 - это не касается канарейки, но касается дальнейшего брута $rbp и ret;
Особенно этому подвержен ret; так как во время брута мы просто на просто можем запустить программу заново. Это не будет являться true для нашего символа.
Но и false мы не дождёмся от сервера. Так что если мы ждём дольше 10 секунд - это false
*1 - дело в том, что питон очень пахабно относится к целостности данных. и может просто отработать быстрее чем прийдёт ответ от сервера. и забить на то, что бы этот ответ получить. учитываем это.
В принципе, я думаю должно быть +- понятно как оно работает. На эту тему много инфы в инете.
Вывод: если у нас есть переполнение буффера в приложжении, где используется fork(), и это приложение реагирует на легетимное завершение и не легетимное завершение по разному (или хотя бы с разным промежутком времени) - это значит, что мы можем посимвольно подобрать все байты трёх значений подряд (canary, $rbp, ret
Фух. Едем далее.
nano-stack-pivoting
В прошлый раз за адресом возврата у нас была 1 ячейка памяти, если я не ошибаюсь. Мы могли положить туда адрес для ret; и сделать стек пайвотинг. Кстати, можно записать в конспект: стек пайвотинг - это создание фейкового стекфрейма в контроллируемой нами области памяти для того, что бы вместить туда наш эксплоит. Применяется в том случае, когда для нашего эксплоита места не хватает.
Другими словами, что бы осуществить атаку типа STACK PIVOTING нам нужно заменить содержимое RSP/ESP регистра на значение, которое нам подходит. А именно: сдвигает верхушку стека таким образом, что бы мы могли направить RIP/EIP на “рельсу” нашего чейна. В качестве нового значения для указателя стека (RSP/ESP) может быть практически что угодно, включая HEAP.
В боевых условиях, нам понадобится минималистичный хOP чейн, что бы выполнить инструкции, эквивалентные следующим:
pop esp; ret + payload_addr_on_the_heap
А это говорит о том, что наша “рельса” может находится сразу в начале RSP/ESP
Ок, вроде бы понятно. Меняем значение указателя верхушки стека - меняется сам стекфрейм - меняется его содержимое. Считай - прыгаем в свою функцию и там уже делаем что хотим.
Но, для этого нам нужно как миниму ret; гаджет и 1 ячейка памяти для того, что бы туда прыгнуть. А что делать если её нету? А?
Вот представь, есть у тебя буффер, а за ним 8+8+8 байт которые ты можешь перезаписать. канарейка, дно-стека, адрес возврата. И всё. И не байтом больше.
Где-то с вечера и почти до утра я возился с этой задачкой. А потом открыл для себя
LEAVE; RET;
получается следующий трюк: мы контролируем канарейку, $rbp и ret;
находим JOP-чейн, знакомый нам с прошлого но-врайт-апа, который прыгает на код который указан по адресу, который лежит в $rbp.
ещё раз: JMP [rbp] == выполнить инструкцию, адрес которой лежит в ячейке памяти, адрес которой лежит в регистре $RBP --- это надо понимать!
Сам буффер мы контролируем. ячейки памяти в нем - тоже. оффсеты мы знаем. лики у нас есть. то есть, мы смело можем сказать что в перезаписанном $RBP будет лежать стековый адрес где-то в начале нашего буффера, а в нём будет лежать уже бинарный (из файла ELF) адрес на гаджет LEAVE; RET;
Который просто, мля, твроит чудеса, и детает нам новый стекфрейм прям там, где выполняется.
Магия.
ну а дальше уже обычный роп-чейн.
FD
Файловые дескрипторы системы linux. их три: ввод, вывод, ошибка --- 0, 1, 2 сответственно
Почитай за них в инете подробнее, дорогой читатель, если с ними ещё не знаком. это удобная и полезная вещь в повседневной жизни.
А вот в нашем контексте - это головная боль. Почему?
потому, что у форков есть один недостаток. Файловые дескрипторы тоже наследуются. Получается, что ввод, вывод и ошибки дочернего форка принадлежат тому терминалу, в котором запущен сервер. А работаем мы с ним - через сокет. (на скрине под номером 6). И прикол в том, что после брутфорса ret; у тебя будет примерно так:
и каждый раз этих мёртвых душ будет разное количество. и среди них выжевших сокетов - тоже будет разное количество.
Ты наверное спросишь, при чём тут файловые дескрипторы ввода-вывода к количеству подвисший дочерних форков?
Смотри: есть процесс сервера, у него всегда есть 4 файловых дескриптора: ввод вывод ошибка и сокет.
железобетонно. Далее, в зависимотси от техники эксплуатации, количества обычных подключенных к серверу людей и тд - количество сокетов ДО твоего сокета - будет разным.
Если ты проэксплуатируешь сервер и запустишь там новый как-бы процесс - он унаследует иерархию файловых дескрипторов. И твой /bin/sh откроется. Но откроется он на сервере. Непоредственно в /dev/pts/1 в данном случае. Так вот, есть такая функция в табличке syscall линкса (ссыль на нее будет в конце статьи) - называется dup2(). Она перенаправляет потоки между файловых дескрипторов, если грубо. Хавает она два параметра. int fd и int fd. И это хреново. Хреново потому, что в стеке обычно есть заветный номер файлового дескриптора твоего твоего сокета. Но, он явно за перделами твоего буффера. То есть ты бы мог на него сослаться, мол, лежит интовое значение по адресу, скажем, $RBP + 0x44.
Но мог бы ты это сделать, если бы у тебя функция dup2() кушала int* fd, int* fd. А кушает, она зараза, исключительно целочисленное значение в чистом виде. и ничего более.
Ты конечно же пойдёшь надёшь какой-нибудь гаджет, скопируешь в регистр это значение и его скормишь уже в dup2() - но что же делать, если нету такого гаджета?
Два варианта. 1 вариант - брутфорс. Причем не просто брутфорс. А брутфорс от меньшего к большему. Дело в том, что если ты попытаешься перенаправить:
dup(6, 0) - из нулевого FD в 6-й FD
dup(6, 1) - из первого FD в 6-й FD
( дескриптор #2 с ошибками мы не трогаем, он нам не к чему)
- то у тебя всё сработает и ты получишь шелл.
если ты сделаешь то же самое, но с 5м - то шелл получит не о чём не подозревающая соседка.
А если ты попробуешь перенаправить из нулевого в седьмой - не существующий (или на тот, на который нету прав) - то ничего не произойдёт. привязка дескрипторов останется на том же месте, где и была до вызова функции с не валидными значениями.
Отсюда следует, что если ты будет проделывать эту махинацию со всеми FD от 4х и до +бесконечности - то шансов у тебя будет значительно больше. Кстати, все эти движения происходят в твоем форке, с которым ты работаешь. пока ты их осуществляешь - для внешнего мира ничего, практически, не меняется. Зато когда ты заспаунишь шелл - он унаследует у этого процесса те дескрипторы, которые привязаны к тем сокетам, которые были на момент вызова тобой шелла из этого процесса. Кароче, перед тем как брать шелл - дескрипторы ввода и вывода должны писать в твой сокет.
mprotect
а что делать, если нету нужных гаджетов. и не работает брутфорс. нету просто места у тебя в буффере перебрать столько значений гаджетами.
или просто ты хочешь сделать по красоте?
тогда будем делать стек исполняемым.
Для этого есть фукнция mprotect либо mmap. но с первой работать проще и подходит она нам больше.
Особенность её в том, что она принимает ссылку на адрес, размер и бит привелегий.
Бит - как и на файл rwx. 0 = ничего, 7 = полный доступ.
размер должен быть кратным. 100. 1000. 21000. так далее.
а вот адрес - адрес должен быть кратным размеру.
другими словами первый параметр долже заканчиваться на 000.
а это - головняк. почему? потому, что во первых оффсеты от базы стека до начала буффера в стеке, да и вообще всего стекфрейма в стеке - могут отличаться в форках.
Во вторых - стек может менять свой размер в ходе работы. И если ты слил адрес со стека когда он был размером 0x21000 - это не гарантирует, что он будет такого же размера, когда ты будешь вызывать mprotect.
решение было простым до безобразия:
во время брутфорса мы получаем адрес дна стека - $rbp
а потом берём, и тупо отрезаем у него 2 последний байта, потом добовляем единицу к крайнему, что бы точно попасть, а вместо отрезанных - ставим нули. и поподаем примерно в наш стек. Не обязательно в начало, но в стек.
А дельше - магия. чёрная магия эксплойтинга.
Вот как было:
Ты только зацени как красиво.
В-ж-ж-жух!
Класс!
После этого есть один не маловажный момент: хоть стек и стал исполняемым - наша каретка - $RIP едет по возвратам из стека. По значениям которые лежат на стеке.
А нам надо ее перевести на код, который лежит в стеке. Поможет нам снова JOP-rop. Джопу - плевать что там на стеке лежит. ему важно что бы у него в адресе был указатель на адрес инструкции, хоть в стеке, хоть в куче. Глваное что бы там бит +X стоял.
Приземляемся мы на так называемый nop-sleed. Посадочная полоса. Просто ленивые хакеры не хотят заморачиваться с выссчитываением адреса, по этому заморачиваются потом нажимая SI SI SI в дебаггере
я зашёл на шелл-шторм и написал простой шелл-код на ASM который копирует значение из ячейки адрес которой лежит в r15 в rdi (в r15 я указатель на дескриптор положил заранее, так как в новом стекфрейме его уже будет найти сложнее)
ну и победный
Ссылки:
/usr/include/asm-generic/unistd.h
⇒ список вызовов syscall
https://filippo.io/linux-syscall-table/
⇒ список вызовов syscall
http://shell-storm.org/online/Online-Assembler-and-Disassembler/
Gr33t1ngzZz: fuzzz, Ralf, ex0dus, IntercepterNG, bilka00
Fucking: mrOkey, mrOkey, mrOkey
На связи :3
p.s. для прохождения квеста нужно ещё разобраться с арифметическим выражением XOR - вот тут можно почитать об этом
Crypt, XOR, взлом нешифрованного ZIP и ГПСЧ. Решение задач с r0от-мi Crypto. Часть 2
В данной статье узнаем про функцию crypt, узнаем как подбирать пароли к ZIP архиву с незашифрованными именами файлов, познакомимся с утилитой xortool, а так же...
Вложения
Последнее редактирование модератором: