CVE-2022-44667 和 CVE-2022-44668漏洞分析
任意门 发表于 江苏 漏洞分析 731浏览 · 2024-02-03 16:34

此文分析两个漏洞
Windows DirectMusicPortDownload Double Free - CVE-2022-44668
Windows DirectMusicPortDownload Integer Overflow - CVE-2022-44667

背景知识

dmusic.dll提供了与DirectMusic音乐和音频功能相关的函数和资源,它是DirectMusic API的一部分。
DirectMusic API:DirectMusic是Windows平台上的一套多媒体音乐技术,旨在支持音乐、音效和交互式音频在游戏和多媒体应用程序中的使用。
DirectMusic:核心服务DLL是一个动态链接库(DLL),它是Windows操作系统DirectX多媒体API的DirectMusic组件的一部分。
dmusic.dll包含了用于创建、播放、编辑和管理音乐的函数和资源,它为开发人员提供了一套接口,使他们能够在应用程序中实现音乐的播放、合成和控制等功能。
DirectMusic核心服务DLL提供了在DirectMusic应用程序中管理和播放MIDI和数字音频数据的基本服务。它包含管理MIDI和音频设备、合成器和声卡的功能,以及加载和播放MIDI和音频数据的功能。
然后介绍一下COM组件,因为此漏洞需要通过COM组件进行触发。
COM 对象通过接口公开其功能,接口是成员函数的集合。COM 组件之间的所有通信都通过接口进行,组件提供的所有服务都通过其接口公开。 调用方只能访问接口成员函数。 内部状态对调用方不可用,除非它在接口中公开。
其中区分COM组件的标识为GUID又称之为全局唯一标识符,有16个字节;其接口是强类型。 每个接口都有其自己唯一的接口标识符(名为 IID)也就是Interface IDentifier;还有针对类的CLSID其标识COM对象的GUID称为CLSID其在注册表的位置如下图所示。
COM库在接到客户程序的请求后会根据给定的GUID到注册表中检索COM对象的注册条目,并以此来定位COM对象。

漏洞分析CVE-2022-44667

在dmusic.dll库中发现一个漏洞。攻击者可以利用此漏洞通过特制的应用程序导致整数溢出。因此当加载和解析DLS文件时,IDirectMusicPort COM组件中可能发生越界写操作。其漏洞存在下述COM类的实现中
CLSID:IDirectMusicCollection (480FF4B0-28B2-11D1-BEF7-00C04FBF8FEF)
Interface: IDirectMusicPort;Server: C:\WINDOWS\System32\dmusic.dll

此COM类是DirectMusic的一部分,DLS文件包含不同的块和列表
如Collection, Wave Pool, Wave File, List of Instrument, List of Region, List of Articulators等
DLS 文件的结构微软也是有文档涉及到的

比如上述一文中提到IDirectMusicSynth::Download方法,它的功能是可以将乐器和音频数据下载到合成器。而且此方法应该接受原始数据(通常来自集合文件),并将其存储在呈现引擎可以使用的形式中。当DirectMusic将DLS数据下载到驱动程序时,数据缓冲区的格式是根据几个DirectMusic结构定义的。
下载的数据以两个结构开始:DMUS_DOWNLOADINFO:描述正在下载的信息的固定大小的标头。DMUS_OFFSETTABLE:标头之后的偏移量表,描述下载数据中各种信息块的偏移量。
偏移表后面是实际数据,可以用下列任意一种开头:DMUS_INSTRUMENT-描述DLS的结构。DMUS_WAVEDATA一种包含PCM格式的波数据块的结构。
Dmusic.dll的DLS文件中的每个块和列表都有自己独特的结构。且DLS数据格式在内核模式和用户模式下是相同的在此漏洞中,我们将重点关注Articulators列表的解析。DLS文件还包含不同的块和列表,如Collection, Wave Pool, Wave File, List of Instrument, List of Region, List of Articulators等。
在此漏洞首先来看一些CArticulation::Load函数。这个函数负责从DLS文件加载数据。

从上图可以看出CArticulation::Load 函数使用tag 对数据块进行解析;而其中一个数据块就是Articulators List;此函数还从Articulators列表中提取相关数据并将其存储在m_ArticData中。这是从DLS文件中读取的8个字节实现的,而DLS文件表示CONNECTIONLIST;其CONNECTIONLIST定义的结构体如下

struct _CONNECTIONLIST
{
  ULONG cbSize;
  ULONG cConnections;
};

在往下看会发现dwSize被赋值为12cConnections 此处的类型是DWORD 但是代码中没有考虑到cConnections很大的时候就会超过DWORD的大小。 然后在往下代码进行判断 12cConnections是否小于等于cksize - 8, cksize - 8 就是Articulation List的大小。如果满足的话将分配一个大小为12*cConnections的内存空间。 在往下走就在下图的代码逻辑中其实可以看见此处是有对乘法的结果溢出检测的但是只是检测了_int64(无符号整数)中的溢出;所以乘法不会溢出。

分配了内存区域之后代码将数据读入新分配的m_pConnections内存区域,其大小为dwSize。但m_pConnections内存区域分配的大小比dwSize大得多,大小不够触发不了崩溃。
继续来看另一个函数CInstrObj::Size来计算InstrObj的总大小,包括ExtensionChunkList, ArticulationList, RegionList和CopyrightChunk。

代码中有一段循环的逻辑是为了计算Articulators的全部大小,在循环中都会调用CArticulation::Size函数获取当前Articulator的大小;然后将这个大小加到this->m_dwSize中。
但是m_dwSize = CArticulation::Size(pArticulation_1) + this->m_dwSize 这里的m_dwSize和this->m_dwSize都是DWORD类型;但CArticulation::Size函数却是可以返回一个_int64值。所以此时是可能会发送整数溢出。
然后在CDirectMusicPortDownload::DownloadP函数中使用了CInstrObj::Size函数来确定InstrObj的大小。分配内存来存储InstrObj。

v49的值代表了通过size函数得到InstrObj的总大小;然后在往下通过调用(这里是有一个虚函数表需要去里边找到对应指针的函数)CDirectMusicPortDownload::AllocateBuffer函数分配一个名为dBuffer的缓冲区大小为pdwSize。然后使用CDownloadBuffer::GetBuffer函数将dBuffer拆分为ppvHeader和dwHeaderSize。
在往下ppvHeader的值传入到CInstrObj::Write中去了;CInstrObj::Write(a2->InstObj, ppvHeader)
而在其中又调用了CArticulation::Write函数

继续看数据发现其从m_pConnections复制到pv + 24这里。这代表的其实是在CDirectMusicPortDownload::DownloadP函数中分配的内存区域ppvHeader。所以就明了;因为之前计算InstObj的总大小有整数溢出,这就导致ppvHeader分配的内存放不下InstrObj的数据。所以CArticulation:: write函数就有一个越界写。

CVE-2022-44668

此漏洞的过程相较上一个就比较简单了。还是在dmusic.dll文件中的,攻击者可以利用此漏洞通过特制应用程序导致double free。因此当加载和分析 DLS 文件时,IDirectMusicPort COM 组件中可能会出现double free。 攻击者可以利用此漏洞在受害者的机器上远程执行代码。
直接看一下关键漏洞点的位置CDirectMusicSynthPort::Unload函数;当此函数走到下图入口点时pIDMDownload->lpVtbl->Release;此处就是CDownloadBuffer::Release。通过CDownloadBuffer::Release函数释放CDownloadBuffer *pIDMDownload的内存。 然后在往下走就是v7中;通过CUserModeSynth::Unload使用FreeHandle回调卸载函数来释放CDownloadBuffer的内存。

在CDownloadBuffer::Release函数中代码包含一个FreeHandle的函数它会去释放CDownloadBuffer类


在FreeHandle函数中没有检查;所以最后利用就可以让m_pvBuffer会在CDownloadBuffer::Release释放一次再到FreeHandle释放第二次,造成double free

可以通过如下代码(自己简单修改)生成测试dls用例进行漏洞调试分析;缺少引用的话就到这里找就行;https://github.com/milliet/DDReader/tree/master;

#include <windows.h>
#include <dmusicc.h>
#include <dmusici.h>

#pragma comment(lib, "dmusic.lib")

void GenerateDLS(const wchar_t* dlsFileName) {
    IDirectMusicLoader8* pLoader = nullptr;
    IDirectMusicComposer8* pComposer = nullptr;
    IDirectMusicSegment8* pSegment = nullptr;
    CoInitialize(nullptr);
    CoCreateInstance(CLSID_DirectMusicLoader, nullptr, CLSCTX_INPROC,
        IID_IDirectMusicLoader8, (void**)&pLoader);
    CoCreateInstance(CLSID_DirectMusicComposer, nullptr, CLSCTX_INPROC,
        IID_IDirectMusicComposer8, (void**)&pComposer);
    pComposer->ComposeSegmentFromShape(&pSegment, 1, 1, DMUS_SHAPET_FALLING);
    pLoader->SaveObjectToFile(CLSID_DirectMusicSegment, IID_IDirectMusicSegment8,
       pSegment, dlsFileName, DMUS_SEGF_DEFAULT);
    pLoader->Release();
    pComposer->Release();
    pSegment->Release();
    CoUninitialize();
}

int main() {
    const wchar_t* dlsFileName = L"test.dls";
    GenerateDLS(dlsFileName);

    return 0;
}
0 条评论
某人
表情
可输入 255