Pentesting QNX Neutrino RTOS

One of the most important assets to an information security professional is knowledge. When conducting a penetration test of a system, often we test for known vulnerabilities. Testing for unknown flaws using techniques such as fuzzing can impact the stability of production systems, so penetration testers typically rely on existing security research to do their jobs because of time constraints. More obscure operating systems usually are less researched and thus their security is less understood. However, I find obscure systems interesting for this reason.

After reading HD Moore's presentation on hacking called “Shiny Old VXWorks Vulnerabilities,” I searched for other operating systems that might have similar vulnerabilities. I found an embedded real-time operating system, QNX Neutrino, which is owned by RIM and deployed in over 20 million vehicles worldwide. Examining QNX Neutrino, I came across a number of known security issues, mostly vulnerabilities “by design” and exploitable through user misconfigurations.

While reading the QNX operating system manuals and documentation, one QNX-specific utility caught my eye – a program called pdebug. Pdebug provides the ability to debug processes over serial and TCP/IP. According to the QNX security guide, pdebug typically runs as root and provides the ability to upload and download files, debug processes and execute files remotely without authentication. Note: pdebug doesn’t run by default; it must be launched by the system administrator or enabled in a build script in order to be running.

The first hurdle to overcome was finding a network signature for pdebug. Connecting with netcat to the service did not provide any meaningful output, and nmap had no service fingerprint for pdebug. However, I discovered another QNX-specific service, qconn, which when launched, automatically starts pdebug on TCP port 8000. Qconn prints the string “QCONN” upon receiving a connection, making it much simpler to fingerprint.

Running an nmap scan of a QNX lab machine shows the qconn service running on TCP port 8000:

In order to connect to qconn and abuse its features, we need a QNX version of GDB (requires website registration). QNX’s GDB allows remote debugging and, therefore, remote code execution on a QNX box through the capabilities provided by qconn. Once we have GDB access to the system, we have the ability to download and upload files. A BSD build of Netcat will do fine as a payload.
The screenshot below shows how to upload Netcat to the QNX operating system through the capabilities provided by qconn:

Once in the GDB prompt, we type “target qnx IP:port”. This command uses the QNX remote debugging protocol to connect GDB with the qconn service. As the screenshot shows, we are uploading Netcat (nc) from the local system and copying it into the remote /tmp directory.

After uploading Netcat to the target machine, we use GDB to run a Netcat listener. For some reason, I've experienced errors running Netcat immediately after uploading it – a workaround involves exiting and re-launching GDB, re-attaching the target and running the Netcat command again.

Once Netcat launches, we see the following:

Now it’s a simple matter of using Ncat/Netcat to connect to the target box, and because qconn was running as root in this case, we receive a root shell:

Other QNX Services

One of the more interesting aspects about QNX is that the root user has no password when the system is first installed. Although the FTP and rlogin services do not allow remote authentication without a password, the telnet service does. If inetd is run and the telnet service is launched, anyone can telnet to the system as root without a password.

QNX Post-Exploitation

Once we have a shell on the QNX machine, let’s take a look at what post-exploitation options we have available. Cracking QNX hashes is relatively simple; newer systems use the Unix crypt() function, with a maximum of 8 characters possible in the password. Any QNX password using crypt() is trivial to brute force using widely available tools such as John the Ripper. By default, users are allowed to have blank passwords (according to the environmental variable NOPASSWORDOK in the /etc/default/passwd file). Password complexity is an option but not required.

Older versions of QNX use a function called qnx_crypt(), which is completely reversible. If you’re lucky enough to encounter such an old QNX system, you can use this code to reverse any hash to its plain text format (the original C code on securityfocus was written to be anti-script kiddy, and did not print any output or include a main function, so the code on Github fixes these issues). Here is an example of what a qnx_crypt() entry in the /etc/shadow file would look like:

req:9HfX3F8XD8010:1337292496:0:0

To reverse the password hash, simply compile qnx_decrypt.c, and then run the binary with the hash (in bold above) as the first argument, revealing the plain text password “req”.

Another avenue of post-exploitation attack (which would require root privileges) would involve a password encryption downgrade. Newer QNX systems support the reversible qnx_crypt() for legacy support, so setting the “QNXCRYPT” variable in the file /etc/default/passwd will cause any new or modified passwords to be stored with reversible encryption.

Network Fun

So let’s say you’ve gotten root on a single machine; what about the rest of the machines in the network? If those machines are also running QNX and a proprietary protocol known as Qnet, you are in luck. As explained in the QNX security manual, Qnet allows you to treat remote machines as mounted devices on your own machine and execute code on them. In order to check if Qnet has been configured on the machine, run the following command:

This will show you if there are any machines listed under this directory. In this example, we see two machines, the localhost (EA31839e) and a remote machine connected via Qnet called bob-qnx. If Qnet is enabled, folders under the /net directory will automatically populate with each machine in the QNX network. The machine's hostname is the directory, and host's filesystem is accessible under this directory:

To view /etc/shadow on the remote machine (bob-qnx), assuming you have root on the local machine, you use the cat command, as expected:

$ cat /net/bob-qnx/etc/shadow

Qnet has no concept of remote authentication beyond the local user ID; remote systems are mounted as devices and therefore having local root access to one machine in a Qnet network gives you root access over all machines (nodes) in that network.

In addition to having read/write access to remote filesystems, it is possible to execute commands remotely without authentication. The following example shows how to run a Netcat listener using the on command. The -f argument specifies the name of the remote node that the command will be executed on. For this example, we are on node EA31839e, targeting node bob-qnx.

$ on –f bob-qnx /tmp/netcat –lvp 1337 –e /bin/sh

As you can see, we have Netcat listening on port 1337, ready to give us a shell.

Once a single misconfigured QNX host is compromised, the entire internal network can be taken over if it is running Qnet; this is “by design.”

After looking around to see if there were any obvious real-world applications from this research, I found this video interesting. It shows a Porsche running QNX Neutrino and several Blackberry Playbooks (which run a version of QNX) in the car, all connected via Qnet. Although I have not tested this car's security, I would assume that if a passenger can root their Playbook based on the design of Qnet, they would be able to gain root access over the other passenger's devices as well as the car's operating system/entertainment system. For now, this attack would probably be limited to passengers in the same vehicle. In the future, as more cars are connected to the Internet, this will become more serious. As QNX is developing their QNXCar platform, we can expect to see this type of technology deployed in many more vehicles, and vehicular security will become much more significant in the near future.

It looks like dd should work for memory dumping if you give it an interface such as /dev/mem. Another utility that should be able to dump memory is the QNX dumper utility, which will generate dumps that can be examined using GDB.