Scapy utilities#
Overview#
ISOTP(Packet)
: Scapy Packet object with fragmentation and defragmentation supportISOTPHeader
andISOTPHeaderEA
: Packet classes for parsing CAN-trafficISOTPMessageBuilder
: Helper class for defragmentation of CAN traffic into ISOTP packetsISOTPSession
: Scapy Session class to create ISOTP packets on the fly during asniff()
ISOTP(Packet)#
Create an ISOTP packet
from scapy.all import *
from scapy.contrib.isotp import *
p = ISOTP(b"deadbeef", tx_id=0x123, rx_id=0x321)
p.show()
###[ ISOTP ]###
data = 'deadbeef'
Fragment ISOTP packet into CAN frames
frames = p.fragment()
frames
[<CAN identifier=0x321 data='\x10\x08deadbe' |>,
<CAN identifier=0x321 data='!ef' |>]
Analyze ISOTP frame
ISOTPHeader(bytes(frames[0])).show()
###[ ISOTPHeader ]###
flags =
identifier= 0x321
length = 8
reserved = 0
###[ ISOTPFirstFrame ]###
type = first
message_size= 8
data = 'deadbe'
Analyze ISOTP frame
ISOTPHeader(bytes(frames[1])).show()
###[ ISOTPHeader ]###
flags =
identifier= 0x321
length = 3
reserved = 0
###[ ISOTPConsecutiveFrame ]###
type = consecutive
index = 1
data = 'ef'
Defragment CAN frames into ISOTP packet
print(frames)
ISOTP.defragment(frames)
[<CAN identifier=0x321 data='\x10\x08deadbe' |>, <CAN identifier=0x321 data='!ef' |>]
<ISOTP data='deadbeef' |>
ISOTPMessageBuilder#
Create ISOTP packet
print(frames)
builder = ISOTPMessageBuilder()
builder.feed(frames)
print(len(builder))
isotp_msg = builder.pop()
print(repr(isotp_msg))
print(len(builder))
[<CAN identifier=0x321 data='\x10\x08deadbe' |>, <CAN identifier=0x321 data='!ef' |>]
1
<ISOTP data='deadbeef' |>
0
ISOTPSession#
Create CAN packets
frames = ISOTP(b"This is a long ISOTP message", tx_id=0x123).fragment()
frames += ISOTP(b"short", tx_id=0x123).fragment()
frames += ISOTP(b"And a long message again", tx_id=0x45).fragment()
for f in frames:
print(repr(f))
<CAN identifier=None data='\x10\x1cThis i' |>
<CAN identifier=None data='!s a lon' |>
<CAN identifier=None data='"g ISOTP' |>
<CAN identifier=None data='# messag' |>
<CAN identifier=None data='$e' |>
<CAN identifier=None data='\x05short' |>
<CAN identifier=None data='\x10\x18And a ' |>
<CAN identifier=None data='!long me' |>
<CAN identifier=None data='"ssage a' |>
<CAN identifier=None data='#gain' |>
Serialize CAN packets to pcap
wrpcap("/tmp/can.pcap", frames)
!ls -lah /tmp/can.pcap
-rw-r--r-- 1 root root 333 Mar 23 06:28 /tmp/can.pcap
Use PcapReader as socket and extract ISOTP messages from CAN frames
with PcapReader("/tmp/can.pcap") as sock:
msgs = sniff(session=ISOTPSession,
session_kwargs={"use_ext_addr":False,
"basecls":ISOTP},
count=3, opened_socket=sock)
print(msgs)
print(repr(msgs[0]))
print(repr(msgs[1]))
print(repr(msgs[2]))
<Sniffed: TCP:0 UDP:0 ICMP:0 Other:3>
<ISOTP data='This is a long ISOTP message' |>
<ISOTP data='short' |>
<ISOTP data='And a long message again' |>
ISOTP MITM attack#
Communication overview#
LaTeX terminated with signal -1
This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020/Debian) (preloaded format=pdflatex 2022.11.9) 23 MAR 2023 06:28
entering extended mode
restricted \write18 enabled.
%&-line parsing enabled.
**tikz.tex
(./tikz.tex
LaTeX2e <2020-10-01> patch level 4
L3 programming layer <2021-01-09> xparse <2020-03-03>
! LaTeX Error: File `standalone.cls' not found.
Type X to quit or <RETURN> to proceed,
or enter new name. (Default extension: cls)
Enter file name:
! Emergency stop.
<read *>
l.3 \usepackage
[]{tikz}^^M
*** (cannot \read from terminal in nonstop modes)
Here is how much of TeX's memory you used:
21 strings out of 481252
440 string characters out of 5915888
266126 words of memory out of 5000000
17059 multiletter control sequences out of 15000+600000
403430 words of font info for 27 fonts, out of 8000000 for 9000
14 hyphenation exceptions out of 8191
32i,0n,39p,105b,13s stack positions out of 5000i,500n,10000p,200000b,80000s
! ==> Fatal error occurred, no output PDF file produced!
Scapy example#
In this example, we use vcan0 and vcan1 as interfaces. Source and Destination communicate over the ISOTP addresses 0x241 and 0x641.
Setup two CAN interfaces
sudo modprobe vcan sudo ip link add name vcan0 type vcan sudo ip link add name vcan1 type vcan sudo ip link set dev vcan0 up sudo ip link set dev vcan1 up
Import necessary modules in Scapy
import threading load_contrib('cansocket') conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} load_contrib('isotp')
Create a forward function. Attack code need to be placed here.
def forwarding(pkt): pkt.data = b"ATTACKED" return pkt
Create attacker sockets and run MITM attack
bSocket0 = ISOTPSocket('vcan0', tx_id=0x641, rx_id=0x241) bSocket1 = ISOTPSocket('vcan1', tx_id=0x241, rx_id=0x641) bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=1)