run.veric.dev

Equifax — March–July 2017

Cost: ~147.9 million people exposed; settlements and remediation costs ~$1.4B+ · Time-to-detect: ~78 days (Mar 10 → discovery Jul 29, 2017) · Root cause class: T6 (information-flow reachability) — partial fit; see scope note below

What happened

Between mid-May and late July 2017, attackers exfiltrated personal data — names, Social Security numbers, dates of birth, addresses, and some driver's-license and credit-card numbers — on roughly 147.9 million people from Equifax's consumer-dispute portal. Per the US House Committee on Oversight's December 2018 report and the cited CVE record, the entry vector was an unpatched Apache Struts vulnerability (CVE-2017-5638). A patch had been released on March 7, 2017; Equifax did not apply it before attackers exploited the same flaw on March 10. The attackers then moved laterally for over two months. Detection was delayed because a network-monitoring SSL certificate had expired ten months earlier, leaving traffic uninspected. A Justice Department indictment in February 2020 attributed the intrusion to four members of China's PLA.

The pattern

A web-facing service held a direct path from "anonymous HTTP request" to "table containing the regulated PII of every adult American with a credit file." The breach itself was a CVE-management failure — outside any data-verifier's reach. The deeper architectural pattern that made it catastrophic is in scope: PII-class data was reachable from a public-internet handler with no policy-declared sink restriction enforced on the data path. Any architecture where regulated columns are joined into a table that any web-facing query can read, without an enforced policy gate between them, has this same blast-radius profile.

How veric would catch it

Honest scope first: veric does not catch unpatched-CVE deployment failures — that's CI/SBOM territory. veric catches the structural exposure that turned a CVE into 147M records. With a declared policy ("identity.ssn may not flow to any table reachable from public-internet handlers"), the verifier traces the model graph and flags: "sink dispute_portal.user_view is reachable from public handler WebController.dispute(); transitively joins identity.ssn via 3 model hops — T6 information-flow VIOLATED." This is the T6 PII-reachability tier (v0.4+ in the engine roadmap); the playground demonstrates the same primitive on a smaller analog.

Try it: open the example below and watch the verdict change as you toggle the offending pattern on and off.

See also

Sources

Reproduce in playgroundT6 · Information-flow reachability in the glossary

Opens the playground pre-loaded with a model that exhibits this pattern. Toggle the offending lines to watch the verdict change.