Merge pull request #2030 from gm3197/smartcard-relay

[WIP] Use proxmark3 as standard PCSC smartcard reader
This commit is contained in:
Iceman 2023-11-13 08:43:20 +01:00 committed by GitHub
commit 104744d007
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 231 additions and 14 deletions

View file

@ -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... 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] ## [unreleased][unreleased]
- Use proxmark3 as a generic smartcard reader with other software with `smart relay` (@gm3197)
- Added `tools\mfkeys\staticnested` - program to recover static nested keys (@iceman1001) - Added `tools\mfkeys\staticnested` - program to recover static nested keys (@iceman1001)
- Added `pm3_gen_dictionary.py` - python script to extract and save all keys from MFC dump files. (@iceman1001) - Added `pm3_gen_dictionary.py` - python script to extract and save all keys from MFC dump files. (@iceman1001)
- Changed `hf mfu info` - now detect MIFARE Ultralight AES (@iceman1001) - Changed `hf mfu info` - now detect MIFARE Ultralight AES (@iceman1001)

View file

@ -43,6 +43,7 @@ add_library(pm3rrg_rdv4_mbedtls STATIC
../../common/mbedtls/x509.c ../../common/mbedtls/x509.c
../../common/mbedtls/x509_crl.c ../../common/mbedtls/x509_crl.c
../../common/mbedtls/x509_crt.c ../../common/mbedtls/x509_crt.c
../../common/mbedtls/net_sockets.c
) )
target_include_directories(pm3rrg_rdv4_mbedtls PRIVATE ../../common) target_include_directories(pm3rrg_rdv4_mbedtls PRIVATE ../../common)

View file

@ -20,6 +20,7 @@
#include <string.h> #include <string.h>
#include "cmdparser.h" // command_t #include "cmdparser.h" // command_t
#include "commonutil.h" // ARRAYLEN #include "commonutil.h" // ARRAYLEN
#include "iso7816/iso7816core.h"
#include "protocols.h" #include "protocols.h"
#include "cmdtrace.h" #include "cmdtrace.h"
#include "proxmark3.h" #include "proxmark3.h"
@ -32,6 +33,11 @@
#include "crc16.h" // crc #include "crc16.h" // crc
#include "cliparser.h" // cliparsing #include "cliparser.h" // cliparsing
#include "atrs.h" // ATR lookup #include "atrs.h" // ATR lookup
#include "mbedtls/net_sockets.h"
#include "mifare.h"
#include "util_posix.h"
#include "cmdhf14a.h"
#include "cmdhf14b.h"
static int CmdHelp(const char *Cmd); static int CmdHelp(const char *Cmd);
@ -1158,10 +1164,201 @@ static int CmdSmartBruteforceSFI(const char *Cmd) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static void atsToEmulatedAtr(uint8_t *ats, uint8_t *atr, int *atrLen) {
int historicalLen = 0;
int offset = 2;
if (ats[0] < 2) {
historicalLen = 0;
} else {
if ((ats[1] & 64) != 0) {
offset++;
}
if ((ats[1] & 32) != 0) {
offset++;
}
if ((ats[1] & 16) != 0) {
offset++;
}
if (offset >= ats[0]) {
historicalLen = 0;
} else {
historicalLen = ats[0] - offset;
}
}
atr[0] = 0x3B;
atr[1] = 0x80 | historicalLen;
atr[2] = 0x80;
atr[3] = 0x01;
uint8_t tck = atr[1] ^ atr[2] ^ atr[3];
for (int i = 0; i < historicalLen; ++i) {
atr[4 + i] = ats[offset + i];
tck = tck ^ ats[offset + i];
}
atr[4 + historicalLen] = tck;
*atrLen = 5 + historicalLen;
}
static void atqbToEmulatedAtr(uint8_t *atqb, uint8_t cid, uint8_t *atr, int *atrLen) {
atr[0] = 0x3B;
atr[1] = 0x80 | 8;
atr[2] = 0x80;
atr[3] = 0x01;
memcpy(atr + 4, atqb, 7);
atr[11] = cid >> 4;
uint8_t tck = 0;
for (int i = 1; i < 12; ++i) {
tck = tck ^ atr[i];
}
atr[12] = tck;
*atrLen = 13;
}
static int CmdRelay(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "smart relay",
"Make pm3 available to host OS smartcard driver via vpcd to enable use with other software such as GlobalPlatform Pro",
"Requires the virtual smartcard daemon to be installed and running, see https://frankmorgner.github.io/vsmartcard/virtualsmartcard/README.html"
);
void *argtable[] = {
arg_param_begin,
arg_str0(NULL, "host", "<str>", "vpcd socket host (default: localhost)"),
arg_str0("p", "port", "<int>", "vpcd socket port (default: 35963)"),
arg_lit0("v", "verbose", "display APDU transactions between OS and card"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
uint8_t host[100] = {0};
int hostLen = sizeof(host);
CLIGetStrWithReturn(ctx, 1, host, &hostLen);
if (hostLen == 0) {
strcpy((char *) host, "localhost");
}
uint8_t port[6] = {0};
int portLen = sizeof(port);
CLIGetStrWithReturn(ctx, 2, port, &portLen);
if (portLen == 0) {
strcpy((char *) port, "35963");
}
bool verbose = arg_get_lit(ctx, 3);
CLIParserFree(ctx);
mbedtls_net_context netCtx;
mbedtls_net_init(&netCtx);
PrintAndLogEx(INFO, "Relaying pm3 to host OS pcsc daemon. Press " _GREEN_("Enter") " to exit");
uint8_t cmdbuf[512] = {0};
iso14a_card_select_t selectedCard14a;
iso14b_card_select_t selectedCard14b;
isodep_state_t cardType = ISODEP_INACTIVE;
bool fieldActivated = false;
do {
if (cardType != ISODEP_INACTIVE) {
int bytesRead = mbedtls_net_recv_timeout(&netCtx, cmdbuf, sizeof(cmdbuf), 100);
if (bytesRead == MBEDTLS_ERR_SSL_TIMEOUT || bytesRead == MBEDTLS_ERR_SSL_WANT_READ) {
continue;
}
if (bytesRead > 0) {
if (cmdbuf[1] == 0x01 && cmdbuf[2] == 0x04) { // vpcd GET ATR
uint8_t atr[20] = {0};
int atrLen = 0;
if (cardType == ISODEP_NFCA) {
atsToEmulatedAtr(selectedCard14a.ats, atr, &atrLen);
} else if (cardType == ISODEP_NFCB) {
atqbToEmulatedAtr(selectedCard14b.atqb, selectedCard14b.cid, atr, &atrLen);
}
uint8_t res[22] = {0};
res[1] = atrLen;
memcpy(res + 2, atr, atrLen);
mbedtls_net_send(&netCtx, res, 2 + atrLen);
} else if (cmdbuf[1] != 0x01) { // vpcd APDU
int apduLen = (cmdbuf[0] << 8) + cmdbuf[1];
uint8_t apduRes[APDU_RES_LEN] = {0};
int apduResLen = 0;
if (verbose) {
PrintAndLogEx(INFO, ">> %s", sprint_hex(cmdbuf + 2, apduLen));
}
if (cardType == ISODEP_NFCA) {
if (ExchangeAPDU14a(cmdbuf + 2, apduLen, !fieldActivated, true, apduRes, sizeof(apduRes), &apduResLen) != PM3_SUCCESS) {
cardType = ISODEP_INACTIVE;
mbedtls_net_close(&netCtx);
continue;
}
} else if (cardType == ISODEP_NFCB) {
if (exchange_14b_apdu(cmdbuf + 2, apduLen, !fieldActivated, true, apduRes, sizeof(apduRes), &apduResLen, 0)) {
cardType = ISODEP_INACTIVE;
mbedtls_net_close(&netCtx);
continue;
}
}
fieldActivated = true;
if (verbose) {
PrintAndLogEx(INFO, "<< %s", sprint_hex(apduRes, apduResLen));
}
uint8_t res[APDU_RES_LEN + 2] = {0};
res[0] = (apduResLen >> 8) & 0xFF;
res[1] = apduResLen & 0xFF;
memcpy(res + 2, apduRes, apduResLen);
mbedtls_net_send(&netCtx, res, 2 + apduResLen);
}
}
} else {
if (IfPm3Iso14443a() && SelectCard14443A_4(false, false, &selectedCard14a) == PM3_SUCCESS) {
cardType = ISODEP_NFCA;
} else if (IfPm3Iso14443b() && select_card_14443b_4(false, &selectedCard14b) == PM3_SUCCESS) {
cardType = ISODEP_NFCB;
}
if (cardType != ISODEP_INACTIVE) {
fieldActivated = false;
if (mbedtls_net_connect(&netCtx, (char *) host, (char *) port, MBEDTLS_NET_PROTO_TCP)) {
PrintAndLogEx(FAILED, "Failed to connect to vpcd socket. Ensure you have vpcd installed and running");
mbedtls_net_close(&netCtx);
mbedtls_net_free(&netCtx);
DropField();
return PM3_EINVARG;
}
}
msleep(300);
}
} while (!kbd_enter_pressed());
mbedtls_net_close(&netCtx);
mbedtls_net_free(&netCtx);
DropField();
return PM3_SUCCESS;
}
static command_t CommandTable[] = { static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"}, {"help", CmdHelp, AlwaysAvailable, "This help"},
{"list", CmdSmartList, AlwaysAvailable, "List ISO 7816 history"}, {"list", CmdSmartList, AlwaysAvailable, "List ISO 7816 history"},
{"info", CmdSmartInfo, IfPm3Smartcard, "Tag information"}, {"info", CmdSmartInfo, IfPm3Smartcard, "Tag information"},
{"relay", CmdRelay, IfPm3Iso14443a, "Turn pm3 into pcsc reader and relay to host OS via vpcd"},
{"reader", CmdSmartReader, IfPm3Smartcard, "Act like an IS07816 reader"}, {"reader", CmdSmartReader, IfPm3Smartcard, "Act like an IS07816 reader"},
{"raw", CmdSmartRaw, IfPm3Smartcard, "Send raw hex data to tag"}, {"raw", CmdSmartRaw, IfPm3Smartcard, "Send raw hex data to tag"},
{"upgrade", CmdSmartUpgrade, AlwaysAvailable, "Upgrade sim module firmware"}, {"upgrade", CmdSmartUpgrade, AlwaysAvailable, "Upgrade sim module firmware"},

View file

@ -791,6 +791,7 @@ const static vocabulary_t vocabulary[] = {
{ 1, "smart help" }, { 1, "smart help" },
{ 1, "smart list" }, { 1, "smart list" },
{ 0, "smart info" }, { 0, "smart info" },
{ 0, "smart relay" },
{ 0, "smart reader" }, { 0, "smart reader" },
{ 0, "smart raw" }, { 0, "smart raw" },
{ 1, "smart upgrade" }, { 1, "smart upgrade" },

View file

@ -46,7 +46,8 @@ MYSRCS = \
threading.c \ threading.c \
x509.c \ x509.c \
x509_crl.c \ x509_crl.c \
x509_crt.c x509_crt.c \
net_sockets.c
LIB_A = libmbedtls.a LIB_A = libmbedtls.a

View file

@ -2979,7 +2979,7 @@
* *
* This module provides networking routines. * This module provides networking routines.
*/ */
//#define MBEDTLS_NET_C #define MBEDTLS_NET_C
/** /**
* \def MBEDTLS_OID_C * \def MBEDTLS_OID_C

View file

@ -11790,6 +11790,21 @@
], ],
"usage": "smart reader [-hv]" "usage": "smart reader [-hv]"
}, },
"smart relay": {
"command": "smart relay",
"description": "Make pm3 available to host OS smartcard driver via vpcd to enable use with other software such as GlobalPlatform Pro",
"notes": [
"Requires the virtual smartcard daemon to be installed and running, see https://frankmorgner.github.io/vsmartcard/virtualsmartcard/README.html"
],
"offline": false,
"options": [
"-h, --help This help",
"--host <str> vpcd socket host (default: localhost)",
"-p, --port <int> vpcd socket port (default: 35963)",
"-v, --verbose display APDU transactions between OS and card"
],
"usage": "smart relay [-hv] [--host <str>] [-p <int>]"
},
"smart setclock": { "smart setclock": {
"command": "smart setclock", "command": "smart setclock",
"description": "Set clock speed for smart card interface.", "description": "Set clock speed for smart card interface.",
@ -12049,8 +12064,8 @@
} }
}, },
"metadata": { "metadata": {
"commands_extracted": 698, "commands_extracted": 699,
"extracted_by": "PM3Help2JSON v1.00", "extracted_by": "PM3Help2JSON v1.00",
"extracted_on": "2023-11-09T16:29:08" "extracted_on": "2023-11-11T20:31:02"
} }
} }

View file

@ -1424,6 +1424,7 @@ Check column "offline" for their availability.
|`smart help `|Y |`This help` |`smart help `|Y |`This help`
|`smart list `|Y |`List ISO 7816 history` |`smart list `|Y |`List ISO 7816 history`
|`smart info `|N |`Tag information` |`smart info `|N |`Tag information`
|`smart relay `|N |`Turn pm3 into pcsc reader and relay to host OS via vpcd`
|`smart reader `|N |`Act like an IS07816 reader` |`smart reader `|N |`Act like an IS07816 reader`
|`smart raw `|N |`Send raw hex data to tag` |`smart raw `|N |`Send raw hex data to tag`
|`smart upgrade `|Y |`Upgrade sim module firmware` |`smart upgrade `|Y |`Upgrade sim module firmware`

View file

@ -525,7 +525,7 @@ static void *brute_thread(void *arguments) {
free(revstate); free(revstate);
continue; continue;
} }
// lock this section to avoid interlacing prints from different threats // lock this section to avoid interlacing prints from different threats
pthread_mutex_lock(&print_lock); pthread_mutex_lock(&print_lock);
if (args->ev1) { if (args->ev1) {
@ -533,11 +533,11 @@ static void *brute_thread(void *arguments) {
} }
#if 0 #if 0
printf("thread #%d idx %d %s\n", args->thread, args->idx, (args->ev1) ? "(Ev1)" : ""); printf("thread #%d idx %d %s\n", args->thread, args->idx, (args->ev1) ? "(Ev1)" : "");
printf("current nt(%08x) ar_enc(%08x) at_enc(%08x)\n", nt, ar_enc, at_enc); printf("current nt(%08x) ar_enc(%08x) at_enc(%08x)\n", nt, ar_enc, at_enc);
printf("ks2:%08x\n", ks2); printf("ks2:%08x\n", ks2);
printf("ks3:%08x\n", ks3); printf("ks3:%08x\n", ks3);
printf("ks4:%08x\n", ks4); printf("ks4:%08x\n", ks4);
#endif #endif
if (cmd_enc) { if (cmd_enc) {
uint32_t decrypted = ks4 ^ cmd_enc; uint32_t decrypted = ks4 ^ cmd_enc;
@ -681,7 +681,7 @@ int main(int argc, const char *argv[]) {
// next encrypted command + a full read/write // next encrypted command + a full read/write
int enc_len = 0; int enc_len = 0;
uint8_t enc[ENC_LEN] = {0}; uint8_t enc[ENC_LEN] = {0};
if (argc > 9) { if (argc > 9) {
param_gethex_to_eol(argv[9], 0, enc, sizeof(enc), &enc_len); param_gethex_to_eol(argv[9], 0, enc, sizeof(enc), &enc_len);
cmd_enc = (enc[0] << 24 | enc[1] << 16 | enc[2] << 8 | enc[3]); cmd_enc = (enc[0] << 24 | enc[1] << 16 | enc[2] << 8 | enc[3]);

View file

@ -235,7 +235,7 @@ uint64_t *nested(NtpKs1 *pNK, uint32_t sizePNK, uint32_t authuid, uint32_t *keyC
free(pRPs); free(pRPs);
return NULL; return NULL;
} }
for (i = 0; i < TRY_KEYS; i++) { for (i = 0; i < TRY_KEYS; i++) {
// We don't known this key, try to break it // We don't known this key, try to break it
// This key can be found here two or more times // This key can be found here two or more times

View file

@ -12,4 +12,4 @@ typedef struct {
uint8_t valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t *parity); uint8_t valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t *parity);
uint64_t *nested(NtpKs1 *pNK, uint32_t sizePNK, uint32_t authuid, uint32_t *keyCount); uint64_t *nested(NtpKs1 *pNK, uint32_t sizePNK, uint32_t authuid, uint32_t *keyCount);
#endif #endif

View file

@ -233,4 +233,4 @@ int main(int argc, char *const argv[]) {
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
error: error:
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }