Merge branch 'RfidResearchGroup:master' into esp32-c2

This commit is contained in:
Niel Nielsen authored and GitHub committed 2026-09-13 17:03:14 +02:00
commit bf703c24ff
4 files changed
+76 -4

No files matched your search

+2
View File
@@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased]
- Fixed `hf mf autopwn` - a sector that cannot be cracked no longer discards the keys already recovered (@iceman1001)
- Fixed `hf mf nested` - a NAK from the tag now ends the nonce collection instead of retrying until the client times out (@iceman1001)
- Fixed `hf mfdes` - select / authenticate failures are now reported once, naming the AID and the failing step (@iceman1001)
- Fixed `-f <fn>` - a filename given with its own extension is now used as-is on both load and save (@iceman1001)
- Fixed `hf mf view` - VIGIK signature verification now recovers the ISO 9796-1 message and checks it against the card (@iceman1001)
+10
View File
@@ -1730,6 +1730,16 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8
auth2_time = auth1_time + delta_time;
len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + (targetKeyType & 0xF), targetBlockNo, receivedAnswer, sizeof(receivedAnswer), par, &auth2_time);
// a NAK is the tag refusing this block, not a glitch. re-sending the
// identical frame only ever gets the identical answer back, so give up
// here instead of spinning until the client times us out
if (len == 1) {
if (g_dbglevel >= DBG_INFO) Dbprintf("Nested: Auth2 NAK, tag refused block " _YELLOW_("%d"), targetBlockNo);
isOK = PM3_EWRONGANSWER;
break;
}
if (len != 4) {
if (g_dbglevel >= DBG_INFO) Dbprintf("Nested: Auth2 error len=%d", len);
continue;
+41
View File
@@ -2228,6 +2228,9 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't
case PM3_EFAILED:
PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is not predictable).\n");
break;
case PM3_EWRONGANSWER:
PrintAndLogEx(FAILED, "Tag refused the nested authentication for block " _YELLOW_("%u") "\n", trgBlockNo);
break;
case PM3_ESOFT:
PrintAndLogEx(FAILED, "No valid key found");
break;
@@ -2331,6 +2334,11 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't
case PM3_EFAILED :
PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is not predictable)\n");
break;
case PM3_EWRONGANSWER:
// tag refused this block, nothing to gain by retrying it
PrintAndLogEx(FAILED, "Tag refused the nested authentication for sector " _YELLOW_("%u") "\n", sectorNo);
calibrate = false;
continue;
case PM3_ESOFT:
//key not found
calibrate = false;
@@ -2942,6 +2950,20 @@ static int CmdHF14AMfNestedHard(const char *Cmd) {
return isOK;
}
// how many keys are in hand right now. a sector that can't be cracked should
// not cost the ones that already came out
static uint8_t mfc_found_key_count(const sector_t *e_sector, uint8_t sector_cnt) {
uint8_t n = 0;
for (uint8_t i = 0; i < sector_cnt; i++) {
for (uint8_t j = MF_KEY_A; j <= MF_KEY_B; j++) {
if (e_sector[i].foundKey[j]) {
n++;
}
}
}
return n;
}
static int CmdHF14AMfAutoPWN(const char *Cmd) {
CLIParserContext *ctx;
@@ -3706,10 +3728,29 @@ tryNested:
switch (isOK) {
case PM3_ETIMEOUT: {
PrintAndLogEx(ERR, "\nError: No response from Proxmark3.");
uint8_t havekeys = mfc_found_key_count(e_sector, sector_cnt);
if (havekeys) {
PrintAndLogEx(INFO, "Keeping the " _YELLOW_("%u") " key%s recovered so far",
havekeys,
(havekeys == 1) ? "" : "s"
);
goto all_found;
}
free(e_sector);
free(fptr);
return isOK;
}
case PM3_EWRONGANSWER: {
// the tag NAKed the nested auth for this block. it is a
// refusal, so move on rather than grind the same sector
PrintAndLogEx(FAILED, "Tag refused the nested authentication, skipping sector " _YELLOW_("%3d") " key type " _YELLOW_("%c"),
current_sector_i,
(current_key_type_i == MF_KEY_B) ? 'B' : 'A'
);
continue;
}
case PM3_EOPABORTED: {
PrintAndLogEx(WARNING, "\nButton pressed. Aborted.");
free(e_sector);
+23 -4
View File
@@ -22,7 +22,8 @@
// sector 0 block 2 8 byte per card identifier, then a constant tail
// sector 9 48 bytes, differs completely between cards
// sector 11 32 bytes, differs completely between cards
// sector 15 the system identifier, the same on every card
// sector 15 the system text, the same on every card, and in block 2
// the number printed on the fob, little endian
// sector 16, 17 MIFARE Classic EV1 signature, not application data
//
// The sector 9 and 11 payload is not decoded. It carries no structure in common
@@ -83,12 +84,30 @@ int hexact_parser_parse(const uint8_t *dump, size_t dumplen) {
const uint8_t *id1 = hexact_sector(dump, dumplen, HEXACT_ID_SECTOR, 1);
const uint8_t *id2 = hexact_sector(dump, dumplen, HEXACT_ID_SECTOR, 2);
PrintAndLogEx(INFO, "System............. %.16s", (const char *)id0);
// The sector spells the system out across its three blocks and it reads as
// one line, so join them. Each block pads its text with spaces, and the
// last one keeps four bytes of its own before the text starts.
char system[16 + 16 + 12 + 3] = {0};
str_append(system, sizeof(system), "%.16s", (const char *)id0);
str_trim(system);
if (id1) {
PrintAndLogEx(INFO, " %.16s", (const char *)id1);
str_append(system, sizeof(system), " %.16s", (const char *)id1);
str_trim(system);
}
if (id2) {
PrintAndLogEx(INFO, " %.12s", (const char *)(id2 + 4));
str_append(system, sizeof(system), " %.12s", (const char *)(id2 + 4));
str_trim(system);
}
PrintAndLogEx(INFO, "System............. " _YELLOW_("%s"), system);
// Sector 15 block 2 keeps the number engraved on the fob as a little endian
// value in front of the INTRATONE text. Checked against the engraving on
// four fobs, all four matched.
if (id2) {
PrintAndLogEx(INFO, "Printed serial..... " _YELLOW_("%u"), MemLeToUint4byte(id2));
}
// sector 0 block 2 holds a per card value followed by a tail that is the