Skip to main content

The repository nobody deleted from the install guide

Metadata

  • Title: The repository nobody deleted from the install guide
  • Source of the case: Real HackerOne report #1791558 (Brave)
  • AWS service(s): S3, DNS (Route 53 / CNAME)
  • Risk archetype: Ghost reference — a live DNS name and a published guide point at a bucket that no longer exists and can be re-registered by anyone
  • One-line hook: Can you prove this DNS name resolves to a bucket an attacker can claim?

0. The challenge (what the reader does first)

Scenario given to the reader:

Brave's APT repository — the bucket Linux users add to install the browser via apt-get — was deleted during some infrastructure change. The DNS name brave-browser-apt-release.s3.brave.com still exists and still CNAMEs to the S3 endpoint. A community install guide checked into a Markdown file still tells people to add that repository. You have the export below and nothing else.

Evidence they're handed (and nothing else):

{
"dns_record": {"name": "brave-browser-apt-release.s3.brave.com", "type": "CNAME", "value": "brave-browser-apt-release.s3.brave.com.s3.amazonaws.com"},
"s3_bucket_exists": false,
"referenced_in": ["community-install-guide.md"],
"http_response": {"status": 404, "body": "NoSuchBucket"}
}
  • The JSON export above. No AWS credentials. No live account. No scripts.

The questions they must answer from the evidence alone:

  1. The DNS record still resolves, but the bucket is gone (404 NoSuchBucket) — is this name dangling, and is the underlying bucket name free to register right now?
  2. Nobody has claimed the bucket yet, but the name is unowned and globally registerable — what is the blast radius the moment someone does, given apt-get runs package installs as root?
  3. Which path makes this exploitable — the live CNAME pointing at an unclaimed S3 endpoint?
  4. Is there a second amplifier beyond DNS — the published community-install-guide.md that keeps sending new victims to the dead reference?
  5. What single rule, enforced at teardown, would have made it impossible to delete a bucket while a DNS record or a doc still references it?

1. The manual problem

To answer by hand you have to correlate three independent facts that live in three different systems. The DNS zone says the name resolves. The S3 control plane says the bucket does not exist (404 NoSuchBucket). The docs repo says people are still told to trust it. None of those systems knows about the other two. A DNS audit shows a healthy record. An S3 inventory shows the bucket is simply absent — and absence does not raise an alarm, because "this bucket is not here" looks identical to "this bucket was never here." Nobody owns the join. And the dangerous part — that S3 bucket names are a global namespace, so the freed name is immediately registerable by anyone in any account — is knowledge you have to bring; it is not in any of the three exports.


2. The reasoning wall

What they hitWhat they said / would say
The DNS record looks healthy in isolation"The zone file was fine. The record resolved. I had no reason to look at what was on the other end."
An absent bucket reads as a non-event, not a finding"S3 just says the bucket isn't there. That's not red. Nothing told me 'this name is now up for grabs.'"
The real risk lives in the gap between DNS, S3, and a docs file"Three green dashboards, and the vulnerability was in the seam between them that nobody owned."

The insight the reader should reach on their own:

A reference that outlives the thing it points to is not missing data — it is an open invitation, and proving that requires joining DNS to ownership, not checking either one alone.


3. Why scanners miss or flatten it

A per-setting scanner checks one resource at a time. The DNS scanner validates that brave-browser-apt-release.s3.brave.com is a syntactically valid CNAME and that it resolves — it passes. The S3 scanner enumerates buckets you own; a bucket you don't own and that doesn't exist simply never appears in the inventory, so there is nothing to flag. Neither tool can see the edge: a live name pointing at a target that no account currently owns, in a global namespace where the name is free to claim. The specific thing no single-resource scanner can express is "this CNAME's target is registerable by a stranger." That is a relationship between a record that exists and a resource that does not — and a checklist that walks resources one at a time has no row for a resource that is absent.


Pivot point. Everything above is the gap. Everything below is Stave filling it. The reader has now done the work and hit the wall. Only now does the tool appear.


4. The evidence Stave consumes

The same static export the reader had — no new privileges, no live cloud:

{
"dns_record": {"name": "brave-browser-apt-release.s3.brave.com", "type": "CNAME", "value": "brave-browser-apt-release.s3.brave.com.s3.amazonaws.com"},
"s3_bucket_exists": false,
"referenced_in": ["community-install-guide.md"],
"http_response": {"status": 404, "body": "NoSuchBucket"}
}

Stave normalizes this into an observation snapshot holding two correlated facts on one logical asset: a DNS record whose target is an S3 endpoint, and the existence state of the bucket that endpoint names (exists = false).


5. The reasoning Stave performs

  • Control / invariant: CTL.DNS.DANGLING.001 — no DNS record may point at a resource that does not exist. CTL.S3.BUCKET.TAKEOVER.001 — no referenced S3 bucket name may be unowned and claimable.
  • What it evaluates: CTL.DNS.DANGLING.001 joins the CNAME's target to the existence of the resource on the other end and fires when the target resolves to a bucket whose s3_bucket_exists is false. CTL.S3.BUCKET.TAKEOVER.001 confirms the named bucket is absent from any owned inventory and therefore registerable in S3's global namespace — encoding the takeover risk the reader had to infer.
  • Verdict produced: Both controls fire and consolidate into one Issue. An unknown bucket-existence value is treated as a violation, not as "safe by default," so a missing fact never silently clears the finding.
Issue: brave-browser-apt-release.s3.brave.com — dangling reference to claimable bucket

CTL.DNS.DANGLING.001 NON_COMPLIANT
asset: dns://brave-browser-apt-release.s3.brave.com (CNAME)
evidence: CNAME target brave-browser-apt-release.s3.brave.com.s3.amazonaws.com
resolves; bucket existence = false; http 404 NoSuchBucket
verdict: live DNS record points at a non-existent resource

CTL.S3.BUCKET.TAKEOVER.001 NON_COMPLIANT
asset: s3://brave-browser-apt-release.s3.brave.com
evidence: bucket does not exist and name is unowned in the global namespace;
referenced_in = community-install-guide.md
verdict: bucket name is claimable — APT repo can be served by an attacker

security_state: NON_COMPLIANT

6. The prevention artifact Stave produces

  • Artifact: A teardown guardrail (SCP-style policy) that forbids deleting an S3 bucket while any DNS record or tracked document still references it, plus the manual remediation: remove the dangling CNAME and the guide reference, or reclaim the bucket name in the owning account.
  • What it forecloses: The latent state from question 2 — the moment a stranger registers the freed name and starts serving .deb packages to apt-get. With the guardrail, the bucket cannot be deleted until its references are gone, so the name is never left dangling and claimable.
# Teardown guardrail: deny bucket deletion while references remain (SCP)
{
"Sid": "DenyDeleteWhileReferenced",
"Effect": "Deny",
"Action": "s3:DeleteBucket",
"Resource": "arn:aws:s3:::brave-browser-apt-release.s3.brave.com",
"Condition": {
"StringEquals": {"stave:reference-count": "0"}
}
}
# stave:reference-count is set from the DNS+docs reference index;
# deletion is permitted only once it reaches 0.

# Manual fix until references are cleared — choose one:
# (a) remove the dangling reference
aws route53 change-resource-record-sets --hosted-zone-id <zone> \
--change-batch '{"Changes":[{"Action":"DELETE","ResourceRecordSet":{
"Name":"brave-browser-apt-release.s3.brave.com","Type":"CNAME",
"TTL":300,"ResourceRecords":[{"Value":"brave-browser-apt-release.s3.brave.com.s3.amazonaws.com"}]}}]}'
# ...and delete the repo line from community-install-guide.md
# (b) reclaim the name so an attacker cannot
aws s3api create-bucket --bucket brave-browser-apt-release.s3.brave.com --region us-east-1

7. What the team no longer does manually

BeforeAfter Stave
Cross-check DNS zones against S3 inventory by hand to find records with no live targetOne control joins record-to-existence and proves the dangling reference deterministically
Recognize from memory that a freed S3 name is globally registerableThe takeover invariant encodes "absent bucket name = claimable" and re-checks it every run
Hope a deleted bucket's references were also cleaned upA teardown guardrail blocks deletion until DNS and doc references reach zero

Positioning line for this case

Stave proves that brave-browser-apt-release.s3.brave.com points at a bucket that no longer exists, proves the freed name is claimable by anyone serving root-level .deb packages, and emits the teardown guardrail that makes deleting a referenced bucket impossible.


Reuse checklist

  • A reader could attempt section 0 with zero Stave knowledge
  • Stave is not named or shown before the pivot point
  • Section 2 quotes are real (or honestly plausible), not slogans
  • Section 3 names the specific thing per-setting tools can't see
  • Section 6 closes the exact latent state raised in section 0, question 2
  • The title names the failure, not the product