在Linux机器上创建并测试x86-64 ELF可执行shellcode
我正在创建关于缓冲区溢出和堆栈/堆攻击的培训。我正在研究一台Ubuntu 12.04 x86_64机器,并希望展示一些示例buggy程序以及可以利用这些漏洞的方式。在Linux机器上创建并测试x86-64 ELF可执行shellcode
我想从目前发现的最基本的shellcode开始,简单的退出调用,它应该退出程序溢出。
特此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
我得从其他教程该ASM文件,架构然而,对于i386的写入。接下来要做的是生成一个目标文件,并使其成为一个二进制可执行文件:
# 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
正如你所看到的二进制结果执行好,不知怎的让我有信心(与strace的验证口0),该ASM文件正确的。所以我现在应该做的是从它创建一个shellcode字符数组,所以我可以测试下面的示例shellprogram.c executor。我只是把十六进制值从objdump的开始读从左到右,从上到下,导致下面的测试:
char code[] = "\x31\xdb\xb0\x01\xcd\x80";
int main(int argc, char **argv) {
int (*exeshell)();
exeshell = (int (*)()) code;
(int)(*exeshell)();
}
当我编译该文件并执行它,我得到一个分段错误 ,但是:
# 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)
有没有人有一个想法,我在做事情错了。我的策略是将二进制elf64文件转换为shellcode字符串错误,还是原始asm不是64位兼容?
我读的地方,以产生I可以使用的shellcode下面XXD linux命令:
# 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
这个阵列是相当长的时间,然后从objdump的所述一个(可执行部分的显示汇编内容)并且还包含很多空字节,这在shell代码中是不可取的,因为null(00)之后的shellcode不会被执行。
我试着用一个样例Hello World.asm来尝试相同的策略,试图使它与x86-64兼容,并使用shell程序进行测试......也失败了。有没有人有一些关于如何编写shellcode兼容asm的好材料(即不使用字符串的数据部分并使地址独立)?
任何帮助真的很感激, Boeboe。
问题是,测试程序shellprogram.c是不正确的,因为我想用它的目的,如@Iwillnotexist Idonotexist所述。由于操作系统强制执行内存保护,您无法执行数据。
的起作用(制备含炭[]的shellcode可读&可执行数据节)中的溶液调用的最终结果:
#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");
}
:
mprotect((void*)((intptr_t)code & ~0xFFF), 8192, PROT_READ|PROT_EXEC);
为简单exitcall例如最终结果最终结果“you win!\ r \ n”给控制台:
#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");
}
再次感谢您展示解决方案!
恭喜您计算出它!对于你自己和将来的读者,'&〜0xFFF'将地址对齐到一个页面,'8192'是x86系统上的常见页面大小的两倍(4096字节,但这可能会改变!),在这种情况下,足以覆盖一切。如果你有更大的shellcode,增加这个值。这是必要的,因为'mprotect'只能改变页面粒度的保护。 –
如果它可以帮助您使用来自32位应用程序的内存寄存器指针 - 您需要用RAX和RBX替换EAX和EBX以获得64位。
一年后期,我知道,无疑你已经走了,但我想我会提到它!
他正在使用的寄存器都不被用作指针。另外,'xor ebx,ebx' *会清除完整的64位寄存器,因为对32位寄存器的所有写入都会将64位寄存器的高32位清零。 32位'int 0x80' ABI可用于64位进程。如果有任何问题,它可能是eax高24位的垃圾。我猜Linux只会查看'al',而不是'eax',以获取系统调用编号,否则他很高兴高位已经被清零(他们在实践中正在启动过程中,即使ABI没有不需要它。) –
你的_exploit_是如此的“经典”,以至于内存保护已经到位,使它难以执行你想要的。您的shellcode字符串被视为数据,因此包含它的内存页被标记为不可执行。试图执行反正会使CPU产生一个异常,操作系统将通过将你的过程遗忘而处理这个异常。你必须另外做的是用'mprotect()'标记包含数据的内存页面为可读+可执行文件('PROT_READ | PROT_EXEC')。提醒'mprotect'只能改变_whole pages_。 –
为了演示目的,我添加了以下GCC标志以绕过默认编译器保护机制:\t“CFLAGS = -g -w -fno-stack-protector -z execstack”。我认为这将删除“标记为不可执行”的问题......? – Boeboe
但这并没有什么区别;你的第一个shellcode是一个全局的'char'数组。它根本不在堆栈中,它在'rdata'部分。您需要调用'mprotect((void *)((intptr_t)code&〜0xFFF),8192,PROT_READ | PROT_EXEC)'或类似的命令来使数据部分包含该shellcode可执行文件。 –