Solution
The copy function contains an exploitable buffer overrun. We will use exploit.c to exploit it. The exploit program builds the buffer which will eventually serve as the input to the copy function. The first half of the buffer is filled with NOPs, this is followed by the shellcode which in turn is followed by copies of an address. We want that address to point to somewhere in the NOPs. If we can overwrite the return address from copy with this address then execution will jump into the NOPs when copy returns, skip over them and execute the shellcode.
To guess an appropriate address in the NOPs we look at ESP in exploit and subtract an offset from it. We assume vul inherits the same stack base address. So what's an appropriate offset? Well, we are passing in a buffer of length 1032 bytes (i.e. 8 bytes more than our buffer can cater for in order to overwrite the return address from copy), this will go on the stack to begin with, followed by some local variables followed by the copy stack frame and buffer itself, another 1024 bytes. So let's say this sums to 2100 bytes, then an offset of 2100 would land us at the base of the buffer in copy. To give ourselves some leeway we don't go that far, let's try 1800 and hope we land right in the NOPs. Here it is in action:
$ ./exploit 1032 1800
Stack: 0xbfffdf04
Using address 0xbfffd798
Sending in buffer length: 1032
Vulnerable stack top at 0xbfffdb50
Vulnerable buffer at 0xbfffd74c
sh-2.05b$ exit
exit
$ ./exploit 1032 1800
Stack: 0xbfffe304
Using address 0xbfffdb98
Sending in buffer length: 1032
Vulnerable stack top at 0xbfffdf50
Vulnerable buffer at 0xbfffdb4c
sh-2.05b$ exit
exit
$ ./exploit 1032 1800
Stack: 0xbfffe484
Using address 0xbfffdd18
Sending in buffer length: 1032
Vulnerable stack top at 0xbfffe050
Vulnerable buffer at 0xbfffdc4c
sh-2.05b$ exit
exit
$
Overview
In this lab we will:
- Derive a hexadecimal representation of some shellcode suitable for use in a buffer overflow attack
- Attempt to test your shellcode
- Use your shellcode in a buffer overflow attack
- Derive some code which, rather than launching a shell, causes the file named abc to be renamed to def
A. Deriving shellcode
Having read Aleph One's paper and studied your lecture notes you are now in a position to produce some shellcode of your own. I suggest you adopt the following approach:
- Write a simple C program which launches a shell, call it sc.c
- Disassemble using gdb to see the corresponding assembly
- Study the assembly and work out what you need
- Write a new C program called dummy.c - have it simply call a function which is of type void, takes no parameters and does nothing
- Use gcc -S to convert dummy.c into assembly code in dummy.s
- Now you have a framework in place you can embed your assembly in the function which does nothing in dummy.s
- Add required data i.e. strings
- Add labels, jmp and call instructions
- Add null terminating instructions if required
- When finished you should have something like this
- Build an executable
- Convert to a hexadecimal representation
- Rewrite any instructions that generate nulls
- You should now have something like this
- Convert to a hexadecimal representation
B. Testing your shellcode
Try compiling and running your new assembly program. What happens? Why? Well it's because the code attempts to modify itself i.e. the write that null-terminates the string is carried out in the .text section which is read-only, executable (missing write permission). So a segmentation fault occurs. Simply comment out the line that null-terminates the string, build the executable and it should run fine.
If you want to see your code run on the stack then you can try it this way. If this does not work, the reason is that the kernel you are using is taking advantage of hardware NX (No eXecute) support to prevent code from running on the stack. We need to disable this feature in order to test the code and there are ways to do it:
- Take a copy of xstack.c. Make an executable from it and run it against the shelltest executable. It manually enables execute permission on the stack by modifying the executable's ELF header. You can then run the program and it will not segfault.
- Add noexec=off to the kernel boot parameters (you can do this only if you have root access, through Yast->System-> Boot Loader).
- Use an older machine that does not have NX support.
- Use an older kernel that does not exploit NX support.
C. Using shellcode in a buffer overflow attack
Take a copy of the source code in vul.c. Build a debug executable with:
$ gcc -g -mpreferred-stack-boundary=2 -o vul vul.c
Follow the advice under B above to ensure that when the vul program runs its stack is executable.
Examine the source code to determine where the vulnerability lies. Write a new program exploit.c (following the advice in your lecture notes if necessary) which exploits the buffer overflow in vul to cause it to execute the shellcode you derived above. Your exploit program should take two parameters, a buffer size and an offset. Does the value of the offset that causes the shellcode to be executed make sense?
D. Deriving rename code
Following the same steps as you did while deriving your shellcode above, derive a hexadecimal string which when injected into a vulnerable process causes the file named abc to be renamed def. You'll need to know the rename system call number, it is 38 (decimal). For future reference you can consult the following system call table.