From 71e0f2e8fc5c52c12c6f02855fda8f85006f5b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonard=20H=C3=BCbner?= Date: Mon, 19 Oct 2020 12:50:58 +0200 Subject: [PATCH] basic Man-in-the-Middle extension to the Relay-Card --- virtualsmartcard/src/vpicc/Makefile.am | 1 + virtualsmartcard/src/vpicc/vicc.in | 6 +++- .../virtualsmartcard/VirtualSmartcard.py | 30 +++++++++++++++++-- .../src/vpicc/virtualsmartcard/cards/Relay.py | 9 +++++- .../virtualsmartcard/cards/RelayMiddleman.py | 22 ++++++++++++++ 5 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 virtualsmartcard/src/vpicc/virtualsmartcard/cards/RelayMiddleman.py diff --git a/virtualsmartcard/src/vpicc/Makefile.am b/virtualsmartcard/src/vpicc/Makefile.am index 441ffb6..3a937ea 100644 --- a/virtualsmartcard/src/vpicc/Makefile.am +++ b/virtualsmartcard/src/vpicc/Makefile.am @@ -24,6 +24,7 @@ vpicccards_PYTHON = virtualsmartcard/cards/__init__.py \ virtualsmartcard/cards/ePass.py \ virtualsmartcard/cards/nPA.py \ virtualsmartcard/cards/Relay.py \ + virtualsmartcard/cards/RelayMiddleman.py \ virtualsmartcard/cards/cryptoflex.py do_subst = $(SED) \ diff --git a/virtualsmartcard/src/vpicc/vicc.in b/virtualsmartcard/src/vpicc/vicc.in index b016998..983a61d 100644 --- a/virtualsmartcard/src/vpicc/vicc.in +++ b/virtualsmartcard/src/vpicc/vicc.in @@ -66,6 +66,10 @@ relay.add_argument("--reader", type=int, default=0, help="number of the reader containing the card to be relayed (default: %(default)s)") +relay.add_argument("--mitm", + action="store", + type=str, + help="relative path to a file containing a Man-in-the-Middle class that is supposed to be used with the relay") npa = parser.add_argument_group('Emulation of German identity card (`--type=nPA`)') npa.add_argument("--ef-cardaccess", @@ -153,7 +157,7 @@ else: hostname = args.hostname vicc = VirtualICC(args.datasetfile, args.type, hostname, args.port, - readernum=args.reader, ef_cardaccess=ef_cardaccess_data, + readernum=args.reader, mitmPath=args.mitm, ef_cardaccess=ef_cardaccess_data, ef_cardsecurity=ef_cardsecurity_data, ca_key=ca_key_data, cvca=cvca, disable_checks=args.disable_ta_checks, esign_ca_cert=esign_ca_cert, esign_cert=esign_cert, logginglevel=logginglevel) diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py b/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py index c4d0f95..c34b827 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py @@ -370,6 +370,30 @@ class Iso7816OS(SmartcardOS): self.mf.current = self.mf +def loadMitMFromPath(path: str): + from importlib import import_module + from pathlib import Path + def onNth(tup,ind,func): + start = tup[0:ind] + (func(tup[ind]),) + return start if ind + 1 == 0 else start + tup[ind+1:] + + def pathToModuleName(path): + return ".".join(onNth(Path(path).parts,-1,lambda fName : str(Path(fName).stem))) + + mitmModule = import_module(pathToModuleName(path)) + + # Sanity Checks + if not getattr(mitmModule,"get_MitM",None): + raise ValueError("The provided module does not contain a 'get_MitM' method.") + mitm = mitmModule.get_MitM() + if not getattr(mitm,"handleInPDU",None): + raise ValueError("The produced MitM has no 'handleInPDU' method.") + if not getattr(mitm,"handleOutPDU",None): + raise ValueError("The produced MitM has no 'handleOutPDU' method.") + + return mitm + + # sizeof(int) taken from asizof-package {{{ _Csizeof_short = len(struct.pack('h', 0)) # }}} @@ -393,7 +417,7 @@ class VirtualICC(object): """ def __init__(self, datasetfile, card_type, host, port, - readernum=None, ef_cardsecurity=None, ef_cardaccess=None, + readernum=None, mitmPath=None, ef_cardsecurity=None, ef_cardaccess=None, ca_key=None, cvca=None, disable_checks=False, esign_key=None, esign_ca_cert=None, esign_cert=None, logginglevel=logging.INFO): @@ -429,7 +453,9 @@ class VirtualICC(object): self.os = CryptoflexOS(MF, SAM) elif card_type == "relay": from virtualsmartcard.cards.Relay import RelayOS - self.os = RelayOS(readernum) + from virtualsmartcard.cards.RelayMiddleman import RelayMiddleman + mitm = loadMitMFromPath(mitmPath) if mitmPath else RelayMiddleman() + self.os = RelayOS(readernum,mitm=mitm) elif card_type == "handler_test": from virtualsmartcard.cards.HandlerTest import HandlerTestOS self.os = HandlerTestOS() diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/Relay.py b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/Relay.py index 3fad8b8..c850f2e 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/Relay.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/Relay.py @@ -22,6 +22,7 @@ import logging import smartcard import sys from virtualsmartcard.VirtualSmartcard import SmartcardOS +from virtualsmartcard.cards.RelayMiddleman import RelayMiddleman class RelayOS(SmartcardOS): @@ -31,7 +32,7 @@ class RelayOS(SmartcardOS): an actual smart card reader and sends the responses back to the vpcd. This class can be used to implement relay or MitM attacks. """ - def __init__(self, readernum): + def __init__(self, readernum, mitm=RelayMiddleman()): """ Initialize the connection to the (physical) smart card via a given reader @@ -57,6 +58,8 @@ class RelayOS(SmartcardOS): logging.info("Connected to card in '%s'", self.reader) + self.mitm = mitm + atexit.register(self.cleanup) def cleanup(self): @@ -116,6 +119,8 @@ class RelayOS(SmartcardOS): # sendCommandAPDU() expects a list of APDU bytes apdu = map(ord, msg) + apdu = self.mitm.handleInPDU(apdu) + try: rapdu, sw1, sw2 = self.session.sendCommandAPDU(apdu) except smartcard.Exceptions.CardConnectionException as e: @@ -130,5 +135,7 @@ class RelayOS(SmartcardOS): else: rapdu = rapdu + [sw1, sw2] + rapdu = self.mitm.handleOutPDU(rapdu) + # return the response APDU as string return "".join(map(chr, rapdu)) diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/RelayMiddleman.py b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/RelayMiddleman.py new file mode 100644 index 0000000..0d6b2ba --- /dev/null +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/RelayMiddleman.py @@ -0,0 +1,22 @@ +class RelayMiddleman(object): + """ + The RelayMiddleman class serves as a base from which a user might derive + their own relay middle man class. This base class implements the simplest + Man-in-the-Middle: the NoOp. + """ + + def handleInPDU(self, inPDU: bytes): + """ + This method is called on each PDU that is fed into the realy (vdpu -> vicc). + It may be overwritten to modify the packages send from the terminal to the + real smart card. + """ + return inPDU + + def handleOutPDU(self, outPDU: bytes): + """ + This method is called on each PDU that is produced by the relay (vicc -> vdpu). + It may be overwritten to modify the packages send from the real smart card to the + terminal. + """ + return outPDU