MX 1 год назад
Родитель
Сommit
b65ccd8ef5

+ 1 - 1
application.fam

@@ -13,7 +13,7 @@ App(
     ],
     ],
     stack_size=4 * 1024,
     stack_size=4 * 1024,
     fap_description="App to communicate with NFC tags using the PicoPass(iClass) format",
     fap_description="App to communicate with NFC tags using the PicoPass(iClass) format",
-    fap_version="1.16",
+    fap_version="1.17",
     fap_icon="125_10px.png",
     fap_icon="125_10px.png",
     fap_category="NFC",
     fap_category="NFC",
     fap_libs=["mbedtls"],
     fap_libs=["mbedtls"],

+ 3 - 3
helpers/iclass_elite_dict.c

@@ -3,14 +3,14 @@
 #include <lib/toolbox/args.h>
 #include <lib/toolbox/args.h>
 #include <lib/flipper_format/flipper_format.h>
 #include <lib/flipper_format/flipper_format.h>
 
 
-#define ICLASS_ELITE_DICT_FLIPPER_NAME APP_ASSETS_PATH("iclass_elite_dict.txt")
+#define ICLASS_ELITE_DICT_FLIPPER_NAME    APP_ASSETS_PATH("iclass_elite_dict.txt")
 #define ICLASS_STANDARD_DICT_FLIPPER_NAME APP_ASSETS_PATH("iclass_standard_dict.txt")
 #define ICLASS_STANDARD_DICT_FLIPPER_NAME APP_ASSETS_PATH("iclass_standard_dict.txt")
-#define ICLASS_ELITE_DICT_USER_NAME APP_DATA_PATH("assets/iclass_elite_dict_user.txt")
+#define ICLASS_ELITE_DICT_USER_NAME       APP_DATA_PATH("assets/iclass_elite_dict_user.txt")
 
 
 #define TAG "IclassEliteDict"
 #define TAG "IclassEliteDict"
 
 
 #define ICLASS_ELITE_KEY_LINE_LEN (17)
 #define ICLASS_ELITE_KEY_LINE_LEN (17)
-#define ICLASS_ELITE_KEY_LEN (8)
+#define ICLASS_ELITE_KEY_LEN      (8)
 
 
 struct IclassEliteDict {
 struct IclassEliteDict {
     Stream* stream;
     Stream* stream;

+ 1 - 17
picopass_device.c

@@ -20,17 +20,7 @@ const char unknown_block[] = "?? ?? ?? ?? ?? ?? ?? ??";
 
 
 PicopassDevice* picopass_device_alloc() {
 PicopassDevice* picopass_device_alloc() {
     PicopassDevice* picopass_dev = malloc(sizeof(PicopassDevice));
     PicopassDevice* picopass_dev = malloc(sizeof(PicopassDevice));
-    picopass_dev->dev_data.auth = PicopassDeviceAuthMethodUnset;
-    picopass_dev->dev_data.pacs.legacy = false;
-    picopass_dev->dev_data.pacs.se_enabled = false;
-    picopass_dev->dev_data.pacs.sio = false;
-    picopass_dev->dev_data.pacs.biometrics = false;
-    memset(picopass_dev->dev_data.pacs.key, 0, sizeof(picopass_dev->dev_data.pacs.key));
-    picopass_dev->dev_data.pacs.elite_kdf = false;
-    picopass_dev->dev_data.pacs.pin_length = 0;
-    picopass_dev->dev_data.pacs.bitLength = 0;
-    memset(
-        picopass_dev->dev_data.pacs.credential, 0, sizeof(picopass_dev->dev_data.pacs.credential));
+    memset(picopass_dev, 0, sizeof(PicopassDevice));
     picopass_dev->storage = furi_record_open(RECORD_STORAGE);
     picopass_dev->storage = furi_record_open(RECORD_STORAGE);
     picopass_dev->dialogs = furi_record_open(RECORD_DIALOGS);
     picopass_dev->dialogs = furi_record_open(RECORD_DIALOGS);
     picopass_dev->load_path = furi_string_alloc();
     picopass_dev->load_path = furi_string_alloc();
@@ -176,12 +166,6 @@ static bool picopass_device_save_file(
     FuriString* temp_str;
     FuriString* temp_str;
     temp_str = furi_string_alloc();
     temp_str = furi_string_alloc();
 
 
-    if(dev->format == PicopassDeviceSaveFormatPartial) {
-        // Clear key that may have been set when doing key tests for legacy
-        memset(card_data[PICOPASS_SECURE_KD_BLOCK_INDEX].data, 0, PICOPASS_BLOCK_LEN);
-        card_data[PICOPASS_SECURE_KD_BLOCK_INDEX].valid = false;
-    }
-
     do {
     do {
         if(use_load_path && !furi_string_empty(dev->load_path)) {
         if(use_load_path && !furi_string_empty(dev->load_path)) {
             // Get directory name
             // Get directory name

+ 16 - 16
picopass_device.h

@@ -19,35 +19,35 @@
 #endif
 #endif
 #define LOCLASS_MACS_TO_COLLECT (LOCLASS_NUM_CSNS * LOCLASS_NUM_PER_CSN)
 #define LOCLASS_MACS_TO_COLLECT (LOCLASS_NUM_CSNS * LOCLASS_NUM_PER_CSN)
 
 
-#define PICOPASS_DEV_NAME_MAX_LEN 129
+#define PICOPASS_DEV_NAME_MAX_LEN     129
 #define PICOPASS_READER_DATA_MAX_SIZE 64
 #define PICOPASS_READER_DATA_MAX_SIZE 64
-#define PICOPASS_MAX_APP_LIMIT 32
+#define PICOPASS_MAX_APP_LIMIT        32
 
 
-#define PICOPASS_CSN_BLOCK_INDEX 0
-#define PICOPASS_CONFIG_BLOCK_INDEX 1
+#define PICOPASS_CSN_BLOCK_INDEX             0
+#define PICOPASS_CONFIG_BLOCK_INDEX          1
 // These definitions for blocks above 2 only hold for secure cards.
 // These definitions for blocks above 2 only hold for secure cards.
-#define PICOPASS_SECURE_EPURSE_BLOCK_INDEX 2
-#define PICOPASS_SECURE_KD_BLOCK_INDEX 3
-#define PICOPASS_SECURE_KC_BLOCK_INDEX 4
-#define PICOPASS_SECURE_AIA_BLOCK_INDEX 5
+#define PICOPASS_SECURE_EPURSE_BLOCK_INDEX   2
+#define PICOPASS_SECURE_KD_BLOCK_INDEX       3
+#define PICOPASS_SECURE_KC_BLOCK_INDEX       4
+#define PICOPASS_SECURE_AIA_BLOCK_INDEX      5
 // Non-secure cards instead have an AIA at block 2
 // Non-secure cards instead have an AIA at block 2
-#define PICOPASS_NONSECURE_AIA_BLOCK_INDEX 2
+#define PICOPASS_NONSECURE_AIA_BLOCK_INDEX   2
 // Only iClass cards
 // Only iClass cards
 #define PICOPASS_ICLASS_PACS_CFG_BLOCK_INDEX 6
 #define PICOPASS_ICLASS_PACS_CFG_BLOCK_INDEX 6
 
 
 // Personalization Mode
 // Personalization Mode
-#define PICOPASS_FUSE_PERS 0x80
+#define PICOPASS_FUSE_PERS    0x80
 // Crypt1 // 1+1 (crypt1+crypt0) means secured and keys changable
 // Crypt1 // 1+1 (crypt1+crypt0) means secured and keys changable
-#define PICOPASS_FUSE_CRYPT1 0x10
+#define PICOPASS_FUSE_CRYPT1  0x10
 // Crypt0 // 1+0 means secure and keys locked, 0+1 means not secured, 0+0 means disable auth entirely
 // Crypt0 // 1+0 means secure and keys locked, 0+1 means not secured, 0+0 means disable auth entirely
-#define PICOPASS_FUSE_CRYPT0 0x08
+#define PICOPASS_FUSE_CRYPT0  0x08
 #define PICOPASS_FUSE_CRYPT10 (PICOPASS_FUSE_CRYPT1 | PICOPASS_FUSE_CRYPT0)
 #define PICOPASS_FUSE_CRYPT10 (PICOPASS_FUSE_CRYPT1 | PICOPASS_FUSE_CRYPT0)
 // Read Access, 1 meanns anonymous read enabled, 0 means must auth to read applicaion
 // Read Access, 1 meanns anonymous read enabled, 0 means must auth to read applicaion
-#define PICOPASS_FUSE_RA 0x01
+#define PICOPASS_FUSE_RA      0x01
 
 
-#define PICOPASS_APP_FOLDER ANY_PATH("picopass")
-#define PICOPASS_APP_EXTENSION ".picopass"
-#define PICOPASS_APP_FILE_PREFIX "Picopass"
+#define PICOPASS_APP_FOLDER           ANY_PATH("picopass")
+#define PICOPASS_APP_EXTENSION        ".picopass"
+#define PICOPASS_APP_FILE_PREFIX      "Picopass"
 #define PICOPASS_APP_SHADOW_EXTENSION ".pas"
 #define PICOPASS_APP_SHADOW_EXTENSION ".pas"
 
 
 #define PICOPASS_DICT_KEY_BATCH_SIZE 10
 #define PICOPASS_DICT_KEY_BATCH_SIZE 10

+ 2 - 2
picopass_i.h

@@ -41,9 +41,9 @@
 
 
 #define PICOPASS_TEXT_STORE_SIZE 129
 #define PICOPASS_TEXT_STORE_SIZE 129
 
 
-#define PICOPASS_ICLASS_ELITE_DICT_FLIPPER_NAME APP_ASSETS_PATH("iclass_elite_dict.txt")
+#define PICOPASS_ICLASS_ELITE_DICT_FLIPPER_NAME    APP_ASSETS_PATH("iclass_elite_dict.txt")
 #define PICOPASS_ICLASS_STANDARD_DICT_FLIPPER_NAME APP_ASSETS_PATH("iclass_standard_dict.txt")
 #define PICOPASS_ICLASS_STANDARD_DICT_FLIPPER_NAME APP_ASSETS_PATH("iclass_standard_dict.txt")
-#define PICOPASS_ICLASS_ELITE_DICT_USER_NAME APP_DATA_PATH("assets/iclass_elite_dict_user.txt")
+#define PICOPASS_ICLASS_ELITE_DICT_USER_NAME       APP_DATA_PATH("assets/iclass_elite_dict_user.txt")
 
 
 enum PicopassCustomEvent {
 enum PicopassCustomEvent {
     // Reserve first 100 events for button types and indexes, starting from 0
     // Reserve first 100 events for button types and indexes, starting from 0

+ 19 - 8
protocol/picopass_listener.c

@@ -375,20 +375,20 @@ PicopassListenerCommand
                         PICOPASS_FUSE_CRYPT10) != PICOPASS_FUSE_CRYPT0;
                         PICOPASS_FUSE_CRYPT10) != PICOPASS_FUSE_CRYPT0;
         if(!secured) break;
         if(!secured) break;
 
 
-        uint8_t rmac[4] = {};
-        uint8_t tmac[4] = {};
         const uint8_t* key = instance->data->card_data[instance->key_block_num].data;
         const uint8_t* key = instance->data->card_data[instance->key_block_num].data;
-        bool no_key = !instance->data->card_data[instance->key_block_num].valid;
+        bool have_key = instance->data->card_data[instance->key_block_num].valid;
+        bool no_data = !instance->data->card_data[PICOPASS_ICLASS_PACS_CFG_BLOCK_INDEX].valid;
         const uint8_t* rx_data = bit_buffer_get_data(buf);
         const uint8_t* rx_data = bit_buffer_get_data(buf);
 
 
-        if(no_key) {
-            // We're emulating a partial dump of an iClass SE card and should capture the NR and MAC
+        if(no_data) {
+            // We're missing at least the first data block, save MACs for NR-MAC replay.
             command = picopass_listener_save_mac(instance, rx_data);
             command = picopass_listener_save_mac(instance, rx_data);
             break;
             break;
-        } else {
+        } else if(have_key) {
+            uint8_t rmac[4] = {};
+            uint8_t tmac[4] = {};
             loclass_opt_doBothMAC_2(instance->cipher_state, &rx_data[1], rmac, tmac, key);
             loclass_opt_doBothMAC_2(instance->cipher_state, &rx_data[1], rmac, tmac, key);
 
 
-#ifndef PICOPASS_DEBUG_IGNORE_BAD_RMAC
             if(memcmp(&rx_data[5], rmac, PICOPASS_MAC_LEN)) {
             if(memcmp(&rx_data[5], rmac, PICOPASS_MAC_LEN)) {
                 // Bad MAC from reader, do not send a response.
                 // Bad MAC from reader, do not send a response.
                 FURI_LOG_I(TAG, "Got bad MAC from reader");
                 FURI_LOG_I(TAG, "Got bad MAC from reader");
@@ -396,7 +396,6 @@ PicopassListenerCommand
                 picopass_listener_init_cipher_state(instance);
                 picopass_listener_init_cipher_state(instance);
                 break;
                 break;
             }
             }
-#endif
 
 
             bit_buffer_copy_bytes(instance->tx_buffer, tmac, sizeof(tmac));
             bit_buffer_copy_bytes(instance->tx_buffer, tmac, sizeof(tmac));
             NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer);
             NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer);
@@ -404,6 +403,18 @@ PicopassListenerCommand
                 FURI_LOG_D(TAG, "Failed tx update response: %d", error);
                 FURI_LOG_D(TAG, "Failed tx update response: %d", error);
                 break;
                 break;
             }
             }
+        } else {
+            // CVE-2024-41566 Exploit: The dump has no key, ignore the reader mac
+            // and a dummy response to see if the reader accepts it anyway
+            bit_buffer_reset(instance->tx_buffer);
+            for(size_t j = 0; j < 4; j++) {
+                bit_buffer_append_byte(instance->tx_buffer, 0xff);
+            }
+            NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer);
+            if(error != NfcErrorNone) {
+                FURI_LOG_D(TAG, "Failed tx update response: %d", error);
+                break;
+            }
         }
         }
 
 
         command = PicopassListenerCommandProcessed;
         command = PicopassListenerCommandProcessed;

+ 5 - 11
protocol/picopass_poller.c

@@ -313,15 +313,6 @@ NfcCommand picopass_poller_nr_mac_auth(PicopassPoller* instance) {
             if(instance->mode == PicopassPollerModeRead) {
             if(instance->mode == PicopassPollerModeRead) {
                 picopass_poller_prepare_read(instance);
                 picopass_poller_prepare_read(instance);
                 instance->state = PicopassPollerStateReadBlock;
                 instance->state = PicopassPollerStateReadBlock;
-                // Set to non-zero keys to allow emulation
-                memset(
-                    instance->data->card_data[PICOPASS_SECURE_KD_BLOCK_INDEX].data,
-                    0xff,
-                    PICOPASS_BLOCK_LEN);
-                memset(
-                    instance->data->card_data[PICOPASS_SECURE_KC_BLOCK_INDEX].data,
-                    0xff,
-                    PICOPASS_BLOCK_LEN);
             }
             }
         }
         }
 
 
@@ -431,9 +422,12 @@ NfcCommand picopass_poller_read_block_handler(PicopassPoller* instance) {
             break;
             break;
         }
         }
 
 
-        if(instance->secured && instance->current_block == PICOPASS_SECURE_KD_BLOCK_INDEX) {
-            // Skip over Kd block which is populated earlier (READ of Kd returns all FF's)
+        if(instance->secured && (instance->current_block == PICOPASS_SECURE_KD_BLOCK_INDEX ||
+                                 instance->current_block == PICOPASS_SECURE_KC_BLOCK_INDEX)) {
+            // Kd and Kc blocks cannot be read (card always returns FF's)
+            // Key blocks we authed as would have been already set earlier
             instance->current_block++;
             instance->current_block++;
+            continue;
         }
         }
 
 
         PicopassBlock block = {};
         PicopassBlock block = {};

+ 1 - 1
protocol/picopass_poller_i.h

@@ -6,7 +6,7 @@
 #include <nfc/helpers/iso13239_crc.h>
 #include <nfc/helpers/iso13239_crc.h>
 
 
 #define PICOPASS_POLLER_BUFFER_SIZE (255)
 #define PICOPASS_POLLER_BUFFER_SIZE (255)
-#define PICOPASS_CRC_SIZE (2)
+#define PICOPASS_CRC_SIZE           (2)
 
 
 typedef enum {
 typedef enum {
     PicopassPollerSessionStateIdle,
     PicopassPollerSessionStateIdle,

+ 6 - 6
protocol/picopass_protocol.h

@@ -2,13 +2,13 @@
 
 
 #include "../picopass_device.h"
 #include "../picopass_device.h"
 
 
-#define PICOPASS_BLOCK_LEN 8
-#define PICOPASS_MAX_APP_LIMIT 32
-#define PICOPASS_UID_LEN 8
+#define PICOPASS_BLOCK_LEN           8
+#define PICOPASS_MAX_APP_LIMIT       32
+#define PICOPASS_UID_LEN             8
 #define PICOPASS_READ_CHECK_RESP_LEN 8
 #define PICOPASS_READ_CHECK_RESP_LEN 8
-#define PICOPASS_CHECK_RESP_LEN 4
-#define PICOPASS_MAC_LEN 4
-#define PICOPASS_KEY_LEN 8
+#define PICOPASS_CHECK_RESP_LEN      4
+#define PICOPASS_MAC_LEN             4
+#define PICOPASS_KEY_LEN             8
 
 
 #define PICOPASS_FDT_LISTEN_FC (1000)
 #define PICOPASS_FDT_LISTEN_FC (1000)
 
 

+ 1 - 1
rfal_picopass.h

@@ -3,7 +3,7 @@
 #include <furi_hal_nfc.h>
 #include <furi_hal_nfc.h>
 
 
 #define RFAL_PICOPASS_UID_LEN 8
 #define RFAL_PICOPASS_UID_LEN 8
-#define PICOPASS_BLOCK_LEN 8
+#define PICOPASS_BLOCK_LEN    8
 
 
 enum {
 enum {
     // PicoPass command bytes:
     // PicoPass command bytes:

+ 1 - 1
scenes/picopass_scene_elite_keygen_attack.c

@@ -3,7 +3,7 @@
 #include "../picopass_elite_keygen.h"
 #include "../picopass_elite_keygen.h"
 
 
 #define PICOPASS_SCENE_DICT_ATTACK_KEYS_BATCH_UPDATE (10)
 #define PICOPASS_SCENE_DICT_ATTACK_KEYS_BATCH_UPDATE (10)
-#define PICOPASS_SCENE_ELITE_KEYGEN_ATTACK_LIMIT (2000)
+#define PICOPASS_SCENE_ELITE_KEYGEN_ATTACK_LIMIT     (2000)
 
 
 NfcCommand picopass_elite_keygen_attack_worker_callback(PicopassPollerEvent event, void* context) {
 NfcCommand picopass_elite_keygen_attack_worker_callback(PicopassPollerEvent event, void* context) {
     furi_assert(context);
     furi_assert(context);

+ 1 - 1
scenes/picopass_scene_write_card.c

@@ -3,7 +3,7 @@
 #include "../picopass_keys.h"
 #include "../picopass_keys.h"
 
 
 #define PICOPASS_SCENE_WRITE_BLOCK_START 6
 #define PICOPASS_SCENE_WRITE_BLOCK_START 6
-#define PICOPASS_SCENE_WRITE_BLOCK_STOP 10
+#define PICOPASS_SCENE_WRITE_BLOCK_STOP  10
 
 
 NfcCommand picopass_scene_write_poller_callback(PicopassPollerEvent event, void* context) {
 NfcCommand picopass_scene_write_poller_callback(PicopassPollerEvent event, void* context) {
     Picopass* picopass = context;
     Picopass* picopass = context;