Hello, dear friend, you can consult us at any time if you have any questions, add WeChat: THEend8_
FIT3173 Software Security Assignment
1 Overview
The learning objective of this assignment is for you to gain a first-hand experience on how to exploit the buffer overflow vulnerability and how operating system countermeasures work. All tasks in this assignment can be done on “SeedVM” as used in labs. Please refer to Section 2 for submission notes.
2 Submission
You need to submit a lab report (one single PDF file) to describe what you have done and what you have observed with screenshots whenever necessary; you also need to provide explanation or codes to the observations that are interesting or surprising. In your report, you need to answer all the questions listed in this manual. Please answer each question using at most 100 words. Typeset your report into .pdf format (make sure it can be opened with Adobe Reader) and name it as the format: [Your Name]-[Student ID]-FIT3173-Assignment, e.g., HarryPotter-12345678-FIT3173-Assignment.pdf.
All source code, if required, should be embedded in your report. In addition, if a demonstration video is required, you should record your screen demonstration with your voice explanation and upload the video to your Monash Google Drive or any online service which allows you to share videos. The shared URL of the video should be mentioned in your report wherever required.
Late submission penalty: 10 points deduction per day. If you require a special consideration, the application should be submitted and notified at least three days in advance. Special Considerations are handled by and approved by the faculty and not by the teaching team (unless the special consideration is for a small time period extension of one or two days).
3 Buffer Overflow Vulnerability [50 Marks]
The learning objective of this part is for you to gain the first-hand experience on buffer-overflow vulnerability by putting what you have learned from labs into action. Buffer overflow is defined as the condition in which a program attempts to write data beyond the boundaries of pre-allocated fixed length buffers. This vulnerability can be utilised by an attacker to alter the flow control of the program, even execute arbitrary pieces of code to enable remote access attacks. This vulnerability arises due to the mixing of the storage for data (e.g. buffers) and the storage for controls (e.g. return addresses): an overflow in the data part can affect the control flow of the program, because an overflow can change the return address.
In this part, you will be given a program with a buffer-overflow vulnerability; the task is to develop a scheme to exploit the vulnerability and finally send a remote access to an attacker. In addition to the attacks,you will be guided to walk through several protection schemes that have been implemented in the operating system to counter against the buffer overflow. You need to evaluate whether the schemes work or not and explain why.
3.1 Initial setup
You can execute the tasks using our pre-built Ubuntu virtual machines. Ubuntu and other Linux distributions have implemented several security mechanisms to make the buffer-overflow attack difficult. To simplify our attacks, we need to disable them first.
Address Space Randomisation.
Ubuntu and several other Linux-based systems uses address space randomisation to randomise the starting address of heap and stack. This makes guessing the exact addresses difficult; guessing addresses is one of the critical steps of buffer-overflow attacks. In this part, we disable these features using the following commands:
$ su root
Password: (enter root password “seedubuntu”)
# sysctl -w kernel.randomize_va_space=0
# exit
The StackGuard Protection Scheme.
The GCC compiler implements a security mechanism called “Stack Guard” to prevent buffer overflows. In the presence of this protection, buffer overflow will not work. You can disable this protection if you compile the program using the -fno-stack-protector switch. For example,to compile a program example.c with Stack Guard disabled, you may use the following command:
$ gcc -fno-stack-protector example.c
Non-Executable Stack.
Ubuntu used to allow executable stacks, but this has now changed: the binary images of programs (and shared libraries) must declare whether they require executable stacks or not, i.e.,they need to mark a field in the program header. Kernel or dynamic linker uses this marking to decide whether to make the stack of this running program executable or non-executable. This marking is done automatically by the recent versions of gcc, and by default, the stack is set to be non-executable. To change that, use the following option when compiling programs:
For executable stack:
$ gcc -z execstack -o test test.c
For non-executable stack:
$ gcc -z noexecstack -o test test.c
3.2 Warm Up: Shellcode Practice
Before you start the attack, we want you to exercise with a shellcode example. A shellcode is the code to launch a shell. It is a list of carefully crafted instructions created by malicious users/attackers so that it can be executed once the code is injected into a vulnerable program. Therefore, it has to be loaded into the memory so that we can force the vulnerable program to jump to it. Consider the following program:
#include <stdio.h>
int main( ) {
char *name[2];
name[0] = ‘‘/bin/sh’’;
name[1] = NULL;
execve(name[0], name, NULL);
}
The shellcode that we use is the assembly version of the above program. The following program shows you how to launch a shell by executing a shellcode stored in a buffer.
Practice Task: Please compile and run the following code, and see whether a shell is invoked.
/* call_shellcode.c */
/*A program that creates a file containing code for launching shell */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
const char code[] =
“\x31\xc0” /* Line 1: xorl %eax,%eax */
“\x50” /* Line 2: pushl %eax */
“\x68″”//sh” /* Line 3: pushl $0x68732f2f */
“\x68″”/bin” /* Line 4: pushl $0x6e69622f */
“\x89\xe3” /* Line 5: movl %esp,%ebx */
“\x50” /* Line 6: pushl %eax */
“\x53” /* Line 7: pushl %ebx */
“\x89\xe1” /* Line 8: movl %esp,%ecx */
“\x99” /* Line 9: cdq */
“\xb0\x0b” /* Line 10: movb $0x0b,%al */
“\xcd\x80” /* Line 11: int $0x80 */
;
int main(int argc, char **argv)
{
char buf[sizeof(code)];
strcpy(buf, code);
((void(*)( ))buf)( );
}
Please use the following command to compile the code (don’t forget the execstack option):
$ gcc -z execstack -g -o call_shellcode call_shellcode.c
The shellcode is stored in the variable code in the above program. A few places in this shellcode are worth mentioning. First, the third instruction pushes “//sh”, rather than “/sh” into the stack. This is because we need a 32-bit number here, and “/sh” has only 24 bits. Fortunately, “//” is equivalent to “/”, so we can get away with a double slash symbol. Second, before calling the execve() system call, we need to store name[0] (the address of the string), name (the address of the array), and NULL to the %ebx, %ecx, and %edx registers, respectively. Line 5 stores name[0] to %ebx; Line 8 stores name to %ecx; Line 9 sets %edx to zero. There are other ways to set %edx to zero (e.g., xorl %edx, %edx); the one (cdq) used here is simply a shorter instruction: it copies the sign (bit 31) of the value in the EAX register (which is 0 at this point) into every bit position in the EDX register, basically setting %edx to 0. Third, the system call execve() is called when we set %al to 11, and execute “int $0x80”.