Transcript
Page 1: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

1

1

Buffer Overflows

Learning objectivesUnderstand the definition of a buffer overflowLearn the importance of buffer overflowsKnow how buffer overflows happenKnow how to handle strings safely with regular "C" functionsLearn safer ways to manipulate strings and buffers

2

"C" Programming Issues:DefinitionImportanceExamplesFundamental "C" problemsSurvey of unsafe functionsRelated Issues: Truncation, Character EncodingSafe string librariesPreventing buffer overflows without programming

Page 2: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

2

3

Buffer Overflowsa.k.a. "Buffer Overrun"A buffer overflow happens when a program attempts to read or write data outside of the memory allocated for that data

Usually affects buffers of fixed sizeSpecial case of memory management and input validation

4

Important Vulnerability TypeMost Common (over 60% of CERT advisories)Well understoodEasy to avoid in principle

Don’t use "C" family languages, or be thoroughCan be tricky (off-by-one errors)Tedious to do all the checks properly

Temptation: "I don't need to because I control this data and I *know* that it will never be larger than this"

Until a hacker figures out how to change it

Page 3: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

3

5

Example Overflow

char B[10]; B[10] = x; Array starts at index zeroSo B[10] is 11th elementOne byte outside the buffer is referencedOff-by-one errors are common and can be exploitable!

6

Off-by-one errors Problem

Off-by-one errors occur when a programmer takes the proper precautions in terms of bounds checking, but maybe puts a 512 where she should have put a 511.Can happen to the best programmers no matter how well-informed they are about buffer overflows.

ConsequencesUsually off-by-one errors can do no more than crash the program. They can be made to compromise security-sensitive data. But any buffer overflow is a security risk.

Page 4: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

4

7

Off-by-one errors -Recommendations.

If you have a 512 byte buffer you can only store 511 characters in the string (the last character is a NULL).If you use scanf() to read into a buffer you also have to account for the NULL: use scanf(“%511s”, &My512ByteBuffer) instead of scanf(“%512s”, &My512ByteBuffer) which is unsafe.If you declare an array as int A[100], remember that you cannot access A[100], the highest index you can access is A[99] and the lowest is A[0].The best defense against off-by-one errors of any kind is a thorough combination of testing and code inspection.

8

Old code used for new purposes - Problem.

Often old code is reused in new projects. Even if the old code was thoroughly tested and written in a safe manner, it might not have accounted for things that the new code expects it to support, like international character sets.

Page 5: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

5

9

Old code used for new purposes - Consequences.

“HELLO” in ASCII is 0x48-0x45-0x4C-0x4C-0x4F“HELLO” in UNICODE (http://www.newsbytes.com/news/02/174512.html) is

0x00-0x48- 0x00-0x45-0x00-0x4C-0x00-0x4C-0x00-0x4F

The old code might tell the new code to give it no more than 5 characters because it uses a 5-byte buffer. The new code gives it 5 characters, but in UNICODE instead of ASCII, so they fill 10 bytes. (The assumption that 5 characters = 5 bytes is a dangerous one.)This is more common and more easily exploitable than you might think. The Venetian exploit can hijack a program with a reasonably sized buffer overflow even if UNICODE format forces the attacker to have half of his attack code bytes be zeros.

10

Old code used for new purposes - Recommendations.

Enumerate and challenge all assumptions that you have made about the interaction between old code and new.Test thoroughly.Test old code when you are using it for new purposes, even if you tested it before. If your software allows the user to use UNICODE then do all of the testing you did for ASCII with UNICODE as well.Include the old code in code inspection, even if you inspected it before.Test code on every type of platform it will likely be used on. Depending on how the processor arranges memory you might have an off-by-one error of a single byte that has no effect on program execution for a Sun processor but would have a noticeable effect on program execution for an Intel processor.

Page 6: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

6

11

Other Examplefunction do_stuff(char *a) {

char b[100];...

strcpy(b, a); // (dest, source)...}What is the size of the string located at “a”?Is it even a null-terminated string?What if it was "strcpy(a, b);" instead?

What is the size of the buffer pointed to by "a"?

12

What happens when memory outside a buffer is accessed?

If memory does not exist:Bus error

If memory protection denies access:Segmentation faultGeneral protection fault

If access is allowed, memory next to the buffer can be accessed

HeapStacketc...

Page 7: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

7

13

Real Life Example: efingerd.c, v. 1.6.2

int get_request (int d, char buffer[], u_short len) {

u_short i;for (i=0; i< len; i++) {...}buffer[i] = ‘\0’;return i;

}What is the value of "i" at the end of the loop?Which byte just got zeroed?It is tricky even if you try to get things right...

14

Real Life Example: efingerd.c, v. 1.5

CAN-2002-0423static char *lookup_addr(struct in_addr in){

static char addr[100];struct hostent *he;he = gethostbyaddr(...)strcpy(addr, he->h_name);return addr;

}How big is he->h_name? Who controls the results of gethostbyaddr?How secure is DNS? Can you be tricked into looking up a maliciously engineered value?

Page 8: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

8

15

A Typical Stack ExploitThe stack contains:

Parameters (arguments) to functionReturn AddressLocal variablesAnything pushed on the stack

addr[100+] overwrites the return addressaddr[0] typicallycontains exploitcode (external attack)Return address ischosen to point at exploitcode!

Arguments

Return Address

Low Addresses

High Addresses

Stack grows this way

addr[99]

addr[0]

16

Fundamental "C" Problems

You cannot know the length of buffers just from a pointer

Partial solution: pass the length as a separate argument

"C" string functions are not safeNo guarantees that the new string will be null-terminated!Doing all checks completely and properly is tedious and tricky

Page 9: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

9

17

StrlenWhat happens when you call strlen on an improperly terminated string ?Strlen scans until a null character is found

Can scan outside buffer if string is not null-terminatedCan result in a segmentation fault or bus error

Strlen is not safe to call !Unless you positively know that the string is null-terminated...

Are all the functions you use guaranteed to return a null-terminated string?

18

Strcpychar *strcpy(char *dst, const char *src);

How can you use strcpy safely?Set the last character of src to NUL

According to the size of the buffer pointed to by src or a size parameter passed to youNot according to strlen(src) !Wide char array: sizeof(src)/sizeof(src[0]) -1 is the index of the last element

Check that the size of the src buffer is smaller than or equal to that of the dst bufferOr allocate dst to be at least equal to the size of src

Page 10: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

10

19

Strncpychar *strncpy(char *dst, const char *src,

size_t len);

"len" is maximum number of characters to copy What is the correct value for len?

Initial answer by most people: size of dstIf dst is an array, sizeof(dst)

What if src is not NUL-terminated?Don't want to read outside of src bufferWhat is the correct value for "len" given that?

Minimum buffer size of dst and src, -1 for NUL byteIf arrays,

MIN(sizeof(dst), sizeof(src)) - 1

20

Strncpy (Cont.)

Other issue: "dst" is NUL-terminated only if less than "len" characters were copied!

All calls to strncpy must be followed by a NUL-termination operation

Page 11: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

11

21

QuestionWhat’s wrong with this?

function do_stuff(char * a) {char b[100];

...strncpy(b, a, strlen(a));

...}

The string pointed to by "a" could be larger than the size of "b"!

22

Question / AnswerWhat’s wrong with this? function do_stuff(char * a) {

char *b;...

b = malloc(strlen(a)+1);strncpy(b, a, strlen(a));

...}Are you absolutely certain that the string

pointed to by "a" is NUL-terminated?

Page 12: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

12

23

Strlcpysize_t strlcpy(char *dst, const char *src, size_t size);Guarantees to null-terminate string pointed to by "dst" if "size">0The rest of the destination buffer is not zeroed as for strncpy, so better performance is obtained"size" can simply be size of dst (sizeof if an array)

If all functions are guaranteed to null-terminate strings, then it is safe to assume src is null-terminatedNot safe if src is not null-terminated!

See http://www.courtesan.com/todd/papers/strlcpy.html for benchmarks and more info

size_t strlcpy(char *dst, const char *src, size_t size);Guarantees to null-terminate string pointed to by "dst" if "size">0The rest of the destination buffer is not zeroed as for strncpy, so better performance is obtained"size" can simply be size of dst (sizeof if an array)

If all functions are guaranteed to null-terminate strings, then it is safe to assume src is null-terminatedNot safe if src is not null-terminated!

See http://www.courtesan.com/todd/papers/strlcpy.html for benchmarks and more info (Used in MacOS X, OpenBSD but not Linux)

24

Corrected efinger.c (v.1.6)sizeof is your friend, when you can use it (if an array)static char addr[100];he = gethostbyaddr(...);if (he == NULL)strncpy(addr,inet_ntoa(in),sizeof(addr));

elsestrncpy(addr, he->h_name, sizeof(addr));

What is still wrong?the last byte of addr is not zeroed, so this code can produce non-NUL-terminated strings!

Page 13: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

13

25

Strcatchar *strcat(char *s, const char *append);String pointed to by "append" is added at the end of the string contained in buffer "s"No check for size !

Need to do all checks beforehandExample with arrays:

if (sizeof(s)-strlen(s)-1 >= strlen(append))strcat(s, append);

Need to trust that "s" and "append" are NUL-terminated

Or set their last byte to NUL before the checks and call

26

Strncatchar *strncat(char *s, const char *append, size_t count);No more than "count" characters are added, and then a NUL is addedCorrect call is complex:

strncat(s, append, sizeof(s)-strlen(s)-1)Not a great improvement on strcat, because you still need to calculate correctly the count

And then figure out if the string was truncatedNeed to trust that "s" and "append" are NUL-terminated

Or set their last byte to NUL before the checks and call

Page 14: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

14

27

Strlcatsize_t strlcat(char *dst, const char *src, size_t size);Call semantics are simple:

strlcat(dst, src, dst_len);If an array:

strlcat(dst, src, sizeof(dst));Safety: safe even if dst is not properly terminated

Will not read more than size characters from dst when looking for the append location

Not safe if src is not properly terminated!If dst is large and the buffer for src is small, then it could cause a segmentation fault or bus error, or copy confidential values

28

Issues with Truncating StringsSubsequent operations may fail or open up vulnerabilities

If string is a path, then it may not refer to the same thing, or be an invalid path

Truncation means you were not able to do what you wanted

You should handle that error instead of letting it go silently

Page 15: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

15

29

Truncation DetectionTruncation detection was simplified by strlcpy and strlcat, by changing the return value

The returned value is the size of what would have been copied if the destination had an infinite size

if this is larger than the destination size, truncation occurredSource still needs to be NUL-terminatedInspired by snprintf and vsprintf, which do the same

However, it still takes some consideration to make sure the test is correct:

if (strlcpy(dest, src, sizeof(dest)) >= sizeof(dest)) goto toolong;

30

Multi-Byte Character EncodingsHandling of strings using variable-width encodings or multi-byte encodings is a problem

e.g., UTF-8 is 1-4 bytes longHow long is the string?

In bytesIn characters

Overflows are possible if size checks do not properly account for character encoding!.NET: System.String supports UTF-16

Strings are immutable - no overflow possible there!

Page 16: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

16

31

SafestrFree library available at: http://zork.orgFeatures:

Works on UNIX and WindowsBuffer overflow protectionString format protection

Limitations and differences:Does not handle multi-byte charactersLicense: binaries must reproduce a copyright noticeNUL characters have no special meaningMust use their library functions all the time (but conversion to regular "C" strings is easy)

32

Other Unsafe Functions: sprintf family

int sprintf(char *s, const char *format, /* args*/ ...);

Buffer "s" can be overflowedint snprintf(char *s, size_t n, const char *format, /* args*/ ...);

Does not guarantee NUL-termination of s on some platforms (Microsoft, Sun)MacOS X: NUL-termination guaranteedCheck with "man sprintf"

int vsprintf(char * str, const char * format, va_list ap);

Buffer "str" can be overflowed

Page 17: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

17

33

Gets, fgetschar * gets(char *str);

Buffer "str" can be overflowedchar * fgets(char *str, int size, FILE *stream);

Buffer "str" is not NUL-terminated if an I/O error occursIf an error occurs, returns NULLIf end-of-file occurs before any characters are read, returns NULL also (and buffer is unchanged)Callers must use feof(3) and ferror(3) to determine which occurred.

34

ConclusionBuffer sizes should be passed as a parameter with every pointer

Applies to other buffer manipulations besides strings

Need simple truncation detection

Calls to watch out for

Hundreds of such callsUse static analysis to find these problemsCareful code review is necessary

Instead of: Use: gets(buf) fgets(buf, size, stdin)

strcpy(dst, src) strncpy(dst, src, n)

strcat(dst, src) strncat(dst, src, n)

sprintf(buf, fmt, a1,…) snprintf(buf, fmt, a1, n1,…)(where available)

*scanf(…) Your own parsing

Page 18: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

18

35

Preventing Buffer Overflows Without Programming

Idea: make the heap and stack non-executableBecause many buffer overflow attacks aim at executing code in the data that overflowed the buffer

Does not prevent "return into libc" overflow attacks

Because the return address of the function on the stack points to a standard "C" function (e.g., "system"), this attack does not execute code on the stack

e.g., ExecShield for Fedora Linux (used to be RedHat Linux)

36

Canaries on a Stack (Crispin Cowan)

Add a few bytes containing special values between variables on the stack and the return address. Before the function returns, check that the values are intact.

If not, there has been a buffer overflow!Terminate program

If the goal was a Denial-of-Service, then it still happens, but at least the machine is not compromisedIf the canary can be read by an attacker, then a buffer overflow exploit can be made to rewrite it

Page 19: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

19

37

StackGuard – detectAdd Canary Word next to return address

Observation (true only for buffer o.f.)Return address is unaltered IFF canary word is unaltered (?)

• Guessing the Canary ?• Randomize

38

StackGuard - detect

When compiling the function, it adds prologue and epilogue

Before execution of function, push word canary into canary vector

in addition to the stackAfter execution, before returning from function check whether canary is intactFunction returns ONLY if canary is intact

Page 20: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

20

39

StackGuard – PreventWhile function is active, make the return address read-only

attacker cannot change the return addressany attempt will be detectedUse a library called MemGuard

mark virtual memory pages as read-only and trap every write

legitimate writes to stack causes trapPerformance penalty

40

Canary ImplementationsStackGuardStack-Smashing Protector (SSP)

gcc modificationUsed in OpenBSDhttp://www.trl.ibm.com/projects/security/ssp/

Windows: /GS option for Visual C++ .NETThese can be useful when testing too!

Page 21: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

21

41

StackGuard BypassGuarding a stack is not the answer, as B.O. is not a stack problem but a pointer problem (controlling a pointer –the instruction pointer in this case-)Consider a function with several local variables, some of which are pointers: if we overflow B, we can overwrite pointer A. If this is a function pointer, it will be called, then pointing to our code

Arguments

Return Address

canary

LocVar: pointer A

LocVar: buffer B

LocVar: buffer A

42

StackGuard Bypass (cont.)The return address can be overwritten without touching the canary value (trampolining)Another possibility is to modify pointer A to point to a structure that holds function pointers, modifying an address there; point one of these back to buffer. If function gets called and buffer still around, control achieved.

Arguments

Return Address

LocVar: buffer A

LocVar: pointer A

LocVar: buffer B

canary

Page 22: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

22

43

Protection Using Virtual Memory PagesPage: A unit of virtual memoryPOSIX systems have three permissions for each page.

PROT_READPROT_WRITEPROT_EXEC

Idea: manipulate and enforce these permissions correctly to defend against buffer overflows

Make injected code non-executable

44

Windows Execution Protection

"NX" (No Execute)Windows XP service pack 2 feature

Somewhat similar to POSIX permissionsRequires processor support

AMD64Intel Itanium (family of processors 64 bit)

Page 23: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

23

45

Arithmetic Issues:In mathematics, integers form an infinite set, but in systems they are binary strings of fixed length (precision), so a finite set. Familiar rules of arithmetic do not apply.In unsigned 8-bit integer arithmetic1. 255+1= 0, 2. 16 X 17=16 and 3. 0-1=255In particular, a negative value (as in 3.) can be interpreted as a ‘large’ positive one

46

Example (using 1.)Consider the following code snippet that copies two

character strings into a buffer and checks the combined length so they fit

char buf [128]combine(char *s1, size_t len1, char *s2,size_t

len2) {if (len1+len2+1 <= sizeof(buf)) { strncpy(buf, s1, len1);strncat(buf, s2, len2); }

}The system could be attacked by constructing s1 so that len1<=

sizeof(buf) and set len2=0xFFFFFFFF(as unsigned integer, it corresponds to 4294967295)Now, since len1+0xFFFFFFFF+1 = len1 <=sizeof(buf))The strncat is executed and the buffer overrun.

Page 24: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

24

47

Example (using 3.)Consider the following code snippet int main(int argc, char* argv[]){ char _t[10]

char p[]=“xxxxxxx”; char k[]=“zzzz”; strncpy(_t, p, sizeof(_t);strncat(_t, k, sizeof(_t) – strlen(_t)-1);return 0;

}After execution, the resulting string in _t is xxxxxxxzz;Now if we supply 10 chars in p (xxxxxxxxxx), then sizeof(_t)

and strlen(_t) are equal and the third argument is -1. Since strncat expects unsigned as third argument, it is

interpreted as 0xFFFFFFFF and therefore the strcat is unbounded and the buffer overrun again.

48

Important LessonDeclare all integers as unsigned integers, unless negative ones are really needed. While measuring size of objects, negative ones are not needed. If compiler flags signed-unsigned mismatch, check if both representations are needed; if so, care needed to the checks implemented.Most arithmetic bugs are caused by type mismatch

Page 25: Buffer Overflows - uniroma1.itparisi/Risorse/BufferOverflow.pdf · 2 3 Buffer Overflows a.k.a. "Buffer Overrun" A buffer overflow happens when a program attempts to read or write

25

49

Buffer Overflow in Java?Not really, since Java has a type-safe memory model, and ‘falling off’ the end of an object is not possible. Exploits against Java-based systems are typically language-based (type confusion) attacks and trust exploits (code signing errors) Problem overflow typically occur in supporting code external to the JVM: use, by Java-based services, of components and services written in weakly typed languages like C and C++Java supports loading of DLLs and code libraries, so that exported functions can be used directly

50

example Public class MyJavaPacketEngine extends Thread{

public MyJavaPacketEngine (){

}static{System.loadLibrary(‘’packet_driver32’’);

} }Now calls can be made directly to the DLL.For examplewsprintf(lpAdapter->SymbolicLink, TEXT(‘’\\\\.\\%s%s’’),

DOSNAMEPREFIX, p_AdapterName);Assigns the binding string to an unterminated string buffer


Recommended