Clean up various issues

This commit is contained in:
noproto 2024-09-17 14:38:14 -04:00
parent 4836a54847
commit 3ab752b7a0
4 changed files with 49 additions and 28 deletions

View File

@ -47,6 +47,7 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
furi_string_set(m->header, "Nested Dictionary"); furi_string_set(m->header, "Nested Dictionary");
break; break;
case MfClassicNestedPhaseCalibrate: case MfClassicNestedPhaseCalibrate:
case MfClassicNestedPhaseRecalibrate:
furi_string_set(m->header, "Calibration"); furi_string_set(m->header, "Calibration");
break; break;
case MfClassicNestedPhaseCollectNtEnc: case MfClassicNestedPhaseCollectNtEnc:

View File

@ -15,6 +15,7 @@ typedef enum {
MfClassicNestedPhaseDictAttack, MfClassicNestedPhaseDictAttack,
MfClassicNestedPhaseDictAttackResume, MfClassicNestedPhaseDictAttackResume,
MfClassicNestedPhaseCalibrate, MfClassicNestedPhaseCalibrate,
MfClassicNestedPhaseRecalibrate,
MfClassicNestedPhaseCollectNtEnc, MfClassicNestedPhaseCollectNtEnc,
MfClassicNestedPhaseFinished, MfClassicNestedPhaseFinished,
} MfClassicNestedPhase; } MfClassicNestedPhase;

View File

@ -10,6 +10,7 @@
// TODO: Buffer writes for Hardnested, set state to Log when finished and sum property matches // 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: Load dictionaries specific to a CUID to not clutter the user dictionary
// TODO: Validate Hardnested is collecting nonces from the correct block // TODO: Validate Hardnested is collecting nonces from the correct block
// TODO: Nested entrypoint for cached keys
#define MF_CLASSIC_MAX_BUFF_SIZE (64) #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 mf_classic_poller_handler_analyze_backdoor(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue; NfcCommand command = NfcCommandReset;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
size_t current_key_index = size_t current_key_index =
@ -1152,11 +1153,13 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance)
command = NfcCommandReset; command = NfcCommandReset;
uint8_t target_block = uint8_t target_block =
mf_classic_get_first_block_num_of_sector(dict_attack_ctx->nested_target_key / 4); 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( error = mf_classic_poller_auth_nested(
instance, instance,
target_block, target_block,
&dict_attack_ctx->nested_known_key, &dict_attack_ctx->nested_known_key,
dict_attack_ctx->nested_known_key_type, target_key_type,
&auth_ctx, &auth_ctx,
use_backdoor, use_backdoor,
false); false);
@ -1231,7 +1234,6 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance)
bool found = false; bool found = false;
uint32_t decrypted_nt_enc = decrypt_nt_enc( uint32_t decrypted_nt_enc = decrypt_nt_enc(
cuid, nt_enc_temp_arr[collection_cycle], dict_attack_ctx->nested_known_key); 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++) { for(int i = 0; i < 65535; i++) {
uint32_t nth_successor = prng_successor(nt_prev, i); uint32_t nth_successor = prng_successor(nt_prev, i);
if(nth_successor != decrypted_nt_enc) { 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)) ? (is_weak && !(dict_attack_ctx->static_encrypted)) ?
((dict_attack_ctx->attempt_count + 2) + nonce_pair_index) : ((dict_attack_ctx->attempt_count + 2) + nonce_pair_index) :
1; 1;
MfClassicKeyType target_key_type = ((dict_attack_ctx->nested_target_key & 0x03) < 2) ? uint8_t target_sector = dict_attack_ctx->nested_target_key / (is_weak ? 4 : 2);
MfClassicKeyTypeA : MfClassicKeyType target_key_type =
MfClassicKeyTypeB; (dict_attack_ctx->nested_target_key % (is_weak ? 4 : 2) < (is_weak ? 2 : 1)) ?
// TODO: mf_classic_get_sector_trailer_num_by_sector or mf_classic_get_sector_trailer_num_by_block? Match calibrated? MfClassicKeyTypeA :
uint8_t target_block = (4 * (dict_attack_ctx->nested_target_key / 4)) + 3; 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]; uint32_t nt_enc_temp_arr[nt_enc_per_collection];
uint8_t nt_enc_collected = 0; uint8_t nt_enc_collected = 0;
uint8_t parity = 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) { 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 // TODO: Look into using MfClassicNt more
NfcCommand command = NfcCommandContinue; NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; 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))) ? ((!is_weak) && ((dict_attack_ctx->nested_target_key % 16) < 8))) ?
MfClassicKeyTypeA : MfClassicKeyTypeA :
MfClassicKeyTypeB; MfClassicKeyTypeB;
// TODO: mf_classic_get_sector_trailer_num_by_sector or mf_classic_get_sector_trailer_num_by_block? uint8_t target_sector = dict_attack_ctx->nested_target_key / (is_weak ? 2 : 16);
uint8_t target_block = (is_weak) ? (4 * (dict_attack_ctx->nested_target_key / 2)) + 3 : uint8_t target_block = mf_classic_get_sector_trailer_num_by_sector(target_sector);
(4 * (dict_attack_ctx->nested_target_key / 16)) + 3;
uint8_t parity = 0; uint8_t parity = 0;
if(((is_weak) && (dict_attack_ctx->nested_nonce.count == 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) { NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance) {
// Iterate through keys // This function guides the nested attack through its phases, and iterates over the target keys
//NfcCommand command = NfcCommandContinue;
NfcCommand command = mf_classic_poller_handle_data_update(instance); NfcCommand command = mf_classic_poller_handle_data_update(instance);
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
bool initial_dict_attack_iter = false; bool initial_dict_attack_iter = false;
@ -1795,6 +1796,7 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance
initial_dict_attack_iter = true; initial_dict_attack_iter = true;
} }
} }
// Identify PRNG type
if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseAnalyzePRNG) { if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseAnalyzePRNG) {
if(dict_attack_ctx->nested_nonce.count < MF_CLASSIC_NESTED_ANALYZE_NT_COUNT) { if(dict_attack_ctx->nested_nonce.count < MF_CLASSIC_NESTED_ANALYZE_NT_COUNT) {
instance->state = MfClassicPollerStateNestedCollectNt; instance->state = MfClassicPollerStateNestedCollectNt;
@ -1896,6 +1898,10 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance
instance->state = MfClassicPollerStateSuccess; instance->state = MfClassicPollerStateSuccess;
return command; 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; dict_attack_ctx->nested_phase = MfClassicNestedPhaseCalibrate;
instance->state = MfClassicPollerStateNestedController; instance->state = MfClassicPollerStateNestedController;
return command; return command;
@ -1919,24 +1925,23 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance
} }
// Calibration // Calibration
bool initial_collect_nt_enc_iter = false; bool initial_collect_nt_enc_iter = false;
bool recalibrated = false;
if(!(dict_attack_ctx->calibrated)) { if(!(dict_attack_ctx->calibrated)) {
if(dict_attack_ctx->prng_type == MfClassicPrngTypeWeak) { if(dict_attack_ctx->prng_type == MfClassicPrngTypeWeak) {
instance->state = MfClassicPollerStateNestedCalibrate; instance->state = MfClassicPollerStateNestedCalibrate;
return command; return command;
} }
initial_collect_nt_enc_iter = true; initial_collect_nt_enc_iter = true;
dict_attack_ctx->auth_passed = true;
dict_attack_ctx->calibrated = true; dict_attack_ctx->calibrated = true;
dict_attack_ctx->current_key_checked = false;
dict_attack_ctx->nested_phase = MfClassicNestedPhaseCollectNtEnc; dict_attack_ctx->nested_phase = MfClassicNestedPhaseCollectNtEnc;
} else if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseCalibrate) { } else if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseCalibrate) {
initial_collect_nt_enc_iter = true; initial_collect_nt_enc_iter = true;
dict_attack_ctx->auth_passed = true; dict_attack_ctx->nested_phase = MfClassicNestedPhaseCollectNtEnc;
dict_attack_ctx->current_key_checked = false; } else if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseRecalibrate) {
recalibrated = true;
dict_attack_ctx->nested_phase = MfClassicNestedPhaseCollectNtEnc; dict_attack_ctx->nested_phase = MfClassicNestedPhaseCollectNtEnc;
} }
// Collect and log nonces // Collect and log nonces
// TODO: Calibrates too frequently for static encrypted backdoored tags
if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseCollectNtEnc) { if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseCollectNtEnc) {
if(((is_weak) && (dict_attack_ctx->nested_nonce.count == 2)) || if(((is_weak) && (dict_attack_ctx->nested_nonce.count == 2)) ||
((is_weak) && (dict_attack_ctx->backdoor == MfClassicBackdoorAuth3) && ((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)); memset(dict_attack_ctx->nt_enc_msb, 0, sizeof(dict_attack_ctx->nt_enc_msb));
return command; 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++; dict_attack_ctx->attempt_count++;
} else { } else {
if(is_weak && !(initial_collect_nt_enc_iter)) { if(is_weak && !(initial_collect_nt_enc_iter) && !(recalibrated)) {
if(!(dict_attack_ctx->static_encrypted)) { if(!(dict_attack_ctx->static_encrypted)) {
dict_attack_ctx->nested_target_key++; dict_attack_ctx->nested_target_key++;
} else { } else {
@ -1982,28 +1990,36 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance
if(dict_attack_ctx->nested_target_key % 2 == 0) { if(dict_attack_ctx->nested_target_key % 2 == 0) {
dict_attack_ctx->current_key_checked = false; 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->attempt_count = 0;
} }
dict_attack_ctx->auth_passed = true; dict_attack_ctx->auth_passed = true;
if(!(dict_attack_ctx->current_key_checked)) { if(!(dict_attack_ctx->current_key_checked)) {
dict_attack_ctx->current_key_checked = true;
// Check if the nested target key is a known key // Check if the nested target key is a known key
if(mf_classic_nested_is_target_key_found(instance, false)) { if(mf_classic_nested_is_target_key_found(instance, false)) {
// Continue to next key // Continue to next key
if(!(is_weak)) { if(!(dict_attack_ctx->static_encrypted)) {
dict_attack_ctx->nested_target_key++; dict_attack_ctx->nested_target_key++;
dict_attack_ctx->current_key_checked = false; dict_attack_ctx->current_key_checked = false;
} }
instance->state = MfClassicPollerStateNestedController; instance->state = MfClassicPollerStateNestedController;
return command; 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)) || if((is_weak && (dict_attack_ctx->attempt_count >= MF_CLASSIC_NESTED_RETRY_MAXIMUM)) ||
(!(is_weak) && (!(is_weak) &&
(dict_attack_ctx->attempt_count >= MF_CLASSIC_NESTED_HARD_RETRY_MAXIMUM))) { (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; dict_attack_ctx->attempt_count = 0;
} }
// Collect a nonce
dict_attack_ctx->auth_passed = false; dict_attack_ctx->auth_passed = false;
instance->state = MfClassicPollerStateNestedCollectNtEnc; instance->state = MfClassicPollerStateNestedCollectNtEnc;
return command; return command;

View File

@ -54,6 +54,7 @@ typedef enum {
MfClassicNestedPhaseDictAttack, MfClassicNestedPhaseDictAttack,
MfClassicNestedPhaseDictAttackResume, MfClassicNestedPhaseDictAttackResume,
MfClassicNestedPhaseCalibrate, MfClassicNestedPhaseCalibrate,
MfClassicNestedPhaseRecalibrate,
MfClassicNestedPhaseCollectNtEnc, MfClassicNestedPhaseCollectNtEnc,
MfClassicNestedPhaseFinished, MfClassicNestedPhaseFinished,
} MfClassicNestedPhase; } MfClassicNestedPhase;