Skip to content

Conversation

noproto
Copy link
Contributor

@noproto noproto commented Aug 4, 2024

What's new

  • MIFARE Classic Accelerated dictionary attack: dictionary attacks reduced to several seconds - checks ~3500 keys per second
  • MIFARE Classic Nested attack support: collects nested nonces to be cracked by MFKey, no longer requiring users to downgrade to FW 0.93.0
  • MIFARE Classic Static encrypted backdoor support: collects static encrypted nonces to be cracked by MFKey using NXP/Fudan backdoor, allowing key recovery of all non-hardened MIFARE Classic tags on-device

Verification

This PR currently contains the minimum necessary code to achieve the intended functionality. It will be superseded with performance improvements.

  • Accelerated dictionary attack: Benchmark against standard dictionary attack
    • Note: This PR adds nonce collection methods which trade some of the time reclaimed by this improvement
  • Nested (weak PRNG): Using any weak PRNG tag and MFKey (v3.0 available for testing here)
  • Hardnested (hard PRNG): Verify nonces are stored at /ext/nfc/.nested.log (can be cracked using HardnestedRecovery)
  • Static encrypted (backdoor): Using static encrypted tag and MFKey

Checklist (For Reviewer)

  • PR has description of feature/bug or link to Confluence/Jira task
  • Description contains actions to verify feature/bugfix
  • I've built this code, uploaded it to the device and verified feature/bugfix

@noproto
Copy link
Contributor Author

noproto commented Aug 10, 2024

This PR also resolves bit_buffer_copy_bytes_with_parity improperly storing parity bits: 8dd3daf#diff-1d0dbb15ed26364f785d68ba753267b5326c95629d29641ef53276ac32336f7e

@hedger hedger added the NFC NFC-related label Aug 12, 2024
@noproto
Copy link
Contributor Author

noproto commented Aug 21, 2024

The accelerated dictionary attack is mostly working. I'm tracking down a minor bug in it and making sure the UI reflects the state of the attack.

The fork takes an average of 10 seconds to run dictionary attacks in my tests (1 second of the average is backdoor detection - separate from the dictionary attack). OFW 0.105.0 takes an average of 3 minutes 10 seconds. On real tags the number of unknown keys and the offset in the dictionary are all different, here are five benchmarks on random MIFARE Classic tags:

OFW 0.105.0: 4 min 44 seconds (found 19/32 keys, 16 sectors read)
nestednonces 26845cb: 10 seconds (found 19/32 keys, 16 sectors read)
---
OFW 0.105.0: 5 min 8 seconds (found 18/32 keys, 2 sectors read) 
nestednonces 26845cb: 25 seconds (found 18/32 keys, 2 sectors read)
---
OFW 0.105.0: 22 seconds (found 32/32 keys, 16 sectors read) 
nestednonces 26845cb: 3 seconds (found 32/32 keys, 16 sectors read)
---
OFW 0.105.0: 35 seconds (found 32/32 keys, 16 sectors read) 
nestednonces 26845cb: 10 seconds (found 32/32 keys, 16 sectors read)
---
OFW 0.105.0: 5 min 3 seconds (found 18/32 keys, 16 sectors read) 
nestednonces 26845cb: 10 seconds (found 18/32 keys, 16 sectors read)

@noproto
Copy link
Contributor Author

noproto commented Oct 18, 2024

All issues resolved that I could reproduce, please re-test this PR @skotopes @RebornedBrain @doomwastaken:

So, @RebornedBrain @doomwastaken did some tests. Here is what they found:

Test 1: no plugins, stuck at particular key

Tests were performed without plugins (remove folder Plugins at "SD Card/apps_data/nfc") This card stuck at one particular key. In logs I see that process still goes, but key remains the same, but before keys were changing. See details on the screenshot.

test_1.zip

Test 2: MFC 4k, crash

This MFC 4k (not magic) card where first sector was protected with key A "DEADBEAFFFFF" which is not present in any dictionaries. Result - crash "Wrong sector num", repeats every time. This card has backdoor v2.

test_2.zip

Steps taken

  1. Noted power of 2 in both sector and keys found. Possible issue with datatype. Decided to not remove plugins for initial test.
  2. Using NFC Magic, cloned Plantan_white.nfc to a Gen4 UMC magic card
  3. Read card with NFC app. Crashes on sectors read 32/40, keys found 43/80, 33/40 on progress bar, "[CRASH][NfcWorker] furi_check failed" after "Found key candidate"
  4. Dumped call stack:
__furi_crash_implementation@0x08012280 (/home/ubuntu/Flipper/nestednonces/flipperzero-firmware/furi/core/check.c:170)
mf_classic_get_device_name@0x08036452 (/home/ubuntu/Flipper/nestednonces/flipperzero-firmware/lib/nfc/protocols/mf_classic/mf_classic.c:340)
mf_classic_get_first_block_num_of_sector@0x08036d6a (/home/ubuntu/Flipper/nestednonces/flipperzero-firmware/lib/nfc/protocols/mf_classic/mf_classic.c:565)
mf_classic_poller_handler_key_reuse_auth_key_a@0x080420fe (/home/ubuntu/Flipper/nestednonces/flipperzero-firmware/lib/nfc/protocols/mf_classic/mf_classic_poller.c:916)
(..)
  1. Identified issue: reuse_key_sector assigned to 43, when tag only has 40 sectors (0-39). This is because of a bad assumption (sector number = block number / 4, is not true for MFC 4K)
  2. Fixed in 4be9e79
  3. Re-ran dictionary attack, reproduced test 2 crash at end of nonce collection: "[CRASH][NfcWorker] Wrong sector num"
  4. Dumped call stack:
__furi_crash_implementation@0x08012280 (/home/ubuntu/Flipper/nestednonces/flipperzero-firmware/furi/core/check.c:170)
mf_classic_get_sector_trailer_num_by_sector@0x0803666c (/home/ubuntu/Flipper/nestednonces/flipperzero-firmware/lib/nfc/protocols/mf_classic/mf_classic.c:403)
mf_classic_get_sector_trailer_num_by_sector@0x0803666c (/home/ubuntu/Flipper/nestednonces/flipperzero-firmware/lib/nfc/protocols/mf_classic/mf_classic.c:395)
mf_classic_poller_handler_nested_collect_nt_enc@0x080427fa (/home/ubuntu/Flipper/nestednonces/flipperzero-firmware/lib/nfc/protocols/mf_classic/mf_classic_poller.c:1362)
(..)
  1. Identified issue: off by one in nested_target_key, doesn't crash on MFC 1K since no effect besides reduced performance (same issue as "TODO: Fix rare nested_target_key 64 bug")
  2. Fixed in 4be9e79
  3. Test: Read tag, recovered 29 keys with MFKey (78/80 keys), read tag, recovered 2 keys with MFKey, 80/80 keys 40/40 sectors read. Working.
  4. Began re-testing original issues. Removed plugins folder, unable to reproduce. Removed user dictionary, unable to reproduce.

Conclusion

Two issues: faulty state machine logic, target sector on 4K cards. Fixed in 4be9e79

Test 3: MFC 1k, can't find key

This MFC 1k (magic) card where first sector was also protected with key A "DEADBEAFFFFF". Result Flipper cannot find this key. This card has no any backdoor.

test_3.zip

Steps taken

  1. Verified Prox.nfc has sector 1 key A DEADBEAFFFFF
  2. Using NFC Magic, cloned Prox.nfc to a Gen1a magic card
  3. Read card with NFC app. 31/32 keys. 1 nonce collected in .nested.log
  4. Cracked nonce in MFKey.
  5. Returned to NFC app and read card. 32/32, 16/16 sectors read
  6. Flashed second Flipper device with nestednonces fork
  7. Same result

Conclusion

Possible cache issue? Verify no keys are cached for this tag.
If issue persists, debug logs would be useful.

Test 4: Test with plugins

Flipper reads this card totally when there is no plugins (it passes nested attack and etc). When plugins are present Flipper unable to read it and in logs I see some auth errors.

test_4.zip

Steps taken

  1. Ensured NFC plugins folder present on Flipper device
  2. Using NFC Magic, cloned Small_troyka.nfc to a Gen4 UMC magic card
  3. Read card with NFC app. 32/32 keys.
  4. Flashed second Flipper device with nestednonces fork
  5. Reproduced auth errors. Narrowed issue to interaction between nestednonces and troika_parser.fal NFC plugin.
  6. Identified issue: inconsistent assignment of known key and known key type/sector. This is because of a bad assumption (key found in dictionary attack was assumed to be first key)
  7. Fixed in 897817a and db26c85
  8. Re-ran dictionary attack on all card types, works. No longer able to reproduce original issue.

Conclusion

Inconsistent assignment of known key and known key type/sector led to repeated failed authentication attempts. Keys were provided by specific NFC plugins instead of the dictionary attack. Fixed in 897817a and db26c85

Test 5: On this card Flipper stuck

On this card Flipper stuck

test_5.zip

Steps taken

  1. Ensured NFC plugins folder present on Flipper device
  2. Using NFC Magic, cloned Disappeared_corridor.nfc to a Gen4 UMC magic card
  3. Read card with NFC app. 70/80 keys.
  4. Flashed second Flipper device with nestednonces fork
  5. Same result. Troyka card recognized
  6. Removed plugins folder.
  7. Read card with NFC app. 70/80 keys.
  8. Added a user dictionary file to the device with 1 irrelevant key
  9. Read card with NFC app. 70/80 keys. Unable to reproduce original issue.

Conclusion

Likely related to the other (now fixed) issues. Please re-test.
If issue persists, debug logs would be useful.

@noproto
Copy link
Contributor Author

noproto commented Oct 19, 2024

Since #3961 (comment) will delay 1.1, is it still possible to squeeze this PR in too if the issues identified in QA are resolved by #3822 (comment) ?

These changes would allow me to share an early, significantly easier process with the users as well as limit the scope of each PR versus rolling up more changes into this single PR making it unwieldy to do QA. Additionally, it would be useful to collect feedback from users so we can identify any rare or uncommon issues (thanks to the exceptional QA already done, every issue that was reported to me - even prior to the review - has been resolved).

RE: how users could use the nonces collected by this PR, PR 243 to good-faps would make a complete process available: flipperdevices/flipperzero-good-faps#243

Wanted to bring this up for your consideration. I understand if it's not possible. For context, this is what is required for reading cards today. It becomes a two step process with this PR (3822) plus PR 243 to good-faps.

@RebornedBrain
Copy link
Contributor

@noproto, Hi, I've retested those cases and here are the results:

Test 1: no plugins, stuck at particular key - Fixed, now works fine. ✅
Test 2: MFC 4k, crash - Fixed, now Flipper reads card and collect nonces. ✅
Test 3: MFC 1k, can't find key - Fixed, ✅ but new issue appeared, more info below
Test 4 - Fixed, in both cases (no plugins/with plugins) Flipper reads card ✅
Test 5: On this card Flipper stuck - Fixed. ✅
Nicely done 👍

Now about new issue which I've noticed. Actually, maybe it was even previously, but we didn't see it. When I tested test 2 and 3, sometimes Flipper tries to perform attack again, even if I already have cracked nonces with the help of MFKey and key DEADBEAFFFFF is already present in user_dict.
I tested a lot on two Flippers which I have, in order to narrow roots of possible problem, and came to a conclusion that it mostly depends on key count present in mf_classic_dict_user.nfc. I attach my dictionary with 89 keys in it, with this dictionary one Flipper fails to read one sector more often (1 to 3 fails in 10 attempts), and another one fails rearly (1 fail in 40 attempts), but still. On the other hand when I deleted user dictionary completely, and then added only one key DEADBEAFFFFF then issue dissapeared, which proofs that dictionary size can cause some artefacts.

Here are steps to reproduce:

  1. Import mf_classic_dict_user.nfc to nfc->assets
  2. Check that key DEADBEAFFFFF is already present in users' keys
  3. Try to read card from test 2 or test 3.
    After this step some steps may vary because I don't have the exact sequence.
  4. If card read fine, press retry (sometimes 1 or 2 retries might be enough, sometimes even 20 will give no effect).
  5. If card read fine, exit NFC app, then enter again and retry step 4.
  6. If card read fine, reboot Flipper completely and repeat steps from 4.
    Steps 4-6 can be combined in different ways before issue appears.
  7. If card read fails to read 1 sector, Flipper collect nonces to .nested.logs, then you can delete this file via qFlipper and press retry, this can cause issue to repeat again, but not every time.

Expected result: Flipper reads card totally each time without any attempts of collecting nonces, because it already has known key in user dictionary.

Result: Flipper sometimes doesn't apply known key during read process, instead it collects nonces.

dictionary_with_issue.zip

@noproto
Copy link
Contributor Author

noproto commented Oct 25, 2024

Hi @RebornedBrain, good catch. Fixed in 6dbb46a I believe. Try again.

@skotopes
Copy link
Contributor

PVS Report:
image

@skotopes
Copy link
Contributor

I guess we are ready to merge this PR, couple small things left to cleanup:

@noproto
Copy link
Contributor Author

noproto commented Oct 29, 2024

@skotopes :

Remaining TODOs marked with FL-3926.

I'll open the refactoring PR when 3822 is merged, and I'll keep improving it as needed (clearing all TODOs, unit tests for parity, backdoor detection moved outside the poller).

If its helpful, I'll add FL-3926 to the title of the next PR. RE: PVS:

  • mf_classic_poller.c:1292: Redundant assignment removed in eb1aabb
  • mf_classic_poller.c:1293: Redundant assignment removed in eb1aabb
  • mf_classic_poller.c:1707: Format specifier corrected in c240077
  • mf_classic_poller.c:1938: PVS optimization included in 907019c

All good warnings by PVS this time.

@skotopes skotopes merged commit 8427ec0 into flipperdevices:dev Oct 31, 2024
11 checks passed
@skotopes
Copy link
Contributor

@noproto awesome work )

@noproto noproto deleted the nestednonces branch October 31, 2024 01:38
@psifertex
Copy link

Thanks everyone involved for the work landing this! Looking forward to trying it out.

@mishamyte
Copy link
Contributor

Thanks for your work @noproto.

And an opened question. Do you think, guys, nonces recovery could be implemented on a mobile app side conceptually?
Just as we have for MFKey32, to speed up recovery

@noproto
Copy link
Contributor Author

noproto commented Nov 2, 2024

Thanks for your work @noproto.

And an opened question. Do you think, guys, nonces recovery could be implemented on a mobile app side conceptually? Just as we have for MFKey32, to speed up recovery

I appreciate your reply @mishamyte. As far as offloading goes, I am only interested in doing this for attacks which are not possible to run on the Zero (Hardnested).

Since the Flipper Zero is a first generation device, the on-device attacks today are the worst they will ever be. Optimization of the key recovery process on the device has brought multiple recovery methods from taking months to minutes. I will continue to optimize and develop code for the process defined in this PR, but as new hardware revisions are imminent I expect that even without changes it will soon run in seconds rather than minutes (which would immediately deprecate any offloading effort done except for Hardnested).

@mishamyte
Copy link
Contributor

Thanks for your work @noproto.

And an opened question. Do you think, guys, nonces recovery could be implemented on a mobile app side conceptually? Just as we have for MFKey32, to speed up recovery

I appreciate your reply @mishamyte. As far as offloading goes, I am only interested in doing this for attacks which are not possible to run on the Zero (Hardnested).

Since the Flipper Zero is a first generation device, the on-device attacks today are the worst they will ever be. Optimization of the key recovery process on the device has brought multiple recovery methods from taking months to minutes. I will continue to optimize and develop code for the process defined in this PR, but as new hardware revisions are imminent I expect that even without changes it will soon run in seconds rather than minutes (which would immediately deprecate any offloading effort done except for Hardnested).

Thank you!
Personally I don't think my proposal should replace existing functionality.
IMO for me it's just an alternative.
Calculating on Flipper is good for small amount of keys, but when you need to calculate full diversified tag, it takes an eternity (that's feedback from guys, who tested).

So personally I would prefer to have both options. As we had it previously - with Python script + old fap

Copy link

@RaYRAy773 RaYRAy773 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool

@davidsobey1
Copy link

Awesome work @noproto , do you think this might now work on legacy oyster card, which I think are based on MIFARE Classic?

@noproto
Copy link
Contributor Author

noproto commented Dec 30, 2024

Awesome work @noproto , do you think this might now work on legacy oyster card, which I think are based on MIFARE Classic?

I don't see why not. Update to the latest firmware and try it out, let me know if you experience any issues. Here is a guide to using the features: https://flipper.wiki/mifareclassic/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NFC NFC-related
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants