The trick
Split APKs, feature modules, and “update the plugin” paths all end the same way: bytes land under the app's data dir, then DexClassLoader / PathClassLoader maps them into the process. If that file is writable by someone other than a verified pack — world-writable cache, unzip without path checks, overlay from an exported provider — the next cold start runs their code as the app.
Play-style extractors have shipped this class when the unzip target was the same directory the loader trusted. Lab target is lab.sample.app. No extractor name, no payload DEX.
D NATIVE & RUNTIME 44 JNI / loader observer session — lab.sample.app [load] DexClassLoader [load] path under app files — writable after extract [load] no APK signature check on that file [load] bytes not dumped on this page mitsec@lab.sample.app:
What AndroScope is allowed to say
The runtime observer prints the loader class and whether the path sits in a writable tree. It does not drop a replacement DEX. Persistence after reboot is a second finding only if the same file is loaded again — write that as two rows, not as a kit.
Fix
- Load only from the APK / a verified split. Do not load from cache after an unzip.
- If you must extract, verify a signature you hold before the loader runs.
- Directory traversal in the unzip is a sibling bug. Fail closed on
... - Mark extracted files private to the UID. Never world-writable.