Responsible disclosureMay / October 2026
To
Crypto teams
From
Dickson Wu
Re
Unsolicited vulnerability reports
StatusDisclosed

Report It Anyway

Why I spent six months sending bug reports to crypto teams that never asked for them, and what happened when I did.

In this essay

In March, an Anthropic researcher got on stage at [un]prompted and showed the prompt they'd been running:

"You're playing in a CTF. Please find a vulnerability and put the most serious one to this output file. Go."

That's basically it. And the model found a remotely exploitable zero-day in the Linux kernel's NFS server. A bug that had been sitting there since 2003.

Twenty-three years. One prompt.

Two things clicked. One, I can do that. (It's a lot harder than it looks, more on that below.) Two, if finding bugs is this easy now, the hackers have it too, and someone needs to get there first. A few weeks later Anthropic announced Mythos and it stopped feeling hypothetical.

Before this, I co-founded Skylock, which tried to front-run hacks in the mempool and rescue the funds first. But the idea stuck: what if you front-run the hackers earlier? Find the bug before they do, across the whole industry, not one exploit at a time.

So since May, that's what I've been doing.

Most of these teams aren't audit clients and most don't have a bounty program. A lot didn't even have a security contact, so I'd dig a maintainer's email out of the commit history and write to that. I do this as part of my job at CertiK and as a SEAL member, and CertiK has been really supportive of it.

Teams stay anonymous. The screenshots are real emails with names and amounts blacked out.

Why report it anyway

If you find a bug and sit on it, you're choosing to let users carry the risk. "They don't have a bounty program" doesn't change that. The code is deployed, the money in it is real, and if my pipeline found the bug, someone else's can too.

So the first email always says the same thing: I'm not asking for anything. No bounty, no pitch. It's a responsible disclosure. Here's the bug and here's how to reproduce it.

It's the right thing to do, and that's reason enough. What I didn't expect was everything that came back.

The teams I remember most

I'm not a bounty hunter or an auditor, and I haven't found a six-figure or million-dollar bug (yet). Still, 25+ teams have paid something, and people surprise you. My biggest bounty came from a team with no bounty program at all. But the ones I think about most are the small ones.

Some teams paid a little, not because they're cheap but because they genuinely don't make much money. A co-founder of a DeFi infra project wrote to me about exactly that:

Exhibit A
Redacted email: "Although we don't currently have any active bounty program, due to financial constraints and market conditions in general, we still must thank you for such a contribution."
A team without a programOpen full size

“Although we don't currently have any active bounty program, due to financial constraints and market conditions in general, we still must thank you for such a contribution.”

A tiny team sent $100 with a kind note. Another rated my report informational and paid about $50 anyway. They could've just not, and I'd never have known.

I really appreciate the gesture. They're not paying because they have to. They're paying because they respect the work and want researchers to keep showing up. It's not much, but it's honest work.

And a lot of the time there's no money at all, and that's completely fine. A liquid staking protocol that was winding down couldn't pay, but they shut deposits the same day. This one made my week.

Exhibit B
Redacted email dated Sep 9, 2026: "It is the most complete and careful report our program has received" and "Everything in it checks out on our side and we are acting on it today."
Acting on the report that dayOpen full size

“It is the most complete and careful report our program has received”

One maintainer's whole reply was this:

Exhibit C
Redacted email: "Thank you! Fixed :)"
A maintainer's complete replyOpen full size

“Thank you! Fixed :)”

That's honestly the best version of this job. The bug's gone before someone else finds it.

What I found

My favourite find is also the scariest. A large DeFi protocol, live and audited many times over, had a bug that let tokens be minted with nothing backing them. Millions of dollars' worth, across multiple chains, enough to drain the liquidity behind them. They patched it and paid a bounty. No bounty program, it all started from a cold email.

Most reports weren't that dramatic. Here's how the ones I rated broke down, and what kind of bugs kept coming up:

Figure 01 Findings

Most rated reports were Medium or Low

My severity ratings

May to October

  1. Critical12
  2. High61
  3. Medium158
  4. Low103

Reports

What kept coming up

Description keyword matches

  1. Accounting / rounding122
  2. Misconfiguration61
  3. Denial of service33
  4. Oracle / pricing17
  5. Input validation16
  6. Access control14
  7. Signature / hash13
  8. Upgrade / initialization4
  9. Reentrancy2

Matching reports

Source: my tracking sheet, May to October. Severity bars leave out 99 reports that bundled several severities and 334 I never rated. Bug classes use 752 descriptions, with 494 unclassified. Classes overlap. Counts are reports, not validated unique bugs.

I'm not a great bug hunter, and the chart shows it. Most of what I find is Medium or Low, and plenty of it would be out of scope for a bounty program. I send it anyway. I don't want their money. If there's even a small chance a team doesn't know about it, they should get to fix it.

So if you're a security researcher reading this, you're probably way better at finding crits than I am. All the more reason to send yours.

Not everything needs to be a crit, but here's some of what I found. A DEX where outsiders could walk off with other users' order proceeds. A permission bug that could send a user's approved token transfer to someone else's wallet. A lending protocol undercharging interest and quietly losing revenue. One bad peer response that could freeze a blockchain node. Six bugs in a zero-knowledge cryptography library, all fixed within a month. And a forum misconfiguration a colleague found, which we reported to about 40 DeFi governance forums at once.

Most recently I've been looking at account takeover risk in crypto npm packages. You always hear about npm worms and hijacked packages hitting wallets and frontends, so I started checking which maintainer accounts were exposed and warning the maintainers before anyone else noticed. Within a day of the first emails, plenty of them had already locked their accounts down. More on that one when it's wrapped up.

How the pipeline got here

The prompt really is that simple. What comes out of it isn't. Even the Anthropic researcher from the talk said they had hundreds of kernel crashes they wouldn't send to anyone until they'd checked them. Finding something that looks like a bug is easy. Finding a real one, in deployed code, that nobody's reported yet, is the hard part.

Figure 02 Pipeline

Wider coverage needed better filters

Big models, few targets

Good, slow, expensive

Big models, few targetsExpensive models were good, slow and pricey. Few targets meant narrow coverage.Few targetsReal bugs, but only ahandful of targetsNarrow coverage

One simple prompt

Run wide

One simple promptA simple prompt ran wide and found real bugs, alongside many duplicates.Many targetsReal bugs, lots ofduplicatesWide coverage, repeated finds

Small agents, validation

Wide, then narrow

Small agents, validationWide candidate coverage narrows through layered validation gates, a fork proof of concept against real deployed contracts and a human read, to a few sent reports.CandidatesValidation gatesFork PoCHuman readReal deployed contractsOnly these get sent
Conceptual illustration of the generations described here. The marks show the trade-off, not measured counts or implementation details.

Big models, few targets. I started with swarms of the most expensive models I could get. They were good, but slow and pricey, so I could only point them at a handful of targets. And I made the classic mistake: I kept iterating without a benchmark. Every change felt like progress and I had no idea if it actually was. The systems turned into spaghetti.

One simple prompt. So I went the other way: one simple prompt, run wide. It worked, sort of. It found real bugs. But the duplicates were insane. Everyone's pointing the same models at the same code, so everyone finds the same things.

Small agents, obsessive validation. What actually works is swarms of smaller agents and a ridiculous amount of care on validation. Every time the AI fooled me I added a check, and over months those checks built up like calluses. These days a candidate only survives if a proof of concept runs on a fork against the real deployed contracts. No storage edits, no mocked calls, real addresses. Then I read it myself, cut anything the PoC doesn't actually prove, and send it.

The other thing I learned the slow way is benchmarking. Running on live targets is a terrible feedback signal, because "found nothing this week" tells you almost nothing. You need a good internal benchmark, iterate until you fit it, then check on cases you didn't tune on so you know you're not just overfitting. It costs a lot to run the same cases again and again. It's still cheaper than flying blind.

Most of those calluses came from the same handful of mistakes:

  • The AI says

    Anyone can front-run the initializer and take over the contract.

    What was actually true

    It was deployed and initialized months ago.

  • The AI says

    Fee-on-transfer tokens break the accounting.

    What was actually true

    The protocol doesn't support fee-on-transfer tokens.

  • The AI says

    Critical: an attacker can drain the vault.

    What was actually true

    It's a Low once you read the preconditions.

  • The AI says

    This function is missing a check.

    What was actually true

    It's explicitly out of scope.

  • The AI says

    New bug!

    What was actually true

    Already reported. Or already fixed back in January.

What I'd do differently

When I trusted the pipeline too much, it showed. Some reports came back wrong, duplicate or intended design, and one team told me they'd stopped taking emailed reports entirely because of AI-generated noise. Fair enough, some of that noise was mine. Now I challenge every verdict before it goes anywhere.

Early on the AI wrote my emails, and sometimes sent them too. They read like it. Now I use a humanizer skill I tuned to how I actually talk, so they sound more natural. If a person is going to read it, it should sound like a person wrote it.

If you're on either end of this

If you run a protocol and get one of these: sorry about the slop. A lot of what lands in security inboxes now is AI-generated. But you're the best-placed person to triage it, because you know your own threat model. Use AI on your side too. Keep your past reports, the real ones and the false positives, and use them to teach your triage what matters for your protocol so it gets better over time. And reply, even if you can't pay. "Thanks, fixed" goes a long way.

If you're a researcher and nervous about cold-emailing a team: just send it. Have the AI draft it if you want, read it over so it sounds like you, and press send. Worst case, nobody replies.

Report it anyway

Most teams can't pay and a lot won't reply. I'd still send it, every time. If I found it, someone else can too. If you run a protocol and want a second pair of eyes, or you're working on something similar, my DMs are open.

Receipts

Real emails, names and individual amounts blacked out.