AWS Cloud Fundamentals

Notes on core AWS services, permission models, and common cloud fundamentals.

Getting Ready for AWS

(First, create an account and sign in to AWS. Remember that you initially sign in as the AWS account root user.)

IAM

Like Linux or Windows, which have a highest-privileged root/administrator account, AWS has one too. It feels a little like a domain setup. ROOT has the highest privileges in AWS and can control everything. But everyone knows what happens if you keep using root: if the account gets compromised, the attacker does not even need to escalate privileges—they already have everything. That is why IAM exists.

With IAM, you can create a user and assign access to a service. Say I want to grant someone permissions for S3. AWS asks me to set a username and password, then generates a dedicated URL that can be used to sign in to that account, also known as an IAM account. When you host services such as Tomcat or Nginx locally, they usually run as a user like www-data. This is somewhat similar. The domain analogy comes from the fact that ROOT can change a lot of things, as shown below.

You can create user groups, users, roles, policies, and so on. It feels a lot like a domain. Linux and Windows work similarly too: you can assign roles and define policies, almost like setting a security baseline. The difference is that AWS manages everything centrally and distributes the configuration instead of operating on a single machine, which is why it feels like a domain controller. For the rest of this learning process, we need to create an administrator account.

Creating an Account

Providing AWS Management Console access lets an IAM user work through the web interface (the AWS console), rather than only through the API. I enable everything here because, while learning, I want to understand both the web interface and the API.

Click Next to reach the permissions page.

Create a group directly and choose the first permission. The goal here is to create a group with administrator privileges. If I want to create more users with administrator privileges later, I can simply drop them into this group. If that does not matter to you, click Attach policies directly on the far right and attach AdministratorAccess instead. (The architecture is extremely similar to Windows/Linux account management.)

At the bottom of this page, there is also an option for setting a permissions boundary. This is easy to understand: AdministratorAccess grants all permissions, but if I want an IAM user to manage only EC2 and not S3, I can set that restriction here even if the user belongs to the administrators group.

We do not need this here, so just continue to the next step.

This only adds tags, similar to descriptions for users and user groups. Just click Create user.

Roles

Here is a quick introduction to the roles section.

Normally, two default roles are generated. The important part is the ARN.

Every role has an ARN, somewhat like /etc/passwd on Linux or a SID on Windows. My current understanding is that if you want to learn cloud penetration testing, you have to understand IAM roles because they come into play later during privilege escalation.

As I understand it, an IAM role lets AWS services such as EC2 and Lambda access AWS resources automatically without an API Key. In other words, if one service needs resources from another service, you can configure a role for it. You can retrieve its temporary credentials like this:

1
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/

This returns temporary credentials. If you assign the role to EC2, however, you must be logged in to that EC2 instance to access this URL. The address is a special IP, 169.254.169.254, visible only to the current EC2 instance and inaccessible externally. IMDS automatically identifies the EC2 instance and returns temporary credentials for the IAM role currently attached to it.

This is both safe and convenient. If you access a service with an APIKEY as described above, that key is long-lived. Once stolen, an attacker can keep using it for a long time. These temporary credentials usually survive for only an hour, so even if someone obtains them, they will soon be unable to sign in or maintain persistent control.

Advanced IAM Penetration Testing (Paused)

That covers the basic concepts. The penetration-testing topics below are still blank because I have not started studying them yet, but I am leaving the outline here.

1️⃣ Identifying IAM account information

  • Use aws sts get-caller-identity to retrieve information about the current IAM account
  • Use aws iam list-users and aws iam list-roles to identify users and roles in the account
  • Determine AWS account ownership from an IAM ARN

2️⃣ IAM privilege escalation (Privilege Escalation)

  • Use iam:AttachUserPolicy to attach administrator privileges (AdministratorAccess)
  • Use sts:AssumeRole to switch to a more privileged role
  • Use iam:CreateAccessKey to create a new API Key for accessing AWS
  • Use iam:PassRole + ec2:RunInstances to attach a highly privileged role through EC2

3️⃣ Abusing IAM roles

  • Use aws sts assume-role to obtain temporary permissions
  • Read the IAM role of an EC2 instance (169.254.169.254/latest/meta-data/iam/security-credentials/)
  • Read IAM role credentials through SSRF (IMDS v1 vulnerability)
  • IMDS v2 protections and bypass techniques

4️⃣ Analyzing IAM access policies

  • Parse policy content returned by aws iam list-policies
  • Interpret IAM policies in JSON format
  • Use aws iam get-policy-version to view policy version history
  • Find overly privileged IAM roles (Overly Permissive Policies)

5️⃣ IAM role abuse

  • Use aws iam list-attached-role-policies to inspect policies attached to an IAM role
  • Use sts assume-role to obtain cross-account access
  • Find misconfigured external identity providers (OIDC/SAML)

6️⃣ Accessing AWS resources with an IAM account

  • Use aws s3 ls to enumerate an IAM account’s access to S3
  • Use aws ec2 describe-instances to check access to EC2 resources
  • Use aws lambda list-functions to view the IAM roles attached to Lambda functions

7️⃣ IAM account data exposure

  • Public S3 buckets (aws s3 ls s3://target-bucket --no-sign-request)
  • Exposed CloudFormation/Terraform configuration files
  • Use aws iam get-account-authorization-details to retrieve detailed IAM account permissions
  • Use aws iam list-access-keys to check for exposed AWS API Keys

8️⃣ Defense and detection

  • Monitor AWS CloudTrail logs to detect IAM account abuse
  • Use AWS GuardDuty to monitor unusual IAM activity
  • Restrict the scope of sts:AssumeRole access for IAM roles
  • Enable IAM Access Analyzer

EC2

You can think of EC2 as a “virtual machine in the cloud” provided by AWS. Its main concepts include:

  • Instance: A cloud server that can be started, stopped, and restarted.
  • AMI (Amazon Machine Image): An operating system image for EC2, such as Ubuntu, Amazon Linux, or Windows Server.
  • Instance Type: Determines the CPU, memory, and bandwidth, such as the free t2.micro.
  • EBS (Elastic Block Store): EC2 disk storage—the cloud equivalent of a hard drive.
  • Security Group: EC2 firewall rules that control which IPs are allowed to connect.
  • Public IP & Private IP:
  • Public IP: An IP that can be accessed directly from the internet.
  • Private IP: An IP that can only be accessed inside a VPC (the AWS internal network).

Create an instance by searching for EC2 and opening the instance creation page, then use the configuration shown below. On the free tier, an instance is free if total use stays under 750 hours per month. Since 750 hours is a little over 31 days, you might wonder why they do not simply call it free. EC2 lets you create multiple instances, though: if you create two, each can only run for 375 hours. I use the configuration below. (Amazon Linux is widely used outside China and integrates conveniently with AWS, so I create that here. It is also a chance to learn how Amazon Linux differs from ordinary Linux distributions.)

That is enough. Connect over SSH and try it out—the main goal is simply to understand what EC2 is.

There is also the Security Group, which is very similar to what you see with Alibaba Cloud and other Chinese cloud providers. Those providers enable this firewall automatically, so even after opening a port on the machine itself, you still have to open it in the Alibaba Cloud console. It is basically a firewall.

Next is EBS. I think of it in terms of a VMware virtual machine: when a virtual machine needs more storage, you can attach another virtual disk. The disk itself is virtualized and does not disappear. It is like plugging in another hard drive that can store data independently—essentially a convenient standalone cloud disk.

That may be the whole idea behind cloud services. EFS, meanwhile, is a disk shared over NFS. I do wonder whether communication might lag, but it still sounds pretty useful.

S3

S3 is object storage, not a traditional file system.

Alibaba Cloud OSS provides plenty of familiar real-world examples. When you upload a file, for instance, it is often uploaded to an OSS service and read back from there. No matter how you upload it, the file never reaches the local server; it goes to another cloud service instead. To me, S3 does not seem very different from OSS.

Differences Between S3 and OSS

FeatureAWS S3Alibaba Cloud OSS
Bucket nameGlobally uniqueUnique within a region
Region restrictionsYou must choose an AWS region, such as us-east-1You must choose an Alibaba Cloud region, such as cn-hangzhou
Storage classesStandard, Infrequent Access (IA), GlacierStandard, Infrequent Access, Archive, Cold Archive
API compatibilityS3 APIPartially compatible with the S3 API, with additional proprietary Alibaba Cloud APIs
Default accessPrivate by default; must be changed manuallyPrivate by default; must be changed manually
Cross-region replicationSupports replication across AWS regionsSupports replication across Alibaba Cloud regions

Buckets are private by default, so you can store backups and other material without other people seeing it. To start working with S3, we need to learn how to create a bucket and try operating it through a URL and AWSCLI.

Creating an S3 Bucket

Use the following configuration.

After creating the bucket, find it and upload any file.

Then enter the following policy under Bucket policy:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::mentalityxttest/*"
        }
    ]
}

By default, only the current user can access uploaded resources. For learning purposes, however, it is easier to make everything public. After allowing the bucket policy to control access, we still need to add a rule permitting external access to the bucket. At that point, the image becomes accessible.

AWS CLI

First, Check Whether AWS CLI Is Installed

Run:

1
aws --version

If it is not installed, see the official documentation:

  • Linux/macOS:
1
2
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target /
  • Windows: Download AWS CLI

Configuring AWS CLI (Linking Your AWS Account)

You need to configure an Access Key and Secret Key, which can be created in IAM, and then run:

1
aws configure

Enter the following when prompted:

  • AWS Access Key ID: Your AWS access key
  • AWS Secret Access Key: Your AWS secret key
  • Default region name: ap-southeast-1 (the region you selected)
  • Default output format: json (JSON is the default and recommended format)

The region is shown on the home page. Both keys are under the user’s access keys.

Common S3 CLI Operations

These are common S3 commands. Together with the bucket permissions above, you can test them directly in a terminal.

Create an S3 bucket:

1
aws s3 mb s3://your-bucket-name

mb = make bucket

List buckets:

1
aws s3 ls

Upload a file to S3:

1
aws s3 cp localfile.jpg s3://your-bucket-name/

cp = copy

To upload an entire folder recursively:

1
aws s3 cp ./my-folder s3://your-bucket-name/ --recursive

Download a file from S3:

1
aws s3 cp s3://your-bucket-name/3.jpg ./localfile.jpg

List files in a bucket:

1
aws s3 ls s3://your-bucket-name/

Delete a file from an S3 bucket:

1
aws s3 rm s3://your-bucket-name/3.jpg

Delete an entire bucket:

1
aws s3 rb s3://your-bucket-name --force

rb = remove bucket, and --force deletes all contents before deleting the bucket.

Lambda

Lambda is a managed environment that can run code automatically. Think of it like this: you write some Python code → but do not need to run a server for it.

  • Event Source: When does the Lambda code run?
  • S3 event: Run Lambda automatically after a file is uploaded
  • API Gateway: Triggered when a user accesses an API
  • CloudWatch event: Triggered on a schedule
  • SNS / SQS message: A message triggers Lambda

Runtime:

  • Lambda supports several languages, including Python, Node.js, Go, Java, C#, and Ruby.
  • You need to choose a runtime, such as Python 3.9.

Execution time limit:

  • Lambda has a maximum execution time of 15 minutes.
  • The longer the code runs, the more it costs.

Search for Lambda to find it. When you first open it, there is a tutorial you can follow step by step, so take your time. One particularly interesting part is how it responds to events. As listed above, both S3 events and API events can trigger Lambda functions, which is pretty neat.

The defaults are fine. Add a trigger first, mainly to get familiar with the process.

Start testing by uploading a file to S3.

1
aws s3 cp .\3.jpg s3://*********/3.jpg

You can see that it was triggered.

Of course, you can view the details in CloudWatch.

As for the exact event, all we can see at this point is that Lambda was triggered. We cannot tell which event triggered it or what the result was.

Go back to Lambda, enter the following code, and click Deploy to save it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import json

def lambda_handler(event, context):
    print("=== Lambda Triggered ===")
    print(json.dumps(event, indent=4))  # Print the event that triggered Lambda

    response = {
        'statusCode': 200,
        'body': json.dumps('Lambda ran successfully')
    }

    print("=== Lambda Return Value ===")
    print(json.dumps(response, indent=4))  # Print the result returned by Lambda

    return response

Trigger it again. Uploading a file or clicking the code test button will both work.

This is the log from testing the code directly. You can see that the returned JSON is identical to the JSON in the test and was recorded in the log.

This is the log triggered by uploading a file to the S3 bucket. It includes detailed AWSCLI fields, the uploaded filename, the bucket name, the file size, and more.

event (event data)

  • event is the data AWS passes in automatically when it triggers Lambda. For example:
  • When S3 triggers Lambda, event contains the uploaded filename and bucket name.
  • When API Gateway triggers Lambda, event contains HTTP request information.
  • When CloudWatch triggers Lambda, event contains scheduled-task information.

That is why printing event gave us so much information about the S3 bucket, including the uploader’s IP.

context (execution environment)

context is not event data. It contains environment information from AWS while Lambda is running, such as:

  • context.function_nameLambda function name
  • context.memory_limit_in_mbAllocated memory
  • context.aws_request_idID of the current request
  • context.get_remaining_time_in_millis()Remaining Lambda execution time

context contains the information above. If you want to inspect it, you can also reference it in the function.

AWS CLI

First, make sure you have configured AWS CLI correctly with aws configure and have sufficient permissions.

Listing All Lambda Functions

1
aws lambda list-functions

This command returns a list of your Lambda functions. You should see MyFirstFunction.

Getting Lambda Details

1
aws lambda get-function --function-name MyFirstFunction

This displays the Lambda code storage location, runtime (Python 3.9), execution role, and other information. It also returns a link to the ZIP archive containing the code, which you can download.

Invoking Lambda Manually

You can trigger Lambda directly from the CLI, which is equivalent to the console’s “Test” feature.

1
2
3
4
5
6
7
aws lambda invoke \
    --function-name MyFirstFunction \
    --payload '{"key1": "value1", "key2": "value2"}' \
    --cli-binary-format raw-in-base64-out \
    response.json

aws lambda invoke --function-name MyFirstFunction --payload "{\"key1\": \"value1\", \"key2\": \"value2\"}" response.json --cli-binary-format raw-in-base64-out

After Lambda finishes, the result is saved to response.json.

If you use a newer version of AWS CLI v2, add –cli-binary-format raw-in-base64-out so AWS CLI sends the JSON request body directly without base64 encoding, allowing Lambda to parse it correctly.

View the result:

1
cat response.json

You should see:

1
2
3
4
{
    "statusCode": 200,
    "body": "\"Lambda ran successfully\""
}

If your Lambda code prints event, you will also see logs showing how it parsed the input data.

Updating Lambda Code

Suppose you have a new lambda_function.py. You can upload it like this:

1
2
3
4
5
zip function.zip lambda_function.py

aws lambda update-function-code \
    --function-name MyFirstFunction \
    --zip-file fileb://function.zip

This command updates the Lambda code directly, so you do not need to upload it manually through the AWS console.

Updating Lambda Configuration

To change the Lambda runtime, memory, or timeout, use:

1
2
3
4
aws lambda update-function-configuration \
    --function-name MyFirstFunction \
    --memory-size 256 \
    --timeout 30

This changes the Lambda memory to 256MB and the timeout to 30 seconds.

Deleting a Lambda Function

If you no longer need this Lambda function, delete it with:

1
aws lambda delete-function --function-name MyFirstFunction

Warning: This operation is irreversible!

Writing and Uploading Lambda Code

I will skip the basics here. More advanced work involves boto3, which I still need to learn.

1
2
3
4
5
6
7
8
import json

def lambda_handler(event, context):
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }
1
zip function.zip lambda_function.py
1
2
3
4
5
6
aws lambda create-function \
    --function-name BackdoorLambda \
    --runtime python3.8 \
    --role arn:aws:iam::<ACCOUNT_ID>:role/<HIGH_PRIV_ROLE> \
    --handler lambda_function.lambda_handler \
    --zip-file fileb://function.zip

CloudTrail

CloudTrail is the audit log for an AWS account.

  • Records who (users, roles, or services) did what in AWS, such as creating, modifying, or deleting resources.
  • Records when and from where, including the IP address and geographic location, someone accessed AWS.
  • Records API calls made through the AWS console, CLI, or SDK.

Put simply, CloudTrail is like the “black box” of an AWS account. It records AWS activity to help troubleshoot problems and spot anomalies.

It is mainly used for activity auditing, much like a bastion host recording every action performed by operations staff.

Create one and you will be able to see events here. Calls to S3 interfaces, such as running ls or downloading a file, are not recorded unless data events are enabled. As shown below, data events can be enabled, but they cost money, so I am leaving them alone. It is enough for now to understand how to call these APIs. Next, let us look at AWSCLI.

AWS CLI

Querying Recent CloudTrail Events

1
aws cloudtrail lookup-events --max-results 10

This queries the 10 most recent events. By default, it returns all management events, excluding S3 data events.

Querying by Event Name

1
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=CreateBucket

This filters for all CreateBucket events, meaning S3 bucket creation operations.

1
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=GetObject

This attempts to find S3 file download (GetObject) events, provided you have already enabled S3 data events.

Querying by IAM User

1
aws cloudtrail lookup-events --lookup-attributes AttributeKey=Username,AttributeValue=MentalityXt --max-results 10

This finds the 10 most recent operations by the MentalityXt user.

Querying by Resource Name

1
aws cloudtrail lookup-events --lookup-attributes AttributeKey=ResourceName,AttributeValue=mentalityxttest

This filters for all events involving the mentalityxttest S3 bucket.

Querying by Time Range

1
aws cloudtrail lookup-events --start-time "2025-03-03T00:00:00Z" --end-time "2025-03-03T23:59:59Z"

This queries all CloudTrail events from March 3, 2025.

Listing All CloudTrail Trails

1
aws cloudtrail list-trails

This lists every CloudTrail trail in the AWS account.

Getting Details About a Trail

1
aws cloudtrail describe-trails

This queries detailed information about CloudTrail trails, including their S3 log storage locations.

Stopping CloudTrail Logging

1
aws cloudtrail stop-logging --name management-events

This stops logging for the management-events trail. It does not delete logs that have already been stored.

Restarting CloudTrail Logging

1
aws cloudtrail start-logging --name management-events

This restarts logging for the management-events trail.

Downloading CloudTrail Event Logs

1
aws s3 ls s3://aws-cloudtrail-logs-6502********-63db363a/AWSLogs/6502********/CloudTrail/

This lists CloudTrail logs stored in S3, if you enabled S3 storage.

1
2
aws s3 cp s3://aws-cloudtrail-logs-6502********-63db363a/AWSLogs/6502********/CloudTrail/2025/03/03/LOG_FILE.json.gz .
gunzip LOG_FILE.json.gz

This downloads and extracts a CloudTrail log file so it can be analyzed locally.

I listed a whole pile of commands above, but I think the log-download command is the most useful because it lets you download all the logs and analyze them at your own pace. The individual queries are more useful when the data volume is particularly large. If there is not much data, downloading everything for analysis is probably better. So far, I have noted only a handful of AWS CLI commands. They are really meant for a scenario where a leaked Access Key and Secret Key can be used to retrieve information through the API. There is no need to memorize all of this, but you should know that these API request methods exist.

CloudWatch

CloudWatch is AWS’s monitoring and log management service. It can:

  • Monitor AWS resources, including EC2, S3, Lambda, and RDS
  • Collect and store logs from Lambda, EC2, CloudTrail, and more
  • Configure alarms, such as an alarm triggered when CPU usage exceeds 80%
  • Visualize data by creating Dashboards

It is similar to the previous service, but CloudTrail primarily audits user actions, while CloudWatch monitors resources. If you have used almost any security appliance, you have probably seen something like this in a WAF, IPS/IDS, situational-awareness platform, and so on.

Just search for it, open it, and have a look. API auditing can sometimes be broken down in more detail. We actually created something in the Lambda section and opened it once, so I will drop in a screenshot here.

You can click through the other items one by one to get a general feel for them. Also:

  • CloudWatch itself does not record API requests, but CloudTrail records API calls
  • To see API logs in CloudWatch, you need to send CloudTrail events to CloudWatch Logs
  • Then you can use AWS CLI to query API call logs!

I will not go into more detail here. If I need to deploy services on AWS later, I can learn more then. It is not necessary right now.

While looking around, I found an interesting feature: the traffic monitor. It requires an EC2 instance, though, and I had already shut mine down because I barely use it after finishing the lesson and it might cost money. That is why there is no traffic data below.

With the default setup, it records rejected traffic but not successful flows. The method below can forward those logs.

Pay attention to the location in the upper-left corner. It is a bit of a hassle, so I will not set it up here. It is worth mentioning that this can also be configured and queried through AWSCLI. I thought of a scenario: suppose a target’s KEY is exposed, and the target also runs a popular site. Its traffic may be unencrypted, or it might use JS encryption that can be reversed. Capturing other users’ traffic could easily reveal their passwords or COOKIEs, which would be pretty serious. (That actually does not work. It can only record the source IP/target IP, source port/target port, transport protocol (TCP/UDP/ICMP), and packet/byte counts.) That makes it feel useful mostly for development, apart from cloud security cases where you also need to enable it. I have realized that everything I am studying covers both attack and defense: logging systems can support tracing, incident response, and reconstruction of an attack chain.

The crossed-out idea above can be handled another way. AWS also has a WAF where you can write custom rules. When a login request contains sensitive terms such as username/passwd, you can add a rule and capture it, but that costs money.

Never mind. It is enough to know that the feature exists.

VPC

  • A VPC is a Virtual Private Cloud on AWS
  • You can create resources such as EC2 instances, databases (RDS), and load balancers (ELB) inside a VPC
  • A VPC lets you control the network topology, including subnets, route tables, security groups, and NACLs
  • All AWS resources run inside some VPC

I first learned this concept during an internship in 2022. At the time, I wanted to buy a VPS to run some services, but I found that a VPC seemed cheaper than a VPS and felt roughly the same. What I really wanted was the public IP.

A VPC is mainly a network environment. Everything below relates to network configuration. With the right setup, you could actually build an internal AWS lateral-movement lab with dual-NIC machines, which sounds pretty interesting.

In the EC2 section, I mentioned that this seemed different from aliyun. Over there, the firewall is built in, while I could not find it here. It turns out it is here.

My understanding is still pretty basic, so it is worth looking through everything. VPC handles network traffic, while EC2 is mainly for compute, such as assigning CPU and memory to an operating system.

On the defensive side, VPC is mainly about access control and traffic management. On the offensive side, the goal is to obtain configuration information or bypass those defenses.

That is roughly how I understand it. The defensive VPC features are listed on the left in the screenshot above, while our focus is mainly offensive. Here are some important APIs for querying this configuration information, assuming you have obtained a KEY.

ServiceWhat the API doesPotential offensive use
VPCDescribeVpcsRetrieve the CIDR, status, default VPC, and other details for all VPCs
SubnetDescribeSubnetsView the IP ranges, availability zones, and other details for all subnets
Route TableDescribeRouteTablesRetrieve routing configuration for the current VPC and inspect public-access policies
NACL (Network ACL)DescribeNetworkAclsView ACL rules for the current VPC, including inbound and outbound restrictions
Security GroupsDescribeSecurityGroupsRetrieve EC2 security-group rules and look for open ports
Internet Gateway (IGW)DescribeInternetGatewaysDetermine how the VPC connects to external networks and whether it is exposed publicly
NAT GatewayDescribeNatGatewaysRetrieve NAT proxy information that might allow public-network restrictions to be bypassed
VPN ConnectionDescribeVpnConnectionsRetrieve VPN connection information and potentially attempt to hijack the VPN
DNS FirewallListFirewallRulesRetrieve DNS firewall rules and potentially attempt to bypass filtering
Elastic IPDescribeAddressesRetrieve public IPs attached to the current account and look for targets

These APIs can all be queried through AWS CLI or an SDK, for example:

1
2
3
aws ec2 describe-security-groups --region us-east-1
aws ec2 describe-route-tables --region us-east-1
aws ec2 describe-network-acls --region us-east-1

Use the results from these APIs to find vulnerabilities and work out how to bypass them.

  • Create new security-group rules to loosen access restrictions
  • Create a hidden IAM account to maintain a backdoor
  • Modify VPC flow logs to hide access records
  • Create an Elastic IP and attach it to an instance you control

RDS

RDS is mainly used to manage relational databases. Compared with deploying MySQL yourself:

  1. Less manual administration: AWS automatically manages backups, patches, monitoring, and scaling.
  2. High availability: You can deploy across multiple Availability Zones (AZs) so the database stays available during failures.
  3. Security: You can use private VPC deployment, encrypted storage, and automatic backups.
  4. Performance optimization: It supports automatic scaling, Read Replicas, and Aurora for high-performance reads and writes.

This is where things start to become a little more important. Developers often use cloud database services because they are more convenient, more secure, and cheaper.

Key RDS Concepts

ConceptPurpose
RDS instanceThe host running the database, equivalent to a database server
Database engineSupports MySQL, PostgreSQL, MariaDB, Oracle, SQL Server, and Aurora
Subnet GroupRuns the database in specific VPC subnets
Security GroupControls access to RDS, including whether external connections are permitted
Parameter GroupControls database configuration parameters, such as MySQL’s max_connections
SnapshotBacks up the database so it can be restored at any time
Read ReplicaOptimizes read operations and reduces load on the primary instance
Multi-AZ deploymentRuns the primary database across multiple Availability Zones (AZs) so primary/standby failover does not interrupt service

Setting Up MYSQL

As before, find the corresponding console. This time, we are looking for RDS.

Follow my configuration below, or you may be charged.

For everything else, clicking through with the defaults is fine.

Connecting to MYSQL (AWSCLI)

I will skip the first method, which is connecting from EC2. It is just a normal database connection.

1
mysql -h your-rds-endpoint -u admin -p

The second method, through AWSCLI, is the main one here.

Querying RDS Instances

1
aws rds describe-db-instances

Example response:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
    "DBInstances": [
        {
            "DBInstanceIdentifier": "mydatabase",
            "DBInstanceClass": "db.t3.micro",
            "Engine": "mysql",
            "DBInstanceStatus": "available",
            "Endpoint": {
                "Address": "mydatabase.xxxxxxxx.us-east-1.rds.amazonaws.com",
                "Port": 3306
            },
            "VpcSecurityGroups": [
                {
                    "VpcSecurityGroupId": "sg-0abcd1234",
                    "Status": "active"
                }
            ]
        }
    ]
}
  • You can get the RDS connection address from “Endpoint”
  • “DBInstanceStatus” shows the database status

Backing Up RDS

1
aws rds create-db-snapshot --db-instance-identifier mydatabase --db-snapshot-identifier mybackup
  • Create a snapshot (backup)

Deleting RDS

1
aws rds delete-db-instance --db-instance-identifier mydatabase --skip-final-snapshot
  • Delete the database immediately, skipping the final snapshot

Checking Whether RDS Allows Public Access

1
aws rds describe-db-instances --query "DBInstances[*].[DBInstanceIdentifier, PubliclyAccessible]"

Example response (access was denied):

1
2
3
4
5
6
[
    [
        "database-1",
        false
    ]
]

Enabling Public Access to RDS

1
2
3
4
5
6
aws rds modify-db-instance \
    --db-instance-identifier mydatabase \
    --publicly-accessible \
    --apply-immediately

aws rds modify-db-instance --db-instance-identifier mydatabase --publicly-accessible --apply-immediately

Once the command completes, it returns the MySQL address and enables public access.

Enabling the RDS Security-Group Policy

The step above successfully enabled external access to MySQL in RDS, but the security group still blocks it. Even though public access is enabled, the security group is a firewall, and the database remains unreachable until the traffic is allowed. Here, we add an inbound rule to the security group.

1
aws rds describe-db-instances --query "DBInstances[*].[DBInstanceIdentifier,VpcSecurityGroups]"

result:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[
    [
        "database-1",
        [
            {
                "VpcSecurityGroupId": "sg-0e8c9518e********",
                "Status": "active"
            }
        ]
    ]
]

Remember the VpcSecurityGroupId.

Grant access:

1
2
3
4
5
6
7
8
9
aws ec2 authorize-security-group-ingress \
    --group-id sg-0123456789abcdef \
    --protocol tcp \
    --port 3306 \
    --cidr YOUR_IP/32

For safety, you can also run curl ifconfig.me
After finding the IP, replace YOUR_IP above with the current IP rather than 0.0.0.0
aws ec2 authorize-security-group-ingress --group-id sg-0123456789abcdef --protocol tcp --port 3306 --cidr 0.0.0.0/0

Success.

Changing the MYSQL Password

1
2
3
aws rds modify-db-instance \
    --db-instance-identifier database-1 \
    --master-user-password "NewPassword123!"

The command above can change the password directly, after which you can connect and inspect the data. This is generally not allowed, though. Even during an authorized penetration test, doing this could get you into serious trouble. Just make a note of it.

That wraps up RDS. We added an inbound rule with AWSCLI and configured RDS for external access. Now delete everything through the web console.

The second step is to disable remote access to RDS.

1
aws rds modify-db-instance --db-instance-identifier mydatabase --no-publicly-accessible --apply-immediately

Steps:

  • Go to the AWS consoleRDS service.
  • Find your database-1 database in the Database instances list.
  • Click Modify.
  • Find the Connectivity section and clear Publicly accessible.
  • Choose Apply Immediately or Wait for the maintenance window.
  • Save the changes and wait for the RDS instance to restart.

I already deleted mine, so I will not include screenshots of these steps.

Fundamentals Complete

That completes the fundamentals. If you need to set up a service, define a baseline, configure access control, manage storage, or do anything similar, everything covered above can help you do it. You can even use what you learned to build a free web server with a solid architecture spanning several different services.

There are plenty of directions to go next. I could take AWS Certified Security – Specialty first, for example, or choose one of the following:

  • Follow the security path: dig deeper into IAM, VPC, attack-and-defense exercises, and penetration testing
  • Follow the DevOps & automation path: Terraform, CI/CD, and Serverless
  • Explore specialized AWS services: AI, IoT, blockchain, and more

CI/CD appears quite a lot in Jenkins, Azure DevOps, and similar platforms. I am not sure whether AWS’s built-in DevOps tooling can be used to carry out penetration testing. My next step is penetration testing on the security track.