Skip to content

Commit 0c13737

Browse files
committed
1.0.0 - Public release
0 parents  commit 0c13737

38 files changed

+4689
-0
lines changed

FAQ.md

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# FAQ
2+
3+
Q: What is that?
4+
5+
A: This app implements nested and static nested attacks (nonces collection part)
6+
7+
Q: Why can't I recover keys on Flipper Zero itself?
8+
9+
A: In fact, you can. There is already an implementation of https://github.com/noproto/FlipperMfkey, and although the author has done a great job with optimization, for a nested attack it would just take a huge amount of time (~~2 hours in worst case for 1 key in regular~~ 30 minutes for 1 key, days in worst case for 1 key in delay + PRNG prediction). Maybe in a great future we will be able to know the exact PRNG value and then we can fit ourselves into these 30 minues. So it's all down to my lack of knowledge of the low-level NFC protocol/Flipper Zero itself.
10+
11+
Q: I don't have keys, how I can read tag?
12+
13+
A: You need to use darkside/reader attack. Reader attack is already implemented in Flipper firmware. Darkside attack isn't available now.
14+
15+
Q: App says "Scan tag and find at least one key to start", but I already scanned tag and found keys. What I need to do?
16+
17+
A: You need to save tag. More -> Save after scanning tag.
18+
19+
Q: How I can recover keys?
20+
21+
A: You need script: https://github.com/AloneLiberty/FlipperNestedRecovery
22+
23+
Q: App name is "Mifare Nested" or "Flipper Nested"?
24+
25+
A: In the early stages of development it was called Mifare Nested. But then I thought it was just better to avoid Mifare in the name, so I replaced it with Flipper. "Flipper Nested" does not really make it clear what this app is for, so it is called "Flipper (Mifare) Nested" in the app list. App can be called by any name: Flipper Nested, Mifare Nested, Flipper (Mifare) Nested, Mifare Nested Attacks for Flipper Zero, etc... But I prefer Flipper Nested.
26+
27+
Q: How to check Flipper Zero/app logs?
28+
29+
A: You need access to CLI. You can use lab.flipper.net (if you are chromium user) or use ./fbt cli. Then type: log debug.
30+
31+
Q: How I can recover keys on phone?
32+
33+
A: Probably you can't. The only way is to forward serial port to Termux and run script (never tried it).
34+
35+
Q: Where are collected nonces/found keys located?
36+
37+
A: App/desktop script write files in /ext/nfc/nested/ folder. "Check found keys" adds keys directly to your user dictionary.
38+
39+
Q: I recovered keys via script, but still no new keys found when I try to scan tag?
40+
41+
A: You need to run "Check found keys" in app, it will check keys on your tag and add valid to user dictionary. Then you can scan it via NFC app.
42+
43+
Q: What is nonces?
44+
45+
A: By nonces I mean authorization attempts on the tag. Because we can predict PRNG of the tag we can recover keys used in authorization attempts. If you want more info about Nested attach you can read [this paper](https://www.cs.umd.edu/~jkatz/security/downloads/Mifare3.pdf).
46+
47+
Q: How I can ask for help?
48+
49+
A: Create issue **with label "Question"** or use [discussions](https://github.com/AloneLiberty/FlipperNested/discussions). If you can't share private info in issue you can contact me in [Telegram](https://t.me/liberydev).
50+
51+
Q: What is being done at the moment and what are the plans?
52+
53+
A: Check TODO.md.
54+
55+
Q: When darkside/hardnested attacks?
56+
57+
A: Hardnested attack requires active connection to PC so I'm not going to do it.
58+
59+
Darkside is easier but I don't have time for that (nested took me extra ~~month~~ two months). I would be happy to accept PR if you implement it.

LICENSE.md

+674
Large diffs are not rendered by default.

README.md

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Mifare Nested Attacks for Flipper Zero
2+
3+
Ported nested attacks from proxmark3 (Iceman fork)
4+
5+
## Download
6+
7+
[![FAP Factory](https://flipc.org/api/v1/AloneLiberty/FlipperNested/badge)](https://flipc.org/AloneLiberty/FlipperNested)
8+
9+
## Currently supported attacks:
10+
11+
- static nested attack
12+
- nested attack
13+
14+
## Warning
15+
16+
App is still in early development, so there may be bugs. Your Flipper Zero may randomly crash/froze. Please create issue if you find any bugs (one bug = one issue).
17+
18+
## Disclaimer
19+
20+
The app provided for personal use only. Developer does not take responsibility for any loss or damage caused by the misuse of this app. In addition, the app developer does not guarantee the performance or compatibility of the app with all tags, and cannot be held liable for any damage caused to your tags/Flipper Zero as a result of using the app. By using this app you confirm that the tag belongs to you, you have permission to preform the attack and you agree to hold the app developer harmless from any and all claims, damages, or losses that may arise from its use.
21+
22+
## I need **your** help!
23+
24+
To successfuly recover keys from nested attack we need to correctly predict PRNG value. But we have a problem with that. Due to lack of my knowlege of Flipper Zero NFC HAL, PRNG can jump by quite large values (not like Proxmark3). So app is trying to find a delay where PRNG can be predicted accurately enough. This is not the best option, because we have to try to recover a bunch of unnecessary keys, which takes a lot of time and RAM and also spend a lot of time on timings. I don't know how to fix it.
25+
26+
UPD: Chameleon Ultra devs [faced same issue](https://youtu.be/_wfikmXNQzE?t=202). They seems to use same method: [nested.c](https://github.com/RfidResearchGroup/ChameleonUltra/blob/main/software/src/nested.c) (better know from the beginning of development...)
27+
28+
## How to use it?
29+
30+
This guide assumes that you have already installed the app.
31+
32+
To recover keys from card you first need to collect nonces.
33+
34+
That's what this app was created for. App can't recover keys on Flipper Zero (yet). You need to use external device (PC) to recover keys. You can't use Mfkey32 on your phone with this app.
35+
36+
1. Save tag after scanning (you must have found at least one key). This action will create key cache that app will use for authorization on tag.
37+
38+
2. Run "Nested attack" from app menu
39+
40+
3. Wait until calibration complete
41+
42+
If the calibration passed and nonce collection began, you are very lucky and the tag is vulnerable. If not, you cannot use this app to recover keys from this tag.
43+
44+
Calibration can take a lot of time. If the calibration takes longer than 10 minutes, it is better to see the logs. Some static encrypted nonce tags may lead to inifnity calibration loop.
45+
46+
4. Wait until "Nonces collected!"
47+
48+
5. Recover keys via [desktop script](https://github.com/AloneLiberty/FlipperNestedRecovery)
49+
50+
6. Run "Check found keys" in app menu
51+
52+
When all keys are checked, they will be added to your user key dictonary. You can rescan your tag now.
53+
54+
If not all keys found - run Nested attack again.
55+
56+
## FAQ
57+
58+
For frequently asked questions, please refer to the [FAQ.md](./FAQ.md) file.
59+
60+
## Contacts
61+
62+
Telegram: [@libertydev](https://t.me/libertydev)

TODO.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# TODO:
2+
3+
1. Better (faster) detection of delay in a nested attack
4+
2. Fix infinite calibration on static encrypted nonce tags
5+
6+
## Thinking about:
7+
8+
1. Files (.nonces and .keys) in Flipper file format (why?)
9+
2. Collect nonces without turning off the tag (no idea how PRNG will react to this and what speed increase will it give?) HALT=0x50

application.fam

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
App(
2+
appid="mifare_nested",
3+
name="Flipper (Mifare) Nested",
4+
apptype=FlipperAppType.EXTERNAL,
5+
entry_point="mifare_nested_app",
6+
requires=[
7+
"storage",
8+
"gui",
9+
],
10+
stack_size=4 * 1024,
11+
order=30,
12+
fap_icon="assets/icon.png",
13+
fap_category="NFC",
14+
fap_private_libs=[
15+
Lib(
16+
name="nested",
17+
),
18+
Lib(
19+
name="parity",
20+
),
21+
Lib(
22+
name="crypto1",
23+
)
24+
],
25+
fap_icon_assets="assets",
26+
)

assets/ApplyTag.png

3.71 KB
Loading

assets/DolphinCry.png

3.81 KB
Loading

assets/DolphinSuccess.png

2.62 KB
Loading

assets/Loading.png

3.56 KB
Loading

assets/icon.png

145 Bytes
Loading

lib/crypto1/crypto1.c

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#include "crypto1.h"
2+
#include <string.h>
3+
4+
void crypto1_reset(Crypto1* crypto1) {
5+
furi_assert(crypto1);
6+
crypto1->even = 0;
7+
crypto1->odd = 0;
8+
}
9+
10+
void crypto1_init(Crypto1* crypto1, uint64_t key) {
11+
furi_assert(crypto1);
12+
crypto1->even = 0;
13+
crypto1->odd = 0;
14+
for(int8_t i = 47; i > 0; i -= 2) {
15+
crypto1->odd = crypto1->odd << 1 | FURI_BIT(key, (i - 1) ^ 7);
16+
crypto1->even = crypto1->even << 1 | FURI_BIT(key, i ^ 7);
17+
}
18+
}
19+
20+
uint32_t crypto1_filter(uint32_t in) {
21+
uint32_t out = 0;
22+
out = 0xf22c0 >> (in & 0xf) & 16;
23+
out |= 0x6c9c0 >> (in >> 4 & 0xf) & 8;
24+
out |= 0x3c8b0 >> (in >> 8 & 0xf) & 4;
25+
out |= 0x1e458 >> (in >> 12 & 0xf) & 2;
26+
out |= 0x0d938 >> (in >> 16 & 0xf) & 1;
27+
return FURI_BIT(0xEC57E80A, out);
28+
}
29+
30+
uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted) {
31+
furi_assert(crypto1);
32+
uint8_t out = crypto1_filter(crypto1->odd);
33+
uint32_t feed = out & (!!is_encrypted);
34+
feed ^= !!in;
35+
feed ^= LF_POLY_ODD & crypto1->odd;
36+
feed ^= LF_POLY_EVEN & crypto1->even;
37+
crypto1->even = crypto1->even << 1 | (evenparity32(feed));
38+
39+
FURI_SWAP(crypto1->odd, crypto1->even);
40+
return out;
41+
}
42+
43+
uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted) {
44+
furi_assert(crypto1);
45+
uint8_t out = 0;
46+
for(uint8_t i = 0; i < 8; i++) {
47+
out |= crypto1_bit(crypto1, FURI_BIT(in, i), is_encrypted) << i;
48+
}
49+
return out;
50+
}
51+
52+
uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted) {
53+
furi_assert(crypto1);
54+
uint32_t out = 0;
55+
for(uint8_t i = 0; i < 32; i++) {
56+
out |= crypto1_bit(crypto1, BEBIT(in, i), is_encrypted) << (24 ^ i);
57+
}
58+
return out;
59+
}
60+
61+
uint32_t prng_successor(uint32_t x, uint32_t n) {
62+
SWAPENDIAN(x);
63+
while(n--) x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;
64+
65+
return SWAPENDIAN(x);
66+
}
67+
68+
void crypto1_decrypt(
69+
Crypto1* crypto,
70+
uint8_t* encrypted_data,
71+
uint16_t encrypted_data_bits,
72+
uint8_t* decrypted_data) {
73+
furi_assert(crypto);
74+
furi_assert(encrypted_data);
75+
furi_assert(decrypted_data);
76+
77+
if(encrypted_data_bits < 8) {
78+
uint8_t decrypted_byte = 0;
79+
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0;
80+
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1;
81+
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2;
82+
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3;
83+
decrypted_data[0] = decrypted_byte;
84+
} else {
85+
for(size_t i = 0; i < encrypted_data_bits / 8; i++) {
86+
decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i];
87+
}
88+
}
89+
}
90+
91+
void crypto1_encrypt(
92+
Crypto1* crypto,
93+
uint8_t* keystream,
94+
uint8_t* plain_data,
95+
uint16_t plain_data_bits,
96+
uint8_t* encrypted_data,
97+
uint8_t* encrypted_parity) {
98+
furi_assert(crypto);
99+
furi_assert(plain_data);
100+
furi_assert(encrypted_data);
101+
furi_assert(encrypted_parity);
102+
103+
if(plain_data_bits < 8) {
104+
encrypted_data[0] = 0;
105+
for(size_t i = 0; i < plain_data_bits; i++) {
106+
encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i;
107+
}
108+
} else {
109+
memset(encrypted_parity, 0, plain_data_bits / 8 + 1);
110+
for(uint8_t i = 0; i < plain_data_bits / 8; i++) {
111+
encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^
112+
plain_data[i];
113+
encrypted_parity[i / 8] |=
114+
(((crypto1_filter(crypto->odd) ^ oddparity8(plain_data[i])) & 0x01)
115+
<< (7 - (i & 0x0007)));
116+
}
117+
}
118+
}

lib/crypto1/crypto1.h

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include "../../lib/parity/parity.h"
2+
#include <lib/nfc/protocols/mifare_classic.h>
3+
#include <lib/nfc/protocols/crypto1.h>
4+
#include "stddef.h"
5+
6+
#define LF_POLY_ODD (0x29CE5C)
7+
#define LF_POLY_EVEN (0x870804)
8+
9+
#define SWAPENDIAN(x) (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16)
10+
#define BEBIT(x, n) FURI_BIT(x, (n) ^ 24)
11+
12+
void crypto1_reset(Crypto1* crypto1);
13+
14+
void crypto1_init(Crypto1* crypto1, uint64_t key);
15+
16+
uint32_t crypto1_filter(uint32_t in);
17+
18+
uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted);
19+
20+
uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted);
21+
22+
uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted);
23+
24+
uint32_t prng_successor(uint32_t x, uint32_t n);
25+
26+
void crypto1_decrypt(
27+
Crypto1* crypto,
28+
uint8_t* encrypted_data,
29+
uint16_t encrypted_data_bits,
30+
uint8_t* decrypted_data);
31+
32+
void crypto1_encrypt(
33+
Crypto1* crypto,
34+
uint8_t* keystream,
35+
uint8_t* plain_data,
36+
uint16_t plain_data_bits,
37+
uint8_t* encrypted_data,
38+
uint8_t* encrypted_parity);

0 commit comments

Comments
 (0)