DeFi Security Alliance

Audit deliverables

How to Tell a Fake Audit Report From a Real One

A fake audit report is a document that claims a security review which never happened, or a real report that no longer describes the code you are about to use. Both are caught the same way, by checking the report against the two things it should name: the commit that was audited and the deployed address it maps to. This guide gives the procedure, and a measurement of how often published reports carry those anchors at all.

Paper certificate with a peeling blue seal under a magnifying loupe, representing verification of a smart contract audit report's authenticity.

Key facts

The two anchors
A commit hash ties the report to a revision of the source. A contract address ties it to the chain. Without one of them a genuine report cannot be checked
Measured archives
19 of the 23 DSA member firms publish a reachable report archive, 17 of them on GitHub (September 3, 2026)
Measured anchors
Of 50 published reports sampled from those archives, 31 state a commit, 17 an address, 6 both and 8 neither
Proxy slot
ERC-1967 keeps the implementation at slot 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc, and Etherscan's proxy detection is a heuristic it sums up as trust, but verify
Audit drift
At Nomad only 18.6 percent of the on-chain code in Replica.sol had been audited, in the contract the attack went through

Why a forged report works

A PDF with a logo on it is not evidence. It is a claim about evidence held somewhere else, and the somewhere else is what a reader almost never checks. Forgers know this, which is why the cheapest fraud in this market is a document rather than a contract.

The named cases follow one pattern. In , CertiK stated publicly that it had never audited Keep3rLink and that the report circulating in the project's Telegram was fake. In , PeckShield said the report on DxDy.Finance's website had been forged from its own earlier work for a different protocol. CertiK's own account of brand misuse, published in , names the mining site Wixpool and the Lymex exit scam, which listed CertiK as its auditor after failing the firm's identity checks and cost users around 300,000 dollars.

Forgery is only the crude version. The subtler one is a real report that no longer describes the code in front of you, which is the failure the rest of this guide is built around. Both are caught by the same two anchors.

The commit
The exact revision of the source the firm reviewed, which turns "we audited this protocol" into a checkable statement about a specific set of files.
The address
The deployed contract the code became, on a named chain, which is the only thing your money actually touches.

A report that states both can be checked by anybody with a block explorer. A report that states neither cannot be checked at all, whether or not it is genuine. As the measurement below shows, the second kind is common.

The chain from an audit report to the code that runs today Four links. The report names a commit, the commit is a revision in a repository, the revision compiles to the bytecode deployed at an address, and behind a proxy that address forwards to whichever implementation the admin last set. Each join has a way to break: no commit stated in the report, source that is unverified or matched only loosely, and an implementation upgraded after the audit. EACH JOIN IS A PLACE THE CHAIN BREAKS The report a PDF naming scope and a commit Audited revision files in the repository at that commit Deployment bytecode at an address on a named chain What runs now the implementation the admin last set FIRST JOIN no commit stated 19 of the 50 reports we sampled SECOND JOIN source is unverified or matched loosely, which ignores constructor arguments THIRD JOIN proxy was upgraded after the audit, as at Nomad, where 18.6 percent was audited
Verification walks this chain from left to right. A report you cannot walk past the first join tells you nothing about the address in front of you, whoever signed it.

Step 1: get the report from the firm, not from the project

Never verify a document against the site that handed it to you. The first move is to find the same report where the auditing firm publishes, then compare. CertiK gives the same instruction for its certificates: the authenticity of a badge should be checkable on the issuer's own site, and if the issuer link leads nowhere, the firm may not exist at all.

So we measured what the firms publish. On , for each of the 23 DeFi Security Alliance member firms, we looked for a public report archive in two places: a repository on the firm's GitHub account, ranked by how many report PDFs it holds, and an index page on the firm's own domain, which counted only when it showed at least three PDF links or ten distinct report links. The script and the raw results are kept in the site repository as seo/research/audit-report-anchors-2026-09-03.json.

Where the 23 DSA member firms publish audit reports,
ChannelFirmsNotes
Public archive found19 of 23Either channel counted
GitHub repository17From 3 PDFs at Blaize to 3,927 at Cyberscope
Index on the firm's domain9Nine firms publish in both places
Nothing found4Expelee, Callisto Network, Solidity Finance and PaladinAI, where the site was unreachable, redirected or listed no reports at the paths we tried

Two practical points come out of that. A GitHub archive is the better anchor, because a repository carries file history: you can see when a report was added and whether it was changed afterwards, which a PDF emailed to you cannot tell you. And an archive under a firm's own name is not the same as an archive of that firm's work. SlowMist keeps its reports in a repository called Knowledge-Base and SolidProof keeps its in one called Projects, so searching for the word audit finds neither.

If the firm has no public archive, the report is not automatically fake. It does mean the only route left is asking the firm directly, which is the last section of this guide.

Step 2: match the commit and the file list

Open the report's scope section. ChainSecurity's guide to reading audit reports puts the point plainly: the scope names the files inside the audit and excludes those outside it, and reports either give the commit hash of the repository or list the files with their hashes. Sherlock, describing its own process, calls an audit a point-in-time review of a scoped codebase.

Three real reports show what a usable scope looks like. Trail of Bits reviewed NFTX Protocol v2 at commit c8ddc72b4400ad1e12ed03f4369b765371564a00 in , on-chain contracts only. OpenZeppelin's USDT0 review names the repository and commit 01cdf1d74c1bd4d9a664de4755ac5112f2a9988c with two files formally in scope, dated . Code4rena's Sequence report names commit 4c0a98259624b80ac22385e40f83a2145e8e33e2 across 34 contracts in .

Then we checked how common that is. From the 17 GitHub archives we sampled 50 published reports, three per firm, spread across each archive rather than taken from the top, converted them to text and looked for the two anchors. Sampling per firm rather than per report is a deliberate choice with a cost: an archive of three reports weighs as much as one of 3,927, and three of the fifty are reviews of OpenZeppelin's own library, which has no single deployment to name.

Anchors in 50 published reports sampled from 17 member firm archives,
What the report statesReportsWhat a reader can do with it
A commit hash31 of 50Compare the audited revision against the repository
A contract address17 of 50Compare the report against the chain
Both6 of 50Follow the chain from report to deployment end to end
Neither8 of 50Nothing, even if the report is genuine
A repository URL26 of 50Find the code, though not the revision

How far to take this depends on what is at stake. Ten minutes before a small swap buys step 1 and the verified-source check in step 3, which together catch outright forgery. A treasury allocation or a listing review has no business skipping the commit match or the proxy read, because those are the two places a genuine report quietly stops describing the deployment.

Six in fifty is the number worth carrying away. The commit is the more common anchor, which fits how firms work: they are engaged against a repository, not against a deployment that may not exist yet. That leaves the reader holding a genuine report with no stated link to the address they are about to send money to, and closing that gap is step 3.

When the scope names files rather than a commit, read the list carefully and count what is missing. A protocol whose report covers the token and the staking contract, but not the treasury or the upgrade admin, has been audited exactly as far as that list goes.

Step 3: match the deployed address, then the implementation

Take the address from the project, not from the report, because the report is what you are testing. Open it on a block explorer and read three things in order.

  1. Is the source verified. Verification proves that published source compiles to deployed bytecode. It says nothing about whether that code is safe, and nothing about who reviewed it.
  2. How well it matches. On Etherscan an exact match covers the deployed code together with its constructor arguments, while a similar match happens automatically when the bytecode matches an already verified contract and, in Etherscan's words, does not take into account constructor arguments. Two deployments of the same code with different owners and different fee settings both pass as similar. Sourcify draws the same line: an exact match is byte for byte identical excluding libraries and immutables, and anything less means comments, variable names or source paths may differ from what you are reading.
  3. Whether it is a proxy. If it is, the code you just read is a forwarder and the logic lives elsewhere.

For the third point, read the implementation slot yourself rather than trusting a label. ERC-1967 fixes it at 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc, derived as keccak256('eip1967.proxy.implementation') minus one, with the admin who may replace it at 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103. One command answers it, and the address is the last 20 bytes of a 32 byte word, so the leading zeros are padding rather than part of it.

cast storage <proxy> 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc --rpc-url <rpc>
The same value comes back from a plain eth_getStorageAt call, so a node URL is enough when Foundry is not installed.

Two more patterns need a different lookup. A beacon proxy points at a beacon instead, ERC-1822 keeps the logic address under keccak256("PROXIABLE"), and an ERC-2535 diamond has no single implementation at all, only facets mapped per function selector.

Reading a proxy without trusting the explorer's label
PatternWhere the code address livesWhat to check
Transparent or UUPS under ERC-1967Storage slot ending d382bbcRead the slot, then verify the implementation address separately
Beacon proxyBeacon slot ending 5133d50, then the beacon's own implementationTwo hops, and the beacon can be swapped for every proxy at once
UUPS under ERC-1822keccak256("PROXIABLE")A different slot from ERC-1967, so a single lookup can miss it
Diamond under ERC-2535Many facets, one per selectorEnumerate facets through the loupe functions the standard requires

Etherscan is candid about the limits of its own detection. It looks for bytecode patterns associated with known proxy implementations, notes that contracts using libraries can trigger the same signal without being proxies, and warns that the proxy may not be delegating where it appears to. Its own summary of the situation is "Trust, but verify". For custom proxies it relies on a trace of an earlier delegatecall, so a freshly deployed proxy can show nothing at all.

What an upgrade does to the report

An audit describes a revision. An upgradeable protocol describes whatever the admin last deployed. Between those two facts sits the failure mode that has cost more than any forged PDF.

Nomad is the case to know. Reviewing the bridge after its loss, Zellic compared the on-chain code against the audited code and found that only 18.6 percent of the on-chain code in Replica.sol had been audited, in the contract holding the functions the attack went through. The report was real. The deployment had moved.

Three consequences follow for anybody checking a report today.

  • Date the implementation, not the report. An audit dated last year describes last year's code only if nobody upgraded since. Read the proxy's admin events to see when the implementation last changed.
  • A verified proxy is not a verified implementation. Etherscan stores the implementation it found when verification last ran, so after an upgrade the process has to be repeated before the explorer reflects reality.
  • Ask who can upgrade. The admin slot names the account, and one key behind that account is the difference between an audited system and an audited snapshot of one.

Compiler settings are the honest counterexample to all of this. Sourcify's documentation traces how a single changed byte in the source alters the metadata hash and so the bytecode, which is why a mismatch is a question to ask rather than proof of fraud. Optimization runs and the metadata setting are the usual culprits, and the fix is to ask the team for the exact compiler configuration and rebuild.

Badges, scores and what they actually assert

A badge on a project's website is an image and a link. The image proves nothing, so follow the link and see whether it lands on a page the issuer controls that names this project.

Scores are a different claim again. CertiK's Skynet score combines six categories, the aggregation is proprietary, and it explicitly incorporates third-party audit findings, so a high score is not a statement that CertiK audited anything. Its project pages make the distinction visible: the Celestia page we opened shows a grade of A alongside "CertiK Audit: No" and a separate third-party audit field. Skynet's own methodology says outright that many listed projects lack a comprehensive audit or do not disclose one.

Automated scanners sit lower still. A honeypot check, a holder distribution chart and a bytecode pattern match against known scam templates are useful signals about a token, and none of them asserts that a human reviewed the code. Treat "audit score 92" from a scanner as what it is, which is a heuristic, not an audit. Our analysis of audit companies profiles the firms behind the badges, and the CertiK company analysis covers what that firm's own marks include.

The KYC badge deserves its own sentence, because it is the most misread mark in the market. It says a firm collected identity documents from a team. It does not say the code was reviewed, and CertiK's guidance on spotting forged certificates is aimed at exactly this confusion.

When it does not check out

Rank what you found. A missing commit is a documentation failure, common enough that our sample found it in 19 of 50 reports. An address that does not match the audited code is a live risk. A firm that never audited the project at all is fraud, and the sequence below is worth following in order.

  1. Ask the firm through a channel you found yourself. Use the contact on the firm's own domain, never a Telegram handle supplied by the project. CertiK's brand-misuse page points readers at Skynet, a report form and its moderated channels, and the firm runs an employee verification tool precisely because scammers impersonate its staff to sell audits.
  2. Put the question in writing and keep it narrow. Ask whether the firm audited this repository at this commit for this client on this date, and whether the attached PDF is the report it issued. A firm that will not confirm a document bearing its own name has told you something.
  3. Check the report against its own internal logic. CertiK's guidance on fake certificates lists spelling errors and missing dates as the visible tells. The Bureau Veritas report examined by CryptoInformer in went further: a document dated referenced an audit taking place on , more than two weeks after it was written.
  4. Say what you found. Publicly, if the project will not answer.

Regulators have started saying the same thing in their own register. In , the SEC's chief accountant warned that non-audit crypto engagements were being marketed as if they were audits, and told firms whose names are misused to disassociate publicly.

Finally, keep the exercise in proportion. Verification tells you the report describes the code. It does not tell you the code is safe, and the largest losses do not come from the class of bug an audit hunts: a four-year study of 23,818 public audit findings against 218 exploit incidents totalling about 7.76 billion dollars found that private key compromise, phishing and social engineering account for roughly 49.6 percent of losses while barely appearing in audit findings at all. What a team can run before paying anybody is in the fuzzing guide. Reading a report's findings properly, and the wider diligence process around it, are the subjects of the next two articles in this series, how to read an audit report and the due diligence walkthrough.

Frequently asked questions

How do I check whether a CertiK audit is real?

Find the project on Skynet rather than trusting the badge on its website. The project page states separately whether CertiK audited the project and whether a third party did, so a high score with CertiK Audit set to No is not a CertiK audit. If the page does not exist or contradicts the PDF you were sent, use the report form and moderated channels on certik.com, and check the sender domain of any mail claiming to come from the firm.

Does this work on a chain without Etherscan?

The three steps do, the tools change. Blockscout verifies through its own interface and through Sourcify, and the Verifier Alliance shares one verified-contract database across Blockscout, Routescan and Sourcify, so an address on a smaller EVM chain usually has somewhere to check. What does not travel is the proxy convenience: read the ERC-1967 slot with a node call rather than looking for a Read as Proxy tab. On a non-EVM chain the storage slot conventions do not apply at all, and the commit match in step 2 becomes the whole of the check.

The project shows a different version of the report than the firm does. Which one counts?

The firm's copy, and the difference is worth reading rather than dismissing. Reports are reissued after fixes, so the same engagement can have an initial version with open findings and a later one where they are resolved, both genuine. Compare the commit each version names: a later commit means a real second pass, the same commit with fewer findings means the severity was renegotiated rather than the code fixed, and only the firm can say which.

What if the firm will not confirm the report?

Treat silence as an answer and act on the risk rather than the document. Ask once, in writing, whether the firm audited this repository at this commit for this client, using a contact you found on the firm's own domain. Firms that learn of forgeries do disclaim them publicly, which is how the Keep3rLink and DxDy.Finance cases entered the record, so a non-answer about a document bearing the firm's name is itself informative.