01 — Two clocks on the same phone
A modern image advertises two patch dates. One is the Android Security Update. The other is a Google Play System Update that refreshes Mainline modules without an OTA. Conscrypt — the default Java TLS provider since Android 9, BoringSSL underneath — lives in that second stream. The CA list can move while the rest of the build number stays still.
02 — Two stores, one winner
Classic intercept assumed one bag of roots: /system/etc/security/cacerts, plus a user store the app opted into. From Android 14 the Conscrypt APEX ships its own cacerts. If that folder is present, it wins. A user cert sitting only in the old path is invisible to the TrustManager the process actually constructed.
03 — Why the lab session looks empty
You installed a user CA. Network Security Config on a lab build even allows user CAs. The handshake still dies. AndroScope traffic + defense modules on an authorized lab process answer the only question that matters: which provider, which store path, which NSC flag. They do not publish a mount kit.
C TRAFFIC & SECRETS 35 Request intelligence ★ E DEFENSE 50 TLS pinning observer ★ session — lab.sample.app [tls] provider: Conscrypt [tls] store class: APEX cacerts present [tls] user CA: installed, not in the loaded list [tls] NSC user-ca flag: on — still not the APEX bag [nsc] cleartext: off · domain pin: none mitsec@lab.sample.app:
04 — Walk the class on the lab build
Authorized lab variant. Traffic module + TLS observer. No second Play app. No other UID.
Print the default SSLContext provider. Conscrypt means the APEX path is in play on current images.
Does the process see an APEX cacerts bag? If yes, that list is the one the handshake uses. The user store is a spectator.
networkSecurityConfig can allow user CAs and still lose to the APEX list. Record both facts. Do not collapse them.
Trigger login or a sync. Request intelligence logs the failure class: pin, unknown CA, or expired. Empty log means you did not hit TLS yet.
The public note ends at store identity. Overlay, rbind, zygote remount, and module names stay off this page.
05 — Older images, same idea
Mainline back-ported an updatable CA bag onto 11–13 via Play System Updates. The Java side still fell back to /system below 14 in several builds. Treat “I copied a cert into /system” as incomplete evidence. Ask the process. AndroScope prints what that process loaded, not what the host overlay claims.
06 — What this is not
- Not a Magisk / KernelSU cookbook.
- Not a TrustAll SSLSocketFactory.
- Not a promise that pinning, RASP, or Integrity fall because the store moved.
- Not a report against a live customer. Demo target is
lab.sample.app.
Fix, on the app side
- Do not treat “user CA allowed” as “we control trust.” Pin what you mean, in NSC or in the stack you actually call.
- If you pin, pin the leaf or the intermediate you own. A store swap should fail closed.
- On the operator side, write the store path into the evidence pack. “Proxy cert installed” is not a finding.