Secure Programming Quiz 2
A file's content is a string "Hello World". When this file is mapped to memory (the entire file) using mmap(), and the memory address is stored in a variable map. Please describe what the following printf() statement prints out. char *addr = (char *)map; printf("%s\n", map +6);
- "world" is printed out
Compilers can give a warning if it detects that the number of arguments in printf() does not match with the number of format specifiers. Please comment on the limitation of this countermeasure
- can't catch examples where the format string is dynamically constructed or when the # format specifiers is directly visible
In the Dirty COW attack, can we run two processes, instead of two threads?
- no - when 2 processes map the same file to memory, they'll have their own copy - won't trigger copy on write on the other, so it must be the same process calling write and madvise
The permission of the file /home/seed/zzz is readable and writable to the user seed. Does the following code (executed by seed) modify the content of /home/seed/zzz? int f=open("/home/seed/zzz", O_RDWR); fstat(f, &st); // Map the entire file to memory map=mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, f, 0); memcpy(map, "new content", strlen("new content"));
- No - the memory copy will occur when the child process first writes to memory - since the parent and child have independent memory space, the copy happens on write, or COW
Can we use the StackGuard idea to protected against format-string attacks?
- No, format strings only modify the return addresses, stack guards are meant to detect modifications of the stack, which buffer over flows do. With format strings we ensure only the target memory is modified, no other memory is affected
In the function epilogue, the previous frame pointer, which is stored in the area below the return address, will be retrieved and assigned to the ebp register. However, when we overflow the return address, the previous frame pointer region is already modified, so after the function epilogue, ebp contains some arbitrary value. Does this matter?
- Yes it matters, the ebp is the base pointer of the stack if it has been modified malicious stuff can be done - the ebp can be used to perform many attacks, like return to libc attacks. In return to libc attacks functions in libc may be called like system() and the arguments for system are accessed with respect to ebp. Attacks can take advantage of this to run malicious commands
If we can lock a file, we can solve the race condition problem by locking a file during the check-and-use window, because no other process can use the file during the time window. Why don't we use this approach to solve the race condition problems discussed in this chapter?
- a file can only be locked out to other users or processes only if it's already open. Which means it's in use as a resource during the time window and therefore it's impossible solving the race condition problem by locking the file during the check and use - the whole check, lock, and use operations are all atomic events so i feel that race conditions are still possible
Both buffer-overflow and format-string vulnerabilities can lead to the modification of the return address field, but the ways how the field is modified are different in these two attacks. Please describe their difference, and comment on which one is less restricted
- buffer overflow works by copying a large array of data to a smaller size array, eventually from over flowing the string we get to the return address of that string and we redirect the return address to some where else on the stack - format strings use the flaws in printf, that there is no enforcement that number of format specifiers need to be equal to the number of parameters passed in. Thus if we keep adding format specifiers we can traverse through the stack and print sensitive information, but even modify stuff on the stack. Using the "%x" specifier to read stuff from the stack and "%n" specifier to modify stack memory, %n can be used to modify the return address - format strings are less restricted, in buffer over flows you need to know the size of the array beforehand. However in format strings this requirement is not there. However it is easier to perform a buffer over flow.
What are the some registers that return to libc takes advantage of?
- esp: the stack pointer register - ebp: the frame pointer register, which points to the base of the stack
what are some other files that can be exploited using dirty cow besides etc/passwd
- etc/shadow - etc/sudoers
Note: System() is not in memory 0xAABB1180: A1, ..., Am, ret 0xAABB2290: B1, ..., Bn, ret 0xAABB33A0: C1, ..., Ct, ret Obviously, when we overflow a buffer, we will place 0xAABB1180 in the return address field, so when the function returns, it will jump to the beginning of the sub-sequence A. Please describe what other values that you would place on the stack, so when the subsequence A returns, it will jump to the sub-sequence B, and when the sub-sequence B returns, it will jump to the sub-sequence C.
- example of a return oriented programming (ROP) - Return to libc attacks depend on functions like system - if such functions are not in memory it will not work - with rop an attacker can choose instructions sequences already in machine's memory such that when these sequences are chained together achieve the intended goal, which ends with a ret instruction - ROP jumps from one sequence to another using the rets
In the buffer overflow example shown in the book (i.e., the vulnerable stack.c program), the buffer overflow occurs inside the strcpy() function, so the jumping to the malicious code occurs when strcpy() returns, not when foo() returns. Is this true or false? Please explain
- false - the buffer over flow does occurs inside strcpy(), but the buffer overflow over rides the return address of foo - thus the jumping of malicous code occurs when foo() returns not when strcpy() returns
As we know, the system() function calls /bin/sh, which is a symbolic link to /bin/bash. Recent versions of bash will drop the privilege if it detects that the effective user ID and the real user ID are different. Assume that we still want to use system() in our Return-to-libc attack, please describe how you can overcome this challenge. You are allowed to have zeros in your input (assume that memcpy() is used for memory copy, instead of strcpy())
- find the address of setuid and system using gdb - export an environment variable with the address of /bin/sh - we have to chain the functions setuid and system together - we have to invoke setuid(0) before invoking system("/bin/sh") - overflow our buffer with 0s until we get to our return address and change our return address to setuid - use ebp to access setuid's argument and set it 0, use an indirect way of setting it 0, since a 0 would end the parsing - use our environment variable to directly jump to /bin/sh - access the arguments of /bin/sh using ebp - add a null terminator
In which memory segments are the variables in the following code located? int i = 0; void func(char *str) { char *ptr = malloc(sizeof(int)); char buf[1024]; int j; static int y; }
- i is in the data segment since it's a global variable - the *ptr pointer is on the stack, however the value it's pointing to is on the heap. - buf is allocated on the stack - j is located on the stack - since y is static, it's in the BSS segment
The least-privilege principle can be used to effectively defend against the race condition attacks discussed in this chapter. Can we use the same principle to defeat buffer-overflow attacks? Why or why not? Namely, before executing the vulnerable function, we disable the root privilege; after the vulnerable function returns, we enable the privilege back.
- no - even if privileges are dropped that won't prevent the effects of the stack and return addresses being over written. - Also the actual attack happens after we return from our functions that's having the buffer over flow
If we use the MAP PRIVATE to map a read-only file to the memory, and then use memcpy() to write to it. Will this cause copy-on-write?
- no - map_private causes the read only file to be mapped into a read-only memory block - we can't use memcpy to write to it, it'll just throw an exception - we should use write() instead
In the open() system call, it first checks whether the user has the required permission to access the target file, then it actually opens the file. There seems to be a check-and then-use pattern. Is there a race condition problem caused by this pattern?
- no - the check for the user's permission and opening of the file is an atomic event thus there is no race
When printf(fmt) is executed, the stack (from low address to high address) contains the following values (4 bytes each), where the first number is the content of the variable fmt, which is a pointer pointing to a format string. If you can decide the content of the format string, what is the smallest number of format specifiers that you can use crash the program with a 100 percent probability? 0xAABBCCDD, 0xAABBDDFF, 0x22334455, 0x00000000, 0x99663322
- using %n with an address to an invalid address will crash the program - we know there are those 5 addresses, so just go 1 past that i guess - %x%x%x%x%x%n
What if we want to write a small number such as 0 to a target address using a format string vulnerability, but due to the %x's that we have to place in the format string, the number of characters printed out by printf() is already nonzero before the va list pointer reaches the target address. Is it still possible to write 0 to the target address?
-yes - we can use %hn which expects a short int we can effectively write a small number like 0 to the target address even if the # of characters printed before reaching the target is nonzero. - %hn only considers the 2 lower bytes of the integer argument, allowing us to control the value written to the target address - %hn's range is 32767 to 32768. - if we print out 32769 characters then -32767 will be printed out, if we keep adding the # of character to 32769 , the # printed out will increase - so 65536=32769+32767 would give us 0
Does the following privileged Set-UID program have a race condition problem? If so, where is the attack window? Please also describe how you would exploit this race condition window. 1 filename = "/tmp/XYZ"; 2 fd = open (filename, O_RDWR); 3 status = access (filename, W_OK); ... ... (code omitted) ... ... 10 if (status == ACCESS_ALLOWED) { 11 write_to_file(fd); 12 } else { 13 fprintf(stderr, "Permission denied\n"); 14 }
-yes - the window is between line 3 and 11 - the file permissions could be changed in that time - this can be done through a symbolic link
Since printf() does not require any privilege, we can temporarily disable the program's privilege before we execute this statement; this way, even if the format-string vulnerability is exploited, attackers will not be able to gain much privilege. Please comment on this idea
the attacker could construct a format string to modify the return address to go to some where in the code that modifies setuid to be 0 and then we can run our malicious code
Copy on write
when a child process is created using fork() copy on write- os lets the child process share the parent's process's memory by making page entries point to the same physical memory - if the memory is only read, memory copy is not required -if any one tried to write to the memory, an exception will be raised and the os will allocate new physical memory for the child process(dirty page), copy the contents from the parent process's (parent and child) page tables so that it point to it's own private copy
. This problem is built on top of Problem S4.9. Assume that the structure for Node becomes the following (a new integer field is added to the beginning). Please redo Problem S4.9. It should be noted that in Figure 1, the variable p will now point to the area 4 bytes below the next field. struct Node { int value; struct Node *next; struct Node *pre; };
you have to put in some more filler into the buffer so we go past the int value in the struct to next and pre pointers. Gotta factor in those additional 4 bytes.
mmap()
- system call to map files or devices into memory - maps an area of a process's virtual memory to a file - reading from mapped area causes file to be read
When launching the return-to-libc attack, instead of jumping to the beginning of the system() function, an attacker causes the program to jump to the first instruction right after the function prologue in the system() function. Please describe how the attacker should construct the input array.
?
Please write a function that takes a variable number of strings as its arguments, and prints out their total length.
int myprint(int Narg, ...) { va_list ap; va_start(ap, Narg); // pointer to first opptional argument for(int i=0;i<Narg;i++) { printf("%d ",va_arg(ap,int)); printf(%f\n), va_arg(ap, double); } va_end(ap) } int main() { myprint(1,2,3.5); myprint(2,2,3.5,3,4.5); }
Why does ASLR make buffer overflow more difficult?
- ASLR= Address Space Layout Resolution - is an OS approach - ASLR randomizes the start location of the stack - so every time the code is loaded in memory, the stack addresses change - makes it harder to guess the stack address in memory using the debugger - also makes it harder to guess the ebp address( the base pointer location or the base of the stack currently) as as the address of your target code
The buffer overflow example was fixed as below. Is this safe ? int bof(char *str, int size) { char *buffer = (char *) malloc(size); /* The following statement has a buffer overflow problem */ strcpy(buffer, str); return 1; }
- No - It can still be possible to pass a value of n that is not the size of the string passed in - A fix for this problem would be to use strncpy fixed version: int bof(char *str, int size) { char *buffer = (char *) malloc(size); strncpy(buffer, str,size-1); buffer[size-1]="\0"; // makes sure we have a null terminator return 1; }
with format string attack what would the benefit of using %hn format strings over %n be
The %hn format specifier, unlike %n, allows you to write a specific value to a memory location that is smaller than an integer (e.g., a short integer or two bytes). This can be beneficial in format string attacks when you want to precisely control the value written to memory and the size of the write operation. By using %hn, you can write a shorter value to memory, which might be necessary depending on the target memory location and the vulnerability you are exploiting.
Several students had issue with the buffer overflow attack. Their badfile was constructed properly, with the shellcode being put at the end of badfile. However when they try different return addresses, they get the following observations. Can you explain why some addresses work and some do not? buffer address : 0xbffff180 case 1 : retAddr = 0xbffff250 -> Able to get shell access case 2 : retAddr = 0xbffff280 -> Able to get shell access case 3 : retAddr = 0xbffff300 -> Cannot get shell access case 4 : retAddr = 0xbffff310 -> Able to get shell access case 5: retAddr = 0xbffff400 -> Cannot get shell access
The strcpy functions stops copying whenever it encounters a 0 byte, thus for case 3 and 5, we aren't able to fully process those 2 addresses
Why cannot we implement copy-on-write in memcpy(), so we can use it to write to a private copy of the mapped memory?
copy-on-write functionality is typically implemented at a higher level, such as in the memory management system or in functions specifically designed for copy-on-write operations.
What direction does the stack grow?
Downward, from High address to low address
A server program takes an input from a remote user, saves the input in a buffer allocated on the stack (Region 2 in Figure 1). The address of this buffer is then stored in the local variable fmt, which is used in the following statement in the server program: printf(fmt). When this statement is executed, the current stack layout is depicted in Figure 1. If you are a malicious attacker, can you construct the input, so when the input is fed into the server program, you can get the server program to execute your code? Please write down the actual content of the input (you do not need to provide the exact content of the code; just put "malicious code" in your answer, but you need to put it in the correct location). i
- our pointer to the buffer(fmt) is 28 bytes away from the input field AABBCDA6 - AABBCCDD = C9 = 201 - return address is 201 bytes away from the the user input buffer - 201+28 = 229 229/4= 57.25 (%x is 4 bytes) - %x%x...%x (57 %xs)%n(modify the return address) - [padding bytes (229 bytes)][address of attacker's code (4 bytes)]
Dirty COW vulnerability
- race condition vulnerability in older versions of linux - modified protected files like etc/passwd - gains root privileges by exploiting race conditions - done through a Copy On Write
When you construct the attack string in Problem S4.8., you have many choices when deciding what value to put in the return address field. What is the smallest value that you can use? Problem 4.8 code: int bof(char *str) { char buffer[24]; strcpy(buffer,str); return 1; } // buffer address starts at 0xAABB0010 // return addresses stored at 0xAABB0050
- since the buffer is 24 bytes long, the return address will be at an offset of 24 bytes from the start if the buffer - the smallest return address value can be any valid address right after the buffer plus the offset - so like 0xAABB0010+24= 0xAABB0028
Instead of jumping to the system() function, we would like to jump to the execve() function to execute "/bin/sh". Please describe how to do this. You are allowed to have zeros in your input (assume that memcpy() is used for memory copy, instead of strcpy())
- so we need to determine the address of execve, we can figure that out using the gdb/debugger and save that address some were for reference - export an environment variable named "MYSHELL" with the value of "/bin/sh" - MYSHELL is passed to the vulnerable program as an environment variable which is stored on the stack, then we find the address of this environment variable - fill the string we are overflowing with 0s, put in as many 0s you need to get to the return address then put in the address of execve - use the ebp register, with respect to the ebp register we can feed in the arguments of execve, that being our environment variable to MYSHELL - add a null terminator
f= open(fileName, O_CREAT, O_EXCL)
- the 2 options are combined together will not open the specified file if the file already exists - this guarantees atomicity - O_CREAT flag: creates a new file if it does not already exist, if it already exists open() just opens the file - O_EXCL: This call is used to ensure a new file is created, if the file already exists, there will be an error
After using the "-z noexecstack" option to compile a C program, a buffer-overflow attack that causes the vulnerable program to return to the code on the stack is supposed to fail, but some students find out that the attack is still successful. What could be the reason? The students did everything correctly.
- the code may be doing its attack without the need of an executable stack, like through a return to libc attack. In return libc attacks they manipulate the return address to jump to an existing library functions. Now return to libc attacks do not require an executable stack since they use existing code( like library functions) rather than new code put onto the stack. An example of a specific function in libc, would be like system()
The fork() system call creates a new process from a parent process. The new process, i.e., the child process, will have a copy of the parent process's memory. Typically, the memory copy is not performed when the child process is created. Instead, it is delayed. Please explain when the memory copy will occur.
- the memory copy will occur when the child process 1st writes to memory - since the parent and child have independent memory, the copy happens on write
The following function is called in a privileged program. The argument str points to a string that is entirely provided by users (the size of the string is up to 300 bytes). When this function is invoked, the address of the buffer array is 0xAABB0010, while the return address is stored in 0xAABB0050. Please write down the string that you would feed into the program, so when this string is copied to buffer and when the bof() function returns, the privileged program will run your code. In your answer, you don't need to write down the injected code, but the offsets of the key elements in your string need to be correct. Note: there is a trap in this problem; some people may be lucky and step over it, but some people may fall into it. Be careful. int bof(char *str) { char buffer[24]; strcpy(buffer,str); return 1; }
- the offset to reach the return address from the buffer is 64 bytes (AABB0050 - AABB0010 = 40{hex} = 4*16^1 + 0*16^0 = 64 {decimal}) - we need to do some padding to fill the buffer completely, so need to fill in data for those 24 bytes. We can do this by putting in garbage data, like a bunch of As - 64-24=40 bytes used up so far - so we got to the end of the buffer, now we gotta add 40 more bytes of filler to get to the return address. Like before just put in a bunch of As - now we got to where the return address is suppose to be, now we insert the address to the code we want to inject - after putting in the address we want we need to put in a null terminator or a 0
To write a shellcode, we need to know the address of the string "/bin/sh". If we have to hardcode the address in the code, it will become difficult if ASLR is turned on. Shellcode solved that problem without hardcoding the address of the string in the code. Please explain how the shellcode in exploit.c achieved that.
- the shell code is placed in a buffer array -instead of hard coding the address of /bin/sh the shellcode make use of a pointer esp to dynamically determine the address of /bin/sh extra info below _______________________________________________________________________________ 1. get_sp() retrieves the current value of the stack pointer esp and returns it to main 2. the addr variable = our stack pointer + offset 3. in the for loop it copies the calculated address addr into the buffer multiple times. To increase the chances of hitting the correct address when the buffer overflows 4. rest of buffer filled with a shellcode 5. then a null terminator is added at the end
int bof(char *str) { char buffer[X]; strcpy(buffer,str); return 1; } // str points to a 300 byte string provided by users // x is unknown, but between 20-100 // buffer address: 0xAABBCC10 // distance between buffer and the return address is 8 Please write down the string that you would feed into the program, so when this string is copied to buffer and when the bof() function returns, the server program will run your code. You only have one chance, So you need to construct the string in a way such that you can succeed without knowing the exact value of X. In your answer, you don't need to write down the injected code, but the offsets of the key elements in your string need to be correct.
- we know Return address= (buffer start address)+X+8 char userinput[300]; // will be passed to bof() char *endPtr; for(int i=0;i<X;i++) // { userinput[i]='A'; // padding if(i>19) // X is at least 20 { endPtr= &userinput[i]; } } endPtr=endPtr+1; // add 1, we never got to the final element int offset=8; // distance between end of buffer and return add char *return_address = endPtr+offset
Please use the least-privilege principle to fix the race condition problem in the following program. if (access("/tmp/XYZ", W_OK) == ACCESS_ALLOWED) { f = open("/tmp/XYZ", O_WRITE); write_to_file(f); } else { fprintf(stderr, "Permission denied\n"); }
- we modify the access function to use the O EXCL flag in the open function. By setting this flag, the open() call is guaranteed to fail if the file already exists, preventing the race condition
We are going to exploit a format-string vulnerability using the return-to-libc technique. Please describe in details what part of the stack needs to be modified and how you can achieve that by exploiting a format-string vulnerability. Please use Figure 1 when describing your solution
- we need to overwrite the return address on the stack with the address of a function in the c library - use printf(fmt) - fmt is 28 bytes away from the user input buffer 1. attacker provides input string 2. attacker puts the new address of system() at the return address 3. Enough %xs are used to reach the return address 4. %n is used to write a new value when we get there 5. The address of the string "/bin/sh" must be placed on the stack using an environment variable before any of the other steps 6.
A student proposes to change how the stack grows. Instead of growing from high address to low address, the student proposes to let the stack grow from low address to high address. This way, the buffer will be allocated above the return address, so overflowing the buffer will not be able to affect the return address. Please comment on this proposal.
- would not solve the problem - now you would not be able to hijack the current stack frame, but you could still override the return address of the next stack frame ex: Let's say we have these 2 functions void bar(char* str) { char c[7]; strcpy(c, str); } void foo() { bar("overlflow"); } - normally in the case where the stack grows from high to low address if you passed a string larger than the buffer into bar(), it would override the return address of bar - now since the stack grows from low to high here, we wouldn't override the bar function but could instead override the address of the strcpy function
How are the addresses decided for the following variables a and x, i.e., during the runtime, how does the program know the address of these two variables? void foo(int a) { int x; }
- x will be on the stack since it's a local variable - the argument a is also on the stack. When a function is called, we push the function arguments onto the stack in reverse order
Does ASLR in Linux randomize the addresses of library functions, such as system()?
- yes - ALSR randomizes the memory addresses of executable files and shared libraries - However for library functions there is a fixed offset between library functions and the executables
Assume we develop a new system call, called faccess(int fd, int mode), which is identical to access(), except that the file to be checked is specified by the file descriptor fd. Does the following program have a race condition problem? int f = open("/tmp/x", O_WRITE); if (!faccess(f, W_OK)) { write_to_file(f) } else { close(f); }
- yes - after f is opened to write with the O_WRITE flag, but the faccess checks for write permission between faccess call and the write_to_file or close call. The permission file could change, f could be made tom point to some where else
S7.1. Does the following Set-UID program have a race condition vulnerability?
- yes - a race condition can occur between access and open - any protected file can be written - During this gap the permissions on 'etc/passd' could change, allowing an attacker to manipulate the situation
How many race conditions do attackers have to win in the following program? int main() { struct stat stat1, stat2; int fd1, fd2; if (access("/tmp/XYZ", O_RDWR)) { fprintf(stderr, "Permission denied\n"); return -1; } else fd1 = open("/tmp/XYZ", O_RDWR); if (access("/tmp/XYZ", O_RDWR)) { fprintf(stderr, "Permission denied\n"); return -1; } else fd2 = open("/tmp/XYZ", O_RDWR); The program then checks whether fd1 and fd2 refer to the same file, if so, the program will write to fd1 (or fd2). Otherwise, the program will do nothing and exit. }
-3 1. between the 1st access and the fd1 open 2. between fd1 open and the 2nd access 3. between the 2nd access and open()
Symbolic link
-it's like a soft that points to another file - useful for race conditions
If we make the stack non-executable, can we exploit the format string vulnerability to get the victim program to spawn a shell?
-yes - attackers can use the return libc technique to defeat the counter measure
The following root-owned Set-UID program needs to write to a file, but it wants to ensure that the file is owned by the user. It uses stat() to get the file owner's ID, and compares it with the real user ID of the process. If they do not match, the program will exit. Please describe whether there is a race condition in the program? If so, please explain how you can exploit the race condition. The manual of stat() can be found online.
-yes - the program checks ownership of the file after opening it but before writing to it. In this window the attacker can change the ownership of the to match their own userid. -to exploit the race condition 1. create a file /tmp/link_to_XYZ 2. create a symbolic link to the target file with a different name, that being "/tmp/link_to_XYZ" 3. Run the vulnerable program, which will open and check the ownership of "/tmp/link_to_XYZ" 4. While the program is still running and before it writes to the file change the ownership of /tmp/link_to_XYZ to match the attacker's user id Note: even though the program is accessing /tmp/XYZ, since /tmp/XYZ is a symbolic link to /tmp/link_to_XYZ, the stat function will return information about the target file that points to, not the link itself
When a process maps a file into memory using the MAP PRIVATE mode, the memory mapping is depicted in Figure 1. (1) Please describe what is going to happen when this process writes data to address 0x5100. (2) The Dirty COW race condition occurs inside the write() system call. Please explain exactly where the problem is. (3) How can this race condition vulnerability be exploited?
1. Write to 0x5100 Map_Private In map private mode when a process write to a memory mapped file, it does not write directly to the file on disk, but instead to a copy of the mapped page in its own private memory space. 2. Dirty COW when a write operation is performed on a read only page mapped with Map_private the kernel must 1st make a writable copy of this page(copy on write) there is a small window of time between the moment the kernel decides to make a copy of the page and the moment the new page is actually marked as writable if a 2nd thread reads the program; read-only page during the window, it might receive the old data an attacker can trigger a write operation to the original page while the system is processing the previous write on copy 3. Race Condition Vulnerability - an attacker can start 2 threads: one triggers copy on write by writing to the memory mapped file and another that repeatedly tries to read from the same location - the write threads exploit the race condition to modify the read only memory page before copy is writable - read threads attempt to access the mapping simultaneously hoping to read from the original page after the write thread has modified it, but before the copy on write
Function foo() has a buffer overflow problem when copying your input to a buffer that is inside its stack frame. We would like to get it to return to a sequence of function calls: bar() ➙ bar() ➙ bar() ➙ xyz(3, 5) ➙ exit(). Assuming we know their address. Please describe how you would use the buffer overflow problem to construct the stack before letting foo() return. You should provide a stack diagram in your answer.
High Address _________________________________________________ Foo() -------------------------------------- |Padding | Return Address (bar()) -------------------------------------- |Padding | Return Address (bar()) -------------------------------------- |Padding | Return Address (bar()) -------------------------------------- |Return Address xyz(3, 5) -------------------------------------- |Padding | Arg 2 (5) | Arg 1 (3) -------------------------------------- |Return Address exit() _________________________________________________ Low Address
Function foo() has a buffer overflow problem when copying your input to a buffer that is inside its stack frame. You would like to get it to return to a library function xyz(0). You cannot skip xyz()'s function prologue); nor can you put any zero in your input. There is another library function called setzero(addr), which can set the 4-byte memory at address addr to zero. Please describe how you would construct your input. We do not care whether the program will crash or not after xyz(0) returns. You should provide a stack diagram in your answer.
High Address _________________________________________________ | Foo() ------------------------------------------------ | Padding | Return Address (setzero(addr)) ------------------------------------------------ | Argument (addr = xyz(0)) ------------------------------------------------ | Return Address xyz(0) ______________________________________________________________ Low Address - here we set the return address of setzero with the argument addr pointing to the return return of xyz(0) - after setzero(addr) is executed the return address of xyz(0) will be set to 0
The problem adds an additional requirement to Problem S5.10.. Function foo() has a buffer overflow problem when copying your input to a buffer that is inside its stack frame. You would like to get it to return to a library function sequence xyz(0x11111111) ➙ xyz(0) ➙ xyz(0x22222222). You cannot skip xyz()'s function prologue); nor can you put any zero in your input. There is another library function called setzero(addr), which can set the 4-byte memory at address addr to zero. Please describe how you would construct your input. We do not care whether the program will crash or not after xyz(1) returns. You should provide a stack diagram in your answer.
High Address _________________________________________________ | Foo() ------------------------------------------------ |Padding | Ret Address (xyz(0x11111111)) ------------------------------------------------ |Padding | Arg 1 (11111111) ------------------------------------------------ |Padding | Ret Address (xyz(0)) ------------------------------------------------ |Padding | Arg 1 (setzero(addr)) ------------------------------------------------ | Argument (addr = xyz(0x22222222)) ------------------------------------------------ | Padding | Ret Addr (setzero(addr)) ______________________________________________________________ Low Address
Please draw the function stack frame for the following C function. int bof(char *str) { char buffer[24]; strcpy(buffer,str); return 1; }
High address _________________________________ -------------------------- main() stack frame _________________________________ -------------------------- start of bof() frame _________________________________ str(pointer) _________________________________ Return Address (points back to main) _________________________________ Previous Frame pointer _________________________________ buffer[24] .......................................... buffer[0] _________________________________ end of bof() frame -------------------------- Low address note: in a buffer over flow the return address gets over written and if done thoroughly then an attacker can have the return address point to a segment of malicious code
Problem 4.9: In this problem, we will figure out how overflowing a buffer on the heap can lead to the execution of malicious code. The following is a snippet of a code execution sequence (not all code in this sequence is shown here). During the execution of this sequence, the memory locations of buffer and the node p, which are allocated on the heap, are depicted in Figure 1. You can provide your input (up to 300 bytes) in user input, which will be copied to buffer. Your job is to overflow the buffer, so when the target program gets to Line 3, it will jump to the code that you have injected into the heap memory (assuming that the heap memory is executable). The return address is stored at location 0xBBFFAACC
Hint: You still want to place the starting address of your malicious code into the return address field located at 0xBBFFAACC. Unlike stack-back buffer overflows, where you can naturally reach the return address field via overflowing, now the buffer is on the heap, but the return address is on the stack; you cannot reach the stack by overflowing something on the heap. You should take advantage of the operations on the linked 4 Buffer Overflow Attack list (Lines 1 and 2) to modify the return address field. This is a simplified version of how a buffer overflow on the heap can be exploited. The linked list is not part of the vulnerable program; it is actually part of the operating system, which uses it to manage the memory on the heap for the current process. Unfortunately, the linked list is also stored on the heap, so by overflowing an application's buffer, attackers can change the values on this linked list. When the OS operates on the corrupted linked list, it may change the return address of function, and trigger the execution of the injected code. 1. Overflow the buffer to reach Node p 2. Doing this will over write both the buffer both also the next and previous pointers 3. I believe(not sure) each variable in the struct are 4 bytes over since they are all pointer struct variables. So we would have the address of our node, then 4 bytes over from that address we would have the address of the next pointer 4. we want to over write p->pre to point to a fake node so that p->pre->next corresponds to the memory location on the stack where the return address is stored (0xBBFFAACC) 5. By overwriting p->next with the address of the injected malicious code, when the line q->next= p->next is executed, it will actually change the return address on the stack to point to the injected code