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...
## [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 `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)

View file

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

View file

@ -20,6 +20,7 @@
#include <string.h>
#include "cmdparser.h" // command_t
#include "commonutil.h" // ARRAYLEN
#include "iso7816/iso7816core.h"
#include "protocols.h"
#include "cmdtrace.h"
#include "proxmark3.h"
@ -32,6 +33,11 @@
#include "crc16.h" // crc
#include "cliparser.h" // cliparsing
#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);
@ -1158,10 +1164,201 @@ static int CmdSmartBruteforceSFI(const char *Cmd) {
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[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"list", CmdSmartList, AlwaysAvailable, "List ISO 7816 history"},
{"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"},
{"raw", CmdSmartRaw, IfPm3Smartcard, "Send raw hex data to tag"},
{"upgrade", CmdSmartUpgrade, AlwaysAvailable, "Upgrade sim module firmware"},

View file

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

View file

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

View file

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

View file

@ -11790,6 +11790,21 @@
],
"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": {
"command": "smart setclock",
"description": "Set clock speed for smart card interface.",
@ -12049,8 +12064,8 @@
}
},
"metadata": {
"commands_extracted": 698,
"commands_extracted": 699,
"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 list `|Y |`List ISO 7816 history`
|`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 raw `|N |`Send raw hex data to tag`
|`smart upgrade `|Y |`Upgrade sim module firmware`

View file

@ -525,7 +525,7 @@ static void *brute_thread(void *arguments) {
free(revstate);
continue;
}
// lock this section to avoid interlacing prints from different threats
pthread_mutex_lock(&print_lock);
if (args->ev1) {
@ -533,11 +533,11 @@ static void *brute_thread(void *arguments) {
}
#if 0
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("ks2:%08x\n", ks2);
printf("ks3:%08x\n", ks3);
printf("ks4:%08x\n", ks4);
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("ks2:%08x\n", ks2);
printf("ks3:%08x\n", ks3);
printf("ks4:%08x\n", ks4);
#endif
if (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
int enc_len = 0;
uint8_t enc[ENC_LEN] = {0};
uint8_t enc[ENC_LEN] = {0};
if (argc > 9) {
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]);

View file

@ -235,7 +235,7 @@ uint64_t *nested(NtpKs1 *pNK, uint32_t sizePNK, uint32_t authuid, uint32_t *keyC
free(pRPs);
return NULL;
}
for (i = 0; i < TRY_KEYS; i++) {
// We don't known this key, try to break it
// 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);
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);
error:
exit(EXIT_FAILURE);
}
}