A big part of what I do for PentesterLab is reading CVEs. I spend a lot of time going through them: it's how I find interesting content, spot trends, and decide what to build next. The best ones end up as hacking labs or code review labs.
Every so often, something else happens. I'm reading the patch that's supposed to fix a vulnerability, and I find a way around it. The fix doesn't actually fix the issue.
When that happens, I report the bypass before I publish anything. The reason is simple: I don't want learners studying a broken patch and walking away thinking that's how you're supposed to fix the problem. A vulnerable patch dressed up as a solution is worse than no example at all.
It's thankless work, to be honest. Very little recognition: rarely a thank-you, a note, or a CVE of your own. But these are some of the best learning opportunities I come across. Watching how people fail while they're actively trying to fix something is fascinating. It tells you exactly where the hard parts are and what's difficult to get right.
The issues I keep running into (for the last 12 months) cluster around two classics: SSRF fixes and directory traversal. People get these wrong again and again, which also makes them, right now, some of the most rewarding things a code reviewer or web hacker can look for.
For directory traversal, the most common mistake is fixing the traversal but not
the path manipulation. People block ../ and call it done, forgetting
that you don't always need to traverse anywhere to reach data you shouldn't.
Say I'm logged in as admin123 and I can legitimately access
/var/files/admin123/. If the check is a naive prefix match, I might also reach
/var/files/admin/, because /var/files/admin123/ starts with
/var/files/admin. No ../ required. The path was never manipulated
in the way the filter expected; it just matched.
This is also why APIs like Go's new os.Root
matter so much for the future of secure applications. Instead of asking developers to reason
about every string trick, they constrain operations to a directory at the OS level.
For SSRF, the recurring mistakes are more specific. The big one is redirects: people forget
to stop the HTTP client from following them, or they validate the first URL but never apply
the same filtering to the redirect target. A request to an allowed host comes back
with a 302 pointing at http://169.254.169.254/, and the client follows it
straight to the internal service.
The other classic is DNS rebinding. The hostname resolves to a public IP when you validate it, and to a private IP a moment later when the request is actually made. Both are rarely reported as the next step after a bypass, and both are hard to fix.
What makes SSRF special is that it's the one major class where frameworks still don't hand you a safe default. SQL injection, XSS, CSRF, password storage, crypto: developers increasingly get secure defaults for all of them. For SSRF they're mostly on their own, and every mitigation they add (denylists, allowlists, redirect checks) tends to fix one case while opening another. That's why so many SSRF CVEs aren't carelessness. They're reasonable-looking protections that fail on a single edge case.
Sometimes I don't find a bypass. I find a fix I'd never have thought of, and that's valuable in its own way. It changes how I think about the whole class of problem.
A good recent example is CVE-2026-25738 in Indico. The fix monkey-patches
socket.getaddrinfo() to add a per-request DNS cache stored in Flask's
g object, so the hostname resolves to the same address during validation
and during the actual request. That closes the DNS rebinding window directly. I'd never seen
SSRF prevented this way, and I probably wouldn't have come up with it myself. I'm not in love
with the complexity of the code, but the idea is gold.
You can also see, very clearly, how a low-quality report shapes a bad fix.
Take CVE-2025-12203 in Vvveb. It
was reported as a null byte injection: the researcher found that
/.%00./.%00./.%00./ slipped past the directory filter. But that framing was
wrong. The real issue was non-recursive filtering, and the null byte was only one of many
characters that exposed it. Because the report focused on the null byte, the developer
blocked the null byte. The original payload stopped working; the actual vulnerability did
not. A precise description of the root cause would have produced a real fix. The
narrow report produced a narrow patch.
As I wrote in my book, CVEs are the most underutilised gold mine you'll find. New variants of old vulnerabilities, novel ways to fix them, bypasses, trends across an entire ecosystem: it's all in there, mostly ignored.
Sometimes the most interesting CVE is the one hiding inside someone else's CVE.
Want to build these skills hands-on?
PentesterLab has 700+ real-world labs on web hacking, code review, and vulnerability analysis. Start with a free account.