Few days ago I had a small talk (on our local Defcon group meeting - DC#7812) regarding CAN (Controller Area Network) and ECU analysis with help of CANToolz framework. Therefore, here I would like to repeat some of the ideas from that talk, give some explanations about "Why I have created Yet Another CAN Hacking Tool" and what are my goals.
Here you can find original slides -
Automotive Security is an extremely hot-topic now, and that is why I am interested in this field and very lucky to be a part of this automotive industry. Actually, this topic is HUGE: many of the technologies, big attack surface and blah blah blah….
However, security of vehicle's local network has been the main topic for last few years. Yes, I am talking about CAN bus and this is a good moment to remind an awesome research done by Charlie Miller and Chris Valasek: http://illmatics.com/car_hacking.pdf. I am not going to talk about CAN security in general since this is well-known thing (but highly important!).
When I have started my own “actions in this field”, I met one big issue - lack of tools that can help me to do what I want. Actually, in the Internet you can find many different tools for working with CAN bus. These tools are quite good and helpful, but if you want to use them "together", perform a MitM or make something more, then you have to "customize" them. It is good if you have one car... but what is if you have more targets to test?
Finally, I came into a conclusion that I need to have one common framework where I can work with CAN bus and ECU devices together with minimum "code-writings". If you are familiar with such kind of tools like BurpSuite or MetaSploit then you will understand my words. I wanted such kind of tool as it is going to make my work easier in case if I will share results with someone else or work in a team. Thus, my aim was to have module-based framework that can perform MitM (or work with more than one bus), will be hardware independent and have one-standard interface and even GUI. One more important thing I wanted to have is an open-source project – when all people together will have access to more vehicles than bunch of researchers. I think, together we can create more useful modules that can be used by testers, vendors (in an ideal world) and enthusiasts all over the world.
With help of our DC#7812 community (thanks to Sergey Kononenko and Boris Ryutin), I have created beta version of such framework. It is based on Python 2.7 (Python 3 not supported yet). To work with hardware you need to (pip) install pyserial. Right now, I have added support for two CAN hardware modules that will allow to access CAN bus via USB: USBtin and CANBus Triple. In future (and, I hope, with help of contributors) we will add support for another popular hardware.
Below, I am going to repeat some of the examples of CANToolz usage from my DC#7812 talk and will represent "typical work process on CAN bus testing" and demonstrate how to use this app.
Example 1: MitM with blocking
When you are "sniffing" CAN traffic, you do not know an exact source and destination of CAN Frame (at the beginning, in general). This is where MiTM can help you to find a direction of the traffic, but just sniffing - not.
Let’s say we are trying to understand which of the CAN frames comes from CAN Gateway to HeadUnit (HU) and which is coming from HU to CAN Gateway. If you will, for instance, "cut" CAN bus in between and connect your hardware than you are going to have TWO interfaces - one from HU and another from CAN gateway. This already will tell you more about frames that you already have (again, we are talking about blackbox analysis).
In addition, now we can handle those CAN frames before re-sending (we can use MitM), so we are able to do proxy-fuzzing on the fly, or blocking some CAN frames. Also, we can use blocking, to understand which frames are responsible for what.
Let's take "door-lock" example, from a great book - The new Car Hacker's Handbook (2016). Now we can block some frames "on the fly" (by frame arbitration field) and see if the door is unlocked after we did an action from HU control (if this is applicable for your car). Then we can block another frames, etc.
Then we can see anomalies and different behavior of the car target and its elements that could tell us more about blocked/fuzzed frames! If you want to perform this action in CANToolz, you just create a configuration file (but be careful with your car when you "cut" in between):
In that part I would like to say few words about config file:
First section is load modules – you have to describe which of the CANToolz modules you need. If you need the same module but in different instance, you can load it twice by adding "~1" or "~2" at the end of the name. A full list of modules you can find in ./modules folder
Second section is action - describe steps and on each step you tell which of the modules you want to use and what parameters should be on this step. The most important parameter is "pipe" (value 1 by default) and it is all about general design of CANToolz. So each module handle one CAN Frame and each module have INPUT and OUTPUT. The module take input (one CAN Frame) from pipe, do something with it and then PUT it back. For instance, we have two pipes - let me just read this config:
- On the step one, READ one CAN frame form FIRST USBtin device and put it into PIPE 1.
- On the step two, check if current CAN Frame from PIPE 1 in the white list. If not, then block it (remove CAN Frame from PIPE 1)
- On the step three, READ one CAN frame form SECOND USBtin device and put it into PIPE 2.
- On the step four, count and save CAN Frame for statistic (in PIPE1)
- On the step five, count and save CAN Frame for statistic (in PIPE2) // here we have the same module on the both PIPES, so in general we can read steps 4 and 5 like: count and save all CAN Frames from both pipes. (but if CAN Fame was blocked on the step 2, then we will not see those frames)
- On the step six, write CAN Frame from PIPE 2 via FIRST USBtin device.
- On the step seven, write CAN Frame from PIPE 1 via Second USBtin device.
It may seem difficult, but, in general, this is able to make your work easier, because you will "assemble" different modules with a logic... and here it is - simple MiTM, where you will block all messages from the FIRST device, except those that have arbitration ID 1337.
Once you have config done, run cantoolz.py -g w -c mitm_config.py
Then you will see the same config but in GUI (http://localhost:4444)
In Actions and steps, we can see pipes (as two columns), and modules, by clicking on the module we can edit parameters and send commands. Output generated to send command to mod_stat.
Example 2: CAN Gateway scanner
Another test scenario will be about CAN Gateway.Let's say we want to test our CAN Gateway and find CAN Frames that are passing from OBD2 bus to HeadUnit/IVI. First, we will connect CANToolz to both buses (OBD2 and IVI) and then send CAN frames from ID 1 to ID 2000 (maximum value is 2047 of Arbitration ID for 11bit, but you can use extended format with 29 bits). After sending, we can check which of the packets we are able to read on another bus and, finally, we can say which of the frames will pass from OBD2 to HU and from HU to OBD2.
Here we just switch to gen_ping and activate this module (by default it was not active). Then this module will generate 2000 frames with different IDs, but with same data field:"\x01\x02\x03\x04\x05\x06\x07\x08". Then USBtin devices (on both pipes) will get some of them that are not blocked by CAN Gateway. mod_firewall clean pipes from all other frames, thus we are interested only in generated and passed CAN gateway. Filtration here done by data field. Finally, just switch to mod_stat and see which of the frames have not been filtered:
Example 3: Replay traffic
This example is taken from Car Hacker's Handbook. Here you will find answers on how to find CAN frames that are used for door-unlock. This is going to be simple: sniff traffic when you doing unlock, then replay this traffic. Remember that inside the traffic there are a lot of other frames. If all is Ok, replay half of this traffic, if event happen, then our frames in that half, if not then in another. Continue this "binary search" until you find needed frames.Let's repeat this test scenario in CANToolz:
After all, stop sniffing mode and see the amount of gathered frames. If you want to replay those frames, you have to switch module gen_replay to second PIPE...
Then let's start binary search through replaying!
Other example: Scan, ISO-TP, UDS...
Another interesting topic is finding UDS modes, CAN "commands" and features that are not inside the traffic (so you can't sniff it, at least so easy...). Moreover, I should say that fuzzing is interesting thing - maybe not for "bugs", but for understanding what "bytes" mean in data section. Anyway fuzzing without some information about CAN frame can be dangerous. Do not do mass fuzzing for random ID and etc.
Now, you see that we have gen_ping for generating packets, gen_fuzz for fuzzing DATA - both modules are supporting ISO-TP format and gen_ping supports UDS. mod_stat that also have "traffic analysis" feature trying to detect and rebuild ISOTP and UDS.
Finally, it could look like that:
When gen_ping have finished its work, you will have some traffic: generated requests and some responses...
For analyzing CAN network you will need general modules and test-scenarios. The most interesting thing will happen after you finish this analysis. There you will find some frames, understand traffic, commands, etc.; and after that you have to build module that will reproduce results of your research (or you will need to add another general module for more tricks).
Anyway, let me describe how easily you could do it. Imagine, I have found some CAN frames that control brightness level and IDLE status of dashboard display and now I want to add these controls into a CANToolz module.
Message 06F#01XX000000000000 - change brightness level to XX
Message 06F#0200000000000000 - IDLE mode off
Message 06F#0201000000000000 - IDLE mode on
Next code will represent those controls as a module:
- from libs.module import *
- from libs.can import *
- import copy
- class mod_panel_control(CANModule):
- name = "Panel control module"
- help = """
- This module change dashboard panel things.
- Init parameters: None Module
- parameters: 'pipe' - integer, 1 or 2 - from which pipe to print, default 1
- def do_init(self, params):
- self._active = True
- self._cmdList['1'] = ["Idle mode on", 0, "", self.turn_off]
- self._cmdList['0'] = ["Idle mode off", 0, "", self.turn_on]
- self._cmdList['b'] = ["Change brightness level (0 - 255)", 1, "<level>", self.change_level] self._frame = None
- def do_effect(self, can_msg, args):
- if self._frame:
- can_msg.CANFrame = copy.deepcopy(self._frame)
- can_msg.CANData = True
- self._frame = None
- return can_msg
- def turn_off(self):
- self._frame = CANMessage(111,8,[2,0,0,0,0,0,0,0], False, CANMessage.DataFrame)
- def turn_on(self):
- self._frame = CANMessage(111,8,[2,1,0,0,0,0,0,0], False, CANMessage.DataFrame)
- def change_level(self, level):
- b_level = int(level) if 0 < b_level < 256:
- self._frame = CANMessage(111,8,[1,b_level,0,0,0,0,0,0], False, CANMessage.DataFrame)
This method will be called when the module is loaded, and it will take unit parameters from a load_module section. In this step, we are initializing commands for control and we have just three commands: idele of, on and change brightness.
It is the main method - will be called in the loop. Input parameter can_msg is the message from PIPE. The module should return the message back after handling, because our module is not about reading messages - we do not care about input, but we will generate new messages (command to dashboard). The same way works in all gen_* modules. It is mean that this module can "rewrite" last message from PIPE, so you do not need to put those modules after some other modules in the same PIPE.
In this step, you need to check if we have messages to send (self._frame) and then put it into PIPE.
Other functions are commands that will be called by user request and that are important to set-up message to send (self._frame).
A simple config for this setup will be:
Other methods you can get from other modules or by review ./libs/module.py
GUI, where you can easily perform these actions:
GUI, where you can easily perform these actions:
Above, I have tried to describe why I started to do this and why it is important, and I hope that some of you will find this tool useful and will contribute to it.The main idea here is to give a chance for more people have an access to vehicles and hardware that we can build modules for specific things and we can create more test scenarios and modules for BlackBox testing. In addition, OEM developers and vendors can use it for tests verification or something like that (if it suits). I don’t know... so let's see together if it will fly or not 8)