RfidResearchGroup / proxmark3

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

Fix APDU length in emvcore - excessive Lc with no data packet #171

Closed jslawek closed 5 years ago

jslawek commented 5 years ago

Hi,

the EMV APDUs with no data (just headers) are incorrectly generated with excessive Lc byte.

According to specification, APDU consists of:

There are 4 possible cases for APDU packet: 1) no command data, no response needed - just 4 bytes of mandatory header 2) no command data, response required - 4 bytes header + Le (5 bytes total) 3) command data, no response required - 4 bytes header + Lc + Data (variable len) 4) command data, response required - 4 bytes header + Lc + Data + Le (variable len)

In case there is no data, the emvcore generates APDU packet with excessive Lc, thus making it one byte (0x00) longer, and then adds Le at the end. For example, an APDU "read first record from SFI" generated in emvcore:

CLA INS P1 P2 Lc Le
00 B2 01 0C 00 00

but should be

CLA INS P1 P2 Le
00 B2 01 0C 00

Many cards process this one byte longer, incorrect APDU, but there are some that respond with 0x6F00 and the EMV exchange communication stops.

I think the easiest fix would be to take EMVExchange approach from the main repo: https://github.com/Proxmark/proxmark3/blob/master/client/emv/emvcore.c#L368-L374 In case the APDU is 5 byte len, it is assumed the Le is there already, and the code generates correct len APDUs. Current RRG repo code adds the Le anyway and makes it 6 bytes incorrectly. By the way, the EMVExchange call to EMVExchangeEx has a typo anyway - the fifth parameter should not be channel (it is the first parameter), but rather IncludeLe bool:

    return EMVExchangeEx(channel, false, LeaveFieldON, apdu, (channel == ECC_CONTACTLESS), Result, MaxResultLen, ResultLen, sw, tlv);
merlokk commented 5 years ago

sometimes it true, sometimes not.

commands on different cards and via different channels needs to have different apdu sructure and sometimes it not as iso said.

in real life terminals have different "kernels" that make APDUs with a their hardcoded way. pm3 tried to guess the way. if we try some general card - this will work (maybe like 98% of cards) there are many points in code that checks errors and reissue commands or issue commands with EMVExchangeEx(). If you look deep - almost all the code uses guessing, not only EMVExchange().

merlokk commented 5 years ago

P.S. I have tried bank certification device on pm3 and it passes all public part several most commonly used card's standard.

merlokk commented 5 years ago

https://github.com/devnied/EMV-NFC-Paycard-Enrollment/issues/17 American Express Kernel '04' Discover Kernel '06' JCB Kernel '05' MasterCard Kernel '02' UnionPay Kernel '07' Visa Kernel '03' girocard Kernel '2A'

I have not remembered from where i have copied my list. so i cant share it (im not sure if it can be shared) but i found this) it not full but in general it enough)

jslawek commented 5 years ago

Hi, thanks for such quick response. Yes, I have read the code and noticed these many "checks" and "guesses". I filed the issue because in my specific case they do not work with RRG repo client, but the official repo client does work. My case is MasterCard, however not a plastic card but mobile contactless payment (Android Host Card Emulation). When I try to execute EMV, reading records from AFL fails and the execution stops:

pm3 --> emv exec -sAT
[=] Channel: CONTACTLESS
(...)
* Read records from AFL.
* * SFI[01] start:01 end:01 offline count:00
* * * SFI[01] 1
[+] >>>> 00 B2 01 0C 00 00
[+] <<<< 6F 00
[!!] APDU(00b2) ERROR: [6F00] Command aborted - more exact diagnosis not possible (e.g., operating system error).
[!] Error SFI[01]. APDU error 6f00

There is this 6-byte APDU for read record.

I noticed one of these "checks" introduced in EMVReadRecord - "trying to reissue command without Le": https://github.com/RfidResearchGroup/proxmark3/blob/master/client/emv/emvcore.c#L614-L617

    if (*sw == 0x6700) {
        PrintAndLogEx(INFO, ">>> trying to reissue command withouth Le...");
        res = EMVExchangeEx(channel, false, LeaveFieldON, (sAPDU) {0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv);
}

that workaround would work in my case - the resulting APDU will be 5 bytes, the 5th Lc will be treated as Le by card. But as far as I understand the execution does not reach this point because the exception is raised before.

I checked my reader (Sumup) against the same HCE card. It sends 5 bytes APDU for reading AFL, and it works.

Also the same setup but with official repo client works in my case (5 byte APDU):

* Read records from AFL.          
* * SFI[01] start:01 end:01 offline count:00          
* * * SFI[01] 1          
 >>>> 00 b2 01 0c 00           
 <<<< 70 72 9f 6c 02 00 01 (...)

And executing the commands by hand in RRG repo client works as well:

pm3 --> emv select -satk a0 00 00 00 04 10 10         
[=] Channel: CONTACTLESS 
(...)
pm3 --> hf 14a apdu -tk 00 B2 01 14 00                
>>>>[keep TLV] 00 B2 01 14 00           
<<<< 70 81 A3 57 13 55  (...)
merlokk commented 5 years ago

maybe ill add 6F00 error to list. ill think

jslawek commented 5 years ago

Oh, I have missed the fact it is 0x67 not not 0x6F...

I have added 0x6F00 here to EMVReadRecord:

if (*sw == 0x6700 || *sw == 0x6F00) {

and that did the trick for my case:

* * SFI[02] start:01 end:01 offline count:01          
* * * SFI[02] 1          
[+] >>>> 00 B2 01 14 00 00           
[+] <<<< 6F 00           
[!!] APDU(00b2) ERROR: [6F00] Command aborted - more exact diagnosis not possible (e.g., operating system error).          
[=] >>> trying to reissue command withouth Le...          
[+] >>>> 00 B2 01 14 00           
[+] <<<< 70 81 A3 57 13 55 73 (...)

But that is not really elegant solution in my opinion, also may be a bit misguiding in future - there is actually Lc which is treated as Le by the card in practice... And I still think it would be better to comply with the standard in the first place by default, and then add exceptions for the cases that deviate from it. Now it works the other way around - by default emvcore creates packets that are not strictly compliant to standard. Are you sure most cards do not respond here to these 5 bytes and need 6? Like previously mentioned, my reader sends here 5. I do not have possibility to check other readers at the moment...

merlokk commented 5 years ago

looks it done, thanks

iceman1001 commented 5 years ago

Lets see what @jslawek says

jslawek commented 5 years ago

It works, thanks! Now the APDUs have correct length and my cards do not respond with errors, also no more "trying to reissue command without Le". It is way more elegant solution than adding another workaround for 0x67 / 0x6F.

iceman1001 commented 5 years ago

EMV search leaves the rf field on afterwards, even if it doesnt find a tag