FM11RF08S backdoor detection

This commit is contained in:
noproto
2024-08-15 17:58:37 -04:00
parent 8d1a2203ae
commit cc8cae770f
3 changed files with 73 additions and 32 deletions

View File

@@ -6,14 +6,16 @@
extern "C" {
#endif
#define MF_CLASSIC_CMD_AUTH_KEY_A (0x60U)
#define MF_CLASSIC_CMD_AUTH_KEY_B (0x61U)
#define MF_CLASSIC_CMD_READ_BLOCK (0x30U)
#define MF_CLASSIC_CMD_WRITE_BLOCK (0xA0U)
#define MF_CLASSIC_CMD_VALUE_DEC (0xC0U)
#define MF_CLASSIC_CMD_VALUE_INC (0xC1U)
#define MF_CLASSIC_CMD_VALUE_RESTORE (0xC2U)
#define MF_CLASSIC_CMD_VALUE_TRANSFER (0xB0U)
#define MF_CLASSIC_CMD_AUTH_KEY_A (0x60U)
#define MF_CLASSIC_CMD_AUTH_KEY_B (0x61U)
#define MF_CLASSIC_CMD_SE_BACKDOOR_AUTH_KEY_A (0x64U)
#define MF_CLASSIC_CMD_SE_BACKDOOR_AUTH_KEY_B (0x65U)
#define MF_CLASSIC_CMD_READ_BLOCK (0x30U)
#define MF_CLASSIC_CMD_WRITE_BLOCK (0xA0U)
#define MF_CLASSIC_CMD_VALUE_DEC (0xC0U)
#define MF_CLASSIC_CMD_VALUE_INC (0xC1U)
#define MF_CLASSIC_CMD_VALUE_RESTORE (0xC2U)
#define MF_CLASSIC_CMD_VALUE_TRANSFER (0xB0U)
#define MF_CLASSIC_CMD_HALT_MSB (0x50)
#define MF_CLASSIC_CMD_HALT_LSB (0x00)

View File

@@ -891,32 +891,69 @@ NfcCommand mf_classic_poller_handler_nested_analyze_prng(MfClassicPoller* instan
}
NfcCommand mf_classic_poller_handler_nested_analyze_backdoor(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
// TODO: Check for Fudan backdoor
// Can use on more than S variant as a free key for Nested
/*
do {
uint8_t auth_type = (key_type == MfClassicKeyTypeB) ? MF_CLASSIC_CMD_AUTH_KEY_B :
MF_CLASSIC_CMD_AUTH_KEY_A;
uint8_t auth_cmd[2] = {auth_type, block_num};
bit_buffer_copy_bytes(instance->tx_plain_buffer, auth_cmd, sizeof(auth_cmd));
NfcCommand command = NfcCommandReset;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
if(is_nested) {
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
crypto1_encrypt(
instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
error = iso14443_3a_poller_txrx_custom_parity(
instance->iso14443_3a_poller,
instance->tx_encrypted_buffer,
instance->rx_plain_buffer, // NT gets decrypted by mf_classic_async_auth
MF_CLASSIC_FWT_FC);
if(error != Iso14443_3aErrorNone) {
ret = mf_classic_process_error(error);
break;
}
*/
dict_attack_ctx->backdoor = MfClassicBackdoorNone;
uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector);
uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);
MfClassicAuthContext auth_ctx = {};
MfClassicNt nt = {};
MfClassicKey fm11rf08s_backdoor_key = {.data = {0xa3, 0x96, 0xef, 0xa4, 0xe2, 0x4f}};
MfClassicError error;
Iso14443_3aError iso_error;
bool backdoor_found = false;
do {
// Step 1: Perform full authentication once
error = mf_classic_poller_auth(
instance,
block,
&dict_attack_ctx->current_key,
dict_attack_ctx->current_key_type,
&auth_ctx);
if(error != MfClassicErrorNone) {
FURI_LOG_E(TAG, "Failed to perform full authentication");
break;
}
FURI_LOG_E(TAG, "Full authentication successful");
// Step 2: Attempt backdoor authentication
uint8_t auth_type = (dict_attack_ctx->current_key_type == MfClassicKeyTypeB) ?
MF_CLASSIC_CMD_SE_BACKDOOR_AUTH_KEY_B :
MF_CLASSIC_CMD_SE_BACKDOOR_AUTH_KEY_A;
uint8_t auth_cmd[2] = {auth_type, block};
bit_buffer_copy_bytes(instance->tx_plain_buffer, auth_cmd, sizeof(auth_cmd));
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
crypto1_encrypt(
instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
iso_error = iso14443_3a_poller_txrx_custom_parity(
instance->iso14443_3a_poller,
instance->tx_encrypted_buffer,
instance->rx_plain_buffer,
MF_CLASSIC_FWT_FC);
if(iso_error != Iso14443_3aErrorNone) {
FURI_LOG_E(TAG, "Error during nested authentication");
break;
}
if(bit_buffer_get_size_bytes(instance->rx_plain_buffer) != sizeof(MfClassicNt)) {
break;
}
bit_buffer_write_bytes(instance->rx_plain_buffer, nt.data, sizeof(MfClassicNt));
uint32_t nt_enc = bit_lib_bytes_to_num_be(nt.data, sizeof(MfClassicNt));
// Ensure the encrypted nt can be generated by the backdoor
uint32_t decrypted_nt_enc = decrypt_nt_enc(cuid, nt_enc, fm11rf08s_backdoor_key);
backdoor_found = is_weak_prng_nonce(decrypted_nt_enc);
} while(false);
if(backdoor_found) {
FURI_LOG_E(TAG, "Backdoor identified");
dict_attack_ctx->backdoor = MfClassicBackdoorFM11RF08S;
} else {
dict_attack_ctx->backdoor = MfClassicBackdoorNone;
}
instance->state = MfClassicPollerStateNestedController;
return command;
}
@@ -1041,6 +1078,7 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance)
bool found = false;
uint32_t decrypted_nt_enc =
decrypt_nt_enc(cuid, nt_enc_temp_arr[collection_cycle], dict_attack_ctx->current_key);
// TODO: Make sure we're not off-by-one here
for(int i = 0; i < 65535; i++) {
uint32_t nth_successor = prng_successor(nt_prev, i);
if(nth_successor != decrypted_nt_enc) {

View File

@@ -3,6 +3,7 @@
#include "mf_classic_poller.h"
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h>
#include <bit_lib/bit_lib.h>
#include "nfc/helpers/iso14443_crc.h"
#include <nfc/helpers/crypto1.h>
#include <stream/stream.h>
#include <stream/buffered_file_stream.h>