I Spent a Weekend Reading 50 Real CVEs — Here's What I Actually Learned About How Breaches Happen
I had a long weekend and a question that had been bothering me for weeks: how much of what I learn in TryHackMe labs and PortSwigger exercises actually reflects how real vulnerabilities look in the wild?
Labs are designed to be educational. They isolate one vulnerability type, make it exploitable in a controlled way, and guide you through exploiting it. Real software is messier — multiple developers, legacy code, business logic built over years, configurations that made sense at the time. I wanted to know what patterns show up when you look at real CVE reports, not textbook examples.
So I opened the NVD (National Vulnerability Database at nvd.nist.gov), added the MITRE CVE database, and spent a weekend reading. Not skimming — reading. CVE descriptions, technical advisories, proof-of-concept details where available, and the disclosed patches. Fifty reports over two days. This post is what I found.
I didn't pick randomly. I chose CVEs that met three criteria: they had detailed technical write-ups available (not just a one-line description), they covered software types I might actually encounter in bug bounty or penetration testing work (web applications, APIs, common services), and they spanned the last three years so the patterns would be current.
I deliberately included some CVEs from software I use personally — web frameworks, common database systems, authentication libraries. That choice made the reading less abstract. When a CVE affects something you've built with or tested against, the vulnerability description lands differently.
What I was looking for: patterns. Not the specific CVE details (which are specific to specific software versions), but the classes of mistake that appear repeatedly across different codebases, different companies, different years. Those patterns are what transfers to your own testing methodology.
- The 5 patterns that appeared most frequently across all 50 CVEs
- What surprised me — things I didn't expect to find
- The class of vulnerability I underestimated most
- What CVE reading taught me that labs didn't
- How to read CVEs yourself — the right way
Pattern 1 — Broken Authentication and Authorisation (Everywhere)
Authentication and Authorisation Failures
~30 of 50 CVEs involved thisThe single most common pattern across the 50 CVEs I read was some form of broken authentication or authorisation. This covers a wide range — from missing authentication on admin endpoints, to privilege escalation through parameter manipulation, to session tokens that don't expire, to API endpoints that check authentication but not authorisation (confirming you're logged in, but not confirming you're allowed to access this specific resource).
The IDOR vulnerabilities I've been finding in bug bounty work fall into this category. So does the "admin panel accessible without authentication" class of finding that appears constantly in penetration test reports. The surprising thing about reading the CVEs: many of these were not subtle. Some were endpoints that simply had no authentication check at all — not a bypassed check, not a weak check, no check. The developer assumed the endpoint would never be called directly.
→ Returns all user data including password hashes
→ No authentication header required
→ Developer comment in commit history: "temporary debug endpoint"
→ "Temporary" endpoint deployed for 14 months before CVE filed
Pattern 2 — Input Validation That Validates the Wrong Thing
Input Validation Failures — Not Missing, Just Wrong
~12 of 50 CVEsSomething I didn't expect: most of the input validation CVEs I read weren't cases where validation was completely absent. They were cases where validation was present but validated the wrong property of the input. Validating that a filename doesn't contain certain characters, but not validating that the full path doesn't escape the intended directory. Validating that a number is within a range, but not validating that arithmetic on that number can't overflow. Validating that a string is the correct length, but at the wrong point in the processing pipeline.
The path traversal CVEs in this group were particularly instructive. The application validated that the filename didn't contain "../" — but didn't normalise the path before validating. An encoded version of "../" (using URL encoding or double encoding) passed the validation check and still traversed the directory. The validation was checking the raw input string rather than the decoded, normalised value that the filesystem would actually interpret.
Validation check: does input contain "../"? → No → Passes
URL decode step: "../../../etc/passwd"
Filesystem access: /etc/passwd
Root cause: validate AFTER normalisation, not before
Pattern 3 — Default Credentials and Configuration That Never Gets Changed
Default Credentials and Unchanged Configurations
~8 of 50 CVEsEight CVEs in my 50 were essentially the same vulnerability in different software: default credentials that are documented in the manual, never changed in production deployments, and exploitable trivially. Admin interfaces with admin/admin. Database installations with root and no password. API keys embedded in default configurations that nobody rotates.
What struck me about this pattern: these are not clever attacks. There is no technical sophistication involved. The attacker reads the documentation, tries the default credentials, and gets in. Yet they keep appearing as CVEs because the software defaults to insecure configurations, and the install documentation doesn't make changing them an explicit, required first step.
I found this pattern directly relevant to my own experience — when I did the personal security audit I wrote about on this blog, I found my home router was using default admin/admin credentials. I'm a cybersecurity student. I knew better. I just hadn't done it. The CVE pattern and the personal experience are the same phenomenon at different scales.
Pattern 4 — Dependency Vulnerabilities Nobody Noticed
Third-Party Dependency and Supply Chain Vulnerabilities
~7 of 50 CVEsSeven of the CVEs I read were not vulnerabilities in the application's own code — they were vulnerabilities in a library or framework the application depended on. The application itself was written correctly. The vulnerability was in a dependency that the development team included, sometimes years ago, and never updated.
What made this pattern interesting from a security research perspective: the CVE is often filed against the dependency, not the application using it. But the exploitable instances are in the applications that never patched the dependency. The researcher who finds the initial vulnerability might get the CVE credit; the actual attack surface is in thousands of applications that used the library and never updated.
This directly connects to the DIAM project I built for my MCA capstone — I had to consciously choose up-to-date versions of every dependency (Ethers.js v6.x, React Native v0.76+, Node.js v18.x+) and would have needed a dependency monitoring process for any production deployment. The development mindset of "it works, don't touch it" is exactly what creates this CVE pattern.
Pattern 5 — Race Conditions in Multi-Step Processes
Race Conditions in Business Logic
~5 of 50 CVEsRace conditions were the category I understood least before reading these CVEs. Conceptually I knew what they were — two processes accessing shared state simultaneously, producing unexpected results. In practice, the CVEs showed me exactly what they look like in web application context: a multi-step process where step 1 checks a condition, and step 2 acts on it, with a window between the check and the act where another request can change the condition.
The classic example from one of the CVEs: a discount code check. The server checks if a discount code has been used (step 1), and if not, applies the discount and marks it used (step 2). By sending two simultaneous redemption requests at exactly the right moment, both pass step 1 (the code hasn't been used yet), both apply the discount, and both mark it used — but by that point both discounts have already been applied. The code was used twice despite the check.
PortSwigger's Web Security Academy has a race condition lab category that I went back and completed after reading these CVEs — the lab experience made the vulnerability much more concrete than the CVE description alone.
What Surprised Me — Things I Didn't Expect
The Five Things That Genuinely Surprised Me
- The average time between introduction and discovery is shockingly long. Across the CVEs where I could find the commit dates, the average vulnerability had existed in the codebase for 18-24 months before being discovered and reported. Some had existed for years. This means at any given moment, production systems are almost certainly running code with vulnerabilities that haven't been found yet — the absence of a known CVE is not the same as the absence of vulnerabilities.
- Most CVEs are found by researchers looking systematically, not by attackers exploiting them. The CVE disclosure process involves a researcher finding the vulnerability, responsibly disclosing it to the vendor, waiting for a patch, and then publishing. The majority of the CVEs I read were found by security researchers doing exactly what I'm doing in my TryHackMe and bug bounty practice — systematic testing against known vulnerability patterns. This was encouraging.
- The CVSS scores felt calibrated differently than I expected. Several CVEs I read had CVSS scores of 9.8 (Critical) for vulnerabilities that sounded severe but required conditions that would be unusual in most deployments. And some Medium-severity CVEs described vulnerabilities that seemed to me more practically dangerous in realistic deployment scenarios. CVSS scores are calculated mathematically; practical danger depends on context the score doesn't capture.
- Patch notes often reveal vulnerability classes better than CVE descriptions. Several times I found the CVE description was vague, but the commit message in the patch ("fix: sanitize user input before passing to SQL query") was explicit about what the vulnerability was. Reading patch notes alongside CVE descriptions is a habit I've kept since this weekend.
- The same developer mistakes appear in enterprise software from major vendors. I had assumed that large companies with dedicated security teams would have fewer of the basic mistakes. The CVEs disabused me of this. Broken authentication on admin endpoints, missing input validation, unchanged default credentials — these appear in software from companies with hundreds of engineers and dedicated security teams. Security is genuinely hard at scale.
How to Read CVEs Yourself — The Right Approach
If you want to do what I did, here's the approach that worked:
- Start at nvd.nist.gov, not just CVE listings. The NVD adds CVSS scores, CWE classifications, and reference links that the basic CVE listing doesn't have. The CWE (Common Weakness Enumeration) classification tells you the class of vulnerability, which is more useful for pattern recognition than the specific CVE details.
- Follow the references. Every NVD entry has reference links — these often lead to the vendor advisory, the researcher's write-up, the proof-of-concept code, and the patch commit. The vendor advisory and the researcher's write-up together give you a much richer picture than the CVE description alone.
- Read the patch, not just the vulnerability description. The diff between the vulnerable code and the patched code shows you exactly what changed — which often makes the vulnerability clearer than any description. GitHub commit links in CVE references frequently lead directly to the relevant diff.
- Sort by CWE, not by severity. If you're trying to understand a specific vulnerability class (authentication failures, injection vulnerabilities, path traversal), filter CVEs by CWE category. You'll see the same class of mistake across different software, which builds pattern recognition faster than reading random severe CVEs.
- Connect to something you know. I noticed I retained information much better from CVEs that affected software I'd used or tested. If you've done PortSwigger SQL injection labs, read SQL injection CVEs (CWE-89). The lab experience and the real CVE together create a much more complete mental model than either alone.
0 Comments