|
| 1 | +--- |
| 2 | +author_name: Raajhesh Kannaa Chidambaram |
| 3 | +title: Bypass GuardDuty Pentest Findings via Botocore Config |
| 4 | +description: Override the default botocore user-agent string in boto3 to prevent GuardDuty PenTest findings from firing. |
| 5 | +--- |
| 6 | + |
| 7 | +# Bypass GuardDuty Pentest Findings via Botocore Config |
| 8 | + |
| 9 | +<div class="grid cards" markdown> |
| 10 | + |
| 11 | +- :material-account-edit:{ .lg .middle } __Original Idea__ |
| 12 | + |
| 13 | + --- |
| 14 | + |
| 15 | + This article is based on [Hacking the Cloud Issue #453](https://github.com/Hacking-the-Cloud/hackingthe.cloud/issues/453). |
| 16 | + |
| 17 | +</div> |
| 18 | + |
| 19 | +## Background |
| 20 | + |
| 21 | +AWS [GuardDuty](https://aws.amazon.com/guardduty/) inspects the `User-Agent` header in AWS API requests (recorded in CloudTrail) to detect calls made from known penetration testing distributions. When it sees user-agent strings containing identifiers for Kali Linux, ParrotOS, or Pentoo Linux, it triggers a [PenTest finding](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-iam.html#pentest-iam-kalilinux) such as: |
| 22 | + |
| 23 | +- `PenTest:IAMUser/KaliLinux` |
| 24 | +- `PenTest:IAMUser/ParrotLinux` |
| 25 | +- `PenTest:IAMUser/PentooLinux` |
| 26 | + |
| 27 | +The [existing article](guardduty-pentest.md) on this topic covers using Burp Suite as an intercepting proxy to rewrite the `User-Agent` header for the AWS CLI. That approach works but requires setting up a proxy, dealing with TLS certificates, and routing all traffic through Burp. |
| 28 | + |
| 29 | +If you are working directly with the AWS SDK (boto3/botocore), there is a simpler option: override the user-agent string natively using `botocore.config.Config`. |
| 30 | + |
| 31 | +## How It Works |
| 32 | + |
| 33 | +When you create a boto3 session or client, botocore builds a user-agent string that includes your OS, Python version, and botocore version. On Kali Linux, that string will contain something like `kali` in the platform identifier, which is what triggers the GuardDuty finding. |
| 34 | + |
| 35 | +The [`botocore.config.Config`](https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore-config) class accepts a `user_agent_extra` parameter, but more importantly, you can use `user_agent_appid` or directly patch the user-agent to replace the entire string. Starting with newer versions of botocore, you can set `user_agent_appid` in the config, but the most reliable way to fully control the user-agent is to override it at the session level. |
| 36 | + |
| 37 | +## Technique |
| 38 | + |
| 39 | +The following snippet creates a boto3 session with a completely custom user-agent string, removing any OS-specific identifiers that GuardDuty would flag: |
| 40 | + |
| 41 | +```python |
| 42 | +import boto3 |
| 43 | +from botocore.config import Config |
| 44 | + |
| 45 | +# Define a clean user-agent that does not reveal your OS |
| 46 | +custom_config = Config( |
| 47 | + user_agent_extra="", # prevent appending extra info |
| 48 | +) |
| 49 | + |
| 50 | +session = boto3.Session() |
| 51 | + |
| 52 | +# Patch the user-agent at the session level |
| 53 | +session._session.user_agent_name = "Boto3" |
| 54 | +session._session.user_agent_version = "1.35.0" |
| 55 | +session._session.user_agent_extra = "" |
| 56 | + |
| 57 | +# Create clients using this session |
| 58 | +sts = session.client("sts", config=custom_config) |
| 59 | +print(sts.get_caller_identity()) |
| 60 | +``` |
| 61 | + |
| 62 | +After running this, the `User-Agent` header in your API requests will look something like `Boto3/1.35.0 Python/3.12.0 Botocore/1.35.0` with no mention of Kali, Parrot, or Pentoo. |
| 63 | + |
| 64 | +You can verify what user-agent is being sent by checking CloudTrail. The `userAgent` field in the event record will reflect whatever you set. |
| 65 | + |
| 66 | +### Picking a Realistic User-Agent |
| 67 | + |
| 68 | +To blend in, pick a user-agent value that matches what the target environment likely uses. For example, if the account runs Lambda functions with Python 3.12, a user-agent like `Boto3/1.35.0 Python/3.12.0 Botocore/1.35.0` would look normal in CloudTrail logs. You can reference [Pacu's user-agent list](https://github.com/RhinoSecurityLabs/pacu/blob/master/pacu/user_agents.txt) for realistic values. |
| 69 | + |
| 70 | +## Limitations |
| 71 | + |
| 72 | +- **SDK only.** This technique works for boto3/botocore. The AWS CLI builds its own user-agent string and appends additional metadata (CLI version, command name, etc.) that you cannot control through `botocore.config.Config` alone. For CLI use cases, the [Burp Suite proxy method](guardduty-pentest.md) is still the recommended approach. |
| 73 | +- **Internal attribute access.** Setting `session._session.user_agent_name` relies on botocore internals. While this has been stable across many releases, it could change in a future version. Test before relying on it in an engagement. |
| 74 | +- **CloudTrail is not the only signal.** GuardDuty uses multiple data sources. Changing your user-agent avoids the PenTest finding specifically, but other findings based on network traffic, DNS logs, or anomalous API behavior will still fire. |
| 75 | + |
| 76 | +## Detection Guidance |
| 77 | + |
| 78 | +If you are on the defensive side: |
| 79 | + |
| 80 | +- **Do not rely solely on GuardDuty PenTest findings.** The user-agent string is attacker-controlled and trivially spoofed. Treat PenTest findings as a bonus signal, not a primary detection. |
| 81 | +- **Baseline user-agent patterns.** Build detections in CloudTrail that alert on user-agent strings that are unusual for your environment. A sudden appearance of a generic `Boto3/1.35.0` with no Lambda or SDK context in an account that normally uses the CLI could be suspicious. |
| 82 | +- **Monitor for `botocore.config` usage patterns.** If you control the compute environment, watch for code that patches `_session.user_agent_name` or sets unusual `Config` values. |
0 commit comments