您遇到了利用缓冲区溢出最困难的部分:确定如何让代码执行。一旦你发现了漏洞,注入代码是微不足道的,但让 CPU 开始执行该代码,不是那么多。
如果没有深入了解堆栈框架布局,您会发现其余部分难以理解。
需要注意的一点是,在 C 中main
,从进程的角度来看,它只是另一个被调用的函数(这在 C++ 中有所不同,请参见下面的评论)。虽然您将其视为执行的开始,但对于 CPU 而言,它是一个完整的函数调用,返回地址位于堆栈上。因此,利用其中的缺陷main
与利用任何其他功能中的缺陷相同。
作为攻击者,你对缓冲区溢出所能做的就是写入进程的数据部分,你需要找到一种方法让进程从控制将要执行的内容的数据中读取一些内容。基本上,您需要在数据存储器中找到一个位置,该过程将查看该位置以确定接下来应该执行什么(即:确定程序计数器或 PC 的值)。
影响 PC 的数据存储器中最常见的位置类型是函数的返回指针。从函数返回时,CPU 将从堆栈中读取返回指针并将 PC 设置为它(或它之后的指令 - 详细信息取决于 CPU)。除非您从该main
函数返回,否则您不太可能执行成功的攻击。您需要在数据中的某个位置,最好是在堆上,处理器将读取用于设置 PC 的数据。
数据存储器中用于利用缓冲区溢出攻击的其他“常见”(虽然不太常见)位置是函数指针后跟函数调用,以及异常表后跟异常。这些似乎都不适用于您的应用程序。所以你可能有一个不可利用的堆栈缓冲区溢出漏洞。我怀疑你能做的最糟糕的事情就是让你的应用程序崩溃。
攻击者需要采取标准堆栈缓冲区溢出利用的步骤是:
- 识别堆栈缓冲区溢出漏洞。
- 在漏洞发生时确定堆栈地址。
- 确定当前堆栈帧中函数返回指针的地址。
- 编写代码(通常称为shellcode)进行攻击。
- 创建加载代码并修改函数返回指针以指向堆栈上的 shellcode 的输入。
所以现在您已经找到了漏洞,您需要确定调用的堆栈地址main
。最简单的方法是打印id
. 接下来,返回指针的位置。这在内存中可能比 . 低两个单词id
。类似的东西(char *)id-2*sizeof(int)
。
下一步是编写你的 shellcode。我建议您在程序中创建一个函数,打印字符串“攻击成功”然后退出。让你的 shellcode 调用这个函数。
现在坐下来在堆栈上布局数据(这个堆栈框架布局参考应该会有所帮助),包括跳转到id
变量缓冲区的返回地址。将堆栈数据转换为 ASCII 并输入到程序中。
这个页面有一个我所描述的更具体的例子。