athena

Athena’s Trojan Horse: Cross-Tenant Data Poisoning and Exfiltration via AWS Athena

Dan Gansel November 02, 2026

Athena’s Trojan Horse: Cross-Tenant Data Poisoning and Exfiltration via AWS Athena

Book A Demo
See how Upwind uses runtime context to turn cloud security fingdings into real-time protection.
Book a Demo

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 catches BucketAlreadyExists and BucketAlreadyOwnedByYou identically – 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.

image-7
Source: aws/aws-sdk-pandas

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:

image-9-1024x442
Mock of the code in aws-sdk-pandas
image-8
Illustration of the exception handling

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.

image-10-1024x424
AWS Docs: Specify a query result location

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. 

image-11-1024x241
AWS Docs: View AWS account identifiers

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:

  1. The library calls create_athena_bucket(), which attempts to create the bucket
  2. S3 returns BucketAlreadyExists – the attacker already owns it
  3. The library catches the exception, logs a debug message, and proceeds
  4. Athena writes all query results to the attacker’s bucket
  5. 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-5678

This 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:

  1. EventBridge detects the Object Created event and triggers the attacker’s Lambda
  2. The Lambda reads the original Parquet files from the manifest
  3. It injects an additional column into the real data and writes a modified Parquet file
  4. It overwrites the manifest to point to the poisoned Parquet
  5. 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  INJECTED

The 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 BucketAlreadyExists exception 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

Recommendations

If you use aws-sdk-pandas with Athena today:

  1. Always set s3_output explicitly when calling wr.athena.read_sql_query() – do not rely on the default bucket.
  2. Configure your workgroup with EnforceWorkGroupConfiguration: true and an explicit ResultConfiguration output location.
  3. 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.
  4. 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.
  5. Upgrade to aws-sdk-pandas v3.16.1 or later.

Responsible Disclosure Timeline

DateEvent
Late April 2026Vulnerability identified in aws-sdk-pandas
Late April 2026Report submitted to AWS Security via HackerOne
Late April 2026Triaged by AWS
Early May 2026Fix released in aws-sdk-pandas v3.16.1 (PR #3314)
Late October 2026Assigned 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

  1. Training on Enemy Ground: Cross-Tenant RCE in Amazon SageMaker
  2. Athena’s Trojan Horse: Cross-Tenant Data Poisoning and Exfiltration via AWS Athena  (this post)
  3. Spark in the Dark: Silent Cross-Tenant Log Exfiltration in Amazon EMR
  4. Kick the Bucket: S3 Bucket Squatting Through a Series of Half-Patched AWS Patterns

Demo the Upwind Platform

See how Upwind is reducing risk and strengthening cloud security for global enterprises.
Get a Demo
Contents
Add the Upwind RSS Feed to Slack
Connect the Upwind RSS Feed to your Slack.
Follow the how-to here.
Threat RSS
Add the Upwind RSS Feed to Slack
Connect the Upwind RSS Feed to your Slack.
Follow the how-to here.
Main RSS