先知社区转移:https://xz.aliyun.com/t/8594
简介
这几天在twitter不经意间发现了一种类型的混淆paper,然后决定分析一下Windows本文还基于内核类型混淆的漏洞模式paper分析学习,首先介绍C 中型混淆的原理,然后介绍一个模块的漏洞 。读完本文你会发现实际上后面这个内核洞和前面介绍的C 类型的混淆并不重要。是的,我也这么认为。这样做的原因是前一部分适合学习原则。原则介绍清楚后,它们将依赖于积累和分歧。事实上,你会发现仍然有许多相似之处。参考文章链接在文章末尾
C 强制类型转换
C 强制类型转换有很多种,这里简要介绍一下static_cast
和dynamic_cast
,C语言中的强制类型转换模型如下
// C double scores = 95.5; int n = (int)scores;
C 强制类型转换的使用方法如下,仅从文字上看,static_cast
和dynamic_cast
主要区别是动态和静态,这让我想起了动态链接和静态链接,虽然没关系
class B { public: int m_iNum; virtual void foo(); }; class D:public B { public: char* m_szName[100]; }; void func(B* pb) { D* pd1 = static_cast<D*>(pb); D* pd2 = dynamic_cast<D*>(pb); }
从汇编层面来看,Debug静态转换版本没有做太多的事情,只是简单地保存指针,只检查编译时的数据类型,不检查操作,但优点是速度快,效率高
然而,动态转换将检查指针是否空,检查类型是否正确,但这也降低了效率,仅限于多态类,类似于CFG
保护机制等
C 类型混淆
以静态转换为例。在下图中,子类可以转换为父类指针,但父类不能转换为子类指针。这将导致许多安全问题。下面有一个描述。
其危害如下图所示。当父类被迫转换为子类时,调用子类的虚拟函数或设置子类的一些新添加的字段会导致访问错误,这是类型混淆的基本原理。然而,这种低级错误在实际场景中肯定很难遇到,因此有必要传播一些新的想法。
理解它也很容易。两种不同类型的指针指向相同的内存。不同的字段类型会导致一些使用机会,如下所示
一个很简单demo可以清楚地描述上述过程,其结构如下图所示
在Linux编译这部分代码可以弹出计算器,可以自己调试
// g test.cc -o test // test.cc #include <iostream> #include <cstdlib> using namespace std; class Base { }; class Exec : public Base { public: virtual void hack(const char* str) { system(str); } }; class Greeter : public Base { public: virtual void sayHi(const char* str) { cout << str << endl; } }; int main(int argc, char* argv[]) { Base* b1, * b2; Greeter* g; b1 = new Greeter(); b2 = new Exec(); cout << hex << b1 << endl; cout << hex << b2 << endl; g = static_cast<Greeter*>(b1); g->sayHi("hello world"); g = static_cast<Greeter*>(b2); g->sayHi("/usr/bin/xcalc"); delete b1; delete b2; return 0; }
CVE-2018-8405
以上内容主要是关于C 然而,我只关心类型转换中可能存在的问题Windows这种漏洞模式在内核中是否常见Windows内核中关于类型混淆的的公开信息较少,在Adobe,更多的浏览器出现。
Windows核心主要由C编写,因此上述问题实际上很难遇到。下面的洞实际上与上面相同C 这个模型没关系,但是原理差不多,发散学习思路也很好。这个洞是2018年湛卢实验室的师傅fuzz到的,这里讨论不多fuzz相关内容主要是了解这个洞的原理。这个漏洞的背景是湛卢paper里面说的很清楚,我就不赘述了。我主要从总结者的角度看待这种漏洞。这个洞存在于 dxgkrnl.sys
,类型混淆首先需要确定类型是如何产生的,以首先要看漏洞对象的创建过程。
创建对象的逻辑如下,其中之一 isCrossAdapter
如果设置了跨设备标志,将申请 0xE8
内存,并将设置0x20标志用于后续检查。如果没有跨设备,直接申请 0xC0
大小内存。这个漏洞可以手动构造0x20标志导致后续逻辑认为申请了 0xE8
然而,大小内存实际上只申请了0xC0
内存混淆
漏洞点 DXGDEVICE::CreateAllocation
函数部分的逻辑如下所示,首先检查0x20标志,即创建在上面0xE8
如果大小对象中设置的标志通过检查,则进行以下步骤,包括v175~v177
都是用户可控的数据。换句话说,漏洞函数判断对象大小的基础是0x然而,如果触发器手动构建了这个标志,并将其引入0xC0
对象的大小,这种类型的混淆会导致越界写的问题,从而导致漏洞
Patch是在 DXGSHAREDRESOURCE::CreateSharedResource
创建对象后,添加检查,即在上层DXGDEVICE::OpenResourceObject
这个0在函数中x检查20标志的位置
前面提到上面这个漏洞是fuzz有几个漏洞。感兴趣的朋友可以看看参考链接ZDI对于分析文章,肉眼审计肯定很难关注这一点。一般这样fuzz都是越界读写造成的。crash,对于没有特殊池,需要打开特殊池crash感觉类型还是很难的fuzz出来的。
对于审计来说,我认为主要的重点是创建对象的地方和上半部分C 类型混淆的paper它还讨论了他们是如何检查类型混淆的。他们强制检查所有对象的情况,分析对象申请内存的位置,并跟踪对象的分配类型。还写了一个工具,感兴趣的朋友可以使用。
参考链接
-
如果您想了解该模块的攻击面或漏洞,请参考湛卢实验室分析文章
https://github.com/RanchoIce/44Con2018/blob/master/44Con-Gaining Remote System Subverting The DirectX Kernel.pdf
-
如果您想了解更详细的类型混淆原理和工具信息,请参考hexhive团队的文章
https://nebelwelt.net/files/18SyScan360-presentation.pdf
-
如果您想了解上述模块的其他类似漏洞,请参考ZI分析的翻译文章
https://www.anquanke.com/post/id/167332#h3-4