style and text. Might need some hint for the next step when/how unhash output is used

This commit is contained in:
iceman1001 2024-10-01 10:15:44 +02:00
commit e162cc3953
3 changed files with 86 additions and 52 deletions

View file

@ -4156,13 +4156,14 @@ static int CmdHFiClassUnhash(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "hf iclass unhash", CLIParserInit(&ctx, "hf iclass unhash",
"Reverses the hash0 function used generate iclass diversified keys after DES encryption, returning the DES crypted CSN.", "Reverses the hash0 function used generate iclass diversified keys after DES encryption,\n"
"hf iclass unhash --divkey B4F12AADC5301A2D" "Function returns the DES crypted CSN. Next step bruteforcing.",
"hf iclass unhash -k B4F12AADC5301A2D"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
arg_str1(NULL, "divkey", "<hex>", "The card's Diversified Key value"), arg_str1("k", "divkey", "<hex>", "Card diversified key"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -4174,14 +4175,16 @@ static int CmdHFiClassUnhash(const char *Cmd) {
CLIParserFree(ctx); CLIParserFree(ctx);
if (dk_len && dk_len != PICOPASS_BLOCK_SIZE) { if (dk_len && dk_len != PICOPASS_BLOCK_SIZE) {
PrintAndLogEx(ERR, "Diversified Key is incorrect length"); PrintAndLogEx(ERR, "Diversified key is incorrect length");
return PM3_EINVARG; return PM3_EINVARG;
} }
PrintAndLogEx(INFO, _YELLOW_("Div Key: ")"%s", sprint_hex(div_key, sizeof(div_key))); PrintAndLogEx(INFO, "Diversified key... %s", sprint_hex_inrow(div_key, sizeof(div_key)));
invert_hash0(div_key); invert_hash0(div_key);
// iceman: add hint for next step?
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS; return PM3_SUCCESS;
} }

View file

@ -424,12 +424,16 @@ void invert_hash0(uint8_t k[8]) {
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
y |= ((k[i] & 0x80) >> (7 - i)); // Recover the bit of y from the leftmost bit of k[i] y |= ((k[i] & 0x80) >> (7 - i)); // Recover the bit of y from the leftmost bit of k[i]
pushbackSixBitByte(&zTilde, (k[i] & 0x7E) >> 1, i); // Recover the six bits of zTilde from the middle of k[i] pushbackSixBitByte(&zTilde, (k[i] & 0x7E) >> 1, i); // Recover the six bits of zTilde from the middle of k[i]
if (g_debugMode > 0) printState("z~", zTilde); if (g_debugMode > 0) printState("z~", zTilde);
p |= ((k[i] & 0x01) << i); p |= ((k[i] & 0x01) << i);
} }
if (g_debugMode > 0) PrintAndLogEx(INFO, " y : %02x", y); // value of y (recovered 1 byte of the pre-image) if (g_debugMode > 0) PrintAndLogEx(INFO, " y : %02x", y); // value of y (recovered 1 byte of the pre-image)
// check if p is part of the array pi, if not invert it // check if p is part of the array pi, if not invert it
if (g_debugMode > 0) PrintAndLogEx(INFO, " p : %02x", p); // value of p (at some point in the original hash0) if (g_debugMode > 0) PrintAndLogEx(INFO, " p : %02x", p); // value of p (at some point in the original hash0)
int remainder = find_p_in_pi(p); int remainder = find_p_in_pi(p);
if (remainder < 0) { if (remainder < 0) {
p = ~p; p = ~p;
@ -450,19 +454,26 @@ void invert_hash0(uint8_t k[8]) {
uint8_t pre_image_base[8] = {0}; uint8_t pre_image_base[8] = {0};
pre_image_base[1] = y; pre_image_base[1] = y;
//calculate pre-images based on the potential values of x (should we use pre-flip p and post flip p just in case?)
// calculate pre-images based on the potential values of x. Sshould we use pre-flip p and post flip p just in case?
uint64_t zTil_img[8] = {0}; // 8 is the max size it'll have as per max number of X pre-images uint64_t zTil_img[8] = {0}; // 8 is the max size it'll have as per max number of X pre-images
for (int img = 0; img < x_count; img++) { // for each potential value of x calculate a pre-image for (int img = 0; img < x_count; img++) { // for each potential value of x calculate a pre-image
zTil_img[img] = zTilde; zTil_img[img] = zTilde;
pre_image_base[0] = x_array[img]; pre_image_base[0] = x_array[img];
uint8_t pc = p; // redefine and reassociate it here or it'll keep changing through the loops uint8_t pc = p; // redefine and reassociate it here or it'll keep changing through the loops
if (x_array[img] & 1) { // Check if potential x7 is 1, if it is then invert p if (x_array[img] & 1) { // Check if potential x7 is 1, if it is then invert p
pc = ~p; pc = ~p;
} }
// calculate zTilde for the x preimage // calculate zTilde for the x preimage
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
uint8_t p_i = (pc >> i) & 0x1; // this is correct! uint8_t p_i = (pc >> i) & 0x1; // this is correct!
uint8_t zTilde_i = getSixBitByte(zTilde, i) << 1; uint8_t zTilde_i = getSixBitByte(zTilde, i) << 1;
if (k[i] & 0x80) { // this checks the value of the first bit of the byte (value of y_i) if (k[i] & 0x80) { // this checks the value of the first bit of the byte (value of y_i)
if (p_i) { if (p_i) {
zTilde_i--; zTilde_i--;
@ -481,6 +492,7 @@ void invert_hash0(uint8_t k[8]) {
printState("0|0|z~", zTil_img[img]); // we retrieve the values of z~ printState("0|0|z~", zTil_img[img]); // we retrieve the values of z~
PrintAndLogEx(INFO, " p or ~p : %02x", pc); // value of p (at some point in the original hash0) PrintAndLogEx(INFO, " p or ~p : %02x", pc); // value of p (at some point in the original hash0)
} }
// reverse permute // reverse permute
BitstreamIn_t p_in = { &pc, 8, 0 }; BitstreamIn_t p_in = { &pc, 8, 0 };
uint8_t outbuffer_1[] = {0, 0, 0, 0, 0, 0, 0, 0}; uint8_t outbuffer_1[] = {0, 0, 0, 0, 0, 0, 0, 0};
@ -537,26 +549,33 @@ void invert_hash0(uint8_t k[8]) {
// When these values are present we need to generate additional pre-images if they have the same modulo as other values // When these values are present we need to generate additional pre-images if they have the same modulo as other values
// Initialize an array of pointers to uint64_t (start with one value, initialized to 0) // Initialize an array of pointers to uint64_t (start with one value, initialized to 0)
uint64_t *hydra_heads = (uint64_t *)malloc(sizeof(uint64_t)); // Start with one uint64_t uint64_t *hydra_heads = (uint64_t *)calloc(sizeof(uint64_t), 1); // Start with one uint64_t
hydra_heads[0] = 0; // Initialize first value to 0 hydra_heads[0] = 0; // Initialize first value to 0
int heads_count = 1; // Track number of forks int heads_count = 1; // Track number of forks
// Iterate 4 times as per the original loop // Iterate 4 times as per the original loop
for (int n = 0; n < 8; n++) { for (int n = 0; n < 8; n++) {
uint8_t hydra_head = getSixBitByte(c, n); uint8_t hydra_head = getSixBitByte(c, n);
if (hydra_head <= (n % 4) || hydra_head >= 63 - (n % 4)) { if (hydra_head <= (n % 4) || hydra_head >= 63 - (n % 4)) {
// Create new forks by duplicating existing uint64_t values // Create new forks by duplicating existing uint64_t values
int new_head = heads_count * 2; int new_head = heads_count * 2;
hydra_heads = (uint64_t *)realloc(hydra_heads, new_head * sizeof(uint64_t)); hydra_heads = (uint64_t *)realloc(hydra_heads, new_head * sizeof(uint64_t));
// Duplicate all current values and add the value to both original and new ones // Duplicate all current values and add the value to both original and new ones
for (int i = 0; i < heads_count; i++) { for (int i = 0; i < heads_count; i++) {
// Duplicate current value // Duplicate current value
hydra_heads[heads_count + i] = hydra_heads[i]; hydra_heads[heads_count + i] = hydra_heads[i];
uint8_t small_hydra_head = 0; uint8_t small_hydra_head = 0;
uint8_t big_hydra_head = 0; uint8_t big_hydra_head = 0;
uint8_t hydra_lil_spawns[4] = {0x00, 0x01, 0x02, 0x03}; uint8_t hydra_lil_spawns[4] = {0x00, 0x01, 0x02, 0x03};
uint8_t hydra_big_spawns[4] = {0x3f, 0x3e, 0x3d, 0x3c}; uint8_t hydra_big_spawns[4] = {0x3f, 0x3e, 0x3d, 0x3c};
if (hydra_head <= n % 4) { // check if is in the lower range if (hydra_head <= n % 4) { // check if is in the lower range
// replace with big spawn in one hydra and keep small in another // replace with big spawn in one hydra and keep small in another
small_hydra_head = hydra_head; small_hydra_head = hydra_head;
for (int fh = 0; fh < 4; fh++) { for (int fh = 0; fh < 4; fh++) {
@ -564,7 +583,9 @@ void invert_hash0(uint8_t k[8]) {
big_hydra_head = hydra_big_spawns[fh]; big_hydra_head = hydra_big_spawns[fh];
} }
} }
} else if (hydra_head >= 63 - (n % 4)) { // or the higher range } else if (hydra_head >= 63 - (n % 4)) { // or the higher range
// replace with small in one hydra and keep big in another // replace with small in one hydra and keep big in another
big_hydra_head = hydra_head; big_hydra_head = hydra_head;
for (int fh = 0; fh < 4; fh++) { for (int fh = 0; fh < 4; fh++) {
@ -579,7 +600,8 @@ void invert_hash0(uint8_t k[8]) {
} }
// Update the count of total values // Update the count of total values
heads_count = new_head; heads_count = new_head;
} else { //no hydra head spawns } else {
// no hydra head spawns
for (int i = 0; i < heads_count; i++) { for (int i = 0; i < heads_count; i++) {
pushbackSixBitByte(&hydra_heads[i], hydra_head, n);; pushbackSixBitByte(&hydra_heads[i], hydra_head, n);;
} }
@ -587,9 +609,11 @@ void invert_hash0(uint8_t k[8]) {
} }
for (int i = 0; i < heads_count; i++) { for (int i = 0; i < heads_count; i++) {
// restore the two most significant bytes (x and y) // restore the two most significant bytes (x and y)
hydra_heads[i] |= ((uint64_t)x_array[img] << 56); hydra_heads[i] |= ((uint64_t)x_array[img] << 56);
hydra_heads[i] |= ((uint64_t)y << 48); hydra_heads[i] |= ((uint64_t)y << 48);
if (g_debugMode > 0) { if (g_debugMode > 0) {
PrintAndLogEx(DEBUG, " | x| y|z0|z1|z2|z3|z4|z5|z6|z7|"); PrintAndLogEx(DEBUG, " | x| y|z0|z1|z2|z3|z4|z5|z6|z7|");
printState("origin_r1", hydra_heads[i]); printState("origin_r1", hydra_heads[i]);
@ -609,17 +633,24 @@ void invert_hash0(uint8_t k[8]) {
// verify result, if it matches add it to the list as a valid pre-image // verify result, if it matches add it to the list as a valid pre-image
bool image_match = true; bool image_match = true;
for (int v = 0; v < 8; v++) { for (int v = 0; v < 8; v++) {
if (img_div_key[v] != k[v]) { //compare against input key k
// compare against input key k
if (img_div_key[v] != k[v]) {
image_match = false; image_match = false;
} }
} }
uint8_t des_pre_image[8] = {0}; uint8_t des_pre_image[8] = {0};
x_num_to_bytes(original_z, sizeof(original_z), des_pre_image); x_num_to_bytes(original_z, sizeof(original_z), des_pre_image);
if (image_match) { if (image_match) {
PrintAndLogEx(INFO, _GREEN_("Valid pre-image: ")_YELLOW_("%s"), sprint_hex(des_pre_image, sizeof(des_pre_image))); PrintAndLogEx(INFO, "Pre-image......... " _YELLOW_("%s") " ( "_GREEN_("valid") " )", sprint_hex_inrow(des_pre_image, sizeof(des_pre_image)));
} else if (!image_match && g_debugMode > 0) { } else {
PrintAndLogEx(INFO, _RED_("Invalid pre-image: %s"), sprint_hex(des_pre_image, sizeof(des_pre_image)));
if (g_debugMode > 0) {
PrintAndLogEx(INFO, "Pre-image......... " _YELLOW_("%s") " ( "_RED_("invalid") " )", sprint_hex_inrow(des_pre_image, sizeof(des_pre_image)));
}
} }
} }
// Free allocated memory // Free allocated memory

View file

@ -3656,16 +3656,16 @@
}, },
"hf iclass unhash": { "hf iclass unhash": {
"command": "hf iclass unhash", "command": "hf iclass unhash",
"description": "Reverses the hash0 function used generate iclass diversified keys after DES encryption, returning the DES crypted CSN.", "description": "Reverses the hash0 function used generate iclass diversified keys after DES encryption, Function returns the DES crypted CSN. Next step bruteforcing.",
"notes": [ "notes": [
"hf iclass unhash --divkey B4F12AADC5301A2D" "hf iclass unhash -k B4F12AADC5301A2D"
], ],
"offline": true, "offline": true,
"options": [ "options": [
"-h, --help This help", "-h, --help This help",
"--divkey <hex> The card's Diversified Key value" "-k, --divkey <hex> Card diversified key"
], ],
"usage": "hf iclass unhash [-h] --divkey <hex>" "usage": "hf iclass unhash [-h] -k <hex>"
}, },
"hf iclass view": { "hf iclass view": {
"command": "hf iclass view", "command": "hf iclass view",
@ -12956,6 +12956,6 @@
"metadata": { "metadata": {
"commands_extracted": 747, "commands_extracted": 747,
"extracted_by": "PM3Help2JSON v1.00", "extracted_by": "PM3Help2JSON v1.00",
"extracted_on": "2024-10-01T07:52:00" "extracted_on": "2024-10-01T08:14:57"
} }
} }