2018年9月,FortiGuard实验室研究员Honggang Ren在Windows JET引擎Msrd3x40中发现了一个代码执行漏洞,并遵循Fortinet的负责披露流程向微软报告。2019年1月的星期二,Microsoft发布了一个安全公告,其中包含此漏洞的补丁,并将其标识为CVE-2019-0538。
存在漏洞的DLL msrd3x40是所有受支持Windows版本的组件,从Windows 7到Windows 10。我们报告的漏洞可以使用精心设计的mdb文件触发。解析mdb PoC文件时,由于释放无效的堆地址而发生堆损坏。由此导致代码执行漏洞利用。
在本博客中,我们将分享对此漏洞的详细分析。
分析
有两种方法可以重现此漏洞。
方法1:
通过下面的参数使用Excel oledb外部数据源加载PoC文件,可以看到Excel崩溃。PoC文件可以位于本地或smb共享中。
图1. 输入PoC.mdb
方法2:
在Windows 10中,可以在命令窗口中执行“cscript.exe trigger.vbs”。
图2. trigger1.vbs脚本
以下是崩溃发生时的调用堆栈。
图3.发生崩溃时的调用堆栈
从上面的调用堆栈输出中,我们可以看到崩溃发生在函数“msrd3x40!free”中。让我们通过以下命令启用cscript.exe整页堆:“gflags / p / enable cscript.exe / full”。
然后,让我们检查堆内存地址中堆内存空闲失败的原因。内存处于MEM_RESERVE状态,如下所示:
图4.free无效内存
通过逆向工程和跟踪,我们可以看到精心设计的mdb导致调用msrd3x40.dll出错,因为精心设计的mdb文件头版本字段为0。正常版本应为1。正常的mdb文件让程序进入header解密代码。但是,由于精心设计的mdb文件版本字段为0,精心设计的mdb文件没有在msjet40.dll中完成header解密。
图5.与精心制作的mdb版本进行比较并进入了错误的分支
因此,精心设计的mdb文件将导致进入函数msjet40!ErrOpenForeignDatabase + 0x65并调用msrd3x40.dll!ErrIsamOpenDatabase。但处理正常的mdb文件不会调用msrd3x40.dll。因此,精心设计的mdb文件导致它进入错误的代码分支。
图6.调用msrd3x40.dll导致版本值错误
msrd3x40.dll解密精心设计的mdb header。偏移量0x42处的数据最初为0x86。在RC4解密mdb header之后,它使偏移量0x42处的数据变为0。因此采用另一个代码分支。请参阅以下代码:
图7.比较msrd3x40.dll中解密的mdb header构造字节
到目前为止,仍然没有生成无效的堆内存地址。通过进一步的逆向工程和跟踪,我们发现用于生成无效堆内存的key因子来自以下代码:
图8.key乘法因子变量在msrd3x40.dll中获得
运行上面的Database ::AssignUserNumber函数后,key乘法因子变量[esi + 6ch]等于0x100。
接下来,让我们跟踪无效的堆内存生成,如下所示:
图9. msrd3x40.dll中生成的无效堆内存地址
在该函数中,[ecx +6c]是先前获得的密钥乘法因子0x100。这里,dx = 0x1。分配值后,[ecx + eax * 2 + 194h]实际分配给字1,其中这个字实际上是释放的无效存储器地址的低字。也就是说,在调用Database ::MarkCorrupt函数之前,目标对象堆指针是正确的。然而,在将值0x1字分配给上述存储器地址之后,生成无效堆指针。这导致释放无效指针和代码执行条件。
从上面的分析,我们可以看到漏洞的根本原因是格式错误的mdb版本值0x00,这导致程序采取错误的分支,并且精心设计的mdb文件偏移量0x42的数据为0x86,数据偏移量为0x600 -0x7ff 全等于0。这会导致使用0x100值创建乘法因子。在下一个函数调用中,由于乘法因子而导致目标堆地址的低位字被1覆盖,并且生成了无效的堆地址。释放无效堆地址时,会导致崩溃。成功利用此漏洞可能导致远程执行代码。
解决方案
鼓励Microsoft Windows Server存在漏洞版本的所有用户升级到最新的Windows版本。此外,已部署Fortinet IPS解决方案的机构自2018年10月12日起受到以下签名保护,不受此漏洞的影响:
MS.JET.Database.Engine.Msrd3x.Remote.Code.Execution
有关此0 day发现的更多详细信息,请参见此处(here)。