← All field notes
awsredshiftfor responders

Redshift UNLOAD dump: the warehouse export that wasn't yours

An over-privileged analytics role mints temporary cluster credentials and UNLOADs whole tables to an attacker bucket. Querying is routine, so only the export target gives it away.

Redshift’s GetClusterCredentials mints temporary database access, and UNLOAD is the normal way to export tables. That is exactly why a compromised analytics role can dump the warehouse without looking out of place. The one field that exposes the theft is the export destination.

How the attack works

The role calls redshift:GetClusterCredentials to obtain temporary credentials for a database user, connects to the cluster, and runs broad SELECTs across customer, billing, and PII-bearing tables. It then issues UNLOAD to write whole-table extracts to an S3 bucket outside the warehouse account boundary, iterating across more schemas to widen the dump to the same external destination. Obtaining credentials, large reads, and off-hours jobs are all routine for an analytics role, so none of that is the signal. CloudTrail shows the credential issuance, and Redshift audit and query logs plus S3 data events name the exact UNLOAD statements and destination objects, which is where the off-boundary target stands out. In ATT&CK terms this is T1530, Data from Cloud Storage, with T1537, Transfer Data to Cloud Account.

Why it works

The role could obtain broad cluster credentials and UNLOAD to any bucket. Nothing pinned UNLOAD targets to approved destinations, and the in-database grants were wider than the workload needed.

How to fix it

Rebooting the cluster only drops one connection, because the role just re-obtains credentials and resumes. Instead, revoke the role’s redshift:GetClusterCredentials and disable the abused database user so no fresh credentials issue, deny the role’s session by aws:TokenIssueTime, and add a bucket policy or service control policy that denies writes to the external destination. The teaching move is to constrain UNLOAD targets to approved buckets and audit every role that holds GetClusterCredentials, since that permission is the credential-minting choke point.

Practice it

We built this as a GraphLattice Range scenario so responders revoke the credential path and block the bucket, instead of rebooting and resetting a password.