Co-Founder of dissecto GmbH
Co-Founder of dissecto GmbH
Author of Scapy
[email protected]
Maintainers of Scapy
[email protected]
[email protected]
[email protected]
CAN frame as transferred on the bus. Author: Dr. Ken Tindell
ISO-TP fragmented communication example
ISO-TP frame types overview
We need to identify communication parameters:
If one parameter is wrong $\Rightarrow$ no communication
Advantages:
In this example, we use vcan0
interface.
>>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True}
>>> conf.contribs['CANSocket'] = {'use-python-can': False}
>>> load_contrib('cansocket')
>>> load_contrib('isotp')
>>> socks = isotp_scan(CANSocket("vcan0"), range(0x720, 0x7F0))
>>> socks
[<<ISOTPNativeSocket: read/write packets at a given CAN interface using CAN_ISOTP socket > at 0x7f25c963ab50>]
python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 0 --end 100
python3 -m scapy.tools.automotive.isotpscanner --interface vector --channel 0 \
--python-can_args 'bitrate=500000, poll_interval=1' \
--start 0 --end 100
>>> rfc(HSFZ)
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| LENGTH |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TYPE | SRC | DST |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
LENGTH
is payload length + SRC
and DST
TYPE
: 1 is message, 2 is echo/ack, 64 is errorSRC
: Address of TesterDST
: Address of target ECU$ sudo nmap -sT 192.168.17.151 -Pn -p 6801
Starting Nmap 7.70 ( https://nmap.org ) at 2021-10-05 13:18 CEST
Nmap scan report for 192.168.17.151
Host is up (0.00024s latency).
PORT STATE SERVICE
6801/tcp open acnet
Nmap done: 1 IP address (1 host up) scanned in 0.15 seconds
>>> load_contrib("automotive.bmw.hsfz")
>>> load_contrib("automotive.uds")
>>> pkts = list()
>>> for i in range(0x100):
...: with UDS_HSFZSocket(0xf4, i, "192.168.17.151") as sock:
...: pkts += [(i, sock.sr1(UDS()/UDS_DSC(b"\x03"), timeout=0.05))]
>>> [x for x in pkts if x[1] is not None]
[(16,
<UDS service=DiagnosticSessionControlPositiveResponse ...>),
(64,
<UDS service=DiagnosticSessionControlPositiveResponse ...>),
(223,
<UDS service=DiagnosticSessionControlPositiveResponse ...>),
(224,
<UDS service=DiagnosticSessionControlPositiveResponse ...>),
(240,
<UDS service=NegativeResponse |<UDS_NR ...|>>)]
$ sudo nmap -sS 192.168.17.75 -Pn -p 13400
Starting Nmap 7.70 ( https://nmap.org ) at 2021-10-05 15:06 CEST
Nmap scan report for audi_cgw3_ecu (192.168.17.75)
Host is up (0.00013s latency).
PORT STATE SERVICE
13400/tcp open doip-data
MAC Address: 00:1A:37:BF:EE:E4 (Lear)
Nmap done: 1 IP address (1 host up) scanned in 0.96 seconds
>>> resps = list()
>>> for i in range(0x10000):
>>> sock = DoIPSocket("192.168.17.137", activate_routing=False)
>>> resps.append(sock.sr1(
...: DoIP(payload_type=0x5, activation_type=0, source_address=i, reserved_oem=b""),
...: verbose=False, timeout=1))
>>> sock.close()
>>>
>>> print([x.logical_address_tester for x in resps if x.routing_activation_response])
>>> sock = UDS_DoIPSocket6("2001:16b8:3f0e:2f00:21a:37ff:febf:edb9")
Routing activation successful! Target address set to: 0x4010
>>> pkt = UDS() / UDS_RDBI(identifiers=[0x1000])
>>> resp = sock.sr1(pkt, timeout=1)
>>> resp.show()
###[ UDS ]###
service = ReadDataByIdentifierPositiveResponse
###[ ReadDataByIdentifierPositiveResponse ]###
dataIdentifier= 0x1000
###[ Raw ]###
load = '@Tr00pers22'
>>> sock = UDS_DoIPSocket("169.254.117.238")
Routing activation successful! Target address set to: 0x4010
>>> pkt = UDS() / UDS_RDBI(identifiers=[0x1000])
>>> resp = sock.sr1(pkt, timeout=1)
>>> resp.show()
###[ UDS ]###
service = ReadDataByIdentifierPositiveResponse
###[ ReadDataByIdentifierPositiveResponse ]###
dataIdentifier= 0x1000
###[ Raw ]###
load = '@Tr00pers22'
0x7DF
(0x18DB33F1
), response ID 0x7E8
(0x18DAF111
)>>> load_contrib("automotive.obd.obd")
>>> load_contrib("automotive.obd.scanner")
>>> load_contrib("isotp")
>>> socket = ISOTPSocket("can0", tx_id=0x7E8, rx_id=0x7DF, basecls=OBD)
>>> s = OBD_Scanner(socket)
>>> s.scan(timeout=10)
UDS | GMLAN | ||
---|---|---|---|
10h |
DiagnosticSessionControl | 10h |
InitiateDiagnosticOperation |
11h |
ECUReset | ||
27h |
SecurityAccess | 27h |
SecurityAccess |
28h |
CommunicationControl | 28h |
DisableNormalCommunication |
31h |
RoutineControl | ||
34h |
RequestDownload | ||
3Eh |
TesterPresent | 3Eh |
TesterPresent |
A5h |
ProgrammingMode |
diagnosticSessionTypes = {
0x00: 'ISOSAEReserved',
0x01: 'defaultSession',
0x02: 'programmingSession',
0x03: 'extendedDiagnosticSession',
0x04: 'safetySystemDiagnosticSession',
0x40: 'vehicleManufacturerSpecific_40', # proprietary
0x41: 'codingSession', # proprietary
0x42: 'SWTSession', # proprietary
0x43: 'HDDDownloadSession', # proprietary
0x7F: 'ISOSAEReserved'
}
Categories of security access functions:
Simple Arithmetic Operations[1, 2]
$$key = \neg seed$$
Mathematical Operations
$$key = (seed * secret1 + secret2) \bigoplus (seed * secret3 + secret4) \bigoplus secret5$$
... continued ...
Proprietary XOR-Shift-Loops[3]
Cryptographic Operations
$$key = RSA_{sign}(MD5(seed~|~salt), {private\_key})$$
UDS_RDBI.dataIdentifiers[0xf100] = "activeSessionState"
UDS_RDBI.dataIdentifiers[0x100A] = "EnergyMode"
UDS_RDBI.dataIdentifiers[0x100e] = "ExtendedMode"
UDS_RDBI.dataIdentifiers[0xf186] = "activeDiagnosticSession"
{
# diagnosticSessionType == 3
3: { # activeSessionState
0x00: 'Bootloader',
0x81: 'ExtendedSessionEnergyModeFlash',
0x82: 'ExtendedSessionDTCOff',
0x83: 'ExtendedSessionNDCDisabled',
0x84: 'ExtendedSessionFlashModeActivated',
0x85: 'ExtendedSessionFlashExit',
0x86: 'ExtendedSessionStarted'
}
}
A system state machine $M$ is a directed graph $(S, E, \Delta)$, with the following properties:
Packet
):TestCase
:Packet
) Executor
:@EcuState.extend_pkt_with_modifier(UDS_DSCPR)
def UDS_DSCPR_modify_ecu_state(self, req, state):
# type: (Packet, Packet, EcuState) -> None
state.session = self.diagnosticSessionType # type: ignore
@EcuState.extend_pkt_with_modifier(UDS_WDBIPR)
def UDS_WDBIPR_modify_ecu_state(self, req, state):
# type: (Packet, Packet, EcuState) -> None
if self.dataIdentifier == 0xf15a:
dh = hash(req.fingerprint)
state.fingerprint = str(dh)
class FingerprintTest(UDS_Enumerator, StateGeneratingServiceEnumerator):
_description = "Fingerprint supported"
def _get_initial_requests(self, **kwargs):
# type: (Any) -> Iterable[Packet]
return [UDS() / UDS_WDBI(dataIdentifier=0xf15a) /
bytes.fromhex("16 07 26 8f 04 d2 01 00 00 10 00 00 00")]
def get_transition_function_description(self, edge):
# type: (_Edge) -> str
return "Fingerprint"
with ISOTPNativeSocket(interface1, tx_id=0x6f1, rx_id=0x640, basecls=UDS) as sock:
tc = [UDS_DSCEnumerator,
UDS_TPEnumerator,
FingerprintTest]
s = UDS_Scanner(sock, reset_ecu_handler, test_cases=tc,
UDS_DSCEnumerator_kwargs={"scan_range": [1, 2, 3]})
s.scan(timeout=60)
Executor
(UDS_Scanner
) can take an abitrary list of test casesISOTPSockets
, UDS_DoIPSockets
or UDS_HSFZSockets
class FingerprintFuzzer(UDS_Enumerator, StateGeneratingServiceEnumerator):
_description = "Fingerprint-fuzzer"
def _get_initial_requests(self, **kwargs):
# type: (Any) -> Iterable[Packet]
return [UDS() / UDS_WDBI(dataIdentifier=0xf15a) / bytes(RandBin(13))]
def get_transition_function_description(self, edge):
# type: (_Edge) -> str
return "Fingerprint"
General Problem: Lack of information.
Example: after entering a new diagnostic session, what is the current level of security access?
Specific ECUs might report state information through RDBI
Hidden state
State aliasing
[1] Jürgen Dürrwang, Johannes Braun, Marcel Rumez, Reiner Kriesten, and Alexander Pretschner. Enhancement of Automotive Penetration Testing with Threat Analyses Results. SAE International Journal of Transportation Cybersecurity and Privacy, 1(2):91–112, 11 2018. doi:10.4271/11-01-02-0005.
[2] Yuefeng Du Sen Nie, Ling Liu. FREE-FALL: HACKING TESLA FROM WIRELESS TO CAN BUS. 2017. https://www.blackhat.com/docs/us-17/thursday/us-17-Nie-Free-Fall-Hacking-Tesla-From-Wireless-To-CAN-Bus-wp.pdf (accessed 2021-04-14).
[3] Jan Van den Herrewegen and Flavio D. Garcia. Beneath the Bonnet: A Breakdown of Diagnostic Security, pages 305–324. Volume 11098 of Lecture Notes in Computer Science. Springer International Publishing, 2018. URL: http://link.springer.com/10.1007/978-3-319-99073-6_15, doi:10.1007/978-3-319-99073-6_15.