Vulnerability Research - The Unofficial Don’t Waste Six Months of Your Life Playbook Part 2

/nl_img1

Part 1 can be found at https://zetier.com/vulnerability-research-unofficial-playbook-part-1/.

Tactic #11: Here’s Johnny

Secure software is supposed to have a few narrow doors where dirty, untrusted input officially becomes clean, trusted data. Those are the trust boundaries. Find them, then poke them like you hate them:

Audit every validation at these choke points like they personally owe you rent. One sleepy strcmp or forgotten bounds check and boom—the whole security model faceplants.

Tactic #12: Closing Time

Most memory corruption bugs don’t happen during happy, normal usage. They hide in the messy moments when the program is desperately trying to clean up its own mess during connection close, teardown, shutdown, error recovery, object destruction, session destroy, or resource release. That’s when the developers are tired, the code is full of “finally” blocks, and everyone is praying nothing explodes.

The classic pattern to hunt:

Favorite playgrounds for these races:

Quick hunting checklist:

Race conditions during teardown are beautiful because they’re almost never fuzzed properly, rarely covered by unit tests, and developers usually only test the “clean close” case on a single thread with good timing. Mess with the timing, add threads, force errors, slow things down—and watch the use-after-free or double-free party start.

Tactic #13: Hold My Beer

C code still loves its manual refcounting for every pointer holding a ref to an object. Every hand-rolled _retain() / _release() is a tiny opportunity for joy (or tears). Pick an interesting object type and count its refcount.

You’re looking for:

Simple, mechanical, and surprisingly devastating in real-world C code bases. Note that if the code has saturation on for recounting, overflowing won’t work.

Tactic #14: The New New

New features are bug candy: freshly written, barely reviewed, and usually untouched by serious hunters yet.

They show up in recent changelogs, release notes, or git commits with words like new, added, support for, experimental, or shiny marketing hype. Jump straight there:

Why it works: Zero historical audits, low fuzz coverage, and developers still in the “this is perfect” honeymoon phase.

Quick reality check: These bugs often have a short lifespan—new features get the most public eyes and fastest patching cycles. Move fast, or that juicy bug might be gone by next week’s hotfix.

First to the nursery usually wins the easiest bugs.

Tactic #15: Started From the Bottom Now We Here

In big IOCTL dispatchers (drivers, kernel modules, etc.), skip the top of the switch/case or dispatch table—that code has been beaten to death for years.

Jump straight to the bottom:

Tactic #16: Shared Object Showdown

Any time you see multithreading (threads, thread pools, async callbacks, shared objects, concurrent queues), immediately switch to race-condition hunting mode. Target any object that is shared between threads. Multithreaded code is a goldmine for bugs that are insanely hard to catch:

Single-threaded code gets hammered by fuzzers and reviews. Races? They only appear under weird timing or load—so most hunters never find them.

Quick reflex:

Races are painful to repro, but when you nail one it’s usually catastrophic—and almost nobody else cared to look.

Tactic #17: I’m Not Saying She’s a Gold Digger

After you’ve found one real bug in a piece of code, if it doesn’t work out for exploitation, keep looking in that area. Bugs are like gold nuggets—they cluster together.

Here are some possible reasons to explain this:

Keep digging in that cluster. The next bug is usually sitting right next door, just waiting for someone to notice it, too.

Tactic #18: Liar, Liar, Pants On Fire

Source code is adorable. It’s clean, it’s readable, it has nice variable names and helpful comments. It also lies to your face on a regular basis.

Here’s why you should always check the disassembly before you start dreaming about your shiny, new bug:

Quick rule: Found something promising in source? Great. Now open it in the disassembler before you waste days crafting an exploit. Confirm the bug still exists in reality. Confirm the mitigations are (or aren’t) where you expect them. Confirm the compiler didn’t eat your bug for breakfast.

Tactic #19: If You Ain’t First, You’re Last

Figure out if you’re racing humans, machines, or nobody at all. Every target sits somewhere on a brutal spectrum. On one end, you’re the first person who ever opened this binary/firmware/module with evil intent in her heart. No fuzzers have lived here, no Project Zero write-ups exist, no thousand-dollar bounties have been claimed yet. That’s a golden window—classic memory corruption, bad input handling, and obvious copy-paste mistakes are still lying around like candy the day after Halloween. You can win with basic data-flow tracking, IDA cross-references, and a little patience. On the other end of the spectrum, you’re the thousandth hunter (human + machine) to stare at this code. Syzkaller, syzbot, OSS-Fuzz, ClusterFuzz, Android’s internal farms, Project Zero, top VR teams worldwide—they’ve already thrown insane CPU years at it. The low-hanging fruit is long gone. Doing “normal” fuzzing or following the usual strcpy/memcpy patterns here is mostly just adding noise to someone else’s already-complete dataset.

So the first real decision you make isn’t “what tool do I use?” — it’s “who (or what) am I actually competing against?” First in? Spray-and-pray data-flow hunting works stupidly well. Late to the party? You might actually have to read the manual. Most people never figure out which mode they’re in and waste months in the wrong one.

If you’re early, hunt like it’s 2005: follow tainted data from input sinks and look for the classics. If you’re late (Android core, Chrome V8, mainline kernel drivers, anything on OSS-Fuzz for years), you need to do something meaningfully different or you’re just role-playing vulnerability research. Go for fresh code paths, vendor-specific forks, obscure protocol extensions, logic bugs that fuzzers suck at, TOCTOU near trust boundaries, or recently added features that haven’t soaked in public fuzzers yet. Recognize the race early—then pick a lane the crowd hasn’t trampled.

Tactic #20: Why Can’t We Be Friends?

The fastest shortcut in vulnerability research isn’t a new fuzzer or a secret gadget—it’s learning directly from the people who keep finding good bugs while everyone else is still complaining that “everything is patched/mitigated/fuzzed to death”.

Don’t just admire their write-ups from afar. Talk to them. Ask the annoying, specific questions:

If someone has specialized in a particular target (a browser engine, a hypervisor, a specific kernel subsystem, a protocol stack, an obscure IoT firmware family), he usually has a mental map of the system’s weak spots that took him months or years to build. Piggyback on that knowledge. His “this always smells like trouble” heuristics are worth more than any tool.

Real talk: most successful VR people are surprisingly willing to share high-level patterns and triage tricks when you ask thoughtfully. Earn the conversation, show you’ve done some homework, and you’ll often walk away with a cheat sheet of system-specific red flags that would have taken you ages to discover alone.

Learning from others’ successes and screw-ups is not cheating. It’s just refusing to repeat history’s most expensive lessons.

Conclusion

Finding your first real bug is a weird rite of passage. Before it happens you feel like you’re shouting into the void, second-guessing every choice, wondering if maybe this target really is perfect. Then one day something crashes, or leaks, or behaves in a way that makes you go “…wait, seriously?”, and suddenly the whole game changes. That little dopamine hit is addictive. It’s the reason people stay up until 4 a.m. staring at Ghidra, even when they have work in a few hours.

So here’s the only advice that actually matters after you’ve read all the tactics: Just start. Don’t overthink the target, don’t wait until you “know enough,” don’t talk yourself out of it because “this is Google / Apple / Linux / whatever, it’s probably secure.” Spoiler: it’s not. Nothing is. Even the software marketed as “military-grade bulletproof” has bugs, sometimes embarrassingly simple-minded ones. Try stupid things. Dumb fuzzing network ports. Searching for bugs in “hardened” encryption code. Reading the source and going “wait, they really did that?” Some of the nastiest crashes I’ve seen came from someone going “eh, let’s just try it.”

Play to your strengths. Some people live for emulation and harnesses. Some need real hardware in their hands. Some are fuzzing gods. Some are happy staring at code for hours and cursing bad trust boundaries and race conditions. Figure out what clicks for you and lean into it hard. You’ll be faster and happier than if you force yourself to be someone you’re not.

And keep reading write-ups. Not just for the bugs and exploit techniques, but for the thinking. Watch how the best hunters VR, how they decide what’s worth their time and what’s a dead end. That’s the real skill.

For now, that’s all the dirty tricks I’m spilling (for now). If you found this useful (or at least mildly entertaining), drop your own favorite hunting heuristics, weird wins, or epic time-wasting failures in the comments. Let’s make each other better at this ridiculous, addictive game.

Go open something. Find something. Then come back and tell us what broke. Happy hunting.

Content by Zetier, Illustration by Inkinetic Studios.

Your Next Read

Discover more from Zetier

Subscribe now to keep reading and get access to the full archive.

Continue reading