Low-level security or C and the infamous buffer overflow

Buffer overflow

A buffer overflow is a bug that affects low-level code, typically in C and C++, with significant security implications. Normally, a program with this bug will simply crash. But an attacker can alter the situations that cause the program to do much worse.

  • Steal private information (e.g., Heartbleed)
  • Corrupt valuable information
  • Run code of the attacker’s choice

Buffer overflows are still relevant today. C and C++ are still popular. Buffer overflows still occur with regularity. They have a long history. Many different approaches developed to defend against them, and bugs like them. C and C++ still very popular buferoverflow.png

Critical systems in C/C++ Most OS kernels and utilities

  • Windows
  • Linux
  • shell Many high-performance servers
  • Microsoft IIS, Apache httpd, nginx
  • Microsoft SQL server, MySQL, redis, memcached Many embedded systems
  • Mars rover, industrial control systems, automobiles

I use the term buffer overflow to mean any access of a buffer outside of its allotted bounds

  • Could be an over-read, or an over-write
  • Could be during iteration (“running off the end”) or by direct access (e.g., by pointer arithmetic)
  • Out-of-bounds access could be to addresses that precede or follow the buffer

Benign outcome

void func(char *arg1) { char buffer[4]; strcpy(buffer, arg1); ... } int main() { char *mystr = “AuthMe!”; func(mystr); ... }

Upon return, sets %ebp to 0x0021654d strcpy() copy arg1 over %ebp example1.png

strcpy() will let you write as much as you want (til a ‘\0’)

On hacker's hand


Hacker set %eip to 0xbdf. Function will jump to malicious code when return.

Secure coding in C

Since the language provides few guarantees, developers must use discipline.

Design vs. Implementation

In general, we strive to follow principles and rules

  • A principle is a design goal with many possible manifestations.
  • A rule is a specific practice that is consonant with sound design principles.
  • The difference between these can sometimes be fuzzy

Here we look at rules for good C coding

  • In particular, to avoid implementation errors that could result in violations of memory safety

In a future course module we consider principles and rules more broadly

Rule: Enforce input compliance

len = MIN(len,strlen(buf));

Rule: Enforce input compliance

char digit_to_char(int i) { char convert[] = “0123456789”; if(i < 0 || i > 9) return convert[i]; }

General Principle: Robust coding

  • Like defensive driving
  • Avoid depending on anyone else around you
  • If someone does something unexpected, you won’t crash (or worse)
  • It’s about minimizing trust
  • Each module pessimistically checks its assumed preconditions (on outside callers)
  • Even if you “know” clients will not send a NULL pointer
  • … Better to throw an exception (or even exit) than run malicious code

Rule: Use safe string functions


… for string-oriented functions

  • strcat ⟹ strlcat
  • strcpy ⟹ strlcpy
  • strncat ⟹ strlcat
  • strncpy ⟹ strlcpy
  • sprintf ⟹ snprintf
  • vsprintf ⟹ vsnprintf
  • gets ⟹ fgets

Microsoft versions different

  • strcpy_s, strcat_s, …

Rule: Don’t forget NUL terminator

char str[3]; strlcpy(str,”bye”,3); // blocked int x = strlen(str); // returns 2

**Rule: Understand pointer arithmetic ** sizeof() returns number of bytes, but pointer arithmetic multiplies by the sizeof the type.

int buf[SIZE] = { … }; int *buf_ptr = buf; while (!done() && buf_ptr < (buf + sizeof(buf))) { *buf_ptr++ = getnext(); // will overflow } use the right units while (!done() && buf_ptr < (buf + SIZE)) { *buf_ptr++ = getnext(); // stays in bounds }

Defend dangling pointers

Rule: Use NULL after free

int x = 5; int *p = malloc(sizeof(int)); free(p); p = NULL; //defend against bad deref int **q = malloc(sizeof(int*)); //reuses p’s space *q = &x; *p = 5; //(good) crash **q = 3;

**Rule: Use safe string library ** Libraries designed to ensure strings used safely

Rule: Favor safe libraries

Libraries encapsulate well-thought-out design. Take advantage!

  • Smart pointers
  • Pointers with only safe operations
  • Lifetimes managed appropriately
  • First in the Boost library, now a C++11 standard

Networking: Google protocol buffers, Apache Thrift

  • For dealing with network-transmitted data
  • Ensures input validation, parsing, etc.
  • Efficient

Rule: Use a safe allocator

  • ASLR challenges exploits by making the base address of libraries unpredictable
  • Challenge heap-based overflows by making the addresses returned by malloc unpredictable
  • Can have some negative performance impact

All Rights Reserved

Let's register a Viblo Account to get more interesting posts.