memory-safety (week 2) Flashcards

1
Q

what does memory safety refer to?

A

ensuring the integrity of a program’s data structures: preventing attackers from reading or writing to memory locations other than those intended by the programmer

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

why is C vulnerable to buffer overflow bugs?

A

because of the absence of automatic bounds-checking for array or pointer access

a buffer overflow bug is one where the programmer fails to perform adequate bounds checks, triggering an out-of-bounds memory access that writes beyond the bounds of some memory region

attackers can use these out-of-bounds memory accesses to corrupt the program’s intended behavior

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

provide an example as to why code like this is vulnerbale to an attack:

char buf[80];
int authenticated = 0;
void vulnerable() {
    gets(buf);
}
A

Imagine that elsewhere in the code, there is a login routine that sets the authenticated flag only if the user proves knowledge of the password. Unfortunately, the authenticated flag is stored in memory right after buf. If the attacker can write 81 bytes of data to buf (with the 81st byte set to a non-zero value), then this will set the authenticated flag to true, and the attacker will gain access. The program above allows that to happen, because
the gets function does no bounds-checking: it will write as much data to buf as is supplied to it. In other words, the code above is vulnerable: an attacker who can control the input to the program can bypass the password checks.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

why is this variation even more dangerous?

char buf[80];
int (*fnptr)();
void vulnerable() {
    gets(buf);
}
A

The function pointer fnptr is invoked elsewhere in the program (not shown). This enables a more serious attack: the attacker can overwrite fnptr with any address of their choosing, redirecting program execution to some other memory location. A crafty attacker could supply an input that consists of malicious machine instructions, followed by a few bytes that overwrite
fnptr with some address A. When fnptr is next invoked, the flow of control is redirected to address A. Notice that in this attack, the attacker can choose the address A however they like—so, for instance, they can choose to overwrite fnptr with an address where the malicious machine instructions will be stored (e.g., the address &buf[0]). This is a malicious code injection attack. Of course, many variations on this attack are possible: for instance, the attacker could arrange to store the malicious code anywhere else (e.g., in some other input buffer), rather than in buf, and redirect execution to that other location.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

if the web server is running as root, once the attacker seizes control, what is the attacker capable of doing?

A

anything that root can do (even leave a backdoor that allows them to log in as root later)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

what is a stack smashing attack?

A

if the input to a function is too long to fit in the allocate space, the code will write past teh end of buf and the saved SP and return address will be overwritten

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

how can stack smashing be used for malicious code injection?

void vulnerable() {
    char buf[80];
    gets(buf);
}
A
  1. the attacker arranges to infiltrate a malicious code sequence somewhere int he program’s address space, at a known address
  2. the attacker provides a carefully chosen 88 byte input, where the last four bytes hold the address of the malicious code
  3. when vulnerable() returns, the CPU will retrieve the return address stored on the stack and transfer control to that address, handing control over to the attacker’s malicious code
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

modern complications for buffer overflow attacks:

A
  1. malicious code is stored at an unknown location
  2. buffer is stored on heap instead of on stack
  3. attack can only overflow the buffer by a single byte
  4. characters that can be written to the buffer are limited
  5. there is no way to introduce any malicious code into the program’s address space
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

what is one way to avoid buffer overflows in your code?

A

one way is to check that there is sufficient space for what you will write before performing the write

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

explain how format string vulnerabilities happen

void vulnerable() {
char buf[80];
if (fgets(buf, sizeof buf, stdin) == NULL)
    return;
    printf(buf);
}
A

the last line should be printf(“%s”, buf)
if buf contains any % characters, printf() will look for non-existent arguments, and may crash or core-dump the program trying to chase missing pointers

  • printf, sprintf, snprintf, vfprintf, syslog
  • variable number of arguments, one of which is “the format string”
  • an attacker that supplies format string can change function’s behavior
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

if the attacker can see what is printed in a format string vulnerability, what are three attacks that could be mounted:

A
  1. the attacker can learn the contents of the function’s stack frame (supplying the string “%x:%x” reverals the first two words of stack memory”)
  2. the attacker can learn the contents of any other part of memory as well (Supplying the string “%s” treats the next word of stack memory as an address, and prints the string found at that address. Supplying the string “%x:%s” treats the next word of stack memory as an address, the word after that as an address, and prints what is found at that string. To read the contents of memory starting at a particular address, the attacker can find a nearby place on the stack where that address is stored, and then
    supply just enough %x’s to walk to this place followed by a %s. Many clever tricks are possible, and the details are not terribly important for our purposes.) Thus, an attacker can exploit a format string vulnerability to learn passwords, cryptographic keys, or other secrets stored in the victim’s address space.
  3. the attacker can write any value to any address in the victim’s memory; this could be used for malicious code injection
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

what should we assume if our code has a string format bug?

A

that the attacker can learn all secrets stored in memory, and assume that the attacker can take control of your program

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

What’s wrong with this code?

char buf[80];
void vulnerable() {
    int len = read_int_from_network();
    char *p = read_string_from_network();
    if (len > 80) {
        error("length too large: bad dog, no cookie for you!");
        return;
    }
    memcpy(buf, p, len);
}
Here’s a hint. The prototype for memcpy() is:
void *memcpy(void *dest, const void *src, size_t n);
And the definition of size_t is:
typedef unsigned int size_t;
A

If the attacker provides a negative value for len, the if statement
won’t notice anything wrong, and memcpy() will be executed with a negative third argument. C will cast this negative value to an unsigned int and it will become a very large positive integer. Thus memcpy() will copy a huge amount of memory into buf, overflowing the buffer.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

what are some other examples of memory safety violations?

A

using a dangling pointer: a pointer into a memory region that has been freed and is no longer valid
double-free bugs: where a dynamically allocated object is explicitly freed multiple times

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

why is a double free bug so bad?

A

Double free errors occur when free() is called more than once with the same memory address as an argument.

Calling free() twice on the same value can lead to memory leak. When a program calls free() twice with the same argument, the program’s memory management data structures become corrupted and could allow a malicious user to write values in arbitrary memory spaces. This corruption can cause the program to crash or, in some circumstances, alter the execution flow. By overwriting particular registers or memory spaces, an attacker can trick the program into executing code of their own choosing, often resulting in an interactive shell with elevated permissions.

When a buffer is free()‘d, a linked list of free buffers is read to rearrange and combine the chunks of free memory (to be able to allocate larger buffers in the future). These chunks are laid out in a double linked list which points to previous and next chunks. Unlinking an unused buffer (which is what happens when free() is called) could allow an attacker to write arbitrary values in memory; essentially overwriting valuable registers, calling shellcode from its own buffer.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

true or false:

Java is a memory-safe language

A

True

17
Q

five general classes of defensive techniques:

A
  1. secure coding practices
  2. better languages/libraries
  3. runtime checking: have our compiler (or other tools) automatically inject runtime checks everywhere they might be needed to detect and prevent exploitation of memory-safety bugs, so if our code does have a memory safety bug, it won’t be exploitable by attackers
  4. static analysis: scan source code and identify potential memory-safety bugs
  5. testing
18
Q

in general, what are some good checks to make our coding practices more secure?

A
  1. check / ensure that array indices are in-bounds before using them
  2. check that pointers are non-null and in-bounds before dereferencing them
  3. check that integer addition and multiplication won’t overflow or wrap around before performing the operation
  4. that objects haven’t already been de-allocated before freeing them
  5. that memory is initialized before being used
19
Q

describe defensive programming

A

each module takes responsibility for checking the validity of all inputs sent to it (like even if you know the caller will never send you NULL, check anyway)

minimize trust among interacting components because code can change

20
Q

what are some examples of memory safety in Java?

A
Java performs automatic bounds checking on every array access, so programmer error cannot lead to an array bounds violation
Java's String class is memory safe; the method itself performs all necessary runtime checks and resizes all buffers as needed to ensure there is enough space for strings

(C++ does this too, safer than C libraries!)

21
Q

why is bounds checking so expensive for C?

A

one must bounds check every pointer access, which adds several instructions for the check for each instruction that accesses a pointer, and C programs use pointers frequently.
also, to enable such checks, pointers have to carry information about their bounds, which adds overhead for the necessary book-keeping

22
Q

fuzz testing: what is it and what are three different approaches to test generation?

A

FT involves testing the program with random inputs and seeing if the program exhibits any sign of failure
bug detection is to check wither the program crashes

  1. random inputs: construct a random input file and run the program on that input
  2. mutated inputs: start with a valid input file, randomly modify a few bits in the file and run the program on that mutated input
  3. structure-driven input generation: taking into account the intended format of input, devise a program to independently fuzz each field of the input file. for instance, if we know that one part of the input is a string, generate random strings
23
Q

why are arithmetic overflows so useful for conducting buffer overflows?

A

You will find that arithmetic overflows are super useful for conducting buffer overflows. Because often the developer might have length checks for copying data into buffers. The program might have length checks for copying data into the buffer and those length checks will use some integer to do the check. For example, they’ll say something like, “The length of the data I’m copying is less than x.” Often x might have been computed through multiple different computations, so x is not a constant, it might just be a variable that’s the product of a bunch of operations. If the attacker can cause this x to overflow, then the attacker might be able to copy less or more data than the developer intended, and this can be exploited.