diff --git a/lib/nfc/protocols/mf_classic/mf_classic.c b/lib/nfc/protocols/mf_classic/mf_classic.c index 4f92201e3..b1c5c20c9 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic.c +++ b/lib/nfc/protocols/mf_classic/mf_classic.c @@ -543,6 +543,22 @@ void mf_classic_set_key_not_found( } } +MfClassicKey + mf_classic_get_key(const MfClassicData* data, uint8_t sector_num, MfClassicKeyType key_type) { + furi_check(data); + furi_check(sector_num < mf_classic_get_total_sectors_num(data->type)); + furi_check(key_type == MfClassicKeyTypeA || key_type == MfClassicKeyTypeB); + + const MfClassicSectorTrailer* sector_trailer = + mf_classic_get_sector_trailer_by_sector(data, sector_num); + + if(key_type == MfClassicKeyTypeA) { + return sector_trailer->key_a; + } else { + return sector_trailer->key_b; + } +} + bool mf_classic_is_block_read(const MfClassicData* data, uint8_t block_num) { furi_check(data); diff --git a/lib/nfc/protocols/mf_classic/mf_classic.h b/lib/nfc/protocols/mf_classic/mf_classic.h index 38c3e9ba7..6ae7a623e 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic.h +++ b/lib/nfc/protocols/mf_classic/mf_classic.h @@ -213,6 +213,9 @@ void mf_classic_set_key_not_found( uint8_t sector_num, MfClassicKeyType key_type); +MfClassicKey + mf_classic_get_key(const MfClassicData* data, uint8_t sector_num, MfClassicKeyType key_type); + bool mf_classic_is_block_read(const MfClassicData* data, uint8_t block_num); void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data); diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller.c b/lib/nfc/protocols/mf_classic/mf_classic_poller.c index 7417322e9..bc502534f 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller.c +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller.c @@ -8,9 +8,9 @@ // TODO: Buffer writes for Hardnested, set state to Log when finished and sum property matches // TODO: Store target key in CUID dictionary -// TODO: Fix rare nested_target_key 64 bug // TODO: Dead code for malloc returning NULL? // TODO: Auth1 static encrypted exists (rare) +// TODO: Use keys found by NFC plugins, cached keys #define MF_CLASSIC_MAX_BUFF_SIZE (64) @@ -1663,7 +1663,7 @@ NfcCommand mf_classic_poller_handler_nested_dict_attack(MfClassicPoller* instanc "Found key candidate %06llx", bit_lib_bytes_to_num_be(key_candidate->data, sizeof(MfClassicKey))); dict_attack_ctx->current_key = *key_candidate; - dict_attack_ctx->reuse_key_sector = (target_block / 4); + dict_attack_ctx->reuse_key_sector = target_sector; dict_attack_ctx->current_key_type = target_key_type; free(key_candidate); break; @@ -1818,12 +1818,13 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance bool initial_dict_attack_iter = false; if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseNone) { dict_attack_ctx->auth_passed = true; - dict_attack_ctx->nested_known_key = dict_attack_ctx->current_key; bool backdoor_present = (dict_attack_ctx->backdoor != MfClassicBackdoorNone); if(!(backdoor_present)) { for(uint8_t sector = 0; sector < instance->sectors_total; sector++) { for(uint8_t key_type = 0; key_type < 2; key_type++) { if(mf_classic_is_key_found(instance->data, sector, key_type)) { + dict_attack_ctx->nested_known_key = + mf_classic_get_key(instance->data, sector, key_type); dict_attack_ctx->nested_known_key_sector = sector; dict_attack_ctx->nested_known_key_type = key_type; break; @@ -1832,6 +1833,7 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance } dict_attack_ctx->nested_phase = MfClassicNestedPhaseAnalyzePRNG; } else { + dict_attack_ctx->nested_known_key = dict_attack_ctx->current_key; dict_attack_ctx->nested_known_key_sector = 0; dict_attack_ctx->nested_known_key_type = MfClassicKeyTypeA; dict_attack_ctx->prng_type = MfClassicPrngTypeWeak; @@ -2004,9 +2006,7 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance } uint16_t nonce_collect_key_max; if(dict_attack_ctx->prng_type == MfClassicPrngTypeWeak) { - nonce_collect_key_max = dict_attack_ctx->static_encrypted ? - ((instance->sectors_total * 4) - 2) : - (instance->sectors_total * 4); + nonce_collect_key_max = instance->sectors_total * 4; } else { nonce_collect_key_max = instance->sectors_total * 2; } @@ -2047,30 +2047,6 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance 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(!(dict_attack_ctx->static_encrypted)) { - dict_attack_ctx->nested_target_key++; - dict_attack_ctx->current_key_checked = false; - } - instance->state = MfClassicPollerStateNestedController; - return command; - } - - // 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)) || @@ -2096,12 +2072,52 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance dict_attack_ctx->attempt_count = 0; } + FURI_LOG_D( + TAG, + "Nested target key: %u (max: %u)", + dict_attack_ctx->nested_target_key, + nonce_collect_key_max); + + if(!(dict_attack_ctx->current_key_checked)) { + if(dict_attack_ctx->nested_target_key == nonce_collect_key_max) { + // All nonces have been collected + FURI_LOG_D(TAG, "All nonces collected"); + instance->state = MfClassicPollerStateNestedController; + return command; + } + + 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(!(dict_attack_ctx->static_encrypted)) { + dict_attack_ctx->nested_target_key++; + dict_attack_ctx->current_key_checked = false; + } + instance->state = MfClassicPollerStateNestedController; + return command; + } + + // 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; + } + } + FURI_LOG_T(TAG, "Collecting a nonce"); + // Collect a nonce dict_attack_ctx->auth_passed = false; instance->state = MfClassicPollerStateNestedCollectNtEnc; return command; } } + dict_attack_ctx->nested_target_key = 0; dict_attack_ctx->nested_phase = MfClassicNestedPhaseFinished; instance->state = MfClassicPollerStateSuccess; return command; diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 7943c4cfc..be60c6a1c 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,77.2,, +Version,+,77.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/cli/cli.h,, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 8358958f4..a749963b6 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,77.2,, +Version,+,77.3,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, @@ -2559,6 +2559,7 @@ Function,+,mf_classic_get_base_data,Iso14443_3aData*,const MfClassicData* Function,+,mf_classic_get_blocks_num_in_sector,uint8_t,uint8_t Function,+,mf_classic_get_device_name,const char*,"const MfClassicData*, NfcDeviceNameType" Function,+,mf_classic_get_first_block_num_of_sector,uint8_t,uint8_t +Function,+,mf_classic_get_key,MfClassicKey,"const MfClassicData*, uint8_t, MfClassicKeyType" Function,+,mf_classic_get_read_sectors_and_keys,void,"const MfClassicData*, uint8_t*, uint8_t*" Function,+,mf_classic_get_sector_by_block,uint8_t,uint8_t Function,+,mf_classic_get_sector_trailer_by_sector,MfClassicSectorTrailer*,"const MfClassicData*, uint8_t"