skip to content

A retro sweep on a two-year-old C2 domain returns 400 hits on staff browsing a marketing site. What happened?

level: middleimportance: should knowfreq 55%

answer

  1. artefacts change owners over time
  2. a correct match is not always a finding
  3. when was it adversary-controlled?
  4. validity window on the indicator itself
  5. intersect the sweep window with valid_until

basics

~10 s

The matches are real but the intelligence expired: the domain changed hands and now serves a marketing agency. An indicator only means something inside the window during which the artefact was adversary-controlled.

solid answer

~50 s

Nothing in the search misfired — those 400 proxy records genuinely contain that domain, and the people who generated them genuinely visited it. What expired is the *intelligence*. The domain lapsed, was re-registered by a marketing agency, and has been serving ordinary content since long before the window I swept. So this is not a false positive caused by a bad rule; it is a correct match against an artefact that is no longer adversary-controlled. The fix is a validity window on every indicator: a `valid_from` and `valid_until` derived from the reporting source, passive DNS showing when the hosting moved, or the re-registration date. The sweep window must then be intersected with that validity window before any match is raised. IP addresses on shared hosting need the shortest windows because they get reassigned in days; file hashes need none at all, because a hash identifies one immutable byte sequence.

code

json · 11 lines
json
{
  "type": "indicator",
  "spec_version": "2.1",
  "id": "indicator--8f1c...",
  "name": "Second-stage C2 (build-tool update campaign)",
  "indicator_types": ["malicious-activity"],
  "pattern_type": "stix",
  "pattern": "[domain-name:value = 'telemetry-sync.example']",
  "valid_from": "2024-03-11T00:00:00Z",
  "valid_until": "2024-09-30T00:00:00Z"
}

go deeper

for a junior

Know that indicators go stale, that an artefact named in an old report may have changed hands, and that a matching string is not automatically a finding.

for a middle

Explain validity windows concretely: where valid_from and valid_until come from, why a shared-hosting address ages in days while a file hash never ages, and how the sweep window intersects them.

for a senior

Demonstrate you would have caught this before 400 people were listed — validity intersection as a precondition of raising a match, plus re-validating an artefact's current ownership before acting on it.

for a principal

Own the policy for matches that name people: who may see a name attached to an expired indicator, how such records get retracted, and what the default expiry per artefact class is across every feed you buy.

## The match is correct; the intelligence is not The first thing to get right is the vocabulary, because the wrong word here sends the remediation in the wrong direction. - A **false positive** is a rule that was wrong about maliciousness — it matched something that never was what the rule described. - A **benign true positive** is activity that genuinely is the described behaviour but is authorised or harmless — the administrator who really did dump credentials, with a change ticket. - What you have here is neither. The string comparison was exact and the traffic was real, but the **artefact stopped belonging to the adversary** before the traffic happened. The indicator is *expired*. Calling it a false positive leads a team to "fix the rule", which is not where the defect is. The defect is that an indicator was matched outside the period in which it meant anything. ## Why artefacts change hands Indicators are not properties of an adversary; they are observations of infrastructure the adversary was using at a point in time. That infrastructure moves: - **Domains lapse and are re-registered.** A burned C2 domain is often abandoned; domain investors and legitimate businesses buy expired names precisely because they have residual traffic. Two years later it is somebody's campaign landing page. - **Addresses are reassigned.** A cloud or shared-hosting IP can serve a different tenant within days, and a single address behind a CDN or a large hosting provider serves thousands of unrelated sites simultaneously. - **Sinkholes.** After a takedown the domain may be pointed at a research sinkhole, so post-takedown hits mean "this host tried", which is meaningful, but in a completely different way from a live C2 hit. - **Some artefacts do not move at all.** A SHA-256 file hash identifies one exact byte sequence forever. A hash never changes hands, so it never expires — it only becomes less likely to be seen. ## The validity window The mechanism that prevents this is a validity window carried with the indicator. Structured intelligence formats express it directly: a STIX indicator object carries `valid_from` and `valid_until` alongside its `pattern`. When your feed supplies them, honour them. When it does not, derive them: - **first-seen / last-seen** dates in the reporting narrative; - **passive DNS**, which shows when the name's address records moved to a different hosting provider; - **registration data** — a creation or re-registration date after the reported activity is a hard stop; - **certificate issuance** for the name, which often marks a new owner standing up a real site. Where none of that is available, apply a default expiry **by artefact class**, and make the classes differ, because they age at completely different rates: days-to-weeks for an address on shared or cloud hosting, weeks-to-months for a domain, longer for a full URL including a distinctive path, and no expiry for a file hash. ## Where the window is applied The important design point is that the intersection happens **before a match becomes a finding**, not as a post-hoc filter after 400 records already exist. A retro sweep has two windows — the one you searched and the one the indicator was valid for — and only their overlap can produce a raised match. Everything outside the overlap is data you may keep for context but must not present as evidence of anything. If the sweep is already run and the records already exist, the second half of the answer matters: those 400 records name real employees. Somebody's name is now attached to a "threat indicator match" in a case system, potentially visible to their manager. Because the match carries no meaning, the honest action is to retract, not to leave the records sitting with a note. Purge or clearly annotate them, and tell whoever was informed. ## Re-validating before you act Before acting on any old indicator — sweeping it, blocking it, or escalating a hit on it — re-check the artefact's current state. Does the domain still resolve where the report said it did? Is the address inside a hosting range that serves unrelated tenants? Has the name been re-registered? Five minutes of re-validation prevents both directions of this failure: raising historic noise, and blocking a domain that is now somebody's genuine business dependency. ## The failure mode in the other direction Expiry is a two-sided risk. Expire too aggressively and you drop an indicator that is still live, because plenty of adversary infrastructure runs for years. The resolution is not a single global timer but per-class defaults plus re-validation on use — and a bias toward keeping high-context artefacts (full URLs, hashes, distinctive paths) longer than low-context ones (bare addresses).

  • Your feed supplies no validity dates at all. Where do you get them?
    Derive them. First-seen and last-seen dates from the reporting narrative, passive DNS showing when the name's address records moved to a different provider, a registration or re-registration date after the reported activity, and certificate issuance for the name. Failing all of that, apply a default by artefact class — short for shared-hosting addresses, longer for domains, none for file hashes — and re-validate the artefact's current ownership before acting on an old one.
  • Four hundred named employees now appear as indicator matches in your case system. What do you do?
    Retract them rather than annotate and move on. The matches carry no meaning, so no finding exists, and leaving a person attached to a "threat match" record is a real harm with no investigative value. I would purge or clearly void the records, notify anyone who was told, and change the pipeline so validity intersection is a precondition of raising a match instead of a cleanup step afterwards.
  • Doesn't aggressive expiry risk dropping infrastructure that is still live?
    Yes, and that is the other half of the tradeoff — plenty of adversary infrastructure runs for years. The answer is not one global timer but per-class defaults plus re-validation at the moment of use, and a bias toward keeping high-context artefacts such as full URLs and file hashes far longer than bare addresses, which are the ones that both age fastest and generate the most collateral.

A phone number in an old case file: it genuinely was the suspect's number in 2019, and dialling it in 2026 reaches a dentist. The digits did not change — the ownership did.

saying these in an interview costs you the question

  • Calls the 400 matches a false positive caused by a bad rule
  • Treats an ingested indicator as valid forever
  • Expires domains and shared-hosting addresses on the same schedule
  • Assumes a re-registered domain is still adversary-controlled
  • Leaves named staff recorded as indicator hits after retraction

context