Vigil Field Notes

Blockchain

How A Rewritten Database Still Got Caught

We played the attacker: changed a High Risk verdict to Low Risk, then fixed the evidence so the database agreed. The chain didn’t.

A security report is only useful if you can trust that nobody has quietly changed it. Most systems answer that with a hash stored next to the record. The trouble is obvious once you say it aloud: anyone who can edit the record can edit the hash too.

What Goes On-Chain

Every Vigil report has one exact canonical JSON form, and its SHA-256 is the report’s hash. Registering a report writes that hash, plus a hash of the domain when there is one, to a small smart contract called ThreatRegistry. The contract can only append. It has no function to edit or delete a record.

The test transaction’s data was 68 bytes: a function selector and two 32-byte hashes. The link, message or email never leaves the server.

The Attack

Our test script does what someone with write access to the database would do. It changes the verdict, then recomputes the stored hash so the edited report looks self-consistent.

tamper_report.pydemo only
risk_level  →  low_risk
report_hash →  sha256(edited canonical JSON)
after.tamper
Database check: report vs stored hash
Passed · fooled
On-chain check: report vs registry
Mismatch · caught
Independent check in the browser
Mismatch · caught
After restoring the original
Matched again

Why The Chain Saw It

The database check compares a report with a value from the same database, so the attacker controls both sides. The on-chain check ignores the stored hash completely. It recomputes the hash from the report as it stands now and asks the contract whether that matches what was committed. Anyone can write to the registry, so a match only counts if the record was written by Vigil’s own wallet. That stops an attacker registering their edited copy and pointing the report at it.

You don’t have to trust our server for that. The report page can hash the canonical text in your browser and read the contract directly. In testing, a SHA-256 computed outside the backend equalled the on-chain value.

What This Doesn’t Prove

  • We ran this first on a local Hardhat chain, then repeated it on the Sepolia public testnet (see the transaction). Sepolia is a test network, so this shows the check works, not that it has faced a real attack.
  • It protects a report only from the moment it is registered. A report edited before that has nothing to be compared against.
  • A record is only as trustworthy as the wallet that wrote it. Someone who stole Vigil’s signing key could write records that pass.
  • A record proves a report hasn’t changed. It doesn’t prove the report’s conclusion was correct.

Spotted Something?

Run an investigation