← Back to blog
Blog Detail

S3 and MinIO Bucket Misconfigurations and Content-Type XSS: Cloud Storage Exposure in CTEM

Public S3 and MinIO buckets remain one of the fastest paths from cloud misconfiguration to data theft and stored XSS on trusted domains. This CTEM guide covers anonymous access, content-type confusion, detection telemetry, and how Trusteed CTEM turns raw bucket exposure into validated, prioritized findings.

Trusteed Team
Trusteed Editorial
Written On
Sep 30, 2026
Category
CTEM
Read Time
12 min read
  • CTEM
  • Trusteed
  • Cloud Security
  • S3
  • MinIO
  • Attack Surface Management
  • Content-Type XSS
  • Exposure Management
S3 and MinIO Bucket Misconfigurations and Content-Type XSS: Cloud Storage Exposure in CTEM

S3 and MinIO Bucket Misconfigurations and Content-Type XSS: Cloud Storage Exposure in CTEM

TL;DR

  • Public object storage is usually discussed as a data-loss problem. It is just as often an execution problem: if the bucket is served from a domain your users trust, an attacker can get the browser to render markup from that origin.
  • Two misconfiguration families dominate: anonymous access (open bucket policies, legacy ACLs, MinIO anonymous presets) and content-type confusion (attacker-controlled Content-Type metadata that turns a stored object into rendered HTML, XHTML, or SVG).
  • Content-type XSS chains into session theft, phishing with a valid TLS certificate, JavaScript bundle overwrite, and SEO poisoning — often without touching a single line of application code.
  • Trusteed CTEM keeps object storage in the same continuous exposure graph as the domains, services, and web/API endpoints that reference it, so bucket findings are validated against real reachability and business context instead of dumped into an analyst queue.

What is S3 and MinIO bucket misconfiguration?

Amazon S3 and S3-compatible stores such as MinIO are object storage: a bucket holds keys, keys hold objects, and every object carries metadata — including a Content-Type the browser will trust when that object is served over HTTP. A bucket misconfiguration is any combination of policy, identity, and metadata settings that lets someone who should not have access read, list, write, or reinterpret your objects.

In practice, four problems recur:

  • Anonymous read. A bucket policy with Principal: "*" and s3:GetObject, a public ACL on individual objects, or a MinIO anonymous preset set to download.
  • Anonymous list. s3:ListBucket granted to everyone. Read access to one known key is a leak; list access turns it into a searchable index of everything you store.
  • Anonymous write. s3:PutObject (or MinIO's public preset, which grants read and write) lets an attacker plant objects, overwrite assets, and delete data.
  • Metadata confusion. The upload path does not constrain Content-Type, so an .html, .svg, .xhtml, or .js object is served back with a renderable or executable MIME type.

Content-type XSS is the stored-XSS variant that follows from that last item. Instead of injecting a payload into a template, the attacker uploads a file with a renderable MIME type and gets the storage origin — which your users, and sometimes your own front end, treat as trusted — to deliver it. No server-side vulnerability is required. The bucket is the injection point.

Why it matters now

The storage estate is growing faster than the controls around it. Data lakes, ML pipelines, log archives, CI build artifacts, customer uploads, database exports, and Terraform state all land in buckets — and most buckets are created by teams who never open a cloud console. Every new bucket is a new exposure candidate.

Several shifts make this worse rather than better:

  • Trusted-domain delivery. Buckets are usually fronted by cdn., static., assets., or files. hostnames, frequently on the same registrable domain as the application. Cookies scoped to .example.com are sent to those hostnames, phishing filters treat them as legitimate, and the TLS certificate is valid.
  • Write is the new read. Read access leaks data; write access enables supply chain attacks. Replace the JavaScript bundle your customers load, or plant an HTML page that harvests credentials on a domain they already trust.
  • Automation removed the skill barrier. Bucket discovery is a script, not a research project. Certificate transparency logs, JavaScript bundles, mobile app strings, DNS records, and predictable naming conventions all point at storage endpoints.
  • Regulatory weight. Customer PII, payment data, health records, and source code in a world-readable bucket is a breach notification event, not a configuration ticket.
  • Presigned URLs age badly. Signed URLs generated with long expirations and passed through logs, browsers, and chat tools effectively become public access objects.

How attacks / risks work

Dark Trusteed CTEM insight card titled 'Public Buckets, Trusted Origins: The Storage Exposure Kill Chain' showing a five-step left-to-right flow (bucket discovery, anonymous access check, content-type control, trusted-origin render, impact) with a side panel contrasting read access as a data leak versus write access as a stored-XSS injection point.

1. Discovery. Attackers start from data you already publish: DNS CNAMEs pointing at *.s3.amazonaws.com or a storage gateway, hostnames in certificate transparency logs, URLs inside JavaScript bundles and mobile binaries, error messages that leak bucket names, and response headers such as Server: MinIO or x-amz-bucket-region. Common naming patterns (company-backups, company-dev, company-tfstate) are brute-forceable at negligible cost.

2. Access verification. A HEAD request confirms the bucket exists and is reachable. A GET with ?list-type=2&max-keys=1000 confirms whether listing is anonymous. Individual object reads confirm whether content is exposed. All of it is unauthenticated, quiet, and indistinguishable from ordinary CDN traffic unless you are logging it.

3. Metadata abuse. When the upload path does not pin the MIME type, an object can be stored as text/html, application/xhtml+xml, or image/svg+xml. Two variants matter:

  • Direct storage. The object is served with the stored Content-Type, and the browser renders it inline.
  • Response-header override. S3-compatible APIs allow a signed GET to override response headers — response-content-type and response-content-disposition — so an object that normally downloads can be made to render, in the response the browser actually sees.

If the same origin also serves the application (path-based routing, a reverse proxy, or a shared hostname), the injected document inherits that origin's authority: it can read localStorage, call same-origin APIs with the victim's cookies, register a broader service worker scope, and slip past CSP rules that allow self.

4. MinIO specifics. MinIO's footguns are operational rather than conceptual. The console (typically port 9001) and API (typically port 9000) are frequently exposed to the internet; quickstart documentation normalizes minioadmin:minioadmin root credentials; and the mc anonymous presets make it a one-line change to grant download, upload, or public (read and write) access to an entire bucket. Because MinIO is self-hosted, it rarely appears in cloud-native posture tooling.

5. Impact. Anonymous read exposes backups, .env files, terraform.tfstate, private keys, and customer data. Anonymous write enables asset replacement and destructive operations. Content-type XSS converts either one into session theft, credential phishing on a trusted domain, defacement, and search-engine poisoning.

Detection and visibility

Good telemetry comes from three directions, and you need all three.

Storage-side audit. Enable CloudTrail data events for GetObject, PutObject, and DeleteObject, plus S3 server access logs for buckets that matter. Alert on management events that change exposure: PutBucketPolicy, PutBucketAcl, DeletePublicAccessBlock, PutAccountPublicAccessBlock. On MinIO, forward audit webhooks to your SIEM and watch for anonymous principals in the access log.

Content-side inspection. For every bucket you know about, record the HTTP response for a sample of objects: status code, Content-Type, Content-Disposition, X-Content-Type-Options, and the hostname that served it. Objects returning text/html, application/xhtml+xml, or image/svg+xml from a hostname on a trusted domain are the highest-value signal in this entire workflow — that is content-type XSS waiting to happen.

External vantage point. Anonymous access is only visible from outside your network. A continuous, rate-limited probe of known buckets — unauthenticated list and read attempts, plus checks for dangling CNAMEs that point at deleted buckets — tells you whether the exposure is real rather than theoretical. Combined with asset inventory, this is what separates a finding from a risk.

Metrics that hold up in a board deck: number of buckets with anonymous read, number with anonymous write, number of renderable objects served from trusted domains, percentage of bucket findings validated as actionable, and mean time from discovery to remediation.

Reduce risk / best practices

  1. Enforce Block Public Access at the organization and account level. Bucket-level settings are too easy to undo; the account-level controls (BlockPublicAcls, IgnorePublicAcls, BlockPublicPolicy, RestrictPublicBuckets) are the backstop.
  2. Treat Principal: "*" as a finding, not a design choice. Review every bucket policy for wildcard principals, wildcard actions, and missing aws:SourceArn / aws:SourceVpce conditions.
  3. Never grant ListBucket anonymously. Long random key names are a reasonable compensating control; a public listing endpoint is not.
  4. Pin the Content-Type on upload. Validate it server-side against an allow-list, and never accept a client-supplied MIME type for anything you serve back.
  5. Force download for user content. Set Content-Disposition: attachment and X-Content-Type-Options: nosniff at the bucket, CDN, or reverse-proxy layer.
  6. Serve untrusted content from a cookieless origin. Use a separate registrable domain — not just a subdomain — for user uploads, with no session cookies scoped to it and a strict CSP that includes sandbox.
  7. Harden MinIO like production infrastructure. Rotate off default root credentials, restrict the console port to an admin network, set explicit IAM policies rather than anonymous presets, and audit mc anonymous state on a schedule.
  8. Shorten presigned URLs. Reduce expiry, avoid logging full signed URLs, and never let a client control response-header overrides for renderable types.
  9. Watch for dangling references. When a bucket is decommissioned, remove the CNAME the same day. A dangling record pointing at a re-registerable bucket name is subdomain takeover.
  10. Make exposure continuous. Inventory, probe anonymously from the outside, validate, and re-check on a schedule. A quarterly review of a cloud estate that changes daily is not a control.

How Trusteed CTEM helps

Dark Trusteed CTEM insight card: a public S3 object endpoint linked to a trusted CDN domain, a web app bundle host and an API endpoint, feeding a validation gate that splits 1,284 raw scanner hits into 412 actionable 'should_alarm' findings and 872 suppressed ones, with metrics for anonymous-read buckets (38), anonymous-write buckets (7) and validated findings (412).

  • Continuous attack surface and asset inventory. Domains, IPs, services, and technologies are discovered passively and actively under ongoing scan plans, so storage endpoints and the assets that reference them stay in one exposure graph instead of scattered across cloud consoles and spreadsheets.
  • Findings enriched with real-world context. Scanner-driven detection is correlated with catalog CVE data, EPSS/KEV context, and exploit references where available — so a bucket exposure that chains into an actively exploited issue moves up the queue on evidence rather than severity math.
  • A validation gate before the SOC queue. Not every scanner hit becomes an alarm. Trusteed validates exploitability and business context so dashboards and analyst queues focus on the actionable risk (should_alarm) instead of a wall of anonymous-access warnings.
  • Depth where it matters. A dedicated API surface testing worker and a Deep DAST worker cover the web and API paths that reference storage — the same paths where content-type confusion and stored-XSS patterns actually land.
  • Compliance and reporting views. Framework-oriented reporting makes it straightforward to evidence bucket posture and remediation progress over time, not only at audit season.
  • Vulnerability intelligence built in. KEV and emergent-threat narratives from the public Trusteed blog feed prioritization work, which matters when a storage misconfiguration becomes the entry point for a known-exploited issue.

Operate it from app.trusteed.io; product context and research live at trusteed.io.

Trusteed CTEM vs point tools

Capability Typical point tool (Nuclei, Trivy, generic scanner) Trusteed CTEM
Coverage model Template- or container-driven checks that run when you invoke them Continuous asset and service inventory with scheduled scan plans
Storage context Bucket checks only if you write and maintain your own templates Object storage exposure correlated with the domains, apps, and services that reference it
Exploitability context Raw template severity Findings enriched with catalog CVE data, EPSS/KEV context, and exploit references where available
Noise handling Every hit becomes a finding Validation / SOC gate that surfaces actionable risk for analyst queues
Web and API depth Generic web checks Dedicated API surface testing worker plus a Deep DAST worker
Workflow and reporting CLI output; ticketing and reporting are manual Operator workflow and dashboards at app.trusteed.io, with compliance-oriented reporting

Point tools are good at producing signals. CTEM is the workflow that turns signals into prioritized, validated, continuously re-checked exposure.

FAQ

Is a bucket ACL the same thing as a bucket policy? No. ACLs are legacy per-object and per-bucket grants; bucket policies are JSON documents evaluated by the service. Modern S3 defaults new buckets to Bucket Owner Enforced, which disables ACLs entirely. If ACLs are still active in your account, assume both surfaces need auditing.

Does enabling Block Public Access make my bucket safe? It closes the anonymous path, but not every exposure. Overly broad authenticated policies, long-lived presigned URLs, CDN misrouting, and dangling CNAMEs all survive it. Block Public Access is a required control, not a complete one.

How does content-type XSS work if the bucket is on a different subdomain? Browsers treat subdomains as separate origins, which blocks direct same-origin API access. But cookies scoped to .example.com are sent to cdn.example.com, and the page is served over your valid TLS certificate on your domain — enough for phishing, credential harvesting, and SEO poisoning. If storage is path-based or proxied on the application origin, the impact escalates to full session theft.

Is MinIO as risky as S3? The risks are the same class; the operations differ. Because MinIO is self-hosted, it often runs outside cloud-native posture tooling, with default root credentials, exposed consoles, and one-line anonymous policies. That combination is frequently worse than a misconfigured S3 bucket, not better.

How do I find publicly readable buckets before attackers do? Work from the external references you already publish — DNS CNAMEs, JavaScript bundles, mobile app strings, and certificate transparency — then probe unauthenticated list and read requests continuously. Anything you can discover without credentials is discoverable by anyone.

How is CTEM different from running scanners like Nuclei or Trivy? Scanners are excellent at executing checks: templates, container images, CI gates. CTEM is the surrounding discipline — continuous inventory of what actually exists, validation that a finding is genuinely exploitable in your context, and prioritization that keeps analyst attention on what needs action. You can run scanners forever and still not know whether an exposure is reachable, owned, or business-critical. CTEM answers those questions; scanners produce the raw material.

Can I tell if someone has already read or overwritten my objects? Sometimes. CloudTrail data events, S3 server access logs, MinIO audit logs, and GuardDuty S3 Protection findings are your evidence trail. Buckets created without logging enabled are effectively blind spots — which is itself a finding worth fixing before you need it.

Related resources

Join Our Newsletter

Trusteed keeps you informed: emerging risks, platform updates, and practical guides for faster defense.

S3 and MinIO Bucket Misconfigurations: CTEM Guide