
استغلال محلي لجذر النظام لثغرة CVE-2025-21479 (Adreno KGSL) على iQOO Neo8 (SM8475) - قراءة/كتابة الذاكرة الفعلية، تعطيل SELinux، وفتح قشرة جذر (root shell).
كود تجريبي لرفع الامتيازات محليًا (Local Privilege Escalation) لثغرة CVE-2025-21479، يستهدف Qualcomm Adreno GPU (KGSL) على جهاز vivo V2301A (iQOO Neo8 / PD2301، Snapdragon 8+ Gen 1 / SM8475، Adreno A730، Android 14، نواة GKI 5.10).
من حيث المبدأ، يستغل الكود أمر CP_SMMU_TABLE_UPDATE الخاص بـ KGSL للتحكم في SMMU الخاصة بـ GPU، مما يمنح القدرة على قراءة وكتابة أي ذاكرة فيزيائية (Physical Memory)، ومن ثم تعطيل SELinux، وتحديد رموز النواة، والحصول على root shell — دون الحاجة إلى أي تفليش (flashing) طوال العملية، ودون الاعتماد على فتح قفل Bootloader.
لأغراض البحث الأمني فقط على الأجهزة التي تملكها أو لديك الإذن باستخدامها.
and $12, 0x3) يجعله يصنّف حزم Self-Draw-State (SDS) خطأً على أنها RingBuffer (RB). لذلك، عند إرسال أمر CP_SMMU_TABLE_UPDATE مزيف داخل كتلة SDS من وضع المستخدم (user space)، يمكن الكتابة فوق TTBR0 الخاص بـ GPU وجعل جدول صفحات GPU يشير إلى أي صفحة فيزيائية — ومنذ ذلك الحين يمكن لـ GPU قراءة وكتابة أي ذاكرة فيزيائية.stabilize_rw، يتم تقوية التعيين إلى منطقة تعيين ذاتي بحجم 2 MB، مع دعم قراءات سريعة بكميات كبيرة (fast_pread_bytes وغيرها).selinux_state.enforcing في الذاكرة الفيزيائية. ثم تفريغ صورة النواة من العنوان الأساسي الفيزيائي الثابت 0xA8000000 (الحد الافتراضي 43 MB) — لا يحتوي SM8475 على KASLR فيزيائي — مع تحليل بنية kallsyms مباشرةً من التفريغ الخام (مع اكتشاف تلقائي لترتيب البايتات/Endianness)، وبالتالي فإن KASLR الافتراضي لا يؤثر أيضًا.__do_sys_capset باستخدام shellcode من نوع AArch64 (prepare_kernel_cred(0) → commit_creds()، مع تعطيل seccomp). العملية الفرعية الناتجة عن fork تستدعي capset() ← تصبح uid 0 ← تتم استعادة كود النواة المكتوب فوقه ← تعيد العملية الفرعية تنفيذ الأمر الذي تحدده (الافتراضي sh) داخل النطاق u:r:shell:s0. في النهاية، يكون كود النواة مطابقًا بايتًا ببايت لما كان عليه قبل التشغيل.معلومات الجهاز الكاملة موجودة في device_info.txt.
├── source/
│ ├── cheese.c # 漏洞利用主程序(include 了下面两个文件)
│ ├── kallsyms_lookup.c # 针对原始内核映像的 kallsyms 解析器
│ └── adrenaline.h # KGSL ioctl / 命令定义(作者 hawkes)
├── exploit # 预编译 arm64 二进制(动态链接,minSdk 21,
│ # NDK r30-beta2)
├── device_info.txt # 用于调参的目标设备信息
└── README.md
ملاحظة: يقوم cheese.c بعمل #include لكل من kallsyms_lookup.c و adrenaline.h، ويجب وضع الملفات الثلاثة في نفس الدليل عند البناء.
أي إصدار حديث من Android NDK (r25+) سيعمل، مثال:
# 静态链接(自包含、任意 API 等级可运行;体积较大)
aarch64-linux-android34-clang -o cheese source/cheese.c -static
# 动态链接(体积小,依赖设备自带的 libc;API 等级需匹配设备)
aarch64-linux-android34-clang -o exploit_dyn source/cheese.c
# 若要兼容 API 21+ 设备,请改用 aarch64-linux-android21-clang
adb push exploit /data/local/tmp/
adb shell
chmod +x /data/local/tmp/exploit
/data/local/tmp/exploit
اضغط هنا للبرنامج التعليمي المفصل (يحتوي على أداة بنقرة واحدة)
بعد النجاح، ستحصل على shell بصلاحيات uid=0، وسيكون SELinux قد تم تعطيله في هذه الجلسة. بعد اكتمال رفع الامتيازات، تعيد العملية تنفيذ argv[1..] (الافتراضي sh). في كل تشغيل يظهر عد تنازلي ("pte wait 3 second") وتقدّم قراءة 43 MB من النواة ("Read X / 43 MB").

$(find /data/app -type f -name "libksud.so" | grep resukisu) late-load --kmi android12-5.10
الثوابت الخاصة بالجهاز موجودة في أعلى ملف source/cheese.c:
KERNEL_PHYS_BASE — العنوان الأساسي الفيزيائي للنواة (الافتراضي 0xA8000000، SM8475)IDMAP_PG_DIR_OFFSET — إزاحة idmap_pg_dir (الافتراضي 0x245a000)selinux_state_offset — الإزاحة المستخدمة لتحديد موقع selinux_state (الافتراضي 0x729030، العنوان الفيزيائي الفعلي = العنوان الفيزيائي لـ idmap_pg_dir + هذه الإزاحة)gPhyAddrs[] — عناوين فيزيائية أساسية مرشحة تُجرَّب بالتتابعDEFAULT_KERNEL_SIZE_MB — القيمة الاحتياطية / الحد الأقصى لقراءة النواةالمتطلبات المسبقة: GPU من سلسلة Adreno A730/A7xx (البرامج الثابتة المتأثرة)، نواة يمكن استخراج kallsyms منها من التفريغ الفيزيائي، وعدم وجود KASLR فيزيائي.
can't get GPU r/w — فشل تهيئة GPU؛ انتظر أولاً، وإذا لم تنجح فجرّب CHEESE_ATTEMPT=1..n أو حدّد CHEESE_PHYADDR يدويًا؛ ولتصحيح أخطاء محاولة واحدة أضف CHEESE_NO_RETRY=1.CHEESE_READ_FULL_KERNEL=1 (مع توسيع الحد الأقصى عبر CHEESE_KALLSYMS_MAX_MB)، ثم فكّر في CHEESE_FORCE_MANUAL_PATCHFINDER=1.cheese مفتوح المصدر الذي تستند إليه هذه الأداةadrenaline.h من عام 2020، الذي يوفّر تعريفات واجهة ioctlمُعدَّل بناءً على zhuowei/cheese (المستودع الأصلي بدون ترخيص). راجع نشرة الثغرة للمبدأ الكامل.
هذا المستودع مخصص للتعليم والبحث الأمني فقط، استخدمه فقط على الأجهزة التي تملكها أو لديك الإذن باستخدامها. المؤلف غير مسؤول عن أي إساءة استخدام. قد يؤدي استخدام هذه الأداة إلى إبطال الضمان أو انتهاك شروط الخدمة ذات الصلة.
| الطراز | vivo V2301A (iQOO Neo8، الاسم الرمزي PD2301، اللوحة الأم taro) |
| SoC | SM8475 (Snapdragon 8+ Gen 1) + Adreno 730 |
| Android | 14 (UP1A.231005.007، SDK 34، qssi-user) |
| النواة | 5.10.198-gki-gfb2f5774872c |
| SELinux | Enforcing (يتم تعطيله وقت التشغيل بواسطة هذه الأداة) |
| المتغير | الوظيفة |
|---|
CHEESE_PHYADDR=<phys> | تحديد العنوان الأساسي الفيزيائي للنواة إجباريًا (تجاوز جدول الكشف المدمج gPhyAddrs). |
CHEESE_ATTEMPT=<n> | استخدام gPhyAddrs[n] بدلاً من gPhyAddrs[0]؛ عند فشل تهيئة GPU، تقوم maybe_retry بزيادة المحاولة تلقائيًا وإعادة المحاولة. |
CHEESE_SWAPPER_PG_DIR_OFF=<off> | تجاوز فحص swapper_pg_dir واستخدام هذه الإزاحة المطلقة مباشرةً. |
CHEESE_READ_FULL_KERNEL=1 | قراءة صورة النواة كاملة (الحد الأقصى الافتراضي للقراءة هو DEFAULT_KERNEL_SIZE_MB = 43 MB). |
CHEESE_KALLSYMS_MAX_MB=<MB> | تجاوز الحد الأقصى لقراءة kallsyms. |
CHEESE_FORCE_MANUAL_PATCHFINDER=1 | فرض استخدام patchfinder اليدوي كإجراء احتياطي (init_cred، prepare_kernel_cred). |
CHEESE_SKIP_GPU=1 | تخطي مرحلة القراءة/الكتابة عبر GPU (لأغراض التصحيح فقط). |
CHEESE_NO_RETRY=1 | الخروج مباشرةً عند الفشل دون إعادة المحاولة برقم مختلف. |