Course hipPIN

Course hipPIN

Category: Crypto
Author: Amosys

We are provided with the code of a Javacard Applet.

First look

The applet is handling APDU commands to perform the verification of a PIN code. If the PIN is correct, the user have access to the getFlag command:

switch (buffer[ISO7816.OFFSET_INS]) {
    case GET_FLAG:
        if(is_pin_valid(apdu))
            getFlag(apdu);
        return;
    case VERIFY:
        // First, we need to check the PIN code to ensure the user is allowed
        verify(apdu);
        return;
    case WRITE_FLAG:
        writeSecretData(apdu);
        return;
    default:
        ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}

Exploit

When trying to send some PIN, we notice that some inputs take a long time to process. This is a clear indication that there is a timing attack vulnerability.

We can create a simple script to get the PIN, character by character, by measuring the time it takes to process the command.

from pwn import *

def send_cmd(cmd):
    conn = remote("challenges.challenge-ecw.eu", 4242, level="warn")

    conn.sendline(cmd)
    conn.shutdown()

    data = conn.recvall()
    if b"an APDU !\n" in data:
        """
        [>] Sending command 802000000109FF
        CommmandAPDU: 7 bytes, nc=1, ne=255
        [<] Status Code: 0x6300
        [i] Response time: 200.476108ms
        [i] No data received from Card
        """
        return data.split(b"an APDU !\n")[1].strip()
    else:
        return data


CLA = b"80"
INS_VERIFY = b"20"
INS_WRITE = b"30"
INS_GET = b"50"

def gen_apdu(ins, data: bytes, end=b"FF"):
    return CLA + ins + b"0000" + bytes([len(data)]).hex().encode() + data.hex().encode() + end

pin_code = b""
while True:
    times = []
    for i in range(10):
        cmd = gen_apdu(INS_VERIFY, pin_code + bytes([i]))
        res = send_cmd(cmd)
        time = float(res.split(b"Response time: ")[1].split(b"ms")[0])
        print(f"Pin: {pin_code + bytes([i])}, Time: {time}")
        times.append(time)
    print(times)
    max_time = max(times)
    pin_code += bytes([times.index(max_time)])
    print(f"Pin code: {pin_code}")
    print()

We find the following PIN code: \x09\x03\x05\x05\x01\x08. We can now use this PIN code to get the flag:

cmd = gen_apdu(INS_VERIFY, b'\t\x03\x05\x05\x01\x08') + b" " + gen_apdu(INS_GET, b'', end=b"0FFF")
print(cmd)
print(send_cmd(cmd).decode())
b'8020000006090305050108FF 80500000000FFF'
[>] Sending command 8020000006090305050108FF
CommmandAPDU: 12 bytes, nc=6, ne=255
[<] Status Code: 0x9000
[i] Response time: 1200.549773ms
[i] No data received from Card
[>] Sending command 80500000000FFF
CommmandAPDU: 7 bytes, nc=0, ne=4095
[<] Status Code: 0x9000
[i] Response time: 0.190323ms
[<] Data received from card: 0xFF, ...

The data is an JPG image of a SmartCard with ECW{D4mn_I_G0t_D3laaaaays} written on it.