12/08/2018, 00:25

Low-level security or C and the infamous 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., ...

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 ‘’)

On hacker's hand

example2.png

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

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

Replacements

… 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

  • Safety first, despite some performance loss
  • Example: Very Secure FTP (vsftp) string library https://security.appspot.com/vsftpd.html

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
0