美文网首页信息安全CTFCTF-PWN
巧绕NX与ascii armoring-ret2plt

巧绕NX与ascii armoring-ret2plt

作者: intmian | 来源:发表于2018-06-15 21:00 被阅读8次

    前言

    前排广告位招租
    最近忙于乱七八糟的事,导致没时间更博客。除了开发方面,bin方面也需要多多努力,避免被别人远远甩在后面


    ret2plt

    ascii armoring

    为了针对1997年的黑客提出的 return-to-libc 技术,ascii morning被提出,以将libc的函数地址的第一个字节进行零化的方式阻止了system地址被写入栈中导致程序流被劫持到了libc中奇怪的地方,从而拿到shell。

    零字节可以截断字符串使黑客无法将system地址写入栈。

    思路

    还记得我们是怎么写shellcode的嘛,就是千方百计地通过各种方式搞出零字节。这次我们也要使用这种精神。

    为了对抗ascii armoring我们不再使用libc中的函数,我们使用plt中的strcpy一点点的把system的地址拼接出来,写入plt表中的某个函数的空间中。最后再劫持程序流到这个空间中去,从而拿到shell。

    这就有一个问题,我们都知道开了NX之后程序不再可以在栈中执行shellcode,这让需要执行这么多strcpy的我们感觉很麻烦。因此我们通过一种被称为 PPR 的技术,这样子我们就可以连续地执行多个函数。

    PPR由两个pop、和一个ret组成,专门负责清空两个已经用过的参数(plt表中需要写入的东西与需要被写入地system地址的一部分)并且用ret返回到下一个参数中去。

    假定有

    (堆栈中的内容)
    strcpy@plt
    system[0]
    xxx@plt[0]
    PPR的地址
    strcpy@plt <--------EIP指向这里
    (某处)
    pop eax    <--------PPR的地址
    pop eax
    ret
    

    我用文字来描述一般

    1. 程序流走向了strcpy@plt这里后把PPR的地址当成函数的返回地址并把system[0]和xxx@plt[0]当成了函数的两个两个参数运行
    2. 执行完了strcpy后ret返回了某处的PPR源码中,然后通过两个pop eax将两个参数从栈中清除出去(通过增加了esp的值到了两个参数下面),然后通过ret,取出下一个strcpy的地址,并控制程序流到了那里。
    3. 返回第一过程。

    这样子我们就通过预先的栈溢出来控制程序的连续走向不同的plt表中的函数,来达成一个非常复杂的功能,而不是像ret-libc那样只能达成一个单独的函数的功能

    PPR技术主要是为了清除参数,在其他情况中如果没有参数则不需要PPR技术只需把下一个函数的地址填入返回地址中

    过程

    payload

    我们需要从栈中注入如上图大小的块

    但是我们还有几个技术问题没有解决

    1. PPR的代码放在哪里?
    2. strcpy在plt表中的位置
    3. system的地址在哪里?
    4. 放在plt表的哪里
    5. /bin/bash放在那里

    我们按照一步步流程来

    对准EIP

    这个打开ida可以解决问题或者用我们的gdb调一调也可以解决。

    找到strcpy的位置

    可以通过ida来反编译函数来获得地址(没开aslr的情况)。

    找到PPR的地址

    我们唯一可以控制的空间只有栈和plt表,而对后者的控制是建立在PPR的地址已经找到了上。那我们就不能自由的注入PPR了。索性这种平衡栈帧的代码非常(并不)常见。我们去可执行段里随便找一个,然后拿来用。

    system的拷贝

    由于ASCII armoring机制,system的地址含有零字节,造成strcpy拷贝结束,达不到预期的攻击效果。攻击者找到4个地址空间,它的首字节分别是system地址的第一个byte, 第二个byte,第三个byte和第四个byte,然后一个个byte拷贝,将这4个byte拼凑到GOT里面。从而绕过直接拷贝system地址造成失败。

    时下最流行的ubantu没有这个特性哦。

    我们强行逆一波,然后在内存里找到,然后在记在小本本上。

    需要注意,虽然说我们都是经验并不丰富的攻击者,但是还需要特别注意一下,需要找的是内存中存放的字节,而不是字节的ascii码。

    也可以使用find命令在内存中找。为了精确定位,我们应当在内存镜像中寻找以避免地址在加载中偏移,形如find /b 0xaaaaaaaa, 0xbbbbbbbb, 0xccgdb命令可以在a...到b...的空间内写入cc。

    内存镜像的工作原理与硬盘的热备份类似,内存镜像是将内存数据做两个拷贝,分别放在主内存和镜像内存中。

    写入哪个plt表空间为好呢?

    看了我前文的内容你是否有疑惑,我用的strcpy并不是一个字符拷贝函数,而是字符串拷贝函数,也就是说这四个字符的位置可能会溢出,盖掉其他的plt表项,其中可能就包括strcpy。这就让我们需要选一个trycpy前面的,我就随便选个puts吧。

    逆一波程序就可以拿到puts的地址了。

    本文使用了puts作为目标地址,你当然可以选择其他的。

    "\bin\bash"的地址

    同样的,为了精确攻击,我们最好找一个现成的。

    Linux里面有个shell环境变量,表示前使用哪个shell,它的值通常是"/bin/bash",如下:

    $ env | grep -i shell
    SHELL=/bin/bash

    每个进程的环境变量都保存在主线程的栈上,因此可以在主线程栈空间上找到该字符串。由于本文的代码为单线程 ,因此可以沿着esp地址往上找即可:

    (gdb) x/1000s $esp
    …
    0xffffd8b4: "/home/ivan/exploit/stack4"
    0xffffd8ce: "SHELL=/bin/bash"
    0xffffd8de: "TERM=xterm"
    

    环境变量也是作为参数传进主程序的。

    攻击向量

    根据上面的方法我们可以得出我们需要注入的内容。

    A*x
    strcpy@plt + PPR + puts@got[0] + addr of system[0]
    strcpy@plt + PPR + puts@got[1] + addr of system[1]
    strcpy@plt + PPR + puts@got[2] + addr of system[2]
    strcpy@plt + PPR + puts@got[3] + addr of system[3]
    puts@plt + exit + addr of “/bin/bash”

    局限

    1. 需要确切的地址难以对抗aslr防护。
    2. 需要过于多的条件,难以满足多变的情况。
    3. 在内存中需要寻找过于多的地址。
    4. 需要注入plt表的值,在64位天然零化的情况下难以为继
    5. 与ROP技术相比,不具备完备性,局限性太强

    正因为许许多多的漏洞导致了ret2plt并没有流行起来。

    相关文章

      网友评论

        本文标题:巧绕NX与ascii armoring-ret2plt

        本文链接:https://www.haomeiwen.com/subject/moareftx.html