spike/cfe-app
needs attention0c7f4d4 · incrementalpre-PRreviewed 2026-07-06 16:44 UTC12H · 11M · 12L- Purpose
- Recover the RPU→titleholder name lookup API endpoint from the CFE Contigo mobile app (mx.com.cfe.cfecontigo 4.6.0), which CFE deleted from its public web scraper surface in 2026. The mobile backend still resolves titular from RPU alone via ServiceInfoResponse. Goal: find host+path+auth without a rooted device (static OSINT + deobfuscation), then probe server-side.
- Goal
- Staged capture toolkit for the Apple Silicon Mac handoff: arm64 emulator + Frida OkHttp hook to intercept the login+service-info requests pre-TLS, confirming whether the read endpoint requires device attestation (feasibility pivot).
- Sub-goals
- SG-1 ✅ — Static analysis of CFE Contigo 4.6.0 APK (OkHttp/Retrofit, DexGuard string-encryption, DTOs confirm server-resolved titular)
- SG-2 [~] BLOCKED — MITM capture harness built; blocked by x86 emulator SIGSEGV, iOS force-close, non-root Android unrepackageable
- SG-4 ✅ — Feasibility verdict HTML: DON'T ADOPT for headless add-service (attestation blocks replayability)
- SG-5 [ ] — Stretch PoC (depends on SG-3)
- SG-6 [~] PARTIAL — OSINT found appcfecontigo.cfe.mx (live, Imperva-fronted); DexGuard static decryption ruled out (reflection tier); x86_64 emulator ruled out; arm64 + Frida staged; Mac handoff dossier written
- What
- This incremental diff adds all 11 research files since the initial branch provision: SG-1 decompile notes, SG-2 mitmproxy addon + Frida scripts + emulator setup + runbook, SG-4 findings HTML, and the SG-6 OSINT notes + Apple Silicon Mac handoff dossier + Frida OkHttp hook. The branch scope and intent log were also updated to reflect the SG-4→SG-6 pivot.
- Why
- CFE deleted the public IncrementoDeCarga resolver (RPU→name) in 2026. The scraper's registerRpu() now requires the titleholder name as an out-of-band input. The mobile app proves the server still resolves it — recovering that endpoint is the forwward path to restoring automated RPU registration without user-supplied names.
- Areas
- .branch/+130−65docs/spikes/cfe-mobile-app/+132−0scripts/cfe/mobile/+601−0
- Blast
- 11 files, +863/-65 lines across 3 areas. Pure research spike — no production code, no domain entities, no handlers, no schema changes. All files are under scripts/, docs/spikes/, and .branch/.
Findings · 35
correctness16
bodyToStr() drains one-shot RequestBody — breaks POST login/add-service
scripts/cfe/mobile/frida-okhttp.js:22
b.writeTo(buf) on a one-shot RequestBody drains the okio.Source. Both newCall and proceed hooks call logReq, so every POST body is consumed twice — the live app's login POST lands empty → 400/401. Fix: use b.peek().readUtf8() instead.
getAcceptedIssuers returns JS [] not Java X509Certificate[0] — ClassCastException in Conscrypt
scripts/cfe/mobile/frida-unpin.js:45
Conscrypt casts the return value to X509Certificate[] — type mismatch throws ClassCastException inside SSL handshake, causing TLS to fail silently. Fix: return Java.use('[Ljavax.net.ssl.X509Certificate;').$new(0).
wait_online/wait_boot loops never detect emulator process death — hang for full timeout
scripts/cfe/mobile/setup-emulator.sh:32
If the emulator dies (GPU crash, AVD error), loops run for 120–150 s before failing with no indication of the cause. Fix: save emulator PID and check kill -0 $EMU_PID inside the loop.
mapfile -t requires Bash 4.x; macOS ships Bash 3.2 — APKS array stays empty
scripts/cfe/mobile/setup-emulator.sh:65
Script exits '!! no .apk files in $APK_DIR' even when APKs exist. Primary target is macOS (Apple Silicon). Fix: APKS=( "$APK_DIR"/*.apk ) (POSIX-compatible).
frida-server download URL may include build suffix from 'frida --version' → 404
scripts/cfe/mobile/mac-capture-dossier.md:82
pip-installed frida returns '16.2.1 (build …)'; GitHub release tag is '16.2.1'. Fix: FV=$(frida --version | awk '{print $1}').
XAPK glob '*.apk' misses APKs stored in APKs/ subdirectory — zero APKs installed
scripts/cfe/mobile/mac-capture-dossier.md:73
APKPure XAPKs commonly store splits under APKs/. Fix: unzip -d cfe/ then find cfe -name '*.apk' for install-multiple.
Double-logging: newCall + every RealInterceptorChain.proceed prints the same request 4-5 times
scripts/cfe/mobile/frida-okhttp.js
A single request produces 1 newCall + 4-5 proceed prints, making it hard to identify the actual outgoing network call.
TrustManagerImpl.checkTrustedRecursive 6-arg overload may not match Android 34 — false-positive 'hooked' log
scripts/cfe/mobile/frida-unpin.js:23
Android 34 changed this method signature. If overload mismatches, Frida silently skips it but still logs 'hooked'.
_is_noise() endswith(s) without dot prefix — could suppress CFE traffic matching a noise suffix
scripts/cfe/mobile/mitm-cfe.py:47
host.endswith(s) (no dot prefix) is structurally overly broad. Remove the bare endswith(s) clause — redundant with endswith('.' + s).
Fixed sleep 4 after adb root instead of polling — may leave adbd offline
scripts/cfe/mobile/setup-emulator.sh:54
On a slow host 4 s is not enough for adbd restart. Fix: adb root && wait_online 90.
tmpfs mount blocked by SELinux enforcing — CA install silently fails, 'installed CA' false positive
scripts/cfe/mobile/setup-emulator.sh:58
ls verification only checks /data/local/tmp/mc/ (pre-mount path). Fix: setenforce 0 before mount; split shell chain to check each exit code.
adb shell 'fs &' — SIGHUP kills frida-server when adb session ends
scripts/cfe/mobile/mac-capture-dossier.md:86
Fix: nohup /data/local/tmp/fs > /dev/null 2>&1 & or keep a foreground adb shell terminal.
RealInterceptorChain hook failure silently swallowed — operator not told responses are not captured
scripts/cfe/mobile/frida-okhttp.js:57
catch(e){} with no log. Add TAG+' RealInterceptorChain hook skipped: '+e so operator knows they have request-only data.
HostnameVerifier hook targets static default — does not affect OkHttp3 per-client verifier
scripts/cfe/mobile/frida-unpin.js:71
Comment 'HostnameVerifier — accept all' overstates effect. OkHttp3 stores verifier on builder instance, not static default.
response-only hook misses TLS-layer rejections — attestation block would be invisible
scripts/cfe/mobile/mitm-cfe.py
If the service-info call is rejected at TLS layer, no HTTP response fires and the flow is silently dropped. Runbook should note this gap.
Missing fh.flush() — partial JSONL record on crash
scripts/cfe/mobile/mitm-cfe.py
Buffered I/O without flush; on CPython the write may not reach disk before a crash. Add fh.flush() after fh.write().
security4
cfe-capture.jsonl NOT gitignored — contradicts runbook; risks committing live CFE credentials and PII
scripts/cfe/mobile/mitm-cfe.py
capture-runbook.md says 'gitignored by default' — false. Neither root .gitignore nor local covers cfe-capture.jsonl. The file contains Bearer tokens, login credentials, and titular names (third-party PII). Fix: add scripts/cfe/mobile/cfe-capture*.jsonl to .gitignore immediately.
frida-okhttp.js logs full Authorization headers and request body to stdout with no masking
scripts/cfe/mobile/frida-okhttp.js
Dossier asks researcher to 'paste [okhttp] output' to Claude Code with advisory-only scrubbing. One paste-without-scrubbing publishes CFE credentials and titular names. Recommended: mask headers matching 'authorization'/'x-auth'/'cookie' after first 12 chars.
APK from APKPure third-party mirror with no signature/integrity check
scripts/cfe/mobile/mac-capture-dossier.md
APKPure has historically served tampered versions. Android verifies APK signatures on install but a validly-signed tampered APK passes. Add apksigner verify --print-certs step with expected SHA-256 fingerprint.
tmpfs CA ephemeral (correct) but snapshot interaction undocumented
Restoring an emulator snapshot after CA install drops the tmpfs CA. Add a comment: 'Re-run this step after restoring a snapshot.'
conventions4
SG-6 marked [ ] despite active in-progress work across 4 commits
.branch/scope.md:104
Should be [~] (PARTIAL) with a parenthetical summary to match the intent log.
Success Criteria not ticked for clearly completed SGs (SG-1, SG-4)
.branch/scope.md:27
All 6 criteria remain [ ] despite SG-1 (app identification) and SG-4 (HTML verdict) being complete.
Framework Learnings section is a placeholder — should be populated before SG-N
.branch/scope.md:117
Three SGs have concrete learnings (DexGuard field names survive; x86_64 can't run ARM app; attestation vs TLS pinning are orthogonal) that should flow here before SG-N.
Commit f834d195 omits SG prefix — breaks scannable-by-subgoal convention
All other spike commits follow 'docs(cfe): SG-N — …'. This one has 'docs(cfe): elevate the RPU->name read…' without SG tag.
tests3
setup-emulator.sh does not verify app actually installed after adb install-multiple
scripts/cfe/mobile/setup-emulator.sh:65
Add adb shell pm list packages | grep cfecontigo after install-multiple. The mac-capture-dossier includes this check; the script should too.
Proxy setting written but never read back to confirm it applied
scripts/cfe/mobile/setup-emulator.sh:62
Add adb shell settings get global http_proxy immediately after put to confirm proxy is active.
mac-capture-dossier.md boot-completion check has no visible success signal
scripts/cfe/mobile/mac-capture-dossier.md
The until-getprop loop exits silently. Clarify expected output ('1' → proceed) so the operator knows the step succeeded.
improvement8
isNoise() checks full URL string — could suppress appcfecontigo.cfe.mx if query param contains noise domain
scripts/cfe/mobile/frida-okhttp.js:19
Extract host before NOISE check; add explicit cfe.mx allow-list: if (host.endsWith('cfe.mx')) return false — prevents ever losing the target host.
Authenticator callbacks not hooked — token-refresh leg invisible
scripts/cfe/mobile/frida-okhttp.js
OkHttp fires Authenticator.authenticate on 401/407. Token-refresh login and retried request with fresh token are invisible. Hook okhttp3.Authenticator via Java.use + implementation to log trigger URL and returned Request.
setup-emulator.sh hardcodes x86_64 — contradicts dossier, will SIGSEGV app on Apple Silicon
scripts/cfe/mobile/setup-emulator.sh:16
Auto-detect HOST_ARCH=$(uname -m) and branch: arm64 → android-34/arm64-v8a/pixel_6; x86_64 → warn or abort with redirect to dossier.
No anti-root/emulator-detection Frida script staged — most likely blocking failure on handoff
scripts/cfe/mobile/mac-capture-dossier.md
iOS app force-closed on MITM; Android emulator/root detection is the same class of defense. Stage frida-antid.js covering RootBeer no-op, Build.TAGS/FINGERPRINT spoofing, File.exists('/system/bin/su') → false. Pre-staging removes a full roundtrip delay.
apk-mitm with pinned apktool 2.9.3 is an untried fast path — could eliminate Frida entirely
scripts/cfe/mobile/read-endpoint-notes.md
The failure was toolchain version mismatch (apktool≥2.10), not a CFE defense. Try: apk-mitm --apktool /tmp/apktool293.jar cfe.apk. If it succeeds, mitmproxy captures everything with no Frida. 20-min experiment with high upside.
TrustKit (com.datatheorem.android.trustkit) not covered in frida-unpin.js
scripts/cfe/mobile/frida-unpin.js
TrustKit overrides pinning at the OkHttp interceptor layer, not via TrustManagerImpl or CertificatePinner. Common alongside DexGuard. Add best-effort hook on com.datatheorem.android.trustkit.pinning.PinningTrustManager.checkServerTrusted wrapped in try/catch.
HOT list has duplicate 'cuenta' + response body not inspected for keywords
scripts/cfe/mobile/mitm-cfe.py:41
Remove duplicate. Extend hay to include _body(resp) — a GET /servicios/{rpu} carries 'titular' only in the response; without this, the lookup goes untagged.
Waydroid on the dev machine not attempted — viable x86_64 path removing Mac dependency
scripts/cfe/mobile/read-endpoint-notes.md
Waydroid runs ARM Android container on x86_64 Linux, is rootable via Magisk. A 30-min experiment could eliminate the Mac handoff entirely. Add to next-options with install steps.