# C/C++ Security Audit

> C/C++ Code Security Audit Module
> Applicable to: C, C++, Embedded Systems, Systems Programming

## Identification Characteristics

```c
// C/C++ project identification
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// File structure
├── Makefile / CMakeLists.txt
├── src/
├── include/
├── lib/
└── tests/
```

---

## C/C++-Specific Vulnerabilities

### 1. Buffer Overflow

```c
// Stack overflow
char buffer[64];
strcpy(buffer, user_input);  // Overflow!
sprintf(buffer, "%s", user_input);  // Overflow!
gets(buffer);  // Extremely dangerous, deprecated

// Heap overflow
char *buf = malloc(64);
memcpy(buf, user_data, user_length);  // Length not validated!

// Integer overflow causing buffer issues
size_t len = user_controlled_value;
char *buf = malloc(len + 1);  // If len = SIZE_MAX, wraps to 0

// Safe alternatives
strncpy(buffer, input, sizeof(buffer) - 1);
snprintf(buffer, sizeof(buffer), "%s", input);
fgets(buffer, sizeof(buffer), stdin);

// Search patterns
strcpy|sprintf|gets|strcat|scanf
memcpy|memmove + unvalidated length
```

### 2. Format String

```c
// Dangerous: user input as format string
printf(user_input);  // Format string attack!
fprintf(stderr, user_input);
sprintf(buf, user_input);
syslog(LOG_ERR, user_input);

// Exploitation
%x%x%x%x  // Leak stack data
%n        // Write to memory
%s        // Read arbitrary address

// Safe
printf("%s", user_input);

// Search patterns
printf\([^,]*\$|fprintf\([^,]*,[^,]*\$|sprintf\([^,]*,[^,]*\$
```

### 3. Command Execution

```c
// Dangerous
system(user_command);  // RCE!
popen(user_command, "r");
execl("/bin/sh", "sh", "-c", user_command, NULL);
execvp(user_program, user_args);

// Dynamic library loading
dlopen(user_path, RTLD_NOW);  // Load malicious library

// Search patterns
system|popen|exec[lv]p?e?|dlopen
```

### 4. Integer Overflow

```c
// Signed overflow
int size = user_value;
if (size < 0) return;  // Check for negative
char *buf = malloc(size);  // What if size is very large?

// Multiplication overflow
size_t total = count * sizeof(struct item);  // May overflow
char *buf = malloc(total);

// Safe: use overflow checks
if (count > SIZE_MAX / sizeof(struct item)) {
    return -1;  // Overflow
}

// Search patterns
malloc\(.*\*|size.*\*
```

### 5. Use-After-Free

```c
// Dangerous pattern
free(ptr);
// ... other code ...
use(ptr);  // Use-After-Free!

// Double-Free
free(ptr);
free(ptr);  // Double-Free!

// Safe: set to NULL after free
free(ptr);
ptr = NULL;

// Search patterns: difficult to detect statically, requires data flow analysis
```

### 6. File Operations

```c
// Path traversal
char path[256];
snprintf(path, sizeof(path), "/data/%s", user_filename);
FILE *f = fopen(path, "r");  // ../../../etc/passwd

// Symlink attack
// TOCTOU between checking file exists and opening file
if (access(path, R_OK) == 0) {
    // Attacker may replace symlink at this point
    fd = open(path, O_RDONLY);
}

// Predictable temporary files
tmpnam(temp_path);  // Predictable! Use mkstemp

// Search patterns
fopen|open + user input path
tmpnam|tempnam  # Insecure temporary files
```

### 7. Cryptographic Issues

```c
// Weak random
srand(time(NULL));
int key = rand();  // Predictable!

// Should use
#include <fcntl.h>
int fd = open("/dev/urandom", O_RDONLY);
read(fd, &key, sizeof(key));

// Or getrandom() (Linux 3.17+)
getrandom(&key, sizeof(key), 0);

// Search patterns
srand|rand\(\)|random\(\)
```

### 8. Race Conditions

```c
// TOCTOU (Time-of-Check to Time-of-Use)
if (access(file, W_OK) == 0) {
    // Window: attacker can modify file
    fd = open(file, O_WRONLY);
}

// Safe: operate directly, check return value
fd = open(file, O_WRONLY);
if (fd < 0) {
    // Handle error
}

// Search patterns
access.*open|stat.*open
```

---

## C/C++ Audit Checklist

```
Buffer Overflow:
- [ ] Search for strcpy/sprintf/gets/strcat
- [ ] Search for memcpy + unvalidated length
- [ ] Check scanf format width

Format String:
- [ ] Search for printf(variable)
- [ ] Verify format function arguments

Command Execution:
- [ ] Search for system/popen/exec*
- [ ] Search for dlopen

Integer Safety:
- [ ] Check malloc argument overflow
- [ ] Check signed/unsigned conversion
- [ ] Verify array indices

Memory Safety:
- [ ] Check use after free
- [ ] Check double-free
- [ ] Verify pointer validity

File Operations:
- [ ] Check path construction
- [ ] Search for tmpnam/tempnam
- [ ] Check for TOCTOU issues

Cryptography:
- [ ] Search for rand()/srand()
- [ ] Verify source of randomness
- [ ] Check custom crypto implementations
```

---

## Audit Regular Expressions

```regex
# Buffer overflow
strcpy|sprintf|gets|strcat|scanf
memcpy\s*\([^)]*,[^)]*,[^)]*\$

# Format string
printf\s*\([^,)]*\)|fprintf\s*\([^,]*,[^,)]*\)

# Command execution
system\s*\(|popen\s*\(|exec[lvpe]+\s*\(|dlopen\s*\(

# Integer overflow
malloc\s*\(.*\*|calloc\s*\(

# File operations
tmpnam\s*\(|tempnam\s*\(|access\s*\(.*\)\s*.*open\s*\(

# Weak random
srand\s*\(|rand\s*\(\)
```

---

## Common Tools

```bash
# Static analysis
cppcheck --enable=all src/
clang --analyze src/
scan-build make

# Dynamic analysis
valgrind --leak-check=full ./program
AddressSanitizer: gcc -fsanitize=address
UndefinedBehaviorSanitizer: gcc -fsanitize=undefined

# Fuzzing
afl-fuzz -i testcases -o findings ./program @@
```

---

## Minimal PoC Examples
```c
// Buffer overflow
char buf[8];
gets(buf); // PoC: echo AAAAAAAAAAAAAAAAA | ./vuln

// Format string
printf(user_input); // PoC: ./vuln "%x %x %x %n"
```

---

## Advanced Vulnerability Types

### 9. Heap Exploitation

#### Heap Overflow

```c
// Heap overflow
struct chunk {
    size_t size;
    char data[64];
    void (*handler)(void);  // Function pointer overwritten
};

struct chunk *c = malloc(sizeof(struct chunk));
strcpy(c->data, user_input);  // Overflow into handler

// Search patterns: dangerous operations after heap allocation
malloc.*strcpy|malloc.*memcpy|calloc.*sprintf
```

#### Use-After-Free (UAF)

```c
// Classic UAF
void *ptr = malloc(64);
free(ptr);
// ... other program logic ...
memcpy(ptr, user_data, 64);  // UAF!

// UAF in callback functions
struct obj {
    void (*callback)(void);
    char data[32];
};

struct obj *o = create_obj();
register_callback(o->callback);  // Store pointer
free(o);
// Later callback is invoked -> UAF

// Search patterns
free\s*\([^)]+\).*\n.*\1  # Regex is difficult, requires data flow analysis
```

#### Double-Free

```c
// Explicit Double-Free
void *ptr = malloc(64);
free(ptr);
free(ptr);  // Double-Free!

// Implicit Double-Free (multiple free paths)
void cleanup_a(void *ptr) { free(ptr); }
void cleanup_b(void *ptr) { free(ptr); }

cleanup_a(shared_ptr);
cleanup_b(shared_ptr);  // Double-Free!

// Double-Free in error handling paths
void *ptr = malloc(64);
if (error_condition) {
    free(ptr);
    return -1;  // Forgot to return or continues execution
}
free(ptr);  // Double-Free!

// Search patterns
free.*\n.*free|free.*goto.*free
```

#### Heap Feng Shui (Heap Layout Control)

```c
// Attack technique: control heap layout by controlling allocation/free order
// Exploitation scenarios:
// 1. Control freed memory content during UAF
// 2. Control overflow target during heap overflow

// Audit key points:
// - Is allocation size user-controllable
// - Is free timing user-controllable
// - Is there potential for type confusion
```

### 10. Type Confusion

```c
// Type Punning
union {
    float f;
    uint32_t i;
} u;
u.f = user_float;
// Reading via u.i bypasses floating-point checks

// void* type confusion
void process(void *data, int type) {
    if (type == TYPE_ADMIN) {
        struct admin *a = (struct admin *)data;
        // ...
    } else {
        struct user *u = (struct user *)data;  // Wrong type?
        // ...
    }
}

// C++ virtual function type confusion
class Base { virtual void func(); };
class Derived : public Base { void func() override; };

Base *obj = user_controlled_cast();
obj->func();  // Vtable controlled -> RCE

// Search patterns
\(.*\*\)\s*[a-zA-Z_]|reinterpret_cast|dynamic_cast
```

### 11. Null Pointer Dereference

```c
// Unchecked malloc return value
char *buf = malloc(size);
memcpy(buf, src, size);  // What if malloc returns NULL?

// Unchecked function return value
struct config *cfg = get_config();
printf("value: %s\n", cfg->value);  // cfg may be NULL

// Null pointer after conditional check
if (ptr == NULL) {
    log_error("ptr is null");
    // Forgot to return!
}
ptr->field = value;  // Null pointer dereference

// Safe
char *buf = malloc(size);
if (buf == NULL) {
    return -ENOMEM;
}

// Search patterns
malloc.*(?!if.*NULL)|=.*\(\).*\n.*->(?!.*if.*NULL)
```

### 12. Uninitialized Variables

```c
// Uninitialized stack variable
void func(int flag) {
    char buffer[256];  // Uninitialized!
    if (flag) {
        strcpy(buffer, "data");
    }
    printf("%s\n", buffer);  // When flag=0, leaks stack data
}

// Uninitialized heap variable
struct user *u = malloc(sizeof(*u));
if (condition) {
    u->is_admin = 0;
}
// is_admin may retain previous heap data

// Safe
char buffer[256] = {0};  // Initialized
struct user *u = calloc(1, sizeof(*u));  // Zero-initialized

// Search patterns
char\s+[a-zA-Z_]+\[[0-9]+\];(?!.*=)
malloc\s*\((?!.*memset|.*calloc)
```

### 13. Signal Handler Race Conditions

```c
// Non-async-signal-safe functions in handler
void handler(int sig) {
    printf("Signal received\n");  // printf is not async-signal-safe!
    free(global_ptr);  // free is not async-signal-safe!
    exit(1);  // May cause issues
}

// Global variables in signal handlers
volatile sig_atomic_t flag = 0;  // Needs volatile
int normal_var = 0;  // Non-atomic access

void handler(int sig) {
    normal_var = 1;  // Race condition!
}

// Async-signal-safe function list (POSIX):
// _Exit, abort, accept, access, alarm, bind, cfgetispeed, ...
// Does NOT include: printf, malloc, free, exit, etc.

// Search patterns
signal\s*\(.*\n.*printf|signal\s*\(.*\n.*malloc|signal\s*\(.*\n.*free
```

### 14. Integer Truncation and Sign Issues

```c
// Signed/unsigned comparison
int user_len = get_user_input();  // May be negative
if (user_len < MAX_SIZE) {  // -1 < MAX_SIZE is true
    char *buf = malloc(user_len);  // malloc(-1) = malloc(SIZE_MAX)
}

// Integer truncation
size_t big_size = get_size();  // Large number
uint16_t small_size = big_size;  // Truncated!
char *buf = malloc(small_size);
memcpy(buf, data, big_size);  // Overflow!

// size_t wraparound
size_t len = user_len + 1;  // What if user_len = SIZE_MAX?
char *buf = malloc(len);  // malloc(0)

// Safe check
if (user_len > 0 && user_len < MAX_SIZE) {
    // Check both positive and upper bound
}

// Safe addition
if (a > SIZE_MAX - b) {
    return -1;  // Overflow
}
size_t sum = a + b;

// Search patterns
int.*=.*size_t|size_t.*=.*int|uint16_t.*=.*size_t|\+\s*1\s*\)
```

### 15. Memory Leaks

```c
// Leak in error handling paths
void *buf1 = malloc(64);
void *buf2 = malloc(64);
if (error) {
    return -1;  // Leaks buf1 and buf2!
}

// Leak from reassignment
char *ptr = malloc(32);
ptr = malloc(64);  // First allocation leaked!

// Leak in exception handling (C++)
try {
    char *buf = new char[64];
    throw std::exception();
} catch (...) {
    // buf leaked!
}

// Safe: RAII (C++)
std::unique_ptr<char[]> buf(new char[64]);
// Automatically freed

// Search patterns
malloc.*\n.*return(?!.*free)|new\s+.*throw(?!.*delete)
```

---

## Modern Exploitation Techniques Audit Points

### Stack Canary Bypass

```c
// Audit key points:
// 1. Is -fstack-protector-all enabled
// 2. Is there an information leak to obtain the canary
// 3. Is there a write path that bypasses the canary (e.g., overwriting only local variables)

// Check compilation flags
grep -r "fstack-protector\|fno-stack-protector" Makefile CMakeLists.txt
```

### ASLR/PIE Bypass

```c
// Audit key points:
// 1. Is there an address leak
// 2. Is PIE enabled (-fPIE -pie)
// 3. Are there non-randomized regions

// Common leak sources:
// - printf %p or %s leaks
// - Error messages containing pointers
// - Uninitialized data containing pointers

// Check PIE
file ./binary | grep "shared object"  # PIE compiled
readelf -h ./binary | grep Type  # EXEC means non-PIE
```

### NX/DEP Bypass

```c
// Modern exploitation techniques:
// 1. ROP (Return-Oriented Programming)
// 2. JOP (Jump-Oriented Programming)
// 3. ret2libc

// Audit key points:
// - Are there useful gadgets
// - Can return address be controlled
// - Is there a libc version leak

// Check NX
readelf -l ./binary | grep GNU_STACK
# RWE means executable stack (dangerous)
# RW- or no E means NX enabled
```

### RELRO Bypass

```c
// RELRO protects the GOT table:
// Partial RELRO: certain GOT entries are writable
// Full RELRO: GOT is fully read-only

// Audit key points:
// - Is Full RELRO enabled
// - Are there other overwritable function pointers

// Check RELRO
readelf -l ./binary | grep GNU_RELRO
checksec --file=./binary
```

---

## Secure Compilation Flags Check

```bash
# Check binary security features
checksec --file=./binary

# Manual checks
# Stack Canary
readelf -s ./binary | grep __stack_chk

# FORTIFY_SOURCE
objdump -d ./binary | grep __fortify

# PIE
file ./binary

# RELRO
readelf -l ./binary | grep GNU_RELRO

# NX
readelf -l ./binary | grep GNU_STACK
```

**Recommended Compilation Flags**:
```makefile
CFLAGS = -fstack-protector-all \
         -D_FORTIFY_SOURCE=2 \
         -fPIE -pie \
         -Wl,-z,relro,-z,now \
         -Wl,-z,noexecstack
```

---

## Advanced Audit Checklist

```
Heap Vulnerabilities:
- [ ] Search for strcpy/memcpy after malloc
- [ ] Track pointer usage after free
- [ ] Check for double-free possibilities
- [ ] Analyze if allocation size is controllable

Type Safety:
- [ ] Check void* casts
- [ ] Check union type punning
- [ ] Verify C++ virtual function calls

Integer Safety:
- [ ] Check signed/unsigned mixing
- [ ] Check integer truncation
- [ ] Verify arithmetic overflow

Pointer Safety:
- [ ] Check NULL pointer checks
- [ ] Check uninitialized variables
- [ ] Verify array bounds

Compilation Protections:
- [ ] Check Stack Canary
- [ ] Check ASLR/PIE
- [ ] Check NX/DEP
- [ ] Check RELRO
```

---

## Out-of-Bounds Read (CWE-125)

### Dangerous Patterns

```c
// 1. Array out-of-bounds read
// Dangerous: no bounds checking
int arr[10];
int value = arr[index];  // index may be >= 10, leaking stack/heap data

// Safe: bounds check
if (index >= 0 && index < 10) {
    value = arr[index];
}

// 2. String out-of-bounds read
// Dangerous: relies on NUL terminator
char* get_char(char* str, int pos) {
    return str[pos];  // pos may exceed string length
}

// Safe: use strnlen or pass length
char get_char_safe(char* str, size_t len, size_t pos) {
    if (pos < len) {
        return str[pos];
    }
    return '\0';
}

// 3. Pointer arithmetic out-of-bounds
// Dangerous: unbounded pointer arithmetic
void read_buffer(char* buf, int offset) {
    char* ptr = buf + offset;  // offset may cause out-of-bounds
    char c = *ptr;  // Out-of-bounds read
}

// Safe: validate pointer range
void read_buffer_safe(char* buf, size_t buf_len, size_t offset) {
    if (offset < buf_len) {
        char c = buf[offset];
    }
}

// 4. Structure member out-of-bounds
// Dangerous: variable-length array member
struct packet {
    int length;
    char data[1];  // Flexible array
};

void process_packet(struct packet* pkt) {
    // Reading data[pkt->length - 1] may be out of bounds
    for (int i = 0; i < pkt->length; i++) {
        process_byte(pkt->data[i]);  // Need to verify length validity
    }
}

// Safe: validate length
void process_packet_safe(struct packet* pkt, size_t total_size) {
    size_t max_data_len = total_size - offsetof(struct packet, data);
    if (pkt->length > max_data_len) {
        return;  // Invalid length
    }
    for (size_t i = 0; i < pkt->length; i++) {
        process_byte(pkt->data[i]);
    }
}
```

### Information Leak Scenarios

```c
// Heartbleed-type vulnerability
// Dangerous: user-controlled length parameter
void heartbeat_response(char* payload, int payload_len, int claimed_len) {
    char response[1024];
    // Using user-claimed length instead of actual length
    memcpy(response, payload, claimed_len);  // Out-of-bounds read!
    send(sock, response, claimed_len, 0);    // Leaks memory data
}

// Safe: use actual length
void heartbeat_response_safe(char* payload, size_t actual_len, size_t claimed_len) {
    char response[1024];
    size_t copy_len = (claimed_len < actual_len) ? claimed_len : actual_len;
    copy_len = (copy_len < sizeof(response)) ? copy_len : sizeof(response);
    memcpy(response, payload, copy_len);
    send(sock, response, copy_len, 0);
}

// Format string leak
// Dangerous: %s with no length limit
printf("Data: %s\n", user_data);  // May read past NUL terminator

// Safe: use precision limit
printf("Data: %.100s\n", user_data);  // Max 100 characters
```

### C++-Specific Issues

```cpp
// 1. vector out-of-bounds
std::vector<int> v = {1, 2, 3};
int x = v[10];  // Undefined behavior, no exception

// Safe: use at()
try {
    int x = v.at(10);  // Throws std::out_of_range
} catch (const std::out_of_range& e) {
    // Handle out-of-bounds
}

// 2. string out-of-bounds
std::string s = "hello";
char c = s[100];  // Undefined behavior

// Safe
if (index < s.size()) {
    char c = s[index];
}

// 3. Iterator out-of-bounds
std::vector<int>::iterator it = v.begin();
std::advance(it, 100);  // Out-of-bounds iterator
int val = *it;  // Undefined behavior

// Safe: check distance
if (std::distance(v.begin(), it) < v.size()) {
    int val = *it;
}
```

### Detection Commands

```bash
# Find array access
grep -rn "\[.*\]" --include="*.c" --include="*.cpp" | grep -v "define\|const"

# Find pointer arithmetic
grep -rn "\*.*+\|+.*\*" --include="*.c" --include="*.cpp"

# Find memcpy/memmove without length validation
grep -rn "memcpy\|memmove" --include="*.c" --include="*.cpp" -B 2 | grep -v "sizeof\|strlen\|min"

# Static analysis tools
cppcheck --enable=all --inconclusive src/
scan-build make
```

---

**Version**: 2.0
**Last Updated**: 2026-02-04
**Covered Vulnerability Types**: 20+ (including CWE-125 Out-of-Bounds Read)
