diff --git a/CHANGELOG.md b/CHANGELOG.md index 70745ccce..a0aff43f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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] +- Added tag loss detection & recovery into `hf mfdes bruteaid` (@kormax) - Added --credit option for `hf iclass legrec` command to perform a credit key recovery. This is experimental and unfinished as it only partially works.(@antiklesys) - Added hardening for all host binaries. Exact level of hardening depends on the OS (@doegox) - Added `hf aliro read` command (@kormax) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 3f883ca9f..154b24b47 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -49,6 +49,8 @@ #define MAX_KEY_LEN 24 #define MAX_KEYS_LIST_LEN 1024 +#define MFDES_BRUTEAID_RESELECT_ATTEMPTS 5 +#define MFDES_BRUTEAID_RESELECT_WAIT_MS 200 #define status(x) ( ((uint16_t)(0x91 << 8)) + (uint16_t)x ) /* @@ -2251,6 +2253,36 @@ static int CmdHF14ADesBruteApps(const char *Cmd) { PrintAndLogEx(INPLACE, "Brute DESFire AID Progress " _YELLOW_("%0.1f") " %% current AID: %06X", progress, id); res = DesfireSelectAIDHexNoFieldOn(&dctx, id); + if (res == PM3_ECARDEXCHANGE || res == PM3_ETIMEOUT || res == PM3_ERFTRANS) { + for (int attempt = 1; attempt <= MFDES_BRUTEAID_RESELECT_ATTEMPTS; attempt++) { + printf("\33[2K\r"); // clear current inplace progress line before logging + PrintAndLogEx(WARNING, "No card response while checking AID " _YELLOW_("%06X") ". Reselecting card (%d/%d)...", + id, attempt, MFDES_BRUTEAID_RESELECT_ATTEMPTS); + + msleep(MFDES_BRUTEAID_RESELECT_WAIT_MS); + + res = DesfireSelectAIDHex(&dctx, 0x000000, false, 0); + if (res != PM3_SUCCESS) { + if (res == PM3_ECARDEXCHANGE || res == PM3_ETIMEOUT || res == PM3_ERFTRANS) { + continue; + } + break; + } + + res = DesfireSelectAIDHexNoFieldOn(&dctx, id); + if (res == PM3_SUCCESS || res == PM3_EAPDU_FAIL || + (res != PM3_ECARDEXCHANGE && res != PM3_ETIMEOUT && res != PM3_ERFTRANS)) { + break; + } + } + + if (res == PM3_ECARDEXCHANGE || res == PM3_ETIMEOUT || res == PM3_ERFTRANS) { + PrintAndLogEx(FAILED, "Card is not responding after %d reselect attempts. Aborting at AID " _YELLOW_("%06X"), + MFDES_BRUTEAID_RESELECT_ATTEMPTS, id); + DropField(); + return res; + } + } if (res == PM3_SUCCESS) { printf("\33[2K\r"); // clear current line before printing diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index 2e1a1c439..71fb8391b 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -517,8 +517,12 @@ static int DESFIRESendRaw(bool activate_field, uint8_t *data, size_t datalen, ui PrintAndLogEx(SUCCESS, "raw<< %s", sprint_hex(result, *result_len)); } - if (*result_len < 1) { - return PM3_SUCCESS; + if (*result_len == 0) { + return PM3_ECARDEXCHANGE; + } + + if (*result_len < (1 + 2)) { + return PM3_ECARDEXCHANGE; } *result_len -= (1 + 2); @@ -538,6 +542,7 @@ static int DESFIRESendRaw(bool activate_field, uint8_t *data, size_t datalen, ui if (GetAPDULogging()) { PrintAndLogEx(ERR, "Command (%02x) ERROR: 0x%02x", data[0], rcode); } + return PM3_EAPDU_FAIL; } return PM3_SUCCESS; @@ -591,6 +596,9 @@ static int DesfireExchangeNative(bool activate_field, DesfireContext_t *ctx, uin res = DESFIRESendRaw(activate_field, &cdata[sendindx], sendlen, buf, DESFIRE_BUFFER_SIZE, &buflen, &rcode); if (res != PM3_SUCCESS) { + if (respcode != NULL) { + *respcode = rcode; + } uint16_t ssw = DESFIRE_GET_ISO_STATUS(rcode); PrintAndLogEx(DEBUG, "error DESFIRESendRaw %s", DesfireGetErrorString(res, &ssw)); free(buf); @@ -638,8 +646,12 @@ static int DesfireExchangeNative(bool activate_field, DesfireContext_t *ctx, uin res = DESFIRESendRaw(false, cdata, 1, buf, DESFIRE_BUFFER_SIZE, &buflen, &rcode); if (res != PM3_SUCCESS) { + if (respcode != NULL) { + *respcode = rcode; + } uint16_t ssw = DESFIRE_GET_ISO_STATUS(rcode); PrintAndLogEx(DEBUG, "error DESFIRESendRaw %s", DesfireGetErrorString(res, &ssw)); + free(buf); return res; } @@ -716,6 +728,9 @@ static int DesfireExchangeISONative(bool activate_field, DesfireContext_t *ctx, res = DESFIRESendApdu(activate_field, apdu, buf, DESFIRE_BUFFER_SIZE, &buflen, &sw); if (res != PM3_SUCCESS) { + if (respcode != NULL && ((sw & 0xFF00) == 0x9100)) { + *respcode = sw & 0xFF; + } PrintAndLogEx(DEBUG, "error DESFIRESendApdu %s", DesfireGetErrorString(res, &sw)); free(buf); return res; @@ -768,6 +783,9 @@ static int DesfireExchangeISONative(bool activate_field, DesfireContext_t *ctx, res = DESFIRESendApdu(false, apdu, buf, DESFIRE_BUFFER_SIZE, &buflen, &sw); if (res != PM3_SUCCESS) { + if (respcode != NULL && ((sw & 0xFF00) == 0x9100)) { + *respcode = sw & 0xFF; + } PrintAndLogEx(DEBUG, "error DESFIRESendApdu %s", DesfireGetErrorString(res, &sw)); free(buf); return res; @@ -926,28 +944,36 @@ int DesfireSelectAID(DesfireContext_t *ctx, uint8_t *aid1, uint8_t *aid2) { uint8_t resp[257] = {0}; size_t resplen = 0; - uint8_t respcode = 0; + uint8_t respcode = 0xFF; ctx->secureChannel = DACNone; int res = DesfireExchangeEx(true, ctx, MFDES_SELECT_APPLICATION, data, (aid2 == NULL) ? 3 : 6, &respcode, resp, &resplen, true, 0); - if (res == PM3_SUCCESS) { - if (resplen != 0) { + if (res != PM3_SUCCESS) { + if (res == PM3_EAPDU_FAIL && respcode == 0xFF && resplen == 0) { return PM3_ECARDEXCHANGE; } - - // select operation fail - if (respcode != MFDES_S_OPERATION_OK) { - return PM3_EAPDU_FAIL; - } - - DesfireClearSession(ctx); - ctx->appSelected = (aid1[0] != 0x00 || aid1[1] != 0x00 || aid1[2] != 0x00); - ctx->selectedAID = DesfireAIDByteToUint(aid1); - - return PM3_SUCCESS; + return res; } - return res; + if (resplen != 0) { + return PM3_ECARDEXCHANGE; + } + + // no status byte received from card + if (respcode == 0xFF) { + return PM3_ECARDEXCHANGE; + } + + // select operation fail + if (respcode != MFDES_S_OPERATION_OK) { + return PM3_EAPDU_FAIL; + } + + DesfireClearSession(ctx); + ctx->appSelected = (aid1[0] != 0x00 || aid1[1] != 0x00 || aid1[2] != 0x00); + ctx->selectedAID = DesfireAIDByteToUint(aid1); + + return PM3_SUCCESS; } int DesfireSelectAIDHex(DesfireContext_t *ctx, uint32_t aid1, bool select_two, uint32_t aid2) { @@ -966,27 +992,36 @@ int DesfireSelectAIDHexNoFieldOn(DesfireContext_t *ctx, uint32_t aid) { uint8_t resp[257] = {0}; size_t resplen = 0; - uint8_t respcode = 0; + uint8_t respcode = 0xFF; ctx->secureChannel = DACNone; int res = DesfireExchangeEx(false, ctx, MFDES_SELECT_APPLICATION, data, 3, &respcode, resp, &resplen, true, 0); - if (res == PM3_SUCCESS) { - if (resplen != 0) { + if (res != PM3_SUCCESS) { + if (res == PM3_EAPDU_FAIL && respcode == 0xFF && resplen == 0) { return PM3_ECARDEXCHANGE; } - - // select operation fail - if (respcode != MFDES_S_OPERATION_OK) { - return PM3_EAPDU_FAIL; - } - - DesfireClearSession(ctx); - ctx->appSelected = (aid != 0x000000); - ctx->selectedAID = aid; - - return PM3_SUCCESS; + return res; } - return res; + + if (resplen != 0) { + return PM3_ECARDEXCHANGE; + } + + // no status byte received from card + if (respcode == 0xFF) { + return PM3_ECARDEXCHANGE; + } + + // select operation fail + if (respcode != MFDES_S_OPERATION_OK) { + return PM3_EAPDU_FAIL; + } + + DesfireClearSession(ctx); + ctx->appSelected = (aid != 0x000000); + ctx->selectedAID = aid; + + return PM3_SUCCESS; } void DesfirePrintMADAID(uint32_t appid, bool verbose) {