Buffer Overflows in C
Vulnerabilities, Attacks, and Mitigations |
Prof. David Bernstein
|
Computer Science Department |
bernstdh@jmu.edu |
main(int argc, char* argv[])
)
array
is a parameter and, so, is a
pointer typesizeof(array)
is the size of an
int *
, not the size of the array-1
in an array of non-negative
integers)char
and
wchar_t
'\0'
)gets()
gets()
to read characters (until it reaches a
newline or end-of-stream character)strcpy()
strcpy()
strcat()
strcat()
to append a source string to
a target stringsprintf()
sprintf()
to create a (formatted) stringsprintf()
assumes that the buffer it is passed
is large enough and the attacker might provide a string that
is too long (i.e., longer than 80 - 2 - 2 - 1 = 75
characters in this example)strncpy()
to copy the first
n
characters
from the source to the target when the source contains
n
or more characters (which results in a non-null
terminated string)strlen()
uses the null character to determine the
length of the string so any function that uses
strlen()
will have problemsstrcpy()
) iterate until
a null character is encountered so will have problems
Address of eid: 0x0804a030
Address of grade: 0x0804a024
eid
only requires 9 bytes, space
actually exists for 12 (for alignment reasons)
foo: 00401000 bar: 00401045 Stack (Before): 00000000 00000000 7FFDF000 0012FF80 0040108A 00410EDE
If you compile using gcc -S smash.c
a file containing
the assembly language code (named smash.s
) will be generated.
If you compile using gcc -o smash smash.c
and then
run gdb smash
you can use the command
disassemble main
to see the assembly code that will be
executed (i.e., with resolved addresses).
"Hello"
Output Interpretation foo: 0x00401000 bar: 0x00401045 Stack (Before): 0x00000000 0x00000000 0x7ffdf000 0x0012ff80 0x0040108a The return address for foo() 0x00410ede Stack (After): 0x6c6c6548 lleH 0x0000006f o 0x7ffdf000 0x0012ff80 0x0040108a The return address for foo() 0x00410ede
"AAAAAAAAAAAAAAAAAAAAAAA"
Output Interpretation foo: 0x00401000 bar: 0x00401045 Stack (Before): 0x00000000 0x00000000 0x7ffdf000 0x0012ff80 0x0040108a The return address for foo() 0x00410ede Stack (After): 0x41414141 AAAA 0x41414141 AAAA 0x41414141 AAAA 0x41414141 AAAA 0x41414141 AAAA (And the return address for foo()!) 0x41414141 AAAA
"StaticOverrun ABCDEFGHIJKLMNOP\x45\x10\x40"
Output Interpretation foo: 0x00401000 bar: 0x00401045 Stack (Before): 0x77fb80db 0x77f94e68 0x7ffdf000 0x0012ff80 0x0040108a The return address for foo() 0x00410eca Stack (After): 0x44434241 DCBA 0x48474645 HGFE 0x4c4b4a49 LKJI 0x504f4e4d PONM 0x00401045 The address of bar() (and the return address for foo()!) 0x00410eca
system()
or exec()
).std::basic_string
)memcpy()
and use
memcpy_s() instead (C11) -- specifies the size and
reports violations with the return value; reports
NULL
destination; reports the
copying of overlapping segmentsmemmove()
and use
memmove_s() instead (C11) -- specifies the size and
reports violations with the return value; reports
NULL
destinations std::basic_string
)gets()
, use fgets()
and/or getchar()
(which are still vulnerable,
but better) or gets_s()
(C11)strcpy()
and strncpy()
(which have different vulnerabilities) and use
strcpy_s()
instead (C11)strcat()
and strncat()
(which have different vulnerabilities) and use
strcat_s()
instead (C11)size_t __builtin_object_size(void* ptr, int type)
type
determines what is considered
the "end of" the entity
type
is 0 or 2 then
size_t __builtin_object_size(p, type)
returns
sizeof(int)
plus 20 (i.e., the size to the end
of a
; the maximum remaining size)type
is 1 or 3 then
size_t __builtin_object_size(p, type)
returns
sizeof(int)
(i.e., the size to the end
of a.id
; the minimum remaining size)_FORTIFY_SOURCE
is definedCR
, LF
, NULL
-fstack-protector-all
flag/GS
flagsysctl -w kernel.randomize_va_space
/DYNAMICBASE
linker option/NXCOMPAT
-fno-stack-protector
-fno-defer-pop
su echo 0 > /proc/sys/kernel/randomize_va_space
(it normally contains the value 2)execstack -s
/GS