Skip to content

Article by Raajhesh Kannaa Chidambaram

Bypass GuardDuty Pentest Findings via Botocore Config

Background

AWS 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 such as:

  • PenTest:IAMUser/KaliLinux
  • PenTest:IAMUser/ParrotLinux
  • PenTest:IAMUser/PentooLinux

The existing article 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.

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.

How It Works

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.

The botocore.config.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.

Technique

The following snippet creates a boto3 session with a completely custom user-agent string, removing any OS-specific identifiers that GuardDuty would flag:

import boto3
from botocore.config import Config

# Define a clean user-agent that does not reveal your OS
custom_config = Config(
    user_agent_extra="",  # prevent appending extra info
)

session = boto3.Session()

# Patch the user-agent at the session level
session._session.user_agent_name = "Boto3"
session._session.user_agent_version = "1.35.0"
session._session.user_agent_extra = ""

# Create clients using this session
sts = session.client("sts", config=custom_config)
print(sts.get_caller_identity())

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.

You can verify what user-agent is being sent by checking CloudTrail. The userAgent field in the event record will reflect whatever you set.

Picking a Realistic User-Agent

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 for realistic values.

Limitations

  • 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 is still the recommended approach.
  • 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.
  • 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.

Detection Guidance

If you are on the defensive side:

  • 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.
  • 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.
  • 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.