缓冲区溢出原理与用途

1. 缓冲区溢出代码简单示例

缓冲区溢出一般是指的栈溢出,即在函数中处理内存时越界,改写了栈中的其他数据。下面是一份演示代码:

#include <iostream>

// DoAttack是攻击者的想要执行的代码
// 这里为了演示方便,直接一起编译了
// 实际上攻击者可以将此函数先转成机器码,再通过应用程序的对外接口放到buffer中传进来。
void DoAttack()
{
    std::cout << "Do attack!\n";
    exit(0);
}

// 正常业务代码,处理外部输入
void DoSomething(char* buffer, int len)
{
    char dstBuffer[12] = {0};
    // 调用memcpy,没有校验len的大小是否小于12。
    // 实际len是20,比dsBuffer多了8个字节。
    // 多的8个字节中,后面那4个字节(即: buffer+16),就是DoAttack的函数地址,写到了dstBuffer+16处。
    memcpy(dstBuffer, buffer, len);

    // 这里只是为了演示,简单打印一些内容。
    std::cout << "Do something!\n";
}

// debug版本,需要设置工程属性:
// 1. c/c++->代码生成->基本运行时检查,选“默认值”,即关闭。
// 2. 链接器->增量链接,选“否”。
int main()
{
    // buffer可以是通过gets或者restful等从外部获取的不可信数据
    // 这里为了演示方便,直接在代码里写死。
    char buffer[20] = {0};

    // 最后4个字节放的DoAttack函数的首地址
    *(int32_t*)(buffer + 16) = (int32_t)DoAttack;

    // 将特殊构造的buffer传给业务函数
    DoSomething(buffer, sizeof(buffer));
    std::cout << "main end!\n";
}

输出结果:

Do something!
Do attack!

代码开源地址:https://gitee.com/ferrisyu/CodingSea/tree/master/Sample/BufferOverflow

2. 代码说明

main函数中的buffer[20]是模拟外部来源的二进制数据,传给业务函数DoSomething处理,攻击者可以构造特殊的buffer,造成缓冲区溢出,从而调用到自己的函数DoAttack。

下面分析一下,为什么DoAttack函数会被执行。

3. 为什么DoAttack能够被执行

下图是DoSomething执行时栈空间的内存分布:

  • 重点是③处,”DoSomething”返回时,跳转到此地址继续执行,正常情况下,会回到call的下一行,即:③处保存的00CE54C6继续执行,最终打印”main end”。
  • 然而,③处正好是dstBuffer + 16的位置。从图中堆栈可以看到,dstBuffer + 12是④处的位置,dstBuffer + 16就是③的位置。
  • 由于memcpy没有校验长度,dstBuffer + 16的位置(即③处)会被写成DoAttack函数的地址。
  • 所以”DoSomething”返回时,会从”DoAttack”继续执行,并没有回到00CE54C6处。
  • DoAttack中可以实现攻击者需要的任何逻辑,之后,攻击者可以选择恢复原来的寄存器状态,再次回到00CE54C6正常执行。这里为了演示方便,直接exit退出进程。

4. 缓冲区溢出的用途

  • 利用具备高级权限的程序,执行自己本来没有权限执行的代码。

    比如:攻击者没有root账号的情况,可以利用某个具有root权限的程序,调用它的功能,将特殊构造的buffer传给它,从而执行自己的代码。

  • 破坏性攻击,使用程序崩溃,业务中断。

这也是为什么不建议代码中使用memcpy、strcpy等不安全函数的原因。

建站  ·  开发  ·  读书  ·  教程
↑长按或扫码关注“编程之海”公众号↑
↑长按或扫码关注“编程之海”公众号↑
0 0 投票数
文章评分
订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论

0

0

507

0
希望看到您的想法,请您发表评论x