This is the second post in our series on S3 bucket squatting vulnerabilities that survived AWS remediation. In the first post, we showed how a logic bug in the SageMaker Python SDK’s ownership check enabled cross-tenant RCE on live ML pipelines. Here we turn to Athena, where a gap between the service-level fix and the SDK enables silent exfiltration of query results and data poisoning.
TL;DR
We found that the aws-sdk-pandas library – AWS’s official pandas extension for Athena, Glue, Redshift, and more – creates a predictable default S3 bucket for Athena query results and treats attacker-owned buckets as its own. Every Athena query run through the library with default settings writes results to whoever created the bucket first.
The Athena service’s default bucket was previously reported as a squatting target and patched. But aws-sdk-pandas constructs the same predictable bucket name independently – and was never patched. The service-level fix did not propagate to the SDK. The result: silent exfiltration of every query result, and data poisoning through CTAS manifest hijacking – an attacker can modify the data victims read back from their own queries.
We reported the issue to AWS through responsible disclosure, and the fix shipped in aws-sdk-pandas v3.16.1.
AWS CNA also assigned CVE: CVE-2026-118029.
Key Findings
- aws-sdk-pandas constructs a default Athena results bucket named
aws-athena-query-results-{account_id}-{region}and catchesBucketAlreadyExistsandBucketAlreadyOwnedByYouidentically – silently proceeding in both cases - The Athena service was previously reported as vulnerable and patched. The SDK was not.
- Two attack vectors: silent exfiltration of all query results, and data poisoning via CTAS manifest hijacking
- Before v3.16.1, No S3 operation in the library uses
ExpectedBucketOwner– zero results across the entire codebase
Background
aws-sdk-pandas (formerly awswrangler) is an official AWS open-source library that extends pandas with native integrations for Athena, Glue, Redshift, S3, and other AWS services. It has millions of PyPI downloads and is the most common programmatic interface to Athena outside the AWS console and CLI.

When a user runs an Athena query through aws-sdk-pandas without specifying an output location, the library resolves a default bucket through a fallback chain in _get_s3_output():
def _get_s3_output(s3_output, wg_config, boto3_session=None):
if wg_config.enforced and wg_config.s3_output is not None:
return wg_config.s3_output
if s3_output is not None:
return s3_output
if wg_config.s3_output is not None:
return wg_config.s3_output
return create_athena_bucket(boto3_session=boto3_session)The primary workgroup – what every new AWS account starts with – ships with EnforceWorkGroupConfiguration: false and no ResultConfiguration. All three guards fail. The function falls through to create_athena_bucket(), which constructs:
aws-athena-query-results-{account_id}-{region}This is the default configuration. It is also the vulnerable configuration.
The Vulnerability
The Silent Exception Handler
The create_athena_bucket() function catches two S3 exceptions identically:


These two exceptions mean fundamentally different things:
BucketAlreadyOwnedByYou– the bucket exists and belongs to you. Safe to use.BucketAlreadyExists– the bucket exists and belongs to someone else. Not safe.
Both are swallowed with a debug-level log. The function returns the bucket path either way. All subsequent operations – query execution, result reads, manifest parsing – proceed without verifying who owns the bucket.
A search for expected_bucket_owner and similar mechanisms across the entire aws-sdk-pandas codebase returned zero results. No S3 operation in the library verifies bucket ownership.
The Gap Between Service Fix and SDK Fix
Previous research identified this exact bucket naming pattern in the Athena service as a squatting target. AWS made server-side changes to Athena.

But aws-sdk-pandas is not the Athena service. It is a client library that constructs the same predictable bucket name independently. The service-level fix did not reach the SDK. The library continued to create and use aws-athena-query-results-{account_id}-{region} without ownership verification – meaning any account or region where Athena had been used through the SDK remained an open target.
Impact: Data Exfiltration and Data Poisoning
Vector 1: Silent Query Result Exfiltration
The attack starts with the victim’s 12-digit AWS account ID – publicly exposed through AMIs, error messages, IAM role ARNs, and shared resources.

The attacker pre-creates aws-athena-query-results-{victim_account_id}-{region} in their own account and applies a bucket policy granting the victim read/write access.
When the victim runs any Athena query through aws-sdk-pandas without an explicit s3_output:
- The library calls
create_athena_bucket(), which attempts to create the bucket - S3 returns
BucketAlreadyExists– the attacker already owns it - The library catches the exception, logs a debug message, and proceeds
- Athena writes all query results to the attacker’s bucket
- The attacker reads the exfiltrated data at any time
Example Result – victim’s query results exfiltrated to the attacker’s bucket:
full_name email ssn salary credit_card
0 John Smith [email protected] 123-45-6789 125000 4532-1234-5678-9012
1 Maria Garcia [email protected] 987-65-4321 142000 5425-9876-5432-1098
2 Wei Chen [email protected] 456-78-9012 168000 4716-5678-1234-5678This isn’t a one-time leak. Every query the victim runs through the library – against any data source in the Glue catalog, including S3 data lakes, federated queries to RDS and Redshift – writes results to the attacker’s bucket. The attacker accumulates a continuous stream of the victim’s query output: PII, financial data, credentials, business analytics, anything Athena can query.
Vector 2: CTAS Manifest Hijack (Data Poisoning)
The second vector goes beyond reading – the attacker modifies what the victim reads back.
When aws-sdk-pandas uses CTAS (Create Table As Select) – its default query approach – Athena writes Parquet result files and a manifest CSV listing their S3 paths. The library reads this manifest and follows the paths to deserialize the Parquet files into a DataFrame.
The manifest is a plain text file. No signature, no checksum, no verification that the paths point to the expected files.
The attacker deploys a Lambda function triggered by EventBridge on manifest file creation. When Athena writes a CTAS manifest to the squatted bucket:
- EventBridge detects the Object Created event and triggers the attacker’s Lambda
- The Lambda reads the original Parquet files from the manifest
- It injects an additional column into the real data and writes a modified Parquet file
- It overwrites the manifest to point to the poisoned Parquet
- When the victim’s downstream pipeline, notebook, or dashboard reads the results, it follows the modified manifest and deserializes the attacker’s data
Example Result – victim’s real data returned with an attacker-injected column:
full_name email ssn salary credit_card _injected
0 John Smith [email protected] 123-45-6789 125000 4532-1234-5678-9012 INJECTEDThe victim’s original data is preserved – only the injected column reveals tampering. If downstream systems trust DataFrame values for SQL queries, HTML templates, or shell commands, the injected payloads achieve further exploitation. Crafted Parquet files can also exploit parser vulnerabilities such as CVE-2023-47248, which affects PyArrow < 14.0.1 and allows arbitrary code execution via PyArrow IPC deserialization.
In real-world usage, query execution and result consumption are typically separate steps – batch pipelines (Airflow, Step Functions), notebooks, dashboards, and result reuse all introduce gaps of seconds to hours between execution and consumption, giving the attacker ample time to modify the manifest.
Attack Flow
Detection Challenges
- No CloudTrail events in the victim’s account. S3 data events for operations on the attacker-owned bucket are logged in the attacker’s account, not the victim’s. The victim has no audit trail of exfiltrated results.
- Debug-level logging only. The
BucketAlreadyExistsexception is caught with_logger.debug(). Unless the victim has debug logging enabled and is watching for this specific message, the squatting is invisible. - Query results look normal. For the data poisoning vector, the victim’s original data is preserved. The injected content blends with legitimate columns unless someone inspects the schema.
- No “wrong account” signal. The victim can read and write to the bucket. From the SDK’s perspective, everything is working as intended.
AWS Response
We reported this vulnerability to AWS Security through HackerOne. AWS triaged the report and shipped a fix in aws-sdk-pandas v3.16.1 (PR #3314), which adds ExpectedBucketOwner verification to bucket creation and manifest integrity validation for CTAS results – directly addressing both demonstrated attack vectors.
AWS assigned a CVE: CVE-2026-118029
Sources
- https://aws.amazon.com/security/security-bulletins/2026-153-aws/
- https://github.com/aws/aws-sdk-pandas/security/advisories/GHSA-34f4-24c9-rgcm
Recommendations
If you use aws-sdk-pandas with Athena today:
- Always set
s3_outputexplicitly when callingwr.athena.read_sql_query()– do not rely on the default bucket. - Configure your workgroup with
EnforceWorkGroupConfiguration: trueand an explicitResultConfigurationoutput location. - Create the default bucket yourself before first use if you haven’t already –
aws s3 mb s3://aws-athena-query-results-{account_id}-{region}from your own account. - Check bucket ownership – run
aws s3api head-bucket --bucket aws-athena-query-results-{account_id}-{region} --expected-bucket-owner <account-id>and verify the owner matches your account. - Upgrade to aws-sdk-pandas v3.16.1 or later.
Responsible Disclosure Timeline
| Date | Event |
|---|---|
| Late April 2026 | Vulnerability identified in aws-sdk-pandas |
| Late April 2026 | Report submitted to AWS Security via HackerOne |
| Late April 2026 | Triaged by AWS |
| Early May 2026 | Fix released in aws-sdk-pandas v3.16.1 (PR #3314) |
| Late October 2026 | Assigned CVE: CVE-2026-118029 |
Conclusion
Athena’s default bucket squatting was reported, patched, and declared fixed. But the fix covered the Athena service – not every library that constructs the same bucket name. aws-sdk-pandas continued to create aws-athena-query-results-{account_id}-{region} and silently use it regardless of who owned it, catching the security-critical BucketAlreadyExists exception with a debug log and moving on.
The fix that shipped in v3.16.1 adds the ownership verification and manifest integrity validation that were missing. If you use aws-sdk-pandas with Athena, upgrade and check your bucket ownership.
In This Series
- Training on Enemy Ground: Cross-Tenant RCE in Amazon SageMaker
- Athena’s Trojan Horse: Cross-Tenant Data Poisoning and Exfiltration via AWS Athena (this post)
- Spark in the Dark: Silent Cross-Tenant Log Exfiltration in Amazon EMR
- Kick the Bucket: S3 Bucket Squatting Through a Series of Half-Patched AWS Patterns
