CS 208 s20 — Buffer Overflow Attacks
Table of Contents
1 Video
Here is a video lecture for the material outlined below. It covers CSPP section 3.10.3 and 3.10.4 (p. 279–290). It contains sections on
- introduction (0:05)
- stack frame review (0:48)
- what is a buffer? (4:33)
- what is a buffer overflow? (6:57)
- gets known to be harmful (14:09)
- buffer overflow in action (19:36)
- spreadsheet example (22:54)
- code injection attack (30:41)
- buffer overflow exercise (34:22)
- real-world examples (38:47)
- defenses against buffer overflows (44:57)
- end notes (55:57)
The Panopto viewer has table of contents entries for these sections. Link to the Panopto viewer: https://carleton.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=9d4d9870-abf5-4171-b6a0-abba0100ccc6
2 Background
2.1 Stack Frame Review
- In x86-64 Linux
- stack segment of memory starts at
0x00007fffffffffff
and grows down - code segment of memory starts at
0x400000
and grows up
- stack segment of memory starts at
2.2 What is a Buffer?
- array used to temporarily store data
- video buffering is the video being written to a buffer before being played
- bufferes are often used to store user input
2.3 What is a Buffer Overflow?
- arrays can be stored on the stack alongside procedure data like the return address
- C does not prevent writing to elements beyond the end of an array
- together, these two facts allow for a buffer overflow where program state on the stack is corrupted
- for example, overwritting the return address pushed on the stack by the caller would cause the program to jump to an unexpected or invalid place
Input that does not overflow the buffer:
Input that does overflow the buffer, overwriting part of the return address:
- attacker just has to choose the right inputs to overwrite interesting data
- simple attack: overwrite the current return address (sometimes called stack smashing)
- for a long time this was the #1 technical cause of security vulnerabilities
- I say technical cause because the #1 overall cause is pretty much always humans (social engineering, ignorance, etc.)
2.3.1 Example
/* Get string from stdin */ char* gets(char* dest) { int c = getchar(); char* p = dest; while (c != EOF && c != '\n') { *p++ = c; c = getchar(); } *p = '\0'; return dest; }
- what could go wrong here?
- we could read in a lot more data than
dest
has room for, overwriting things gets
has no information about the size ofdest
(just passed as a pointer to the start of the array)
- we could read in a lot more data than
2.3.2 gets
Known to be Harmful
- bugs section of
gets
man page
Never use
gets()
. Because it is impossible to tell without knowing the data in advance how many charactersgets()
will read, and becausegets()
will continue to store characters past the end of the buffer, it is extremely dangerous to use. It has been used to break computer security. Usefgets()
instead.
- also a problem with
strcpy
,scanf
,fsnanf
,sscanf
gcc
even gives you a warning:the `gets' function is dangerous and should not be used.
3 Buffer Overflow In Action
Consider this (very insecure) code:
/* Echo Line */ void echo() { char buf[8]; /* Way too small! */ gets(buf); puts(buf); } void call_echo() { echo(); }
- full source code here
- entering
01234567890123456789012
works fine- overwriting unused space on the stack
- entering
012345678901234567890123
causes an illeagal instruction error- overwriting least significant byte of return address with
'\0'
(0x00
), making it0x400500
- causes program to return into the middle of another instruction, CPU triggers an exception when we try to execute it as code
- overwriting least significant byte of return address with
- entering
0123456789012345678901234
causes a segmentation fault- overwriting two low-order bytes of return address with
'4'
and'\0'
, making it0x400034
- this isn't a valid memory address for our program, triggering a segmentation fault when we try to access it
- overwriting two low-order bytes of return address with
From objdump -d buf-nsp
:
0000000000400566 <echo>: 400566: 48 83 ec 18 sub $0x18,%rsp 40056a: 48 89 e7 mov %rsp,%rdi 40056d: b8 00 00 00 00 mov $0x0,%eax 400572: e8 d9 fe ff ff callq 400450 <gets@plt> 400577: 48 89 e7 mov %rsp,%rdi 40057a: e8 b1 fe ff ff callq 400430 <puts@plt> 40057f: 48 83 c4 18 add $0x18,%rsp 400583: c3 retq 0000000000400584 <call_echo>: 400584: 48 83 ec 08 sub $0x8,%rsp 400588: b8 00 00 00 00 mov $0x0,%eax 40058d: e8 d4 ff ff ff callq 400566 <echo> 400592: 48 83 c4 08 add $0x8,%rsp 400596: c3 retq
4 Code Injection Attack
- very common attack to get a program to execute an arbitrary function
- over a network, program given a string containing executable code (exploit code) with extra data to overwrite a return address with the location of the exploit
- exploit might use a system call to start a shell giving the attacker access to the system
- exploit might do some mischief, then repair the stack and call
ret
again, giving the appearance of normal behavior
4.1 Exercise
vulnerable: subq $0x40, %rsp ... leaq 0x10(%rsp), %rdi call gets ...
What is the minimum number of characters that gets must read in order for us to change the return address to a stack address?
For example, change 0x00 00 00 00 00 40 05 D1
to 0x00 00 7F FF CA FE F0 0D
1
4.2 Real World examples
- buffer overflow exploits are alarmingly common in real programs
- programmers keep making the same mistakes
- recent innovations have improved the situation
4.2.1 Internet Worm (1988)
- protocol for getting the status of a server (
fingerd
) usedgets
to read its argument - worm sent exploit code that executed a root shell on the target machine
- scanned other machines to attack, invaded about 6000 computers in a matter of hours (10% of the Internet at that time)
- see June 1989 article in Comm. of the ACM
- author (Robert Morris) was first person ever convicted under the Computer Fraud and Abuse Act, now faculty at MIT (so I guess things turned out all right for him)
4.2.2 Heartbleed (2014)
- affected Tumblr, Google, Yahoo, Intuit (makers of TurboTax), Dropbox, Netflix, Facebook, and many, many smaller sites
4.2.3 Hacking Cars
- in 2010, UW researchers demonstrated wirelessly hacking a car using buffer overflow
- overwrote the onboard control system’s code
- disable brakes
- unlock doors
- turn engine on/off
4.2.4 Hacking DNA Sequencing Machines
- in 2017, security researchers demonstrated that a buffer overflow exploit could be encoded in DNA
- when read by vulnerable sequencing software, the attack could compromise the sequencing machine
5 Countermeasures
5.1 System Level
5.1.1 Non-Executable Stack
x86-64 added execute permission (not all systems have hardware support, doesn't block all exploits)
5.1.2 Stack Randomization
- in the past, stack addresses were highly predictable, meaning if an attacker could determine addresses for a common web server, than many machines were vulnerable
- make it unpredictable by allocating between 0 and \(n\) bytes on the stack at the start of the program
- part of a larger class of techniques called address-space layout randomization (ASLR)
- in general, this randomization can greatly increase the effort required for a successful attack, but cannot guarantee safety
5.2 Writing Better Code
- Use the safe versions of C library functions:
fgets
instead ofgets
,strncpy
instead ofstrcpy
- Avoid using the
%s
format specifier—provide a max width like%20s
- Use a safer programming language! C has unique vulnerabilities.
5.2.1 Stack Corruption Detection
- detect when stack corruption occurs before it can have harmful effects
gcc
now uses stack protectors to detect buffer overflows- canary value (or guard value) between buffer and rest of the stack
- generated each time the program runs, so hard for attacker to know what it will be
- stored in a read-only segment of memory (so attacker can't modify it)
- prevents many common attack strategies
6 Homework
- CSPP practice problem 3.46 (p. 282)
- Remember that we're switching to handins on Moodle for lab 2: upload your
solutions.txt
to the lab 2 assignment on Moodle. - Quiz due at 9pm tonight.
Footnotes:
gets
would need to read in 54 character (bytes) to overwrite
the return address with a stack address. subq 0x40, %rsp
tells us
that the stack frame for the function is 64 bytes. leaq 0x10(%rsp),
%rdi
tells us that the start of the buffer passed to gets
is 16
bytes above the top of the stack, making it 48 bytes from the start of
the stack frame. The stack address we want to replace the return
address with is 6 non-zero bytes, so we need to pass 48 bytes of
filler followed by 6 bytes of address to gets
in order to execute
this part of our attack, for a total of 54 bytes.