RfidResearchGroup / proxmark3

Iceman Fork - Proxmark3
http://www.icedev.se
GNU General Public License v3.0
3.81k stars 1.01k forks source link

EMV GPO seems to miss a tailing LE if PDOL data is sent along #1094

Closed compr00t closed 3 years ago

compr00t commented 3 years ago

It seems that the EMV GPO is missing a tailing LE if PDOL data is sent along. If we check Salvador Mendoza's "HF - Reading Visa cards & Emulating a Visa MSD Transaction(ISO14443)", that can get past the GPO step we can see that the APDU for the GPO is submitted as following:

#db# [Challenge generated ]
#db# 80 a8 00 00 23 83 21 f6
#db# 20 c0 00 00 00 00 00 00
#db# 00 00 00 00 00 00 00 9f
#db# 1a 95 95 95 95 95 5f 2a
#db# 9a 9a 9a 9c 9f 37 9f 37
#db# 00

If we resend the same PDOL data with emv gpo (after selecting an applet of course) we get the same command APDU except the tailing LE together with an error 67 00:

[usb] pm3 --> emv gpo -kat f620C0000000000000000000000000009f1a95959595955f2a9a9a9a9c9f379f37
[+] >>>> 80 A8 00 00 23 83 21 F6 20 C0 00 00 00 00 00 00 00 00 00 00 00 00 00 9F 1A 95 95 95 95 95 5F 2A 9A 9A 9A 9C 9F 37 9F 37
[+] <<<< 67 00
[!!] ? APDU(80a8) ERROR: [6700] Wrong length

However, I tried sending the command APDU directly but it seems there is another problem in the hf 14a apdu as well for GPO instructions:

[usb] pm3 --> hf 14a apdu -skt 80A80000238321F620C0000000000000000000000000009F1A95959595955F2A9A9A9A9C9F37 9F3700
>>>>[sel keep TLV] 80 A8 00 00 23 83 21 F6 20 C0 00 00 00 00 00 00 00 00 00 00 00 00 00 9F 1A 95 95 95 95 95 5F 2A 9A 9A 9A 9C 9F 37 9F 37 00
<<<< 6D 00
[+] APDU response: 6d 00 - Instruction code not supported or invalid

Steps to reproduce the behavior are as follwing:

  1. emv pse -sk2at
  2. emv select -skat A0000000031010
  3. emv gpo -kat f620C0000000000000000000000000009f1a95959595955f2a9a9a9a9c9f379f37
  4. see error

Looking through the code I think a potential fix would be to extend EMVGPO() in emvcore.c as following:

int EMVGPO(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
    int res = EMVExchange(channel, LeaveFieldON, (sAPDU) {0x80, 0xa8, 0x00, 0x00, PDOLLen, PDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
    if (*sw == 0x6700) {
        PrintAndLogEx(INFO, ">>> trying to reissue command with Le...");
        res = EMVExchange(channel, LeaveFieldON, (sAPDU) {0x80, 0xa8, 0x00, 0x00, PDOLLen, PDOL}, true, Result, MaxResultLen, ResultLen, sw, tlv);
    }
    return res;
}

As I'm new to EMV; can somebody confirm that this would be a potential fix or propose a better solution?

Thanks & cheers!

iceman1001 commented 3 years ago

I can't replicate your issue. It works on my sample.

[#] [ Challenge generated ]
[#] 80 a8 00 00 15 83 13 f6
[#] 20 c0 00 00 00 00 00 00
[#] 00 9f 37 9f 37 5f 2a 9a
[#] 9a 9a 00

-- in a row
[#] 80 a8 00 00 15 83 13 f6 20 c0 00 00 00 00 00 00 00 9f 37 9f 37 5f 2a 9a 9a 9a 00
                         f6 20 c0 00 00 00 00 00 00 00 9f 37 9f 37 5f 2a 9a 9a 9a

Running your sample:

[usb] pm3 --> emv select -ska A0000000031010
[=] Selected channel : CONTACTLESS (T=CL)
[+] >>>> 00 A4 04 00 07 A0 00 00 00 03 10 10 00

[+] <<<< 6F 34 84 07 A0 00 00 00 03 10 10 A5 29 50 04 56 69 73 61 5F 2D 04 72 75 65 6E 87 01 01 9F 38 0E 9F 66 04 9F 02 06 9F 37 04 5F 2A 02 9A 03 BF 0C 05 9F 4D 02 0C 0A 90 00
[=] APDU response status: 9000 - Command successfully executed (OK).

[usb] pm3 --> emv gpo -ka f620c0000000000000009f379f375f2a9a9a9a
[=] Selected channel : CONTACTLESS (T=CL)
[=] PDOL data[21]: 83 13 F6 20 C0 00 00 00 00 00 00 00 9F 37 9F 37 5F 2A 9A 9A 9A
[+] >>>> 80 A8 00 00 15 83 13 F6 20 C0 00 00 00 00 00 00 00 9F 37 9F 37 5F 2A 9A 9A 9A
[+] <<<< .....excluded....  90 00

[=] APDU response status: 9000 - Command successfully executed (OK).
compr00t commented 3 years ago

Hi @iceman1001, I can follow your steps and your example. If you compare your challenge from the standalone mode and the command APDU from EMV GPO you can see the missing LE:

80 A8 00 00 15 83 13 F6 20 C0 00 00 00 00 00 00 00 9F 37 9F 37 5F 2A 9A 9A 9A 00
80 A8 00 00 15 83 13 F6 20 C0 00 00 00 00 00 00 00 9F 37 9F 37 5F 2A 9A 9A 9A

As you expect a response APDU with a data body, you will need an LE according to the specs as far as I understood, do you agree?

However, I can see you did the same steps and end up with a different result. To be sure I'm on the latest version I flashed my proxmark3 rdv4 again with the latest firmware according to the "Homebrew (Mac OS X), automatic installation" instructions on macOS and still get the same length error:

[ CLIENT ]
  client: RRG/Iceman/master/release (git)
  compiled with Clang/LLVM Apple LLVM 12.0.0 (clang-1200.0.32.21) OS:OSX ARCH:x86_64

Further, I flashed my proxmark again with newly compiled sources on Kali and end up again with the same length issue. And I found another user in the proxmark forum with exactly the same problem: http://www.proxmark.org/forum/viewtopic.php?id=7885

iceman1001 commented 3 years ago

As I said, I can't replicate your issue. It works for me. We will need to verify the problem in order to find a good solution. You have a reference in a EMV data sheet for this particular apdu exchange?

compr00t commented 3 years ago

@iceman1001 My reference is the working standalone mode and my understanding of APDU and LE. However, I was just able to verify it directly with my card in code. Currently the function responsible for sending the GPO is the following snippet in emvcore.c:

int EMVGPO(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
    return EMVExchange(channel, LeaveFieldON, (sAPDU) {0x80, 0xa8, 0x00, 0x00, PDOLLen, PDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
}

TBH, I don't fully get how the length is controlled in this function call but to quickly verify I replaced the function with the following in order to force always the addition of an LE:

int EMVGPO(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
    return EMVExchangeEx(channel, false, LeaveFieldON, (sAPDU) {0x80, 0xa8, 0x00, 0x00, PDOLLen, PDOL}, true, Result, MaxResultLen, ResultLen, sw, tlv);
}

And voilà, an LE is added to the command APDU and I get a proper response:

usb] pm3 --> emv gpo -kat f620C0000000000000000000000000009f1a95959595955f2a9a9a9a9c9f379f37
[=] Selected channel : CONTACTLESS (T=CL)
[=] PDOL data[35]: 83 21 F6 20 C0 00 00 00 00 00 00 00 00 00 00 00 00 00 9F 1A 95 95 95 95 95 5F 2A 9A 9A 9A 9C 9F 37 9F 37 
[+] >>>> 80 A8 00 00 23 83 21 F6 20 C0 00 00 00 00 00 00 00 00 00 00 00 00 00 9F 1A 95 95 95 95 95 5F 2A 9A 9A 9A 9C 9F 37 9F 37 00 
[+] <<<< ...redacted... 90 00 
[=] APDU response status: 9000 - Command successfully executed (OK).

What is stated as ...readacted... in my example is what I get back from the card in the data body of the response APDU. Therefore an LE is mandatory, because we are getting data back (https://salmg.net/2017/09/12/intro-to-analyze-nfc-contactless-cards/). Only possibility where an LE can be skipped, is when I do not expect any data. I will try to look this up in the specs in order to find out if a tailing LE is always mandatory for a GPO and then submit a fix in code.

In the mean time, can you please verify, if this modification would work for your sample as well? Because based on this, your example should not have worked but probably the currently used function call to EMVExchange is messing something up while adding the length.

doegox commented 3 years ago

Excerpt from the EMV Books: Screenshot from 2020-12-13 01-05-12 Some cards probalby tolerate the absence of Le but I know Salva is adding the Le=00 and I guess it's better to add it. @iceman1001 can you test your card if it still works with the Le=00 byte ?

iceman1001 commented 3 years ago

OK, on my card it works with and wo Le. I pushed a fix for always using LE. @compr00t Would you mind pulling and testing on your cards?

compr00t commented 3 years ago

@iceman1001 Thanks for your fix, it works perfectly on my card.