Our Blog

Being Stubborn Pays Off pt. 1 – CVE-2018-19204

Reading time ~13 min

Intro

During an internal assessment, I came across monitoring software that had default credentials configured. This monitoring software allowed for the creation of sensors, but, none of which would allow for code execution or any other things that could compromise an underlying system. Turns out, it was a vulnerable version based on a publicly known CVE, but there was no public exploit code. Join me in this quest on building an exploit!

First of all, if we check the advisory for CVE-2018-19204 it gives us a lot of detail:

Vulnerability due to improper handling of user input in the POST parameter ‘proxyport_’ allows remote authenticated attackers with read-write privileges to execute an arbitrary code and OS commands with system privileges. Using a specially crafted HTTP request an attacker can override the ‘writeresult’ command-line parameter for the HttpAdvancedSensor.exe executable file and write arbitrary data to an arbitrary file-system location, as demonstrated by creating an executable file in the \Custom Sensors\EXE directory and executing it through creation of EXE/Script Sensor.

https://www.ptsecurity.com/ww-en/analytics/threatscape/pt-2018-23/

One of the juicy things from building this exploit is that it should give us local Administrative access to the host; hence me wanting to build the exploit as soon as possible within the assessment time.

Finally, after a few days spending a couple of hours and a full day of research, I managed to develop an exploit and found another 2 bugs (CVE-2019-11073, CVE-2019-11074) along the way (This is being disclosed as of this writing, no details for now). This blog post describes the thought process that was followed towards building an exploit for CVE-2018-19204 and the different paths followed to finally build it, including stupid ideas and failures.

Starting point

As a starting point from the advisory, we know that we need to find a sensor that has the proxyport_ parameter in its settings. The advisory also mentions that the vulnerability is triggered when calling the “HttpAdvancedSensor.exe” file. This is also another pointer to a very specific type of sensor. Let’s have a look:

Sensors related to HTTP that have the “Advanced” keyword

We have 2 easy candidates here, so let’s just chose the first one and check its properties to try and confirm the presence of the proxyport_ parameter. As soon as we open the sensor and scroll through its options, nothing really becomes apparent, at least for me it didn’t. I actually had to resort to looking it up online. However, if you are not under the stress of an assessment you’d be looking at the options much more calmly than I did, and see something like:

Checkbox I couldn’t see at first glance

The only thing to do here is uncheck the “inherit” checkbox and two new parameters will show up:

Proxy settings for Http Sensor

A-ha! Here it is! At this point we can just add some placeholder values and intercept it using Burp to inspect the parameters where we will effectively see the proxyport_ parameter:

proxyport_parameter

Ok, we’re onto something and it looks like we may have found the vulnerable request.

Parameters passed into the binary? What binary?!

The next step would be to see how the parameters are passed into the binary to try and overwrite the writeresult parameter that is mentioned in the advisory.

So we head over and get our hands on ProcessExplorer from the SysInternals by Mark Russinovich and launch it on the host where we have our testing PRTG Network Monitor software and run the sensor. I expected the “HttpAdvancedSensor.exe” to pop up somewhere. But, it did not. Since the software has two services running, one for the core server and another one for the probes, I assumed that the binary could be embedded somehow into the probes service or even called in some other mysterious way. With this assumption I had the following plan of action:

  1. Create a binary that pops up a message box with arguments passed to it
  2. Dump the arguments to disk as well
  3. Replace the HttpAdvancedSensor.exe with our binary
  4. Expect the software to call our binary and see how the arguments are passed

To test this, I quickly opened Visual Studio, and wrote the following:

#include "stdafx.h" 
#include "windows.h"
#include "string.h"
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char**argv)
{
char args[500] = { 0 };
for (int i = 0; i < argc; i++)
{
strcat_s(args, " ");
strcat_s(args, argv[i]);
}
ofstream myfile;
myfile.open("C:\\output.txt");
myfile << args; myfile.close();
MessageBoxA(NULL, args, "Arg is", 0);
return 0;
}

I renamed the binary and placed it in the “Sensor System” folder and … !!! Nothing, nada, niente. Restarted the services and still it wouldn’t call my binary. What could be happening?

Digging deeper into the settings and binaries

I was very stubborn on the assumption that the binary was being called but, I wasn’t seeing it. This lead me to try and find a different way to determine how the parameters are passed to the binary. I started reading this blog post about an older vulnerability in the PRTG software where the researcher talks about a file called “PRTG Configuration.dat”. Basically, this file stores the settings for the software and the different parameters set for the sensors. It’s a perfect place to inspect how our parameters are passed into the various sensor binaries. The first thing I tried to do is the following sequence into the proxyport_ parameter to see which characters are allowed into it:

¬!"£$%^&*()_+{}:@~<>?|\,./;'#[]=- ./test.txt

Then, by inspecting the aforementioned “PRTG Configuration.dat” file, we can see that many characters are allowed and some others disappear:

Contents of the “PRTG Configuration.dat”

I tried a couple of smaller subsets but it was a very tedious task because every time I needed to see the pattern reflected in the file, a restart of the services was needed.

At this point I decided to shift my attention into the vulnerable binary, HttpAdvancedSensor.exe since I realised I was basically working in the dark as I had no clue what the advisory meant by the writeresult parameter. I must say I had quite a lot of luck when doing this but, I had a gut feeling that I needed to open it in dnSpy and so I did.

Searching for “writeresult” inside the HttpAdvancedSensor.exe

Since it’s a C# .NET program and no obfuscation had been implemented, it was fairly easy to see the representation of the code. We can see that the writeresult is there and takes text2 as a value. By inspecting the start of the Run function, it’s possible to see that the parameters are read from the command line in a pretty custom way.

Reading arguments from the command line

In short, it is looping through every parameter passed by the command line and assigning to text2 whatever is after the = character and storing in text3 whatever is before the = character, skipping the first character. At first I thought it was because parameters would be passed from the “PRTG Probe” service like ?parameter1=value1&parameter2=value2. But that was not the case as trying something in the following form HttpAdvancedSensor.exe "?url=http://foo.com&writeresult=./test.txt" triggered an error about not being able to parse the hostname (this can be deduced from looking at the code as well). However, by providing something in the form HttpAdvancedSensor.exe aurl=http://foo.com awriteresult=./test.txt it will go to the specified URL, in this case “http://foo.com” and write the contents of the page into the file “test.txt”. Finally, a bit of progress…

Stupid assumption, stupid idea

Carrying on with the assumption that the binary is called somehow and having determined how to pass the parameter, I had the idea of writing a quick bash script to test several inputs. The main problem of why I didn’t try to perform the injection manually was that, first you need to update the settings and then either wait for the timeout until the next sensor probe/refresh or click on refresh. So, there was this need to script it if I didn’t want to try all characters from 0x00 to 0xff manually.

The whole idea behind it was that I expected the parameter to be passed into something that looked like the following:

HttpAdvancedSensor.exe aproxyport=80 awriteresult=SensorLog.txt

Therefore, I assumed I needed to break the parameters by injecting some special character in between parameters:

HttpAdvancedSensor.exe aproxyport=80 [INJECT_HERE] awriteresult=...

I am not going to copy paste the whole script but, in case you are curious about what was tried, here’s the process and the most relevant lines:

  • Intercept with BurpSuite the requests to edit the sensor’s settings and to refresh the sensor’s status and copy these requests as a curl command
  • Create a for loop iterating throughout all 255 characters
  • Replace the character used in each request
!/bin/bash
seg=2
for f in $(seq 0 256); do
i=$(printf "%02x" $f)
echo
echo "[+] Trying with %$i"
echo
# Request to editsettings here using %$i
sleep $seg
# Request to refresh sensor status...
sleep $seg
done

Even after all of this, still, nothing happened on the target host, so I had to take a step back and rethink.

From the beginning, again!

Out of desperation, I tried throwing the “PRTG Probe.exe” and “PRTG Core.exe” binaries into IDA Pro to see if there were any references to the “HttpAdvancedSensor.exe” but again, I found nothing. Thing here is that I have been assuming throughout the whole process that the binary was getting called but, what if actually it wasn’t being called?! One rule of thumb (that I heard at SensePost trainings) is “don’t assume things” – and this keeps coming back to bite me.

Going back, I started creating all the sensors that had the HTTP word in them and get ProcessExplorer running again. At some point, I saw a few binaries popping up which I will not disclose yet for reasons I mentioned earlier. Replacing each binary with the little program as I did before gave me more hints into how parameters were being passed.

Funny thing is, there was no hint of the binary I was looking for. While reviewing everything, I had a good look at the settings for the “HTTP Advanced” sensor and… well:

HTTP Engine option in HTTP Advanced sensor

It mentions an alternative .NET solution. .NET solution you said?! Remember when we used dnSpy to open “HttpAdvancedSensor.exe”? This looks like it’s good news for us. We go to our target machine, fire up ProcessExplorer, launch the sensor and:

HttpAdvancedSensor.exe aka. happiness!

It was now being called and its parameters were just one (quick) click away! Let’s see them:

Parameters passed to HttpAdvancedSensor.exe

Here we can see that the proxyport_ parameter gets reflected in the -Proxy argument, therefore, we can redefine writeresult and point it to wherever we want! For extra coolness, the : character is sanitised but you can just type a UNC path pointing to the localhost in C$ and you’ll get a straight write to wherever you want as system. Ain’t that nice?

Proof of Concept

I will leave the injection for the next blog post and as an exercise for the reader, although you can just guess it from the previous image or from the python script that I present here. I quickly drafted a proof of concept that you can obtain here: https://gist.github.com/n30m1nd/1788ab84b94a03c62847d285ee0cfe81

To make it work, you will need to point it against the target PRTG installation, probably change the extension that you want your payload to be stored into and finally provide the URL where the payload is going to be downloaded from.

Exploit launched retrieving “payload.txt”
User added via malicious EXE/Script sensor

Conclusion

Sometimes, we encounter situations where it seems like there’s not much left to do. Depending on the kind of assessment, it may seem daunting to try and find 0-days in software (More on the next part!). Having a positive attitude and some determination is key.

We have seen how in the end, the exploit was not as complicated as it seemed at the beginning and that, with the right mindset, tooling, and a bit of experience, it’s possible to achieve success.

It is also important to stay focused and try things that we think aren’t complex enough, or try things that we straight up assumed at the beginning weren’t going to work because those could serve helpful in future occasions.