Hello, this is our talk from Blackbox to Automotive Ransomware. My name is Nius and I have Enrico with me. We are both PhD students on DOTH Regensburg and the University of West Bohemia. We are focusing on car hacking since 2016, I think, and today we're going to show how to reverse engineer a body controller and to run Automotive Ransomware. All right, in 2019 we gave a talk IoT backdoors in cars and basically this talk is volume two of it. In this talk we showed how to exploit an OBD GSM dongle over the air and how to obtain execution on the CAN bus. And basically during this talk we showed already how to flash stuff on a car. You can see it here in the picture. This is like a lock from the dongle where we get security access where we flash some payload and this was already our exploit but we couldn't show it at this time. So today we're gonna show the car part. So what we need to do to install a ransomware on the car itself. All right, so will Automotive Ransomware be a thing? We don't know but we made a proof of concept to show the difficulties and to see what you can do and what doesn't work. All right, so in order to make a ransomware we did some theory and thought about what we need for ransomware. We figured out some components. We also wrote a paper about that since we have to publish stuff sometimes. So details you can find in the paper but basically some of these components that we're going to show today are components for infection, for self-distribution, for persistency mechanism inside the car, for malicious activity. User injection is important and some how a revert mechanism that you can uninstall the ransomware after an infection. All right, today we're gonna show the initial infection through an OBD donor or a repressure tester. Both is possible and we chose OBD since it was the easiest way for us. The self-distribution inside the car is possible but it's not part of the proof of concept. But the most likeliest for this ransomware to be distributed would be through repressure testers basically. All right, the persistency mechanism is done by writing to the flesh of the ECU which was not trivial which we show later. And malicious activity was pretty easy for us. We could use repressure tester jobs but we also analyzed the immobilizer system of this ECU in order to make the car run or not run at all. And the user injection which is very important for the ransomware since you want to frighten the user. We used the infotainment system and sent some messages to the car. All right, let's do a plan to action. We want to get ransomware on a black box ECU. First thing we need to do is we need to open security access. After that we have to figure out a way to get code execution. We need to extract the firmware and reverse engineer it. After that we're going to patch the firmware and run some malicious code on it. And last but not least we intimidate the user. Okay, first OBD. OBD is easy for us. It's available in almost every car. Soon or later every car connects to repressure tester. So if your repressure or your repressure test is infected you're gonna get the exploit or you're gonna get the ransomware. OBD gives us a direct access to all the CAN buses and especially to the diagnostic services of all ECUs even if you have a gateway for security measures and stuff. If you plug in an OBD dongle or an OBD-GSM dongle then you can get remotely exploited and if one founds a remote vulnerability inside the telematic unit of this car all we're going to show can also be done remotely and can be done over the air basically. Okay, this is our target. It's a body controller. It acts as a gateway inside the CAN, a gateway for the CAN networks. So the car we attack has two CAN networks and it runs the immobilizer code and has a direct connection to OBD2. The processor is a UPD70F3558 from Renesas with a V852E2M instruction set and two megabytes of code flash. Also we have 144 kilobytes of RAM. We have two CAN transceivers, a single wire CAN and a high speed CAN. A high speed CAN is running with 500 kilobits per second and a single wire CAN has 33.3 kilobits and besides that we have many transistors and relays for body control stuff on that ECU. Okay, how to communicate with that ECU? This ECU uses GMLAN and this is a combination of ISOTP and normal CAN messages. It's very similar to UDS but there's some differences and the documentation for the GMLAN protocol is available. And finally we wrote a SCPI module for GMLAN and we also wrote an ISOTP module, a CAN module, a CCP, OBD2, a UDS, ENET and some IP for SCPI. So check this out if you like SCPI. We try to support as much automotive stuff as possible here. Okay, to get security access let's look into the documentation. It's quite easy. We have a sub function where you can specify to get a seat or to send a key. An even number is requesting a seat, an odd number is sending a key and then we have two bytes like a high and a low byte for the key. Again two bytes of key. This is 16 bits of entropy so not that much for this car. To get security access the tester has to send a request to get a seat. Then ECU sends back a 16-bit seat and the tester calculates the corresponding key in order to obtain security access. Here's an example how security access routine might look like. You have a secret which then gets shifted and moved around with some magic numbers and at the end you obtain the key. It's important to mention here that the key is constant so we get the same seat every time. And security access takes 10 seconds after reboot until we can ask for a seat and if we fuck up and the key is wrong security access is disabled until the next reboot. So we have to power cycle and we can try infinite times. Every ECU sends different but constant seats and the whole cracking took us roughly eight days so we had 10 to 10 times to the power of 16 possibilities for sorry seconds to test every possible key. This is our setup in our laboratory where we have a power supply connected to a car battery and a big relay here on the side next to the cat which is turning on and off the car every 10 seconds and the Raspberry Pi which is then brute forcing every possible seat key combination. All right after we have security access we can start with the enumeration of services to see what kind of attack surface we have on the ECU. Again we use SCEPI for this. We are pretty interested in the programming mode and after security access we can enter this programming mode. Here's a screenshot of SCEPI of the results from enumeration. On the left side you see all possible GMLAN services and on top you see different states where the ECU is in. Most important the state on the most right side the programming session with security access and request download entered. As you can see this one gives us a positive response on the request download job if we enter that. So this is a good sign for us and a very good attack surface. Let's look into request download and transfer data. How does that work? On this ECU we were unlucky. Read memory address was not supported so we could not just say give me an arbitrary address and give me a memory for it. So firmware extraction was not possible through that but request download was possible and transfer data as well. Another feature of transfer data is in this specification the download and execute sub-function. Let's look in the specification. Here we have the GMLAN transfer data function and the sub-function can be two different things download and download and execute or execute. Then you have to give a starting address and a data record. This is how we will basically transfer data to this ECU. And download and execute is really execute. Later we reverse-engineered the bootloader. This is an IDA picture of it. So here you can see the service 36 which is transfer data and the sub-function 80 which then gives us download and execute or execute. And if we enter here we have the four bytes to int which is the starting address. This function gives us the starting address back into R10 and then it's moved into R29 and after that we directly jump to R29. So code execution by design is not a big deal for us. To exploit that you can use scapy again. This is an exploit function. We have two sockets here for communicating with the vehicle and then we have a couple of gmlan functions for initiate diagnostic operations, getting security access and then finally transfer payload transfers our exploit payload to the entry we give it. And then after all this went through you can just send gmlan transfer data with the sub-function download and execute or execute and the starting address you want to jump to. That's it. So Enrico, how do we move forward from here? So first of all we want to show that we really have a code execution and for doing that the simplest way is downloading some garbage into the memory and trying to execute it. So for example if I just transfer a 000 which in this instruction set is a NOP, then the ECU will execute this NOP and then start executing whatever is after it in the memory which might be uninitialized. And in fact if we do this, if we call the previous scapy exploit function with 000 as data, the ECU will crash. Now if instead I terminate the function with 7f00 which in this instruction set is a jump to the link pointer which is how a return from a function is encoded, then the ECU will not crash because we are correctly returning to the caller. Now we were lucky in this case because the function that called our exploit call did not create some stack frame, so we did not have to pop to the stack frame and restore the registers for the caller. But in case that was the case, we would have needed to brute force all the possible shapes of the stack frame which is not really a big issue. But in this case we were lucky, so just sending out these four bytes will let the ECU execute this NOP and then jump back to the normal execution. And yeah, this shows that we can execute code on this ECU. Now we want to dump the firmware because we could not read it out with read memory by address. So we want to build a small exploit, a small software that dumps the firmware for us over KEM. We were able to find an IDE for the V850 family of processor for the V850 architecture, but no tool specific for our processor. We assume that the tools for our version of this processor are only available to automotive OEMs. So in the beginning for us it was easier to write small chunks of V850 assembly to dump the entire firmware over KEM. So this is an example of how we figured out how to send messages over KEM with our small assembly pieces of code. First of all, we have to set the register IP with the address of the KEM interface where we want to send the IP, where we want to send the KEM message from. In this case it is FCN3. We figured this out by reverse engineering the PCB and trying out all the different KEM interfaces. And then we just have to write 300 in hexadecimal to that address in order to let the KEM interface send whatever was in the buffer at that moment in time. Now doing this, the last KEM frame that the issue sent will be sent again. And by seeing the same KEM frame twice we knew that we were successful in sending something over with our assembly code. So the next step was sending some custom data over KEM. This is a bit more complicated because it requires unlocking the message buffer for writing it and then locking it again when sending the message. So this took quite a long time to blindly run code, reading the manual and figuring out how to do this. But in the end we figured it out. It's just a matter of writing 17 in hexadecimal in the same address as before. This unlocks the message buffer for writing. Now we can write the KEM ID. This needs to be multiplied by 4 for some reason. And we can write it. This is written to a different address in the same peripheral. Then we have to write the length of the message. For some reason this CPU has a bit... has a curve where different sizes of registers need to be written at different addresses in the memory map. So before we were writing half words, 16 bits at a time. And now we're writing something in a register which is a single byte. So we have to change the register, change the address of the memory map to register. But after reading the manual enough and figuring out what we were doing wrong, we were able to change the size of the sent KEM message finally, in this case to 4. And finally we can change the data by writing to this address right here. In this case I'm writing dead beef. I have to write it in Lithuanian, so A-D-D-E-E-F-B-E. Also here it was quite confusing the fact that the registers for the buffer are not contiguous. So the first two bytes are stored at offset 0 and the next two bytes are stored at offset 8 and so on. Finally we have to send the buffer that we prepared and this time I have to write 700 hexadecimal instead of 300. This was because I also had to lock the message and cause the buffer to refresh. Now all of these required a lot of effort and trying, but in the end we were able to send the custom messages. So at this point nothing stops us from dumping the entire firmware. So for example, if we want to dump address 1-2-3-4-0, what we have to do is prepare the address that we want to dump into register 2 for example, point the NTP pointer, which is another register, to the CAN interface like before and then read two bytes at a time from R2 and write them into the EP. And this way we were able to dump the entire firmware, one request at a time. So now we have the firmware and it's time for some reverse engineering. This work was made a few years ago and at that point in time there wasn't really a choice for tools to use for reverse engineering these architectures. First we tried with AIDA. We actually have a license at our university, but our license is only for a very old version and it did not support the V850-E2 instruction set. And a new license is super expensive. We tried RADAR, same thing, didn't support the V850-E2 extension of the instruction set. And finally JITRA didn't exist yet, well it wasn't published yet, but when it was published it also didn't support the V850-E2 architecture. So at this point we were really sad because we could not reverse engineer the firmware, we did not have the tools. However you don't need to be sad anymore because recently JITRA started supporting this instruction set and this extension thanks to this pull request here from Alex Lasso, which is one of our students. And he just finished his bachelor writing this plugin as his thesis. So what I ended up doing at the moment for getting to work fast was writing an AIDA plugin for V850-E2 processors. I started from this already existing plugin called Necromancer which supported some instructions but was incomplete. And then I wrote a small parser that from the PDF manual generated the Python arrays, Python code for the AIDA plugin. So this is the manual, this is how an instruction is encoded. There are a bunch of static constant zeros and one and some bits which are representing the registers that this instruction can access and these are variable. And yeah this parser will parse this PDF and output these tables, these arrays which then could be fed into the plugin for AIDA. And this is a screenshot showing it working. So all the instructions that are uppercase here were disassembled from the plugin and they were not already existing in AIDA. They were not supported by AIDA. Now that we can reverse engineer the firmware, we should look at how it is structured. There is a bootloader in the beginning which is 32 kilobytes. This bootloader contains the code that is executing during the programming mode. So when we switch to programming mode with security access, this is what is getting executed. After that there is the firmware, the program for the rest of the application on this issue. This is 1.3 megabytes and this contains everything that runs while you're driving the car. Then there is around 700 kilobytes of empty program flash. So this is great for us because this is where we can write our persistent malware. And finally there is a few kilobytes of configuration. So we have a lot of space to write a persistent malware. So let's try doing that. Let's see how difficult this is. Of course we cannot write simply with the security access and transfer data because those can only write to a very specific portion of the RAM. There is no code in the entire firmware that performs writes to the flashing. So it is clear that for flashing, for updates on this issue, they were relying on the execution of external data from the RAM, this programming mode. And also the reference manual for the processor does not give any information about how to write to the flash and it only says that you should be using this FSL library. So we managed to find this library, download it, and again it didn't work for our specific V850 processor, but we found one that worked for a very similar one. And it's a binary. It's already compiled. There is no source code. There is some example source code, but the examples use the compiled library. So we had to reverse engineer this. And what we could gather from reverse engineering is that first, the flashing code can only run from RAM because the entire flash, which normally contains the program, is disabled during the write and erase operations. So also when we disable the write protection on the flash, the entire address space of the flash gets replaced by some other code, which was confusing, but it looks like some kind of internal ROM or maybe a BIOS. And write and erase operations are simply executed by preparing the command and the parameters like addresses and the new data into registers from R7 to R10, and then jumping to a very specific address here, FEDFFFF0, which performs the flashing operation for us. Of course, the first flash writes were very scary. So I first started writing... as I said, it's scary. So I first started writing on empty places in the flash. And this is just to verify that the flashing was working. And after a few attempts, it was able to write the flash. And then the next step was patching the bootloader in a way that we didn't have to wait 10 seconds before switching to the programming mode. We could do it instantly. So yeah, I patched the bootloader in a way that the issue would always start in the bootloader, so in the programming mode. So now the requirement for writing a complex ransomware here. First, we don't want to write the entire thing in assembly. We imagine we have to write a lot of code, so we want to write it in C. Secondly, we don't want to break the functionality of the body control module, because it's an essential component when you're driving the car. And because of this, it will be very practical to have code running in your own thread. So a thread is just for us. But this issue did not implement threads at all. This issue just has a single program cooperating with the tasking, not even parallel tasks. One function executes after the other one. Other problems. There is a hardware watchdog and a software watchdog. Both are going to reset the CPU when it doesn't respond in time. And this is another reason why we need to write a parallel thread with a yield function, because otherwise we cannot run a long-running program. And also, we did not have the linker. We had a linker that supported a very similar processor, and we could only compile C that ran from RAM, basically. No flash. We could only run our C code from RAM. And finally, another issue. There are these two registers, R5 and GP, which get set statically to a constant value by the compiler. And then for doing the whole execution of the program, they will preserve the same value. And this value is going to be different depending on what the compiler decides. So when executing our code, it was necessary to preserve the original values of these two registers and then restore them back before passing control back to the original code of this VCU. And this makes return-oriented programming very hard, of course, because we cannot just jump to already existing code to let it do our stuff. We have to be careful about these two registers. So let's look at how we ended up implementing our exploits, given all of these limitations. First, we used transfer data to load our code into the empty portion of the RAM, where we can execute it from. And then we execute it, and this one will patch some exploit into the empty flash, which is this large part of flash which is not used, 700 kilobytes. Then we also patched two interrupt service routines. So the interrupt handlers for both a timer, which gets called periodically, where we will execute our thread, and the interrupt service routine for canReceive, so that we can receive can frames for the rest of the car. And if we are interested in them, save them in a message box in a buffer for our thread to pick them up later. After this patching occurs, during the first execution of the timer ISR, our patch will jump to our code in the empty part of the flash, which is now not empty anymore. And this code is written in assembly, because we cannot compile code to the flash. And what this piece of code does, it just copies the rest of the code from flash to the empty position in the RAM, where code normally is downloaded. And then, after the code is copied there, on the next time the timer, interrupt service routine, or canRoutine are called, we are jumping to the RAM exploit, so the code we have placed in the RAM. Also, we have a small signature at the beginning of our RAM exploit to verify that we prepared the exploit there, and that it didn't crash last time. So, we put some safety measures there to ensure that we didn't get locked out of our only ECU. We only had one ECU to do all of this. And finally, to inject the parallel process, this thread, we changed the timer interrupt, as I said before, and then implemented a small yield function, which saves the entire state of the execution of our exploit. Basically, in its own stack, we have a stack for our thread, and this allows us to do stuff like yield or sleep in between our program, and it works like an Arduino where we can keep running the rest of the code of the car and make sure the watchdog doesn't kick us out. So, this is all regarding patching of the firmware. Now, Nils will explain the features that we implemented in our ransomware. So, now after we have basically our own thread in an unknown operating system where we can inject our code. So, all this portion that we showed, which lives in RAM, has all code written in C, and we can write our ransomware completely in C, we have a yield and a time or sleep function in order to wait for certain events, and we can also receive CAN messages, as Enrico said, through the CAN interrupt service routine. We can store incoming CAN messages into our own ring buffer in our ransomware and can then give this message back to the normal operating system. And this is very handy for us since the car acts completely normal. We can still drive the car, we can do everything with the car, and have our ransomware running independently. Okay, to implement these features, we need a couple of things. So, we want still to interact with the user. We wanted to lock and unlock the immobilizer. So, we implemented a way that we can start the car without key, and we can also not start the car with key, basically, in order to really put pressure onto our ransomware. And then we also need to send ICTP messages to make the car behave badly, in order to scare the driver. And we want to use some extracted rehearsal tester jobs to run these actions, basically. Okay, talking to the user. This is very important for the ransomware, because if you cannot tell someone that the car is owned by the ransomware, no one will pay. But yeah, how can we do this? The multimedia ECU of this car has a screen connected. The radio has its screen, and also this car has a Wi-Fi hotspot. So, the car owner can open up a hotspot and connect with its smartphone to the Wi-Fi hotspot. In order to connect the access points from the car, the car driver needs login credentials. And these login credentials need to be transferred from the telematic ECU to the multimedia ECU. And obviously, they get transferred over CAN. And luckily, over the same CAN as our body control module is connected to. So, the module that we own now is connected on the same CAN bus, where also the multimedia and telematic ECU do their job and their message exchange. Here is an example of some SCEPI code, where we analyzed the ICT communication. We see a first frame here, some flow control, and then some data. And if you defragment all of this data, you can see some strings here. So, there's... it's Italian, since we put the car to Italian language. You can see here Imposizioni Wi-Fi and Anula, which means Wi-Fi settings and abort. And these are basically the text fields for the button on the screen in the car. And we can send our own text strings here in order to show any message we want. Okay, so how does this look like? So, here we see the car and it takes roughly 30 seconds to start. We can let you run, the expert basically run on every event. You can wait for a year after the car got infected. You can wait until the car does more than 100 kilometers per hour, whatever. So, it's completely free. So, and this time, here the ransomware starts. We can see the screen, we can have the buttons, and can talk to the user. Here we are playing around with the dashboard to behave badly, basically. We disable GPS. We also spin up the engine van. So, you will hear it now. And we attack again the dashboard to do some stuff. Let some LEDs blink. So, as soon as you want to pay, the expert is over. All right, this was the live demonstration. We also investigated if you can spread the infection to other ECUs. So, during our time that we worked on this car, we investigated the EPIC ECU. We investigated the telematic ECU and the engine control unit. And security access and code execution on the EPIC ECU were trivial. So, theoretically, it would be very likely and possible that you can infect also other ECUs in that car and that this one ransomware spreads around over all the ECUs in the car. There are some limitations since to do this, basically, our ransomware would be prepared with expert codes for all the individual ECUs. And this is also one of the things which is still, in our opinion, the most limiting factor for writing malware or ransomware for cars nowadays. So, lessons learned from here. In this car, we didn't have any cryptographic challenges and the flashing of the body control unit was easy. The most problems we had were by the obscurity. So, the flashing library was locked down only for the OEMs. So, only if you sign NDAs, you get these tools, you get the hardware. The hardware was proprietary. We didn't got any compiler for this processor. So, this is really limiting factor. And also, in total for malware, the most limiting factor is the heterogonous systems in the car. So, that every ECU has a different chip set, has different instruction set, has different bootloaders, of course, which need to be exploited in a different way. And this is, yeah, in our opinion, the most secure, in fact, at the moment. Yeah, and this, as I said, the problems are common for all ECUs. In the future, we expect more Linux in cars and less obscurity and more homogeneous systems and platforms and probably more server-based. So, yeah, the architecture will probably completely change. Some facts about our proof of concept. This proof of concept was released on a car in 2016. We was made for a car released in 2016. We think almost all cars with a lower price range will have similar bad security at the moment, or let's say a little bit older cars also, you can affect even a not connected car as soon as it connects to the repair shop tester and this repair shop tester has a malware, then you have it. And these cars will still drive around for 5 to 10 more years. So, for example, in Germany, the average age is 9.5 years and all cars will frequently connect to a repair shop tester. So, this is a very easy way for infecting this insecure cars. All right, yeah, you can follow us on Twitter if you like and thanks for listening. Thanks for listening.