From 3ab752b7a019e743982f766e5d2ba7e893337ac4 Mon Sep 17 00:00:00 2001 From: noproto Date: Tue, 17 Sep 2024 14:38:14 -0400 Subject: [PATCH] Clean up various issues --- applications/main/nfc/views/dict_attack.c | 1 + applications/main/nfc/views/dict_attack.h | 1 + .../protocols/mf_classic/mf_classic_poller.c | 74 ++++++++++++------- .../mf_classic/mf_classic_poller_i.h | 1 + 4 files changed, 49 insertions(+), 28 deletions(-) diff --git a/applications/main/nfc/views/dict_attack.c b/applications/main/nfc/views/dict_attack.c index 2fae91694..c0cc3802b 100644 --- a/applications/main/nfc/views/dict_attack.c +++ b/applications/main/nfc/views/dict_attack.c @@ -47,6 +47,7 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) { furi_string_set(m->header, "Nested Dictionary"); break; case MfClassicNestedPhaseCalibrate: + case MfClassicNestedPhaseRecalibrate: furi_string_set(m->header, "Calibration"); break; case MfClassicNestedPhaseCollectNtEnc: diff --git a/applications/main/nfc/views/dict_attack.h b/applications/main/nfc/views/dict_attack.h index c2a7b1e68..d28188fbc 100644 --- a/applications/main/nfc/views/dict_attack.h +++ b/applications/main/nfc/views/dict_attack.h @@ -15,6 +15,7 @@ typedef enum { MfClassicNestedPhaseDictAttack, MfClassicNestedPhaseDictAttackResume, MfClassicNestedPhaseCalibrate, + MfClassicNestedPhaseRecalibrate, MfClassicNestedPhaseCollectNtEnc, MfClassicNestedPhaseFinished, } MfClassicNestedPhase; diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller.c b/lib/nfc/protocols/mf_classic/mf_classic_poller.c index 0934a7765..75139faf4 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller.c +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller.c @@ -10,6 +10,7 @@ // TODO: Buffer writes for Hardnested, set state to Log when finished and sum property matches // TODO: Load dictionaries specific to a CUID to not clutter the user dictionary // TODO: Validate Hardnested is collecting nonces from the correct block +// TODO: Nested entrypoint for cached keys #define MF_CLASSIC_MAX_BUFF_SIZE (64) @@ -553,7 +554,7 @@ NfcCommand mf_classic_poller_handler_request_read_sector_blocks(MfClassicPoller* } NfcCommand mf_classic_poller_handler_analyze_backdoor(MfClassicPoller* instance) { - NfcCommand command = NfcCommandContinue; + NfcCommand command = NfcCommandReset; MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; size_t current_key_index = @@ -1152,11 +1153,13 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance) command = NfcCommandReset; uint8_t target_block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->nested_target_key / 4); + MfClassicKeyType target_key_type = + ((dict_attack_ctx->nested_target_key % 4) < 2) ? MfClassicKeyTypeA : MfClassicKeyTypeB; error = mf_classic_poller_auth_nested( instance, target_block, &dict_attack_ctx->nested_known_key, - dict_attack_ctx->nested_known_key_type, + target_key_type, &auth_ctx, use_backdoor, false); @@ -1231,7 +1234,6 @@ 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->nested_known_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) { @@ -1305,11 +1307,12 @@ NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* inst (is_weak && !(dict_attack_ctx->static_encrypted)) ? ((dict_attack_ctx->attempt_count + 2) + nonce_pair_index) : 1; - MfClassicKeyType target_key_type = ((dict_attack_ctx->nested_target_key & 0x03) < 2) ? - MfClassicKeyTypeA : - MfClassicKeyTypeB; - // TODO: mf_classic_get_sector_trailer_num_by_sector or mf_classic_get_sector_trailer_num_by_block? Match calibrated? - uint8_t target_block = (4 * (dict_attack_ctx->nested_target_key / 4)) + 3; + uint8_t target_sector = dict_attack_ctx->nested_target_key / (is_weak ? 4 : 2); + MfClassicKeyType target_key_type = + (dict_attack_ctx->nested_target_key % (is_weak ? 4 : 2) < (is_weak ? 2 : 1)) ? + MfClassicKeyTypeA : + MfClassicKeyTypeB; + uint8_t target_block = mf_classic_get_sector_trailer_num_by_sector(target_sector); uint32_t nt_enc_temp_arr[nt_enc_per_collection]; uint8_t nt_enc_collected = 0; uint8_t parity = 0; @@ -1510,7 +1513,7 @@ static MfClassicKey* search_dicts_for_nonce_key( } NfcCommand mf_classic_poller_handler_nested_dict_attack(MfClassicPoller* instance) { - // TODO: Handle when nonce is not collected (retry counter? Do not increment nested_dict_target_key) + // TODO: Handle when nonce is not collected (retry counter? Do not increment nested_target_key) // TODO: Look into using MfClassicNt more NfcCommand command = NfcCommandContinue; MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; @@ -1532,9 +1535,8 @@ NfcCommand mf_classic_poller_handler_nested_dict_attack(MfClassicPoller* instanc ((!is_weak) && ((dict_attack_ctx->nested_target_key % 16) < 8))) ? MfClassicKeyTypeA : MfClassicKeyTypeB; - // TODO: mf_classic_get_sector_trailer_num_by_sector or mf_classic_get_sector_trailer_num_by_block? - uint8_t target_block = (is_weak) ? (4 * (dict_attack_ctx->nested_target_key / 2)) + 3 : - (4 * (dict_attack_ctx->nested_target_key / 16)) + 3; + uint8_t target_sector = dict_attack_ctx->nested_target_key / (is_weak ? 2 : 16); + uint8_t target_block = mf_classic_get_sector_trailer_num_by_sector(target_sector); uint8_t parity = 0; if(((is_weak) && (dict_attack_ctx->nested_nonce.count == 0)) || @@ -1764,8 +1766,7 @@ bool is_valid_sum(uint16_t sum) { } NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance) { - // Iterate through keys - //NfcCommand command = NfcCommandContinue; + // This function guides the nested attack through its phases, and iterates over the target keys NfcCommand command = mf_classic_poller_handle_data_update(instance); MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; bool initial_dict_attack_iter = false; @@ -1795,6 +1796,7 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance initial_dict_attack_iter = true; } } + // Identify PRNG type if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseAnalyzePRNG) { if(dict_attack_ctx->nested_nonce.count < MF_CLASSIC_NESTED_ANALYZE_NT_COUNT) { instance->state = MfClassicPollerStateNestedCollectNt; @@ -1896,6 +1898,10 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance instance->state = MfClassicPollerStateSuccess; return command; } + if(dict_attack_ctx->backdoor == MfClassicBackdoorAuth3) { + // Skip initial calibration for static encrypted backdoored tags + dict_attack_ctx->calibrated = true; + } dict_attack_ctx->nested_phase = MfClassicNestedPhaseCalibrate; instance->state = MfClassicPollerStateNestedController; return command; @@ -1919,24 +1925,23 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance } // Calibration bool initial_collect_nt_enc_iter = false; + bool recalibrated = false; if(!(dict_attack_ctx->calibrated)) { if(dict_attack_ctx->prng_type == MfClassicPrngTypeWeak) { instance->state = MfClassicPollerStateNestedCalibrate; return command; } initial_collect_nt_enc_iter = true; - dict_attack_ctx->auth_passed = true; dict_attack_ctx->calibrated = true; - dict_attack_ctx->current_key_checked = false; dict_attack_ctx->nested_phase = MfClassicNestedPhaseCollectNtEnc; } else if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseCalibrate) { initial_collect_nt_enc_iter = true; - dict_attack_ctx->auth_passed = true; - dict_attack_ctx->current_key_checked = false; + dict_attack_ctx->nested_phase = MfClassicNestedPhaseCollectNtEnc; + } else if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseRecalibrate) { + recalibrated = true; dict_attack_ctx->nested_phase = MfClassicNestedPhaseCollectNtEnc; } // Collect and log nonces - // TODO: Calibrates too frequently for static encrypted backdoored tags if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseCollectNtEnc) { if(((is_weak) && (dict_attack_ctx->nested_nonce.count == 2)) || ((is_weak) && (dict_attack_ctx->backdoor == MfClassicBackdoorAuth3) && @@ -1970,10 +1975,13 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance memset(dict_attack_ctx->nt_enc_msb, 0, sizeof(dict_attack_ctx->nt_enc_msb)); return command; } - if(!(dict_attack_ctx->auth_passed)) { + if(initial_collect_nt_enc_iter) { + dict_attack_ctx->current_key_checked = false; + } + if(!(dict_attack_ctx->auth_passed) && !(initial_collect_nt_enc_iter)) { dict_attack_ctx->attempt_count++; } else { - if(is_weak && !(initial_collect_nt_enc_iter)) { + if(is_weak && !(initial_collect_nt_enc_iter) && !(recalibrated)) { if(!(dict_attack_ctx->static_encrypted)) { dict_attack_ctx->nested_target_key++; } else { @@ -1982,28 +1990,36 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance if(dict_attack_ctx->nested_target_key % 2 == 0) { dict_attack_ctx->current_key_checked = false; } - if((dict_attack_ctx->backdoor == MfClassicBackdoorAuth3) && - (dict_attack_ctx->nested_target_key % 4 == 0) && - (dict_attack_ctx->nested_target_key < nonce_collect_key_max)) { - dict_attack_ctx->calibrated = false; - } } dict_attack_ctx->attempt_count = 0; } dict_attack_ctx->auth_passed = true; if(!(dict_attack_ctx->current_key_checked)) { + dict_attack_ctx->current_key_checked = true; + // Check if the nested target key is a known key if(mf_classic_nested_is_target_key_found(instance, false)) { // Continue to next key - if(!(is_weak)) { + if(!(dict_attack_ctx->static_encrypted)) { dict_attack_ctx->nested_target_key++; dict_attack_ctx->current_key_checked = false; } instance->state = MfClassicPollerStateNestedController; return command; } - dict_attack_ctx->current_key_checked = true; + + // If it is not a known key, we'll need to calibrate for static encrypted backdoored tags + if((dict_attack_ctx->backdoor == MfClassicBackdoorAuth3) && + (dict_attack_ctx->nested_target_key < nonce_collect_key_max) && + !(recalibrated)) { + dict_attack_ctx->calibrated = false; + dict_attack_ctx->nested_phase = MfClassicNestedPhaseRecalibrate; + instance->state = MfClassicPollerStateNestedController; + return command; + } } + + // If we have tried to collect this nonce too many times, skip if((is_weak && (dict_attack_ctx->attempt_count >= MF_CLASSIC_NESTED_RETRY_MAXIMUM)) || (!(is_weak) && (dict_attack_ctx->attempt_count >= MF_CLASSIC_NESTED_HARD_RETRY_MAXIMUM))) { @@ -2025,6 +2041,8 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance } dict_attack_ctx->attempt_count = 0; } + + // Collect a nonce dict_attack_ctx->auth_passed = false; instance->state = MfClassicPollerStateNestedCollectNtEnc; return command; diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h index 111ddf260..71c972c1a 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h @@ -54,6 +54,7 @@ typedef enum { MfClassicNestedPhaseDictAttack, MfClassicNestedPhaseDictAttackResume, MfClassicNestedPhaseCalibrate, + MfClassicNestedPhaseRecalibrate, MfClassicNestedPhaseCollectNtEnc, MfClassicNestedPhaseFinished, } MfClassicNestedPhase;