basic Man-in-the-Middle extension to the Relay-Card

This commit is contained in:
Leonard Hübner
2020-10-19 12:50:58 +02:00
parent d7d21fa802
commit 71e0f2e8fc
5 changed files with 64 additions and 4 deletions

View File

@@ -24,6 +24,7 @@ vpicccards_PYTHON = virtualsmartcard/cards/__init__.py \
virtualsmartcard/cards/ePass.py \ virtualsmartcard/cards/ePass.py \
virtualsmartcard/cards/nPA.py \ virtualsmartcard/cards/nPA.py \
virtualsmartcard/cards/Relay.py \ virtualsmartcard/cards/Relay.py \
virtualsmartcard/cards/RelayMiddleman.py \
virtualsmartcard/cards/cryptoflex.py virtualsmartcard/cards/cryptoflex.py
do_subst = $(SED) \ do_subst = $(SED) \

View File

@@ -66,6 +66,10 @@ relay.add_argument("--reader",
type=int, type=int,
default=0, default=0,
help="number of the reader containing the card to be relayed (default: %(default)s)") 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 = parser.add_argument_group('Emulation of German identity card (`--type=nPA`)')
npa.add_argument("--ef-cardaccess", npa.add_argument("--ef-cardaccess",
@@ -153,7 +157,7 @@ else:
hostname = args.hostname hostname = args.hostname
vicc = VirtualICC(args.datasetfile, args.type, hostname, args.port, 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, ef_cardsecurity=ef_cardsecurity_data, ca_key=ca_key_data, cvca=cvca,
disable_checks=args.disable_ta_checks, esign_ca_cert=esign_ca_cert, disable_checks=args.disable_ta_checks, esign_ca_cert=esign_ca_cert,
esign_cert=esign_cert, logginglevel=logginglevel) esign_cert=esign_cert, logginglevel=logginglevel)

View File

@@ -370,6 +370,30 @@ class Iso7816OS(SmartcardOS):
self.mf.current = self.mf 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 {{{ # sizeof(int) taken from asizof-package {{{
_Csizeof_short = len(struct.pack('h', 0)) _Csizeof_short = len(struct.pack('h', 0))
# }}} # }}}
@@ -393,7 +417,7 @@ class VirtualICC(object):
""" """
def __init__(self, datasetfile, card_type, host, port, 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, ca_key=None, cvca=None, disable_checks=False, esign_key=None,
esign_ca_cert=None, esign_cert=None, esign_ca_cert=None, esign_cert=None,
logginglevel=logging.INFO): logginglevel=logging.INFO):
@@ -429,7 +453,9 @@ class VirtualICC(object):
self.os = CryptoflexOS(MF, SAM) self.os = CryptoflexOS(MF, SAM)
elif card_type == "relay": elif card_type == "relay":
from virtualsmartcard.cards.Relay import RelayOS 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": elif card_type == "handler_test":
from virtualsmartcard.cards.HandlerTest import HandlerTestOS from virtualsmartcard.cards.HandlerTest import HandlerTestOS
self.os = HandlerTestOS() self.os = HandlerTestOS()

View File

@@ -22,6 +22,7 @@ import logging
import smartcard import smartcard
import sys import sys
from virtualsmartcard.VirtualSmartcard import SmartcardOS from virtualsmartcard.VirtualSmartcard import SmartcardOS
from virtualsmartcard.cards.RelayMiddleman import RelayMiddleman
class RelayOS(SmartcardOS): class RelayOS(SmartcardOS):
@@ -31,7 +32,7 @@ class RelayOS(SmartcardOS):
an actual smart card reader and sends the responses back to the vpcd. an actual smart card reader and sends the responses back to the vpcd.
This class can be used to implement relay or MitM attacks. 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 Initialize the connection to the (physical) smart card via a given
reader reader
@@ -57,6 +58,8 @@ class RelayOS(SmartcardOS):
logging.info("Connected to card in '%s'", self.reader) logging.info("Connected to card in '%s'", self.reader)
self.mitm = mitm
atexit.register(self.cleanup) atexit.register(self.cleanup)
def cleanup(self): def cleanup(self):
@@ -116,6 +119,8 @@ class RelayOS(SmartcardOS):
# sendCommandAPDU() expects a list of APDU bytes # sendCommandAPDU() expects a list of APDU bytes
apdu = map(ord, msg) apdu = map(ord, msg)
apdu = self.mitm.handleInPDU(apdu)
try: try:
rapdu, sw1, sw2 = self.session.sendCommandAPDU(apdu) rapdu, sw1, sw2 = self.session.sendCommandAPDU(apdu)
except smartcard.Exceptions.CardConnectionException as e: except smartcard.Exceptions.CardConnectionException as e:
@@ -130,5 +135,7 @@ class RelayOS(SmartcardOS):
else: else:
rapdu = rapdu + [sw1, sw2] rapdu = rapdu + [sw1, sw2]
rapdu = self.mitm.handleOutPDU(rapdu)
# return the response APDU as string # return the response APDU as string
return "".join(map(chr, rapdu)) return "".join(map(chr, rapdu))

View File

@@ -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