Create and test x86-64 ELF executable shellcode on Linux machines
I’m creating training on buffer overflows and stack/heap attacks. I’m working on an Ubuntu 12.04 x86_64 machine and want to show some example bugs and ways to exploit these vulnerabilities.
I’ve tried to start with the most basic shellcode I’ve found so far, a simple exit call, which should exit the overflowed program.
Hereby exitcall.asm:
; exitcall.asm
[SECTION .text]
global _start
_start:
xor ebx,ebx ; zero out ebx, same function as mov ebx,0
mov al, 1 ; exit command to kernel
int 0x80
I got this asm file from other tutorials, but it was written for the i386 schema. The next thing to do is to generate an object file and make it a binary executable:
# nasm -f elf64 exitcall.asm
# ld -o exitcall exitcall.o
# file exitcall
exitcall: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
# strace ./exitcall
execve("./exitcall", ["./exitcall"], [/* 73 vars */]) = 0
write(0, NULL, 0 <unfinished ... >
+++ exited with 0 +++
# objdump -d exitcall
exitcall: file format elf64-x86-64
Disassembly of section .text:
0000000000400080 <_start>:
400080: 31 db xor %ebx,%ebx
400082: b0 01 mov $0x1,%al
400084: cd 80 int $0x80
As you can see, the binary results perform well (exit 0 verified by strace) somehow convinced me that the asm file is correct as well. So what I should do now is create an array of shellcode characters from it so I can test in the following sample shellprogram.c executor. I just got the HEX value from objdump and started reading left to right, top to bottom, resulting in the following test:
char code[] = "\x31\xdb\xb0\x01\xcd\x80";
int main(int argc, char **argv) {
int (*exeshell)();
exeshell = (int (*)()) code;
(int) (*exeshell) ();
}
When I compile and execute this file, I get a segfault, but :
# gcc shellprogram.c -o shellprogram
# file shellprogram
shellprogram: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=765bdf6201099b9784b63a0111dc16c1115118bb, not stripped
# strace ./shellprogram
execve("./shellprogram", ["./shellprogram"], [/* 73 vars */]) = 0
brk(0) = 0x602000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ| PROT_WRITE, MAP_PRIVATE| MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff8000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY| O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=134914, ...}) = 0
mmap(NULL, 134914, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ffff7fd7000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY| O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320\37\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1845024, ...}) = 0
mmap(NULL, 3953344, PROT_READ| PROT_EXEC, MAP_PRIVATE| MAP_DENYWRITE, 3, 0) = 0x7ffff7a14000
mprotect(0x7ffff7bcf000, 2097152, PROT_NONE) = 0
mmap(0x7ffff7dcf000, 24576, PROT_READ| PROT_WRITE, MAP_PRIVATE| MAP_FIXED| MAP_DENYWRITE, 3, 0x1bb000) = 0x7ffff7dcf000
mmap(0x7ffff7dd5000, 17088, PROT_READ| PROT_WRITE, MAP_PRIVATE| MAP_FIXED| MAP_ANONYMOUS, -1, 0) = 0x7ffff7dd5000
close(3) = 0
mmap(NULL, 4096, PROT_READ| PROT_WRITE, MAP_PRIVATE| MAP_ANONYMOUS, -1, 0) = 0x7ffff7fd6000
mmap(NULL, 8192, PROT_READ| PROT_WRITE, MAP_PRIVATE| MAP_ANONYMOUS, -1, 0) = 0x7ffff7fd4000
arch_prctl(ARCH_SET_FS, 0x7ffff7fd4740) = 0
mprotect(0x7ffff7dcf000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ) = 0
mprotect(0x7ffff7ffc000, 4096, PROT_READ) = 0
munmap(0x7ffff7fd7000, 134914) = 0
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_ACCERR, si_addr=0x601038} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)
Does anyone know what I did wrong. Was my strategy for converting binary elf64 files to shellcode strings wrong, or was the original ASM also 64-bit compatible?
I
read somewhere that in order to generate shellcode, I can use the following xxd linux command:
# xxd -i exitcall
unsigned char exitcall[] = {
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x40, 0x00,
0x05, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xdb, 0xb0, 0x01,
0xcd, 0x80, 0x00, 0x2e, 0x73, 0x79, 0x6d, 0x74, 0x61, 0x62, 0x00, 0x2e,
0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74,
0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xe8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00, 0x01, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x04, 0x00, 0xf1, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0xf1, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x10, 0x00, 0x01, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x10, 0x00, 0x01, 0x00, 0x86, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x10, 0x00, 0x01, 0x00, 0x86, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x10, 0x00, 0x01, 0x00, 0x88, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x78, 0x69,
0x74, 0x63, 0x61, 0x6c, 0x6c, 0x2e, 0x61, 0x73, 0x6d, 0x00, 0x5f, 0x73,
0x74, 0x61, 0x72, 0x74, 0x00, 0x5f, 0x5f, 0x62, 0x73, 0x73, 0x5f, 0x73,
0x74, 0x61, 0x72, 0x74, 0x00, 0x5f, 0x65, 0x64, 0x61, 0x74, 0x61, 0x00,
0x5f, 0x65, 0x6e, 0x64, 0x00
};
unsigned int exitcall_len = 725;
# objdump -d exitcall
exitcall: file format elf64-x86-64
Disassembly of section .text:
0000000000400080 <_start>:
400080: 31 db xor %ebx,%ebx
400082: b0 01 mov $0x1,%al
400084: cd 80 int $0x80
This array is
much longer than the array in objdump (showing the assembler contents of the executable part) and also contains a large number of null bytes, which is undesirable in shell code because shellcode after null (00) is not executed.
I tried the same strategy for the example Hello World.asm, trying to make it x86-64 compatible and testing it with a shellprogram… Also failed. Does anyone have some good material on how to write shellcode compatible with asm (i.e. not using the data part of the string and making your address independent)?
Thank you very much for any help,
Wave.
Solution
The problem is that the test program shellprogram.c is incorrect for what I want to use it for, as mentioned @Iwillnotexist Idonotexist. Because the operating system enforces memory protection, you cannot execute data.
The end result is calling:
mprotect((void*)((intptr_t)code & ~0xFFF), 8192, PROT_READ| PROT_EXEC);
The end result of the simple exitcall example:
<pre class=”lang-c prettyprint-override”>#include <unistd.h>
#include <sys/mman.h>
unsigned char code[] = {
0x31, 0xdb, 0xb0, 0x01, 0xcd, 0x80
};
int main(int argc, char **argv) {
mprotect((void*)((intptr_t)code & ~0xFFF), 8192, PROT_READ| PROT_EXEC);
int (*exeshell)();
exeshell = (int (*)()) code;
(int) (*exeshell) ();
printf("Failed to execute shellcode");
}
Print “You Win!\r\n"
to the console for the final result:
<pre class=”lang-c prettyprint-override”>#include <unistd.h>
#include <sys/mman.h>
unsigned char code[] = {
0xeb, 0x19, 0x31, 0xc0, 0x31, 0xdb, 0x31, 0xd2, 0x31, 0xc9, 0xb0, 0x04,
0xb3, 0x01, 0x59, 0xb2, 0x0a, 0xcd, 0x80, 0x31, 0xc0, 0xb0, 0x01, 0x31,
0xdb, 0xcd, 0x80, 0xe8, 0xe2, 0xff, 0xff, 0xff, 0x79, 0x6f, 0x75, 0x20,
0x77, 0x69, 0x6e, 0x21, 0x0d, 0x0a
};
int main(int argc, char **argv) {
mprotect((void*)((intptr_t)code & ~0xFFF), 8192, PROT_READ| PROT_EXEC);
int (*exeshell)();
exeshell = (int (*)()) code;
(int) (*exeshell) ();
printf("Failed to execute shellcode");
}
Thanks again for showing the solution!