009: Closure wave 3 deblob, handoff, and optional wave4/malloc bacons.
Ship script gates wave 4 behind AXP_DEBLOB_WAVE4, documents secrets cert path, and adds deblob appliers plus tests; docs capture Swissquote fix and parity. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
deaaf42caa
commit
3f941e8451
64 files changed
+6270
-86
No files matched your search
@@ -0,0 +1,180 @@
|
||||
# Swissquote differential investigation — 2026-10-09
|
||||
|
||||
Status: **failure classified, underlying root-detection signal not yet isolated**.
|
||||
Live observations approximately 15:18–15:26 Europe/Berlin. This supersedes
|
||||
the Swissquote runtime inventory in `009-handoff-20261008.md`, not its
|
||||
build/recovery instructions. No firmware was flashed and GOLDEN was read-only.
|
||||
|
||||
## Confirmed new finding
|
||||
|
||||
Swissquote 9.11.0 stores this nonfatal exception on LAB, Island user 10:
|
||||
|
||||
```text
|
||||
com.swissquote.android.FreeRaspException
|
||||
freeRASP: onPrivilegedAccess
|
||||
com.swissquote.android.ThreatDetectedListenerKt$getThreatDetectedListener$1.onPrivilegedAccess
|
||||
ThreatDetectedListener.kt:24
|
||||
app.talsec.rasp.security.api.ThreatListener.onReceive
|
||||
```
|
||||
|
||||
This was read from the app's existing local Crashlytics event JSON. Only
|
||||
the exception classification and stack symbols were retained; no raw
|
||||
Crashlytics report, account data, keys, or session identifiers enter Git.
|
||||
`scripts/009-read-swissquote-rasp.py --device LAB` reproduces that limited,
|
||||
read-only inspection. Retained events may be uploaded/removed by the app;
|
||||
an empty result does not prove compatibility or that no callback occurred.
|
||||
|
||||
Static inspection of the installed APK established that **-69 is an
|
||||
application-defined `RASP_FAILING_EXIT_CODE`**, shared by four handlers:
|
||||
`onPrivilegedAccess`, `onDebug`, `onAppIntegrity`, and `onHooks`. Each records
|
||||
a distinct FreeRaspException, requests the same warning, then calls
|
||||
`System.exit(-69)`. Consequently the exit code alone was not enough to
|
||||
attribute the failure to root. The retained exception now identifies the
|
||||
privileged-access branch for the observed LAB failure. It does not identify
|
||||
which underlying native check fired or exclude additional failures later.
|
||||
|
||||
The APK contains Talsec `v19_2_0_fullRelease` markers and loads `libts.so`.
|
||||
`libsigner.so` belongs to the separately present Adjust signer integration;
|
||||
loading it is not evidence that it caused this exit.
|
||||
|
||||
The previously suppressed toast was recovered by temporarily granting
|
||||
POST_NOTIFICATIONS to LAB user 10, then revoking it in a finally block.
|
||||
Its text lists generic device-safety requirements (root, debugging,
|
||||
emulation, modification, malware, official store); it does **not** disclose
|
||||
the failing check. A later observation showed the permission granted with
|
||||
USER_SET, apparently from concurrent user activity; it was not overwritten.
|
||||
|
||||
## Current comparison, not the earlier handoff inventory
|
||||
|
||||
- Both devices have Swissquote **9.11.0 / 91100003**, target SDK 37,
|
||||
arm64, with Play Store as installer and initiating package.
|
||||
- All three installed APKs match byte for byte:
|
||||
- base: `734293c7b6a7ecf48117e23ffc72ad0a1e3279336025551faf23c1c3c239c8f2`
|
||||
- arm64 split: `6ce67d98a2a4dc4b1ac38a9b3c864e11167b17ba9ed62e8a9d69cf48cb3e090d`
|
||||
- density split: `4e0ab62d8eb7c2faa959014552204c8378977459109b3ba2a0b400a87bb9c3a2`
|
||||
- Both now have Swissquote installed for user 0 **and** user 10. The earlier
|
||||
statement that GOLDEN has it only for user 10 is obsolete.
|
||||
- Island matches: `6.4.2-d46911c9`, versionCode 51801.
|
||||
- GOLDEN: Android 15 / SDK 35, Magisk 29.0. LAB: Android 16 / SDK 36,
|
||||
SDK_FULL 36.1, Magisk 30.7, deblob4 slot b.
|
||||
- Both are user/release-keys with `ro.debuggable=0`, SELinux Enforcing,
|
||||
Zygisk enabled and DenyList **enforced**. Both list the main Swissquote
|
||||
process and its `_zygote` entry. LAB logs the DenyList hit for user 10.
|
||||
- Both expose `verifiedbootstate=orange`, `vbmeta.device_state=unlocked`,
|
||||
`flash.locked=0`, and `veritymode=enforcing` at this measurement.
|
||||
Both protect `/proc/cmdline` with mode 0440.
|
||||
- GOLDEN has only hosts and flag-secure-disabler modules installed.
|
||||
LAB has hosts plus PIF and TEESimulator directories; both latter modules
|
||||
have **disable markers**. Directory presence is not active-module proof.
|
||||
- Owner confirmed GOLDEN Swissquote still opens in Island in this state.
|
||||
Its existing user-10 process was alive during read-only inspection.
|
||||
- Shell developer/ADB settings are enabled on both. No enabled
|
||||
accessibility service was returned for user 10 on either device.
|
||||
|
||||
This is strong evidence against treating spoofed green/locked properties,
|
||||
PIF, or TEESimulator as prerequisites for the observed GOLDEN success.
|
||||
It does not prove that every path through either banking app is independent
|
||||
of attestation.
|
||||
|
||||
## Process-level observations and controlled experiment
|
||||
|
||||
Read `/proc/<Swissquote PID>/mountinfo` and selected maps using root, without
|
||||
attaching a debugger or modifying the banking process. Both mount tables
|
||||
lacked obvious Magisk/module mounts; neither selected maps output contained
|
||||
Magisk/Zygisk/LSPosed libraries. The injected `/system_ext/bin/magisk` was
|
||||
absent beneath each inspected process's `/proc/<PID>/root` view even though
|
||||
it exists in the root shell's view. This supports actual unmounting, beyond
|
||||
merely checking DenyList configuration. It **does not** prove absence of
|
||||
all detectable root signals, sockets, policy changes, or runtime effects.
|
||||
|
||||
Important real ROM difference: GOLDEN has no Yama ptrace_scope node; LAB
|
||||
has Yama enabled and scope 2. The local `apply_009_yama_ptrace.py` already
|
||||
documents that the AXP init rule was inert on GOLDEN's kernel.
|
||||
|
||||
At 15:22, changed **only** LAB's transient ptrace_scope from 2 to 0, cold
|
||||
started Swissquote in user 10, and observed `System.exit(-69)` again.
|
||||
Restored scope **2**, with readback. No persistent property was changed.
|
||||
Thus relaxing ptrace_scope alone does not fix the observed failure; this
|
||||
is not a blanket clearance of every kernel-hardening difference.
|
||||
|
||||
## Why GOLDEN passes: what is known and what remains open
|
||||
|
||||
GOLDEN's actual process environment satisfies Swissquote's startup checks
|
||||
despite unlocked/orange state, Magisk, and USB debugging. LAB triggers the
|
||||
privileged-access callback despite working basic DenyList unmounting.
|
||||
The APK and Island version are controlled; the operating system, kernel,
|
||||
SELinux policy and Magisk implementation are **not** controlled yet.
|
||||
|
||||
Leading branches for investigation, not established causes:
|
||||
|
||||
1. A Magisk 29 → 30.7 difference leaves a root-detection surface after
|
||||
unmounting. Upstream changed file injection, mounting and sepolicy-related
|
||||
handling; simply seeing the same DenyList checkboxes cannot exclude this.
|
||||
2. A ROM/kernel/SELinux difference is interpreted as privileged access by
|
||||
Talsec on Android 16. Yama scope alone has been tested unsuccessfully.
|
||||
3. App-visible package/file/policy differences or retained per-install state.
|
||||
Package listings from shell are not automatically app-visible listings.
|
||||
|
||||
Do not claim “Magisk 30.7 is broken” from upstream issue 9866: that report
|
||||
had **Enforce DenyList OFF**, unlike this LAB, and was closed not planned.
|
||||
Do not infer a specific Talsec false positive from unrelated hardware-
|
||||
attestation reports. Do not mask callbacks, patch Swissquote, or transfer
|
||||
GOLDEN banking data as a diagnostic shortcut.
|
||||
|
||||
## Prepared next discriminator — same build, without Magisk
|
||||
|
||||
The current artifact directory is:
|
||||
|
||||
`/srv/rom/artifacts/009-axp-los23-port/20261009T120025Z-axp-stage1-combo4-cve-hi-releasekeys/`
|
||||
|
||||
Unpacked both boot images with the host's magiskboot. Verified:
|
||||
|
||||
- `boot.img`: SHA256 `e5822d8e8cbb39fe74ca638cdfcd52cd66fc741d130bdf962f433ae4f16299d2`;
|
||||
ramdisk `cpio test` = **1**, Magisk patched.
|
||||
- `boot-make-out.img`: SHA256
|
||||
`6caceaca1e911d327e7ad86217cc6bedb8cd32fe690cadd11c9cedeed0440d1b`;
|
||||
ramdisk `cpio test` = **0**, stock/unpatched.
|
||||
- The extracted kernel SHA256 is identical in both:
|
||||
`e093a3c7edfb2560f109c0b3df68d2722a1e05f06357316d1278b8c0348e8f89`.
|
||||
|
||||
This prepares a **temporary LAB-only boot experiment**, subject to owner
|
||||
agreement and confirming the phone is still on this same build. No image
|
||||
has been booted or flashed in this investigation. Preserve data, vendor SPL,
|
||||
the active slot and the installed APK; do not reinstall/reset the app.
|
||||
Prefer booting the image temporarily if the bootloader supports that path.
|
||||
The stock image has not yet been boot-tested; a verified hash is not boot
|
||||
acceptance. Confirm actual absence of running Magisk after boot before
|
||||
interpreting the Swissquote result; the manager APK and historical `/data`
|
||||
files can remain, so this is not a clean factory/rootless baseline.
|
||||
|
||||
- If it starts, focus on active Magisk integration; consider a compatible,
|
||||
isolated comparison against GOLDEN's version, not a blind ship downgrade.
|
||||
- If it still exits, reclassify the failure before investigating ROM-visible
|
||||
surfaces. Rootless boot failure alone would not prove a Talsec bug.
|
||||
- Return to the installed boot and repeat the original failure for an A/B/A
|
||||
comparison. A process surviving briefly is not a usable-session PASS.
|
||||
|
||||
LAB became unavailable in ADB during investigation. On the next turn the
|
||||
owner confirmed it had been reconnected and authorized the remaining tests.
|
||||
USB enumeration then confirmed LAB was physically present at 3-5.3 as
|
||||
18d1:4ee1 (MTP only), while GOLDEN was 18d1:4ee2 (MTP + debug). The earlier
|
||||
user-facing assertion that LAB was disconnected was too broad: USB was
|
||||
present, but the ADB interface was unavailable. Requested that the owner
|
||||
enable USB debugging on LAB; do not mistake GOLDEN's ADB presence for LAB.
|
||||
|
||||
## Sources and audit trail
|
||||
|
||||
- Project: `009-handoff-20261008.md`, constitution, feature 009 spec,
|
||||
Magisk runbook, current tracked diff and new patch scripts. Relevant recent
|
||||
changes include user/release signing, AXP identity and vendor-SPL guard,
|
||||
user-build signature spoofing, cmdline permissions, AVB flags 0, malloc
|
||||
2-GiB regions, kernel ports/forward-only patching, Yama, USERFAULTFD off,
|
||||
Settings/privacy ports and two deblob waves. No existing work was reverted.
|
||||
- [Talsec callback API](https://docs.talsec.app/freerasp/freerasp/integration/flutter/api)
|
||||
- [Magisk 30.7 DenyList source](https://github.com/topjohnwu/Magisk/blob/v30.7/native/src/core/deny/utils.cpp)
|
||||
- [Magisk changelog](https://github.com/topjohnwu/Magisk/blob/v30.7/docs/changes.md)
|
||||
- [Magisk issue 9866, different enforcement configuration](https://github.com/topjohnwu/Magisk/issues/9866)
|
||||
- [Adjust upstream libsigner reference](https://github.com/adjust/android_sdk/issues/655)
|
||||
- Local scratch: `/tmp/axp-swissquote-20261009/`. Proprietary APKs and
|
||||
decompiled files remain outside the repository and must not be committed.
|
||||
Partial full-APK decompilation was replaced by targeted inspection.
|
||||
Reference in new issue
Block a user