基于栈式的缓冲区溢出

   缓冲区溢出攻击是一个老生常谈的问题了,实际上我们所在的这个世界上,每天无时无刻不在发生各种各样的缓冲区溢出的攻击,随着反制技术的不断升级,这些攻击技巧都在大量进行升级还贷,但,所谓哪里有压迫哪里就有反抗!

   ASLR(地址随机化)、DEP(数据执行保护)、ASCII armoring(地址插零),GS(调用 cookie)等方面的反制技术让攻击变的越来越困难,当人类被压迫的时候会想出各式各样的方法来解决这些问题,只是变得困难了但不是说不能进行攻击,例:DEP 保护  “rop_chains、ret2libc” 可以绕过它们,GS 调用 cookie 保护,有两类方式绕过,一靠猜【蒙】函数的调用 cookie 值(这是一个固定的值)另一种是想办法利用溢出强行覆盖 “SEH(结构化异常处理)catch_chains” 中的虚函数处理地址

   ASCII armoring 保护;就是想办法让每个被加载程序地址空间的 libc 函数的地址中都具有一个 NULL 字节,从而让通过字符串拷贝的 libc 的地址无效,让攻击无从谈起,但人们的智慧是无穷的,当遇到了压迫人们就会奋起的进行反抗,所以 ret2plt 技术应用而生,它利用 PPR(pop %ebx pop %ebp retn)指令序组成一段连续的 strcpy or memcpy 函数把要被调用的函数地址的字节从不同的空间拷贝到栈上,然后在进行调用,从而绕过 “ASCII armoring” 的保护机制。

    上面提到这些攻击技术几乎都是利用栈溢出攻击的,那么堆溢出是不是就真的安全?显然不是的,堆溢出攻击(unlink)相信很多人可能没有听过这个东西,这是一种专门 “堆溢出攻击” 提出的一门技术,它就是想办法利用目标程序的 “堆溢出” 的漏洞,把分配的 “堆内存” 的 “chunk header” 中的虚函数地址给覆盖了,malloc 分配的内存是连续的,即 malloc 两次内存之间的地址是相互连接在一起的(从高到低,跟栈差不多)而 “堆溢出” 就是想办法覆盖掉 “下个 malloc” 函数分配的 “chunk header” 中的 free addr 当 “程序” 调用第二次 free 函数时就会执行攻击的 shellcode。

    所以没有真正安全程序与技术,它只是相对的,但这对我们来说有多大的意义?我本人在这里不说推销这些技术,而是首先我们需要明白一点,技术本身是无罪的,犯罪的只是别有用心的人而已,大多数所谓进行不法活动的人都是些在某些 XX 群里面交了点学费,拿了点资料跟工具就出来祸害人的 “小害虫”,提供工具的人水平不用质疑,但是没用到正道上面!

   我们了解与研究这些技术,并不一定需要真的就去 “专精” 它们,因为我们想要沐浴在 “阳光之下”,不过这个也需要看每个人的选择,或许它们觉得这很 geek ,当然走 IA / MA / SA 也很 geek,只是大多数人不知道而已,它们之间只是发展方向上的不同而已,大多数做安全更多还是去搞 “竞品分析”,地位也不能和 2010 年之前相比了。

   我个体只想走 “阳光正道”,不喜欢走这些东西,而且我的确从打心底来说,并不是那种特别喜欢这些东西的人,但我还是要了解与研究它们?原因是为了更好的防护的我的 “程序” 当我不知道它们的攻击手法,你不可能在软件在做 “瀑布设计” 与 “敏捷开发” 过程中兼顾并考虑到这些问题,那么又从何去解决问题减少损失?坐等 “杀毒流氓、微软” 这些去打补丁?或者说不用考虑的直接关闭我的程式或服务器?但当我们知晓了我自然有更好的办法能够解决这些问题,减少损失不是更好?

基于栈式的缓冲区溢出_第1张图片

    本文只提供 jmp esp 的方式,虽然这个方式在现代来说根本不能用,但是就了解缓冲区溢出攻击是一个很不错的入门试验,后面我可能会写基于 ROP_chains 或 ret2libc 攻击的利用方式,但它们本质是想通的,只是变得更加的麻烦跟费神,但不是说不能应用,其实市面上还有很多可以通过这几类方法利用的漏洞,你不得不说大多数人包括公司的安全意识真的太差劲了。

   但是要运行本文给出的代码,你必须要在 C/C++ 编译器与连接器配置中关闭掉 “GS” 与 “DEP”,DEP主要是为了防止 “不具有 PAGE_EXECUTE_READ ” 内存标志,权限的二进制代码数据的执行,而 JMP ESP 是把 shellcode 复制到栈上执行的,而栈上的内存是不具有 “PAGE_EXECUTE_READ” 的权限的,只有 PAGE_READWRITE 的权限,不过这个问题可以利用 ROP_chains 进行绕过。

基于栈式的缓冲区溢出_第2张图片

基于栈式的缓冲区溢出_第3张图片

    参考上述要进行试验的 C/C++ 配置,说实话这个东西,如果靠给人讲其实并不是形象(有点抽象)或许说了很多其实到最后面人们可能还是没有理解,所以本文将提供可被编译且正确运行的 “C语言示意代码” 用于阐述本文的内容。

#include
#include

static unsigned char shellcode[] = 
{
    0,0,0,0, // ebp-8
    0,0,0,0, // ebp-4
    0,0,0,0, // ebp-0
    0,0,0,0, // ebp+4(retn addr == RIP) -- JMP ESP
    //
    184,0,0,0,0, // MOV EAX,fun_hijacked
    255,224, // JMP EAX
};
static void fun_hijacked()
{
    printf("Attack successful");
    getchar();
    exit(0);
}
static void fun_bug()
{
    int n;  // ebp-8
    memcpy(&n, shellcode, sizeof(shellcode));
}
static void fun_def_jmp_esp()
{
    __asm
    {
        jmp esp
        jmp esp
        jmp esp
    }
}
static void* fun_get_jmp_esp_addr()
{
    unsigned char* p = &fun_def_jmp_esp;
    while (1)
    {
        if (p[0] == 0xFF && p[1] == 0xE4) // jmp esp
        {
            return p;
        }
        p++;
    }
}
int main(int argc, char* argv[])
{
    *(void**)&shellcode[12] = fun_get_jmp_esp_addr();
    *(void**)&shellcode[17] = &fun_hijacked;

    fun_bug();
}

 

你可能感兴趣的:(C/C++)