Last active
March 7, 2024 14:34
-
-
Save pvieito/6224eed92c99b069f6401996c548d0e4 to your computer and use it in GitHub Desktop.
Read EMV contactless debit and credit cards.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2 | |
# -*- coding: utf-8 -*- | |
# | |
# PayPass.py is based on: | |
# | |
# - paypass.py | |
# Mert Sarica <[email protected]> | |
# - ChasePayPassBlink.py | |
# Author: Brad Antoniewicz [email protected] | |
# - ChaAP.py | |
# Author: Adam Laurie <[email protected]> | |
# | |
'''PayPass.py - Pedro José Pereira Vieito © 2016 | |
Read EMV contactless debit and credit cards. | |
Usage: | |
PayPass.py [-hv] | |
Options: | |
-v, --verbose Verbose mode | |
-h, --help Show this help | |
''' | |
from __future__ import print_function | |
from smartcard.CardType import AnyCardType | |
from smartcard.CardRequest import CardRequest | |
from smartcard.CardConnection import CardConnection | |
from smartcard.CardConnectionObserver import ConsoleCardConnectionObserver | |
from smartcard.Exceptions import CardRequestTimeoutException | |
import os | |
import getopt | |
import sys | |
import re | |
import string | |
import binascii | |
from hexdump import hexdump | |
from operator import * | |
from docopt import docopt | |
from colorama import init | |
from colorama import Fore, Back, Style | |
args = docopt(__doc__) | |
verbose = args["--verbose"] | |
Protocol = CardConnection.T0_protocol | |
AIDs = { | |
'VISA': [0xa0, 0x00, 0x00, 0x00, 0x03], | |
'VISA Debit/Credit': [0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10], | |
'VISA Credit': [0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x01], | |
'VISA Debit': [0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x02], | |
'VISA Electron': [0xa0, 0x00, 0x00, 0x00, 0x03, 0x20, 0x10], | |
'VISA Interlink': [0xa0, 0x00, 0x00, 0x00, 0x03, 0x30, 0x10], | |
'VISA Plus': [0xa0, 0x00, 0x00, 0x00, 0x03, 0x80, 0x10], | |
'VISA ATM': [0xa0, 0x00, 0x00, 0x00, 0x03, 0x99, 0x99, 0x10], | |
'MASTERCARD': [0xa0, 0x00, 0x00, 0x00, 0x04, 0x10, 0x10], | |
'Maestro': [0xa0, 0x00, 0x00, 0x00, 0x04, 0x30, 0x60], | |
'Maestro UK': [0xa0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01], | |
'Maestro TEST': [0xb0, 0x12, 0x34, 0x56, 0x78], | |
'Self Service': [0xa0, 0x00, 0x00, 0x00, 0x24, 0x01], | |
'American Express': [0xa0, 0x00, 0x00, 0x00, 0x25], | |
'ExpressPay': [0xa0, 0x00, 0x00, 0x00, 0x25, 0x01, 0x07, 0x01], | |
'Link': [0xa0, 0x00, 0x00, 0x00, 0x29, 0x10, 0x10], | |
'Alias AID': [0xa0, 0x00, 0x00, 0x00, 0x29, 0x10, 0x10], | |
} | |
# Define the APDUs used in this script | |
GET_RESPONSE = [0x00, 0xC0, 0x00, 0x00] | |
SELECT = [0x00, 0xA4, 0x04, 0x00] | |
CMD = [0x00, 0xB2, 0x01, 0x0C, 0x00] | |
# Define SW1 return values | |
SW1_RESPONSE_BYTES = 0x61 | |
SW1_WRONG_LENGTH = 0x6c | |
SW12_OK = [0x90, 0x00] | |
SW12_NOT_SUPORTED = [0x6a, 0x81] | |
SW12_NOT_FOUND = [0x6a, 0x82] | |
SW12_COND_NOT_SAT = [0x69, 0x85] | |
def print_intro(): | |
print("================================") | |
print(" PayPass Credit Card Reader ") | |
print("================================") | |
def try_cmd(card): | |
le = 0x00 | |
apdu = CMD | |
response, sw1, sw2 = send_apdu(apdu) | |
if response: | |
print('[+]', card, 'detected') | |
return response | |
else: | |
print_verbose('[V] False positive, not a', card, 'card') | |
return False, 0, '' | |
def check_return(sw1, sw2): | |
if [sw1, sw2] == SW12_OK: | |
return True | |
return False | |
def send_apdu(apdu): | |
response, sw1, sw2 = cardservice.connection.transmit(apdu, Protocol) | |
if sw1 == SW1_WRONG_LENGTH: | |
apdu = apdu[:len(apdu) - 1] + [sw2] | |
return send_apdu(apdu) | |
if sw1 == SW1_RESPONSE_BYTES: | |
apdu = GET_RESPONSE + [sw2] | |
response, sw1, sw2 = cardservice.connection.transmit(apdu, Protocol) | |
return response, sw1, sw2 | |
def select_aid(aid): | |
apdu = SELECT + [len(aid)] + aid + [0x00] | |
response, sw1, sw2 = send_apdu(apdu) | |
if check_return(sw1, sw2): | |
return True, response, sw1, sw2 | |
else: | |
return False, [], sw1, sw2 | |
def bruteforce_cc(data): | |
cc_response = binascii.hexlify(bytearray(data)) | |
# Regex to find Track 2 Equivalent Data | |
pattern = re.compile("(?P<PAN>[\d]{16})d(?P<ED>[\d]{4})" | |
"(?P<SC>[\d]{3})(?P<DD>[\d]*)f") | |
match = re.search(pattern, cc_response) | |
return match.group("PAN"), match.group("ED") | |
def split_string(w, n): | |
for i in range(0, len(w), n): | |
yield w[i:i + n] | |
def print_ccdata(cc_number, expire_date): | |
print("[+] Primary Account Number (PAN):" + Style.BRIGHT + | |
Fore.GREEN, ' '.join(split_string(cc_number, 4)), | |
Fore.RESET + Style.RESET_ALL) | |
print("[+] Expiration Date:", Style.BRIGHT + Fore.YELLOW + | |
"{1}/{0}".format(*tuple(split_string(expire_date, 2))), | |
Fore.RESET + Style.RESET_ALL) | |
def isbinary(data): | |
index = 0 | |
while index < len(data): | |
if data[index] < 0x20 or data[index] > 0x7e: | |
return True | |
index += 1 | |
return False | |
def hexprint_verbose(bin): | |
if verbose: | |
hexprint(bin) | |
def print_verbose(*string): | |
if verbose: | |
print(*string) | |
def hexprint(int_array): | |
hexdump(str(bytearray(int_array))) | |
if __name__ == '__main__': | |
print_intro() | |
try: | |
try: | |
cardtype = AnyCardType() | |
print('[ ] Insert a card...') | |
cardrequest = CardRequest(timeout=20, cardType=cardtype) | |
cardservice = cardrequest.waitforcard() | |
cardservice.connection.connect(Protocol) | |
compatible = False | |
print('[ ] Connecting...') | |
for card in AIDs: | |
aid = AIDs[card] | |
print_verbose("[V] Trying", card + "...") | |
selected, response, sw1, sw2 = select_aid(aid) | |
if selected: | |
compatible = True | |
print_verbose("[V] Maybe", card, "detected?") | |
hexprint_verbose(response) | |
print_verbose("[V] Testing", card, "response") | |
cc_response = try_cmd(card) | |
if cc_response[0]: | |
hexprint_verbose(cc_response) | |
cc_number, expire_date = bruteforce_cc(cc_response) | |
print_ccdata(cc_number, expire_date) | |
break | |
if not compatible: | |
print("[x] Not PayPass compatible card") | |
print("[ ] Closed connection") | |
except CardRequestTimeoutException: | |
print('[+] Time-out: no card inserted during last 20s') | |
except KeyboardInterrupt: | |
print("[ ] Closed connection") | |
sys.exit() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What smartcard reader did you use when developing this program?