Restore disable_configstore, EuiccDisabler and thermal CDEV guards, branding and rebuild/sync helpers; document device roles, laptop migration, and Gitea. Co-authored-by: Cursor <cursoragent@cursor.com>
184 lines
10 KiB
Markdown
184 lines
10 KiB
Markdown
# Swissquote differential investigation — 2026-10-09
|
||
|
||
> **Outcome (same day, wave 3):** Swissquote **PASS** in Island on LAB after
|
||
> Lineage tools removal — see [`009-closure-parity-20261009.md`](009-closure-parity-20261009.md).
|
||
> Body below is the **pre-PASS** forensics log only.
|
||
|
||
Status (investigation snapshot): failure classified before wave 3.
|
||
Live observations approximately 15:18–15:26 Europe/Berlin. Build/recovery
|
||
instructions in `009-handoff-20261008.md` still apply. 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.
|