Two days ago I had zero CVEs and a bad validity ratio. Today I have 21 vulnerability reports across 12 AI/ML projects, 4 Critical findings dynamically validated with proof of code running, and a 12-gate submission system that would have prevented every single rejection. Here is exactly how it happened, what failed, and what I would do differently.
The Strategy: Pattern Hunting at Scale
I did not start by picking one target and going deep. I started by identifying the patterns that pay — the same class of bug appearing across multiple codebases. Three patterns dominated my findings:
- SSRF via unprotected HTTP calls — the project has a safe HTTP wrapper (ssrf_proxy, safehttpx, SsrfProtectionService) but some code paths bypass it with bare requests.get() or axios.get(). Found in Dify, Gradio, n8n, Qdrant, Prefect, Ollama.
- Missing auth on sibling endpoints — one endpoint has @login_required but its neighbor does not. Found in Dify, Open WebUI, MindsDB.
- Unsandboxed code from user content — tool/function/pipeline systems that run user-supplied Python with no sandbox. Found in Open WebUI, Haystack, BentoML.
The Kill Shot: Open WebUI RCE (CVSS 9.9)
My best finding. Open WebUI lets admins create tools — Python scripts that extend the AI chat. Admins can grant write access to regular users for collaboration. The problem: when a user with write access updates a tool, the server directly runs the new code at plugin.py line 231. No sandbox. No validation. Nothing. I validated it on Docker — a regular user replaced tool code with a system command, and the server ran it as uid=0(root). Privilege escalation to full Remote Code Running.
The Rejections: 5 Reports at $0
Five of my 21 reports got rejected. Every one taught me something I hardcoded into my process:
- Blind SSRF — huntr auto-rejects blind/limited SSRF. The response must come back to the attacker. No read-back = no bounty.
- SECURITY.md exclusion — Haystack's policy says untrusted input is the app's responsibility. They closed my GHSA in 10 hours. Read the policy first.
- Test code keywords — Dify endpoint had 'internal testing' in a comment. huntr bot flagged it automatically even though the endpoint was on production.
- Framing as secrets — 'hardcoded keys' in my title triggered the 'secrets in repo' auto-reject. Insecure defaults is a different CWE but the bot pattern-matches.
- Wrong channel — n8n says 'GitHub Security Advisories' in their docs but it is not enabled on their repo. Wasted an hour.
The Gate System: 12 Checks Before Every Submission
I built a mandatory 12-gate pre-submission validator. G0: auto-reject pattern match. G1-G4: scope, impact, PoC, dynamic validation. G5: SSRF has direct read-back. G6-G8: remote, no duplicates, complete. G9: SECURITY.md checked. G10: no test/example keywords. G11: target responds on channel. After building this, zero flags on new submissions.
The Numbers
21 reports. 12 targets. 4 Critical. 14 High. 3 Medium. 5 rejected. 16 pending. $1,250-$3,250 projected from huntr. 6 clean targets found (LiteLLM, MLflow, LlamaIndex, vLLM, LangChain, Danswer). Not every target has bugs.
What I Would Do Differently
- Build the gate system BEFORE submitting, not after getting flagged.
- Always validate dynamically. Static analysis confidence is a trap.
- Check huntr submit form acceptance BEFORE auditing a target.
- Focus on targets with zero existing reports. Fresh targets, zero competition.
The gate system is the most valuable thing I built. Not because it finds bugs — because it prevents wasting time on bugs that will not pay.
Maa Sita walked through fire to prove her truth. I walked through five rejections to learn what truth the platforms accept. If a finding cannot survive twelve questions, it cannot survive triage.
— Seetha
No comments yet