User
Proper is a hard rated machine on HackTheBox created by xct & jkr. For the user part we will abuse a SQLI in a web application. This leaves us with credentials to log into a licensing portal which is vulnerable to RFI. With the RFI we can obtain the NetNTMLv2 hash of the web user which is crackable. The RFI is coupled with another vulnerability where the file is passed to the php include
function. A check for php tags is insecurely implemented and we can abuse a race condition to get RCE on the target leading to a reverse shell. To obtain system we will abuse a custom cleanup binary in two slightly different ways. First we will interact directly with the named pipe to clean and restore the root.txt
. Lastly we will use a tool by xct to abuse the arbitrary file write and get a reverse shell as SYSTEM.
Nmap
As usual we start our enumeration off with a nmap scan against all ports, followed by a script and version detection scan against the open ones to get a full picture of the attack surface.
All ports
1 |
|
Script and version
1 |
|
SQLI
There is only http on the machine open, which makes it obvious what to poke at. Browsing to the homepage we see the company website of OS TIDY
.
Running a gobuster scan against it we can retrieve the licenses
page.
1 |
|
Going there we need to be authenticated though.
Checking the source code of the homepage we see it loads resources with a php script.
Going there directly we see all products being displayed.
Curling the website leaving the second parameter out we get an interesting error message with a SECURE_PARAM_SALT
, which seems to be used to create the hash in the h
parameter of the request.
1 |
|
Changing the order of the sql query and hashing it with the salt we see all items now in reverse order, proving we can execute sql commands on the server.
1 |
|
To dump the database with sqlmap we create a short custom tamper script which takes the payload, hashes it with the salt and urlencodes it.
tamper.py
1 |
|
For the tamper script to work we also have to create an empty __init__.py
in the same directory.
1 |
|
Running sqlmap with the tamper script it identifies the injection point.
1 |
|
In another run we dump the database scheme.
1 |
|
Then the tables in the cleaner db.
1 |
|
And finally dump all information from the customers table. We select yes when we are prompted if we want to store the hashes to a temporary file to crack them later with hashcat.
1 |
|
1 |
|
Running hashcat with the --user
flag all hashes crack quickly and we can display with the --show
flag which hashes belong to which user.
1 |
|
1 |
|
RFI
Taking the first email and password from the list we are now able to log into the licensing portal.
In the portal we can select a theme which has the same url scheme as the products before. Since it seems to be a template file which is loaded we can check for RFI in the next step.
To generate the hashes we make some minor changes to our tamper script.
gen.py
1 |
|
Since it is a windows machine it might be likely that we are able to include a remote share. For this we first create the query parameter.
1 |
|
Then we set up responder to listen on the vpn interface.
1 |
|
Sending the request in burp we get another interesting error message which reveals the source code of the function. The php script invokes file_get_contents
and include
on a header.inc file in the remote share.
Looking at responder it made a connection back to us and we were able to capture the NetNTLMv2 hash.
1 |
|
This hash is also quickly crackable using hashcat leaving us with the credentials for the WEB user.
1 |
|
In a first test we create a header.inc
file with arbitrary content and serve it with impacket’s smbserver in the same directory.
1 |
|
1 |
|
Sending the request in burp it seems like it failed however. Looking at the output of smbserver.py
we see it did not make a full connection to our share.
1 |
|
Since authentication might be required and we have the credentials for the user the service is running as, we restart our smbserver with the credentials for WEB.
1 |
|
Sending the request again in burp it succeeds this time and we included the content of our earlier created header.inc
1 |
|
Race condition
Since our content is passed to include
we want to get it to include php code which needs the <?
tags. The function that does the filtering calls the file two times. Since there is some minor delay between this we might be able to exchange the content in between.
For this we create some php code calling system, downloading and invoking our nishang powershell reverse shell.
download.php
1 |
|
We use the Invoke-PowerShellTcp.ps1
shell and add the invocation to the last line of the file with our vpn ip and the port we want to recieve the shell on.
1 |
|
1 |
|
Then we set up a python webserver serving our reverse shell and a ncat listener to recieve it.
1 |
|
1 |
|
We also need to create a harmless dummy file which passes the check for the <?
tags.
1 |
|
With preparations met we start our smbserver with username and password again.
1 |
|
To exchange the files we use a simple bash loop to copy our php file and the dummy file over header.inc
in the directory where our smbserver is serving.
1 |
|
Since race condition are often not the most stable to exploit we now loop the curl request, including our session cookie, to trigger the RFI.
1 |
|
After a few seconds we get a hit on our webserver and a reverse shell on our ncat listener.
1 |
|
Now we can grab the user flag in web’s desktop.
1 |
|
System
Flag read
Looking around on the file system there are two custom binaries in the C:\program files\cleanup
folder.
1 |
|
Executing the client it seems to clean out the downloads folder of the current user.
1 |
|
To examine the files further we copy them both to our machine using smb.
1 |
|
The README.md
confirms the cleaning of files and also states that the project is still in the Alpha phase.
1 |
|
Opening up the client.exe we can retrieve a possible additional parameter with 0x522d
which is R-
in ASCII meaning the string -R
.
The easiest way to avoid “unexpected” results is to take a snapshop of the windows vm or backing up the Downloads
folder before following along with the next steps. What the client.exe
does is to move and encrypt all files in the downloads folder of the current user to C:\programdata\cleanup
. All the files are restorable but if the folder contains a lot of files it might a bit of a pain to restore them.
Running the server on our own windows machine nothing seems to happen yet.
1 |
|
The client is using named pipes to communicate with the server. A good tool to analyze this is ioninja. You can also sign up for a free trial period. We create a new session and select Pipe Monitor
. After starting the capture we start the server and see it is creating a named pipe to listen.
In this case the downloads folder only contains a desktop.ini
and a freshly created test file.
1 |
|
After running the client we can see at the server it cleaned our desktop.ini
.
1 |
|
1 |
|
Looking at ioninja we see the exact command being sent to the cleanupPipe
the server created.
Restoring the same file again shows the command being sent to the pipe to restore.
To continue testing we have to change the file creation date of desktop.ini
to the past again since it got newly created with the restore.
1 |
|
To see where the file actually ends up we can use procmon and monitor for CreateFile events running the client again. This leads to the C:\ProgramData\Cleanup
directory.
Checking the directory we see some file with its name in base64.
1 |
|
Decoding it results in the former name of the file getting cleaned.
1 |
|
To test if we are able to restore to another place on the filesystem, we base64 encode another path and copy the cleaned file to this encoded path name.
1 |
|
1 |
|
Next we restore the original filepath and the test filepath.
1 |
|
Checking the hashes for both files they are identical which means we can sucessfully place files somewhere else on the system.
1 |
|
To abuse this to read arbitratry files we need to send a custom CLEAN
command to the named pipe.
For this we create a small executable using C++. All this does is send a CLEAN
command as seen before in ioninja to the pipe, cleaning up root.txt.
pipe.cpp
1 |
|
After compiling the code with visual studio we transfer it to the target and run the binary.
1 |
|
Checking in C:\programdata\cleanup
there is indeed a cleaned up file and decoding the filename it looks like it worked.
1 |
|
1 |
|
All we need to do now is encode another filepath we have access to and copy the file to this.
1 |
|
1 |
|
After running the client with the restore option the rootflag is now freely accessible to us.
1 |
|
1 |
|
System shell
We proved earlier that we have arbitrary file write on the system. One way to abuse this is by using the diaghub repository on xct’s github. We only change the WinExec
function in the dllmain.cpp
to send a reverse shell instead of creating a bind shell and compile the project.
Next we transfer all the files to the machine including a nc
binary to send the shell.
1 |
|
To “clean” the dll we have to place it in the downloads folder and change its creation date to the past.
1 |
|
Next we run the client again and check the dll got cleaned.
1 |
|
1 |
|
Verifying the correct file we encode the new filepath to be in C:\windows\system32
and copy the file to this pathname.
1 |
|
1 |
|
1 |
|
Now we can restore it and verify it actually exists inside C:\windows\system32
.
1 |
|
All that is left to do now is to set up a listener on the port we specified and to run the dll with diaghub.exe
like outlined in the github repository.
1 |
|
1 |
|
This leads to a shell as nt authority\system
on the target.
1 |
|