Finding RCE in Opensource (RaspAP)

shellster
Nerd For Tech
Published in
7 min readDec 31, 2023

I recently discovered RaspAP. It’s a rather handy little router solution that runs on a Raspberry Pi or Armbian based board. Unlike other solutions like OpenWRT, you get a modern Linux kernal and easy access to VPN solutions like Wireguard.

Before using the product, I figured I would do a brief security review. Unfortunately, I quickly found that the code base to be PHP of somewhat dubious security quality (in my opinion). Even worse, I was able to put together a chain of exploits to gain full remote code execution from an unauthenticated starting point given certain reasonable conditions.

I responsibly disclosed these findings to the developers and to their credit, they did patch the bulk of the major issues within 12 hours. However, they also claimed that they were aware of everything I had found from an internal audit “weeks ago”. I think this is frankly bullshit, as they did not patch the vulnerabilities until about 12 hours after I reported them even though they have lingered in the code base for at least a month. You can be the judge on whether you buy that story or not.

Here’s the patched release (3.0.5): https://github.com/RaspAP/raspap-webgui/releases/tag/3.0.5

Here’s the relevant patches for the major things I found (though note that this “fix” still allows an unauthenticated attacker to read the debug log): https://github.com/RaspAP/raspap-webgui/pull/1491

The vulnerabilities I discovered were exemplary of what you might find in a webapp hack-the-flag type contest, so I want to walk through the findings in this blog post as I think it might be instructive for others. I think vulnerabilities of this severity should be publicly disclosed and clearly announced by projects when they are discovered as users need to be aware that they need to update.

Abusing an unathenticated arbitrary file read

The first vulnerability was an unauthenticated, arbitrary file read via path traversal. The vulnerability lay in the /ajax/system/sys_get_logfile.php. For reasons that are unclear to me, this script allowed passing a “filePath” parameter but was only ever intended for reading the /tmp/raspap_debug.log file. Here was the entire content of this script:

As you can see, the script attempts to prevent arbitrary file reads by checking that the passed in path contains the expected debug log. This is trivially bypassed via path traversal:

curl -D- http://127.0.0.1/ajax/system/sys_get_logfile.php?filePath=/tmp/raspap_debug.log./../../etc/raspap/raspap.auth

This file also does not enforce authentication, so anyone can access this page if they are on the same network as a RaspAP box. We are restricted to reading files that the www-data user can access. There are many useful things we can read though. In the example above I’m reading the file that contains the admin credentials for the webui. The password is hashed using Blowfish Crypt, but this is very crackable if the password isn’t too complex.

In addition, I discovered that RaspAP has a bad habit of writing lots of sensitive files to /tmp with predictable names. For instance, if a user sets or changes the WiFi password in RaspAP, the resulting hostapd config will get written to /tmp/hostapddata this file will be owned by www-data and will not get cleaned up until a successful reboot (assuming the OS clears /tmp on shutdown). Therefore it is quite likely that an attacker will be able to retrieve the WiFi credentials in plaintext with the above attack as routers are often not rebooted for long periods of time (if ever).

Remote Code Execution

After the above finding, I theoretically had an attack that would net an unauthenticated attacker with credentialed access the authenticated functions of the site. I next began looking for vulnerabilities inside the code base. The code is littered with “exec”, “shell_exec”, and “system” calls where the commands are interpolating strings. The use of all three of these calls and how they are used is a bit idiosyncratic which suggests to me that the codebase is the work of multiple developers over a period of time. These types of calls maybe unavoidable in this scenario, but this functionality is always potentially dangerous. PHP is particularly bad for these type of vulnerabilities because exec is extremely lenient. It allows running multiple commands, and a command failure will not prevent further commands in the sequence from executing.

The the developers’ credit, most pages have attempted to mitigate these types of vulnerabilities by either validating the inputs to be valid interface names, using restrictive regex, or (the far better solution) of “escapeshellcmd” and “escapeshellarg”. However, in my opinon, RaspAP lacks a cohesive solution and/or discipline with escaping these commands.

I spent several hours looking for a place where attacker controllable data was interleved into command execution. I finally found something workable in /ajax/networking/save_net_dev_config.php. This file contains code which is supposed to allow configuring a cell modem. Unfortunately for us, and fortunately for most RaspAP users, the following vulnerability will not be possible if the cell modem functionality has not been configured, or /etc/wvdial.conf does not exist. I’ll explain shortly.

First let’s take a look at the vulnerably code (truncated to remove irrelevant stuff):

The vulnerable code can be found on lines 27–30. As you can see four different variables are injected into commands without any escaping. All four of these variables come from POST fields that an attacker can obviously control. Unfortunately to get to the vulnerable code there is a “file_exists” check on line 26. This checks for /etc/wvdial.conf to exist. By default this file will not exist unless a cell modem is configured. This functionality will create the file for us, which would allow us to call the vulnerable code once to create the file, then once to gain code execution. This is unfortunately (for us) still not possible if the cell modem functionality was not enabled because RASPI_MOBILEDATA_CONFIG is not configured by default, otherwise, thus causing the file write on line 32 to fail.

However, assuming the above pre-conditions are met, it is rather trivial to get code execution. Here’s a very rough proof-of-concept exploit that I threw together:

You might be tempted to assume that we could simply use sudo to gain root execution immediately. This is not the case as RaspAP developers have attempted to limit root access to a finite list of constrained commands. Therefore a little more work is required. In my payload, we take advantage of the face that www-data has write access in the /etc/raspap directory, and there are a couple scripts such as /etc/raspap/adblock/update_blocklist.sh which we are allowed to run via sudo. A list of commands that we are allowed to run as sudo can be found here: https://github.com/RaspAP/raspap-webgui/blob/320379e449729c6e4f3805f70bbe529bfc103962/installers/raspap.sudoers

There are several escapes possible by reviewing the sudoers file. Though the above example is one of the easiest and most reliable, there’s another set of gadgets that is possible as we are able to create and set a dnsmasq.conffile and restart that service. DNSMASQ allows running scripts (as root) whenever a DHCP lease is created or destroyed. This would be another, albeit, painful way of achieving code execution as root. This one would be difficult to fully block even in RaspAP developers took my advice about futher cleaning up their sudo access. I also identified a pattern of writing files to /tmp as www-data then using sudo to copy or move the files to their proper location. This obviously introduces race conditions that an attacker might be able to abuse, although the necessary conditions for this are rather unlikely.

Other potential vulnerabilities

I only spent about 6–8 hours on this codebase, and I’m sure there are other issues I haven’t taken the time to fully run to ground. One interesting thing I noted is that the theme value in the cookie is added to a path and in then included as a stylesheet throughout the app. The value is quote escaped (see the code here: https://github.com/RaspAP/raspap-webgui/blob/320379e449729c6e4f3805f70bbe529bfc103962/includes/functions.php#L701). This would allow a path traversal outside of the expected path to load an arbitrary web-servable path on the RaspAP server as a CSS file. I reported this to the developers and they have not patched it at the time of this article. I am uncertain if this can actually be exploited, but it is certainly bad practice if not actually exploitable.

Timeline

Dec 30, 2023 1518 ET:

Initial email to security@rasap.com disclosing vulnerabilities following the protocol they requested in the github repo SECURITY.md. (Not included here as it is long and the details are covered above in better detail), though I did provide some additional high level security suggestions.

Dec 31, 2023 0313 ET:

Following response by developers:

Hello,

The vulnerabilities you found were identified by our team weeks ago and a patch has already been applied. Thanks for reaching out, though.

Keep up the good work.

Regards,

RaspAP team

Dec 31, 2023 1218 ET:

I responded with:

Hi,

If that is true, then why did my fresh install from literally yesterday morning still have the vulnerabilities? Why are these vulnerabilities very present in the main branch on github, especially given their high severity?

Why is there no announcement to warn users to upgrade?

These vulnerabilities have been in the code base for a long time, based on the git history. How is it that they were just found “weeks ago”?

When will these vulnerabilities be properly patched in the repo? Since you seem certain they are, I assume you won’t mind me publicly disclosing these vulnerabilities?

Sincerely,

<redacted>

NOTE: They had indeed patched these vulnerabilities at this point, but literally just a few hours before, and I had not yet noted this change (see PR link near top of the article)

Dec 31, 2023 1239 ET:

I followed up with this after reviewing the patch:

I find it highly suspicious that you patched the things I reported a mere 9 hours ago if you found them weeks ago. Especially since they are trivial to fix. It is interesting that you deleted the “save_net_dev_config.php” file but left other traces of this feature still in the code base. Also, it should be noted that there is still a vulnerability in the “sys_get_logfile.php”. You can no longer retrieve arbitrary files, but it is still unauthenticated. Allowing an unauthenticated attacker to grab debug logs seems like a very clear problem to me.

Sincerely,
<redacted>

Dec 31, 2023 1400 ET:

Posted this blog post

Jan 1, 2024 1900 ET:

Informed developer of this blog after not hearing back.

--

--