
An Android HW Attestation demo
This repository accompanies a Quarkslab article on Android hardware Key Attestation and exists to illustrate it.
It is a small, end-to-end proof of concept that shows a simple bypass: an analyst on a rooted phone defeats a backend's hardware attestation check by relaying the attestation to a second, clean device. Nothing is forged. A genuine attestation chain, produced by an unmodified device and bound to the backend's own nonce, is forwarded in place of the rooted device's rejected chain.
Scope and ethics. This is educational material for security research. It uses plain HTTP and disables safety checks on purpose ("demo only" markers throughout). Run it against your own devices and your own backend, on a trusted local network.
A rooted device's Keystore attestation reports an unlocked bootloader, so the backend rejects it. Instead of attacking the crypto, the analyst intercepts the app's own attestation call and swaps in a chain fetched live from a clean device for the same challenge.
The hook replaces KeystoreAttestation.generateAttestedKey(challenge) in the
demo app and never calls the local Keystore, so the rooted device's chain is
never generated. The relayed chain is genuine and passes the backend's verified
boot and hardware-backed checks.
Each component has its own README with the full detail; this file ties them together.
frida-server 17.16.4, with the demo
app installed.backend/requirements.txt)../gradlew assembleDebug).instrumentation/_agent.js) is committed, so a rebuild is
optional.Bring the pieces up in this order.
Backend. On a machine reachable from both devices:
cd backend
pip install -r requirements.txt
python3 attestation_backend.py # listens on 0.0.0.0:8080
Clean device. Build and install the attestation server app, then open it,
tap START SERVER, and note the URL it shows, e.g. http://192.168.1.42:8080.
Rooted device. Start frida-server, build and install the demo app, open
it, and enter the backend address (host:port).
Both apps build from Android Studio or from the command line with the Gradle
wrapper (./gradlew assembleDebug, JDK 17-21); see each app's README for the
details.
Relay. On the workstation:
cd instrumentation
python3 control.py --host <clean-device-ip> --port 8080
The controller spawns the demo app, installs the hook, then resumes it.
Trigger. Tap RUN ATTESTATION in the demo app. The backend returns valid: the relayed chain reports a locked bootloader.
Negative control. Stop control.py and tap RUN ATTESTATION again.
The demo app now sends the rooted device's own chain and the backend rejects
it. This is the check the relay defeats.
Rebuilding the agent (only if you change agent/agent.ts):
cd instrumentation
nvm use # Node 22 from .nvmrc
npm install
npm run build # regenerates _agent.js
apps/
QuarkslabAttestationDemo/ target client app (Android Studio project)
QuarkslabAttestationServer/ clean-device attestation oracle (Android Studio project)
instrumentation/ Frida agent + relay controller
backend/ Python attestation validator
LICENSE MIT
Copyright 2026 Quarkslab. Licensed under the MIT License. See LICENSE.
This repository illustrates a Quarkslab article.
| Component | Path | Role | Runs on |
|---|
| Demo client | apps/QuarkslabAttestationDemo/ | Target app. Requests a nonce, attests, sends the chain to the backend. Hooked by Frida. | Rooted analysis device |
| Attestation server | apps/QuarkslabAttestationServer/ | Attestation oracle. Produces a genuine chain for a given nonce. Validates nothing. | Clean, unmodified device |
| Instrumentation | instrumentation/ | Frida agent (the hook) plus the controller that relays the nonce to the clean device. | Analyst workstation + rooted device |
| Backend | backend/ | Relying party. Issues nonces and validates attestation chains. | Any machine reachable from the devices |