Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MIFARE Classic Key Recovery Improvements #3822

Merged
merged 99 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from 95 commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
cf6d6bb
Initial structure for nonce collection
noproto Aug 1, 2024
b154603
Nonce logging
noproto Aug 2, 2024
ef16770
Dictionary attack structure
noproto Aug 2, 2024
d3bf372
Merge branch 'flipperdevices:dev' into nestednonces
noproto Aug 2, 2024
213ec1d
Fix compilation
noproto Aug 2, 2024
9ba78bf
Identified method to reduce candidate states
noproto Aug 4, 2024
6d66638
Use EXT_PATH instead of ANY_PATH
noproto Aug 4, 2024
e61b543
Merge branch 'flipperdevices:dev' into nestednonces
noproto Aug 4, 2024
09f8a73
Use median calibrated distance, collect parity bits
noproto Aug 5, 2024
7d2cab5
Modify parity collection
noproto Aug 5, 2024
8dd3daf
Fixed parity bit collection
noproto Aug 10, 2024
4b44288
Add note to fix nonce logging
noproto Aug 10, 2024
5feeae8
Fix nonce logging
noproto Aug 10, 2024
3acba77
Clean redundant code
noproto Aug 10, 2024
6332ec7
Fix valid_nonce
noproto Aug 11, 2024
01b1948
First attempt disambiguous nonce implementation
noproto Aug 14, 2024
8d1a220
Merge remote-tracking branch 'origin/dev' into nestednonces
noproto Aug 15, 2024
cc8cae7
FM11RF08S backdoor detection
noproto Aug 15, 2024
79bc887
Initial accelerated dictionary attack for weak PRNGs
noproto Aug 19, 2024
0af28fb
Refactor to nested dictionary attack
noproto Aug 19, 2024
08ca794
Renaming some variables
noproto Aug 19, 2024
b7e63bf
Hard PRNG support for accelerated dictionary attack
noproto Aug 20, 2024
bbc10cd
Update found keys, initial attempt
noproto Aug 20, 2024
75a0e4b
Update found keys, second attempt
noproto Aug 20, 2024
c1f01ce
Code cleanup
noproto Aug 20, 2024
26845cb
Misc bugfixes
noproto Aug 21, 2024
5235592
Only use dicts in search_dicts_for_nonce_key if we have them
noproto Aug 27, 2024
0b33c85
Collect nonces again
noproto Aug 27, 2024
c0331ba
Should be detecting both backdoors now
noproto Aug 27, 2024
4c14594
Relocate backdoor detection
noproto Aug 28, 2024
144424e
Merge branch 'flipperdevices:dev' into nestednonces
noproto Aug 30, 2024
90d0c3d
Hardnested support
noproto Sep 2, 2024
2abeb07
Fix regression for regular nested attack
noproto Sep 2, 2024
ccc4326
Backdoor read
noproto Sep 2, 2024
9c7120e
Backdoor working up to calibration
noproto Sep 2, 2024
2cb2f05
Backdoor nested calibration
noproto Sep 3, 2024
6e9fe1e
Don't recalibrate hard PRNG tags
noproto Sep 3, 2024
2e0cd32
Static encrypted nonce collection
noproto Sep 3, 2024
3cb3eab
Update TODO
noproto Sep 3, 2024
92122b2
NFC app UI updates, MVP
noproto Sep 3, 2024
7bb3349
Merge branch 'flipperdevices:dev' into nestednonces
noproto Sep 3, 2024
b09d5a0
Bump f18 API version (all functions are NFC related)
noproto Sep 4, 2024
9ea7a46
Merge remote-tracking branch 'origin/dev' into nestednonces
skotopes Sep 7, 2024
9558a5f
Merge branch 'dev' into nestednonces
gornekich Sep 9, 2024
cba58ed
Add new backdoor key, fix UI status update carrying over from previou…
noproto Sep 10, 2024
ab8bc3e
Clear TODO line
noproto Sep 10, 2024
d7484ee
Fix v1/v2 backdoor nonce collection
noproto Sep 10, 2024
c43806b
Speed up backdoor detection, alert on new backdoor
noproto Sep 10, 2024
ab0debb
Add additional condition to backdoor check
noproto Sep 10, 2024
8d26636
Merge branch 'flipperdevices:dev' into nestednonces
noproto Sep 11, 2024
13411da
I'll try freeing memory, that's a good trick!
noproto Sep 11, 2024
8edafa3
Do not enter nested attack if card is already finished
noproto Sep 12, 2024
18f8cfb
Do not reset the poller between collected nonces
noproto Sep 16, 2024
4836a54
Merge branch 'flipperdevices:dev' into nestednonces
noproto Sep 16, 2024
3ab752b
Clean up various issues
noproto Sep 17, 2024
8eae5c0
Fix Hardnested sector/key type logging
noproto Sep 17, 2024
d8864a4
Add nested_target_key 64 to TODO
noproto Sep 17, 2024
c1cdd49
Implement progress bar for upgraded attacks in NFC app
noproto Sep 18, 2024
96606dc
Typo
noproto Sep 18, 2024
6eccdc8
Zero nested_target_key and msb_count on exit
noproto Sep 20, 2024
c21b359
Note TODO (malloc)
noproto Sep 20, 2024
6a77ab7
Dismiss duplicate nonces
noproto Sep 23, 2024
901bdf9
Fix calibration (ensure values are within 3 standard deviations)
noproto Sep 23, 2024
4eb0f2a
Log static
noproto Sep 23, 2024
6ae9506
No nested dictionary attack re-entry
noproto Sep 23, 2024
cd76926
Note minor inefficiency
noproto Sep 23, 2024
0ba8ac4
Uniformly use crypto1_ prefix for symbols in Crypto1 API
noproto Sep 23, 2024
61e24fc
Fix include paths
noproto Sep 24, 2024
099bb40
Fix include paths cont
noproto Sep 25, 2024
ba672e7
Support CUID dictionary
noproto Sep 25, 2024
232560f
Merge branch 'dev' into nestednonces
noproto Oct 3, 2024
00f3564
Fix log levels
noproto Oct 3, 2024
4f722a0
Avoid storage errors, clean up temporary files
noproto Oct 4, 2024
a905c14
Handle invalid key candidates
noproto Oct 6, 2024
f346412
Merge branch 'dev' into nestednonces
noproto Oct 6, 2024
a1590fc
Fix memory leak in static encrypted attack
noproto Oct 8, 2024
bcc8d3e
Merge branch 'nestednonces' of https://github.com/noproto/flipperzero…
noproto Oct 8, 2024
56febb1
Merge branch 'dev' into nestednonces
noproto Oct 8, 2024
b843856
Fix memory leak, use COUNT_OF macro
noproto Oct 9, 2024
3976f12
Use single call to free FuriString
noproto Oct 9, 2024
a7c0819
Refactor enums to avoid redefinition
noproto Oct 11, 2024
5404788
Merge branch 'dev' into nestednonces
noproto Oct 11, 2024
889b19b
Merge branch 'dev' into nestednonces
noproto Oct 14, 2024
f530eee
Merge branch 'dev' into nestednonces
noproto Oct 14, 2024
02f7c6b
Merge branch 'dev' into nestednonces
skotopes Oct 14, 2024
2bc02c0
Merge branch 'dev' into nestednonces
skotopes Oct 16, 2024
4be9e79
Fix multiple crashes and state machine logic
noproto Oct 17, 2024
1101748
Merge branch 'nestednonces' of https://github.com/noproto/flipperzero…
noproto Oct 17, 2024
2be0cfb
Merge branch 'dev' into nestednonces
noproto Oct 17, 2024
897817a
Fix inconsistent assignment of known key and known key type/sector
noproto Oct 18, 2024
db26c85
Backdoor known key logic still needs the current key
noproto Oct 18, 2024
92aa70b
Larger data type for 4K support
noproto Oct 22, 2024
4763762
Merge branch 'dev' into nestednonces
noproto Oct 22, 2024
bf7b91f
Fix typo
noproto Oct 23, 2024
6dbb46a
Fix issue with resume logic
noproto Oct 25, 2024
2282587
Mark TODOs for next PR
noproto Oct 29, 2024
eb1aabb
Remove redundant assignment
noproto Oct 29, 2024
c240077
Fix size_t format specifier
noproto Oct 29, 2024
907019c
Simplify auth_passed condition
noproto Oct 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion applications/debug/unit_tests/tests/nfc/nfc_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ NfcCommand mf_classic_poller_send_frame_callback(NfcGenericEventEx event, void*
MfClassicKey key = {
.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
};
error = mf_classic_poller_auth(instance, 0, &key, MfClassicKeyTypeA, NULL);
error = mf_classic_poller_auth(instance, 0, &key, MfClassicKeyTypeA, NULL, false);
frame_test->state = (error == MfClassicErrorNone) ?
NfcTestMfClassicSendFrameTestStateReadBlock :
NfcTestMfClassicSendFrameTestStateFail;
Expand Down
12 changes: 11 additions & 1 deletion applications/main/nfc/nfc_app_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,12 @@
#define NFC_APP_MFKEY32_LOGS_FILE_NAME ".mfkey32.log"
#define NFC_APP_MFKEY32_LOGS_FILE_PATH (NFC_APP_FOLDER "/" NFC_APP_MFKEY32_LOGS_FILE_NAME)

#define NFC_APP_MF_CLASSIC_DICT_USER_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict_user.nfc")
#define NFC_APP_MF_CLASSIC_DICT_USER_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict_user.nfc")
#define NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH \
(NFC_APP_FOLDER "/assets/mf_classic_dict_user_nested.nfc")
#define NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict.nfc")
#define NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH \
(NFC_APP_FOLDER "/assets/mf_classic_dict_nested.nfc")

typedef enum {
NfcRpcStateIdle,
Expand All @@ -93,6 +97,12 @@ typedef struct {
bool is_key_attack;
uint8_t key_attack_current_sector;
bool is_card_present;
MfClassicNestedPhase nested_phase;
MfClassicPrngType prng_type;
MfClassicBackdoor backdoor;
uint16_t nested_target_key;
uint16_t msb_count;
bool enhanced_dict;
} NfcMfClassicDictAttackContext;

struct NfcApp {
Expand Down
129 changes: 122 additions & 7 deletions applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
#include "../nfc_app_i.h"

#include <bit_lib/bit_lib.h>
#include <dolphin/dolphin.h>
#include <lib/nfc/protocols/mf_classic/mf_classic_poller.h>

#define TAG "NfcMfClassicDictAttack"

// TODO: Fix lag when leaving the dictionary attack view after Hardnested
// TODO: Re-enters backdoor detection between user and system dictionary if no backdoor is found

noproto marked this conversation as resolved.
Show resolved Hide resolved
typedef enum {
DictAttackStateCUIDDictInProgress,
DictAttackStateUserDictInProgress,
DictAttackStateSystemDictInProgress,
} DictAttackState;
Expand All @@ -29,7 +33,9 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
} else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {
const MfClassicData* mfc_data =
nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);
mfc_event->data->poller_mode.mode = MfClassicPollerModeDictAttack;
mfc_event->data->poller_mode.mode = (instance->nfc_dict_context.enhanced_dict) ?
skotopes marked this conversation as resolved.
Show resolved Hide resolved
MfClassicPollerModeDictAttackEnhanced :
MfClassicPollerModeDictAttackStandard;
mfc_event->data->poller_mode.data = mfc_data;
instance->nfc_dict_context.sectors_total =
mf_classic_get_total_sectors_num(mfc_data->type);
Expand Down Expand Up @@ -58,6 +64,11 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
instance->nfc_dict_context.sectors_read = data_update->sectors_read;
instance->nfc_dict_context.keys_found = data_update->keys_found;
instance->nfc_dict_context.current_sector = data_update->current_sector;
instance->nfc_dict_context.nested_phase = data_update->nested_phase;
instance->nfc_dict_context.prng_type = data_update->prng_type;
instance->nfc_dict_context.backdoor = data_update->backdoor;
instance->nfc_dict_context.nested_target_key = data_update->nested_target_key;
instance->nfc_dict_context.msb_count = data_update->msb_count;
view_dispatcher_send_custom_event(
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
} else if(mfc_event->type == MfClassicPollerEventTypeNextSector) {
Expand Down Expand Up @@ -117,19 +128,75 @@ static void nfc_scene_mf_classic_dict_attack_update_view(NfcApp* instance) {
dict_attack_set_keys_found(instance->dict_attack, mfc_dict->keys_found);
dict_attack_set_current_dict_key(instance->dict_attack, mfc_dict->dict_keys_current);
dict_attack_set_current_sector(instance->dict_attack, mfc_dict->current_sector);
dict_attack_set_nested_phase(instance->dict_attack, mfc_dict->nested_phase);
dict_attack_set_prng_type(instance->dict_attack, mfc_dict->prng_type);
dict_attack_set_backdoor(instance->dict_attack, mfc_dict->backdoor);
dict_attack_set_nested_target_key(instance->dict_attack, mfc_dict->nested_target_key);
dict_attack_set_msb_count(instance->dict_attack, mfc_dict->msb_count);
}
}

static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) {
uint32_t state =
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack);
if(state == DictAttackStateCUIDDictInProgress) {
size_t cuid_len = 0;
const uint8_t* cuid = nfc_device_get_uid(instance->nfc_device, &cuid_len);
FuriString* cuid_dict_path = furi_string_alloc_printf(
"%s/mf_classic_dict_%08lx.nfc",
EXT_PATH("nfc/assets"),
(uint32_t)bit_lib_bytes_to_num_be(cuid + (cuid_len - 4), 4));

do {
if(!keys_dict_check_presence(furi_string_get_cstr(cuid_dict_path))) {
state = DictAttackStateUserDictInProgress;
noproto marked this conversation as resolved.
Show resolved Hide resolved
break;
}

instance->nfc_dict_context.dict = keys_dict_alloc(
furi_string_get_cstr(cuid_dict_path),
KeysDictModeOpenExisting,
sizeof(MfClassicKey));

if(keys_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) {
keys_dict_free(instance->nfc_dict_context.dict);
state = DictAttackStateUserDictInProgress;
break;
}

dict_attack_set_header(instance->dict_attack, "MF Classic CUID Dictionary");
} while(false);

furi_string_free(cuid_dict_path);
}
if(state == DictAttackStateUserDictInProgress) {
do {
instance->nfc_dict_context.enhanced_dict = true;

if(keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH)) {
storage_common_remove(
instance->storage, NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH);
}
if(keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH)) {
storage_common_copy(
instance->storage,
NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH,
NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH);
}

if(!keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) {
state = DictAttackStateSystemDictInProgress;
break;
}

if(keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH)) {
storage_common_remove(instance->storage, NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH);
}
storage_common_copy(
instance->storage,
NFC_APP_MF_CLASSIC_DICT_USER_PATH,
NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH);

instance->nfc_dict_context.dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
if(keys_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) {
Expand Down Expand Up @@ -164,7 +231,7 @@ void nfc_scene_mf_classic_dict_attack_on_enter(void* context) {
NfcApp* instance = context;

scene_manager_set_scene_state(
instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateUserDictInProgress);
instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateCUIDDictInProgress);
nfc_scene_mf_classic_dict_attack_prepare_view(instance);
dict_attack_set_card_state(instance->dict_attack, true);
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDictAttack);
Expand Down Expand Up @@ -193,7 +260,21 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack);
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventDictAttackComplete) {
if(state == DictAttackStateUserDictInProgress) {
bool ran_nested_dict = instance->nfc_dict_context.nested_phase !=
MfClassicNestedPhaseNone;
if(state == DictAttackStateCUIDDictInProgress) {
nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller);
keys_dict_free(instance->nfc_dict_context.dict);
scene_manager_set_scene_state(
instance->scene_manager,
NfcSceneMfClassicDictAttack,
DictAttackStateUserDictInProgress);
nfc_scene_mf_classic_dict_attack_prepare_view(instance);
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);
nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);
consumed = true;
} else if(state == DictAttackStateUserDictInProgress && !(ran_nested_dict)) {
nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller);
keys_dict_free(instance->nfc_dict_context.dict);
Expand Down Expand Up @@ -222,7 +303,27 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
} else if(event.event == NfcCustomEventDictAttackSkip) {
const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller);
nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, mfc_data);
if(state == DictAttackStateUserDictInProgress) {
bool ran_nested_dict = instance->nfc_dict_context.nested_phase !=
skotopes marked this conversation as resolved.
Show resolved Hide resolved
MfClassicNestedPhaseNone;
if(state == DictAttackStateCUIDDictInProgress) {
if(instance->nfc_dict_context.is_card_present) {
nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller);
keys_dict_free(instance->nfc_dict_context.dict);
scene_manager_set_scene_state(
instance->scene_manager,
NfcSceneMfClassicDictAttack,
DictAttackStateUserDictInProgress);
nfc_scene_mf_classic_dict_attack_prepare_view(instance);
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);
nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);
} else {
nfc_scene_mf_classic_dict_attack_notify_read(instance);
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
dolphin_deed(DolphinDeedNfcReadSuccess);
}
consumed = true;
} else if(state == DictAttackStateUserDictInProgress && !(ran_nested_dict)) {
if(instance->nfc_dict_context.is_card_present) {
nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller);
Expand All @@ -240,7 +341,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
dolphin_deed(DolphinDeedNfcReadSuccess);
}
consumed = true;
} else if(state == DictAttackStateSystemDictInProgress) {
} else {
nfc_scene_mf_classic_dict_attack_notify_read(instance);
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
dolphin_deed(DolphinDeedNfcReadSuccess);
Expand All @@ -262,7 +363,7 @@ void nfc_scene_mf_classic_dict_attack_on_exit(void* context) {

dict_attack_reset(instance->dict_attack);
scene_manager_set_scene_state(
instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateUserDictInProgress);
instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateCUIDDictInProgress);

keys_dict_free(instance->nfc_dict_context.dict);

Expand All @@ -275,6 +376,20 @@ void nfc_scene_mf_classic_dict_attack_on_exit(void* context) {
instance->nfc_dict_context.is_key_attack = false;
instance->nfc_dict_context.key_attack_current_sector = 0;
instance->nfc_dict_context.is_card_present = false;
instance->nfc_dict_context.nested_phase = MfClassicNestedPhaseNone;
instance->nfc_dict_context.prng_type = MfClassicPrngTypeUnknown;
instance->nfc_dict_context.backdoor = MfClassicBackdoorUnknown;
instance->nfc_dict_context.nested_target_key = 0;
instance->nfc_dict_context.msb_count = 0;
instance->nfc_dict_context.enhanced_dict = false;

// Clean up temporary files used for nested dictionary attack
if(keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH)) {
storage_common_remove(instance->storage, NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH);
}
if(keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH)) {
storage_common_remove(instance->storage, NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH);
}

nfc_blink_stop(instance);
notification_message(instance->notifications, &sequence_display_backlight_enforce_auto);
Expand Down
Loading
Loading