PHASE 1
Requirements & Threat Modeling
Understanding the system before we look for bugs
📋
Requirements
🔍
Code Review
💥
Exploit
🧠
Analysis
🎫
Ticket
🛡️
Patch
🎯 Lab Scenario

You are a junior security engineer at Acme Systems Inc. The team is reviewing login_server, a legacy C program that handles user authentication for internal tools. Your task is to walk through the full Secure SDLC: identify a buffer overflow vulnerability, understand how it works, file a security ticket, and verify the fix.

📋 System Requirements
• Accept username + password input
• Validate credentials against a user store
• Log all login attempts
• Support up to 64-char usernames
• Written in C, runs on Linux x86-64
⚠️ Threat Model (STRIDE)
Spoofing — fake credentials
Tampering — corrupt session data
Repudiation — forged logs
Info Disclosure — memory leaks
Denial of Service — crash server
Escalation of Privilege — our target today
💡 KEY CONCEPT — What is a Buffer Overflow? A buffer overflow occurs when a program writes more data to a fixed-size memory buffer than it can hold. The excess data spills into adjacent memory regions — potentially overwriting critical values like return addresses that control where execution jumps next. This is one of the oldest and most dangerous vulnerability classes in software security.
❓ Quick Check — Before We Begin

Which part of a C program's memory layout holds local variables?

PHASE 2
Vulnerable Code Review
Static analysis — reading the source for bugs
🔍 STATIC ANALYSIS We're reading the source code before running anything. This is called static analysis — it's the first line of defense in a Secure SDLC. Look carefully at the highlighted lines.
📄 login_server.c — Source Code
1// login_server.c — Acme Systems Internal Auth
2#include <stdio.h>
3#include <string.h>
4#include <stdlib.h>
5
6void authenticate(char *input) {
7 char username[64]; // Fixed 64-byte buffer on the stack
8 int auth_flag = 0; // 0 = not authenticated
9
10 // ⚠️ VULNERABILITY: strcpy does NO bounds checking!
11 strcpy(username, input); // Copies ALL of input into 64-byte buffer
12
13 if (auth_flag != 0) {
14 printf("[AUTH] Access GRANTED for: %s\n", username);
15 grant_access();
16 } else {
17 printf("[AUTH] Access DENIED for: %s\n", username);
18 }
19}
20
21int main() {
22 char input[512];
23 printf("Username: ");
24 fgets(input, sizeof(input), stdin);
25 authenticate(input);
26 return 0;
27}
🔬 Analysis — What Makes This Dangerous?

The vulnerability is on line 11. Let's break it down:

strcpy(username, input)
strcpy copies bytes from input into username until it hits a null byte \0. It has no idea how large the destination buffer is. If input is longer than 64 bytes, it will keep writing past the end of username.
auth_flag lives right after username on the stack
The C compiler lays out local variables sequentially on the stack. auth_flag sits right after username[64]. An input longer than 64 bytes will overwrite it!
The exploit path:
Send 65+ bytes → overflow username → write non-zero value into auth_flag → the if (auth_flag != 0) check passes → access granted without a valid password!
❓ Code Review Quiz

Why is strcpy() considered an unsafe function in C?

PHASE 3
Exploit the Vulnerability
Dynamic analysis — triggering the bug in a controlled environment
⚠️ CONTROLLED ENVIRONMENT This is a simulation. In real security work, exploits are only run against systems you own or have written permission to test. Unauthorized exploitation is illegal.
💻 Simulated Terminal — Try the Exploit

Enter a username and press Run to simulate program execution. Try different lengths to see what happens.

bash — login_server simulation
$ ./login_server
Program compiled. Ready for input. Try entering a username...
Username:
💡 Hints: Try admin (normal), then AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (65 A's)
PHASE 4
Root Cause & Memory Analysis
Understanding the stack layout during exploitation
🗂️ Normal Execution (58-byte input)
HIGH ADDR
Return Address
0x00401234
0xffffc8a0
Saved RBP
0xffffc8c0
0xffffc870
auth_flag
0x00000000
0xffffc830
username[64]
"AAAAAA..." ✓
LOW ADDR
Stack bottom
↑ grows up
auth_flag stays 0 → Access DENIED (correct)
💥 During Overflow (80-byte input)
HIGH ADDR
Return Address
⚠ may corrupt
0xffffc8a0
Saved RBP
⚠ overwritten
0xffffc870
auth_flag
0x41414141 ✗
0xffffc830
username[64]
"AAAA..." FULL
LOW ADDR
Stack bottom
↑ grows up
auth_flag becomes 0x41 → Access GRANTED (exploit!)
🧮 Why 0x41? — ASCII & Hex Refresher

The letter 'A' has ASCII value 65, which in hexadecimal is 0x41. When we overflow the buffer with 65 'A' characters, the 65th byte writes 0x41 into auth_flag. Since 0x41 ≠ 0, the if (auth_flag != 0) condition evaluates to true.

'A' → decimal 65 → hex 0x41 → not zero → authenticated!
📚 REAL-WORLD IMPACT — Beyond auth_flag In more serious exploits, attackers aim further: overwriting the saved return address allows them to redirect program execution to attacker-controlled code (shellcode). This is the foundation of classic stack smashing attacks. Modern systems use mitigations like ASLR, stack canaries, and NX bits — but these are defeated if software is built without them.
❓ Memory Quiz

In a classic stack buffer overflow, what critical value does an attacker try to overwrite to gain arbitrary code execution?

PHASE 5
File a Security Ticket
Documenting the vulnerability in the SDLC issue tracker
🎫 WHY TICKETS MATTER Every discovered vulnerability must be formally documented so it can be tracked, prioritized, assigned, and verified after patching. This is a critical step in the Secure SDLC — without it, bugs get forgotten and ships stay vulnerable.
📝 New Security Vulnerability Report
Reported by
PHASE 6
Patch & Verify Fix
Remediation, code review, and regression testing
🛠️ Patched Code — login_server_v2.c
1// login_server_v2.c — PATCHED by dev-team · Fixes SEC-2024-001
2#include <stdio.h>
3#include <string.h>
4#include <stdlib.h>
5
6#define MAX_USERNAME 63 // Max length (64 - 1 for null terminator)
7
8void authenticate(char *input) {
9 // FIX 1: Validate input length BEFORE processing
10 if (strlen(input) > MAX_USERNAME) {
11 fprintf(stderr, "[ERROR] Input too long. Rejecting.\n");
12 return;
13 }
14
15 char username[64];
16 int auth_flag = 0;
17
18 // FIX 2: strncpy with explicit size limit
19 strncpy(username, input, sizeof(username) - 1);
20 username[sizeof(username) - 1] = '\0'; // Explicit null terminator
21
22 if (auth_flag != 0) {
23 printf("[AUTH] Access GRANTED for: %s\n", username);
24 grant_access();
25 } else {
26 printf("[AUTH] Access DENIED for: %s\n", username);
27 }
28}
✅ Changes Made
Fix 1: Input length validation before calling strcpy
Fix 2: Replaced strcpy with strncpy + explicit size
Fix 3: Explicit null-terminator to prevent unterminated strings
🔐 Additional Defenses
▸ Compile with: -fstack-protector-all
▸ Enable ASLR on the OS
▸ Use modern C++ alternatives (std::string)
▸ Add fuzzing to CI/CD pipeline
▸ Run static analysis (Clang-tidy, Coverity)
🧪 Regression Test — Verify the Fix

Run the same exploit against the patched binary to confirm it's fixed.

bash — login_server_v2 (PATCHED)
$ ./login_server_v2
Patched binary ready. Try the same attack inputs...
Username:
❓ Final Quiz — SDLC Integration

At which SDLC phase is it least expensive to fix a security vulnerability?