freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

Windows DWrite 组件 RCE 漏洞 (CVE-2021-24093) 分析
奇安信代码卫士 2021-06-24 11:23:02 215631

概述

Windows图形组件DWrite库存在数组越界写漏洞 (CVE-2021-24093),可导致远程代码执行。

当DWrite库解析恶意构造的字体文件时,计算内存分配长度时出现错误,触发越界写,而且字体文件中的数据能够越界写入到任意位置,使攻击者可以做到RCE。

背景知识

TrueType (Free Library)字体通常包含在单个TrueType字体文件中,其文件后缀为.TTF。文件开头是 TableDirectory 结构,TTableDirectory 结构的最后一个字段是可变长度的 TableEntry 结构的数组。TrueType 字体中的所有数据都使用 big-endian 编码。

TrueType字体目录的c语言定义:

typedef sturct{char tag[4];ULONG checkSum;ULONG offset;ULONG length;}TableEntry;typedef struct{Fixed sfntversion; //0x10000 for version 1.0USHORT numTables;USHORT searchRange;USHORT entrySelector;USHORT rangeShift;TableEntry entries[1];//variable number of TableEntry}TableDirectory;

通过 010 Editor 编辑器打开poc.ttf文件,可以清楚的看到 ttf 文件的结构。

1624504411_60d3f85b6af70c9d4a85e.png!small?1624504398742

重点关注 maxp 表中的 maxPoints 字段和 maxCompositePoints,可以看到它们的值分别为0和3,它们相对于maxp表起始地址的偏移值分别为6和10。

复现

获取 PoC 后,将 poc.html 和 poc.ttf 放到同一目录,双击打开 poc.html 文件。

PoC 地址:

https://packetstormsecurity.com/files/161582/Microsoft-DirectWrite-fsg_ExecuteGlyph-Buffer-Overflow.html

1624504448_60d3f88021b85b092a7f3.png!small?1624504434454

1624504456_60d3f888d18bb6fc4cc95.png!small?1624504443366

点击OK,加载 poc.ttf 字体文件。浏览器渲染进程崩溃。

1624504475_60d3f89bd4d7a1b4cb74e.png!small?1624504462159

用 windbg 定位崩溃点。在第2步点击OK之前,将windbg工具attach到6768进程上。

1624504485_60d3f8a5998e8885194fb.png!small?1624504472108

如何确定进程6768是chrome当前渲染进程呢?可以借助procexp64.exe工具。

1624504493_60d3f8ada88a1445d824f.png!small?1624504480396

继续执行,在DWrite!fsg_ExecuteGlyph+0x72c位置触发内存访问违例,代码 “add word ptr [r8+56h],ax” 引用了一个非法地址。

1624504505_60d3f8b9411968e1018b1.png!small?1624504492216

此时的调用栈为:

同时可以使用BrokenType/ttf-otf-dwrite-loader直接加载poc.ttf复现漏洞,崩溃地址不变,调用栈为:

BrokenType/ttf-otf-dwrite-loader的下载地址为:

https://github.com/googleprojectzero/BrokenType/tree/master/ttf-otf-dwrite-loader

漏洞成因

字体加载后会调用 TrueTypeRasterizer::Implementation::Initialize 初始化函数,在 Initialize 内部调用 fs_NewSfnt 函数,fs_NewSfnt 又会调用fsg_WorkSpaceSetOffsets 函数,fsg_WorkSpaceSetOffsets 的作用是计算内存分配长度。查看 fsg_WorkSpaceSetOffsets函数的伪代码如下:

1624504538_60d3f8dadeb6d6dee3fb8.png!small?1624504525386

a1是指向maxp表的指针,v10 = *((WORD *)a1 + 5)取字段maxCompositePoints 的值,*((WORD *)a1 + 3取字段maxPoints,因为maxCompositePoints和maxPoints的偏移分别是10和6,同时a1被强制转化为(_WORD *) 类型,所以伪代码中偏移5和3就对应10和6。

这段代码逻辑是,比较maxPoints和maxCompositePoints的大小,取大值再与数值1比较大小,最后加上数值8后作为第一个参数传入fsg_GetOutlineSizeAndOffsets函数,最后返回一个计算好的数值。

内存分配操作在Initialize函数调用fs_NewSfnt之后不远处,可以看到fsg_WorkSpaceSetOffsets函数返回的数值最终影响内存分配的长度。

1624504550_60d3f8e693d47ac4876d1.png!small?1624504537420

poc.ttf中,maxPoints和maxCompositePoints的值分别是0和3,是畸形数据。

由于fsg_WorkSpaceSetOffsets函数中没有恰当处理,会导致Initialize函数内存分配不足。

当字体渲染时,由于内存分配不足,数据结构fsg_WorkSpaceAddr的内容会覆盖结构体GlyphData。

typedef struct fsg_WorkSpaceAddr{ F26Dot6 *              pStack;                     /* Address of stack                  */ void *                 pGlyphOutlineBase;      /* Address of Glyph Outline Base     */ fnt_ElementType *    pGlyphElement;          /* Address of Glyph Element array    */ boolean *              pGlyphDataByteSet;      /* Address of ByteSet array          */ void *                 pvGlyphData;                /* Address of GlyphData array        */ void *                 pReusableMemoryMarker;  /* Address of reusable memory        */ } fsg_WorkSpaceAddr; struct GlyphData{ char        acIdent[2];             /* Identifier for GlyphData                         */ GlyphData * pSibling;               /* Pointer to siblings                              */ GlyphData * pChild;                 /* Pointer to children                              */ GlyphData * pParent;                /* Pointer to parent                                */ sfac_GHandle hGlyph;                /* Handle for font access                           */ GlyphTypes  GlyphType;              /* Type of glyph                                    */ uint16      usGlyphIndex;           /* Glyph Index                                      */ BBOX        bbox;                   /* Bounding box for glyph                           */ uint16      usNonScaledAW;          /* Nonscaled Advance Width                          */ int16       sNonScaledLSB;          /* Nonscaled Left Side Bearing                      */ uint16      usDepth;                /* Depth of Glyph in composite tree                 */ sfac_ComponentTypes MultiplexingIndicator;/* Flag for arguments of composites                */ boolean     bRoundXYToGrid;         /* Round composite offsets to grid                  */ int16       sXOffset;               /* X offset for composite (if supplied)             */ int16       sYOffset;               /* Y offset for composite (if supplied)             */ uint16      usAnchorPoint1;         /* Anchor Point 1 for composites (if not offsets)   */ uint16      usAnchorPoint2;         /* Anchor Point 2 for composites (if not offsets)   */ transMatrix mulT;                   /* Transformation matrix for composite              */ boolean     bUseChildMetrics;       /* Should use child metrics?                        */ boolean     bUseMyMetrics;          /* Is glyph USE_MY_METRICS?                         */ point       ptDevLSB;               /* Left Side Bearing Point                          */ point       ptDevRSB;               /* Right Side Bearing Point                         */ uint16      usScanType;             /* ScanType value for this glyph                    */ uint16      usSizeOfInstructions;   /* Size (in bytes) of glyph instructions            */ uint8 *     pbyInstructions;        /* Pointer to glyph instructions                    */ fnt_ElementType * pGlyphElement;    /* Current glyph element pointer                    */ };

1624504568_60d3f8f81de17f304ff12.png!small?1624504554775

在fsg_CreateGlyphData中,fsg_AllocateGlyphDataMemory负责计算GlyphData的起始地址,fsg_InitializeGlyphData函数将fsg_WorkSpaceAddr成员pGlyphElement的地址赋给了GlyphData的pGlyphElement。由于结构体fsg_WorkSpaceAddr的内容会覆盖到GlyphData,所以在后续函数fsg_ExecuteGlyph中GlyphData.pGlyphElement上的写入操作就会破坏GlyphData。

事实也是如此,当运行到函数fsg_ExecuteGlyph时,rsi+8(GlyphData+8)落在了memset_0准备设置的内存区间(pGlyphElement数组)内,从而触发越界写。

memset_0调用之后,rsi+8地址处的内容会被清零。

1624504578_60d3f9026b7e7053cf389.png!small?1624504565133

接着是__guard_dispatch_icall_fptr函数的调用,这是微软控制流保护机制,实际调用TrueTypeRasterizer::Implementation::ApplyOutlineVariation函数。ApplyOutlineVariation 会调用GlyphOutlineVariationInterpolator::ApplyVariation,作用是读取ttf文件中的数据,赋值给被memset_0清零的内存区间,rsi+8因为处在区间内,所以值会被改为ttf文件中的内容。

1624504587_60d3f90b9ac0f29accc42.png!small?1624504574315

rsi+8被控制后,可以达到任意位置写,而被写入的内容同样来自于ttf文件,同样可以被控制。这样,就得到了一个任意地址任意写原语。

动态调试

通过windbg动态调试发现(在调用memset_0前打断点),地址rsi+8处的值在调用memset_0后被清零。

1624504607_60d3f91f74d93f417339f.png!small?1624504594205

查看传递给 memset_0 的参数值:

1624504618_60d3f92a90091b4d66959.png!small?1624504605209

计算得出,memset_0准备设置的内存区间为[0x000001f5cdb98bec,0x000001f5cdb98d34],而rsi+8的值为0x000001f5cdb98d18正好落在了上述区间。所以memset_0执行过后,rsi+8地址处的值被置为0。

继续执行,地址rsi+8处的值在调用DWrite!_guard_dispatch_icall_fptr函数前后也发生了变化,并且被修改为poc中的值。

1624504625_60d3f931731b93d5ec0c4.png!small?1624504612226

自此,"add [r8+56h], ax"操作中,r8与ax的值均来自于poc.ttf文件,可以被完全控制,攻击者就可以做到任意地址任意写。

1624504634_60d3f93a9caed6a5ebb19.png!small?1624504621298

补丁

1624504653_60d3f94d1e0cb2f9050f4.png!small?1624504639567

查看补丁文件,发现fsg_WorkSpaceSetOffsets函数作了修改,查阅fsg_WorkSpaceSetOffsets补丁后的伪代码:

1624504659_60d3f95321f9bbd77a6f2.png!small?1624504645608

在原来的逻辑上,又取出了字段maxComponentElements的值(v12 = *((_WORD *)a1 + 14);),并与之比较大小,将数值大者传入函数fsg_GetOutlineSizeAndOffsets。伪代码稍微有点问题,经过汇编代码比较后,v17其实应该是v33。

调试补丁,memset_0准备设置的内存区间为[0x0000017ce0730f64,0x0000017ce07310ac],区间长度仍然为0x148,而rsi+8的值为0x0000017ce0731698落在了区间之外,不再触发数组越界写漏洞。

1624504670_60d3f95eb3f0548560e0b.png!small?1624504657441

总结

DWrite 库文件由于对畸形 ttf 文件的处理不当,导致数组越界写漏洞。攻击者精心构造 ttf 文件后,达到了任意地址任意写,从而能够进一步完成远程代码执行的攻击效果。补丁文件更正了对畸形文件数据的处理逻辑, 使得分配的内存长度更大,避免了越界写的出现。

参考资料

https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2021-24093

https://bugs.chromium.org/p/project-zero/issues/detail?id=2123

http://blog.topsec.com.cn/cve-2021-24093-windows图形组件远程执行代码漏洞分析/

https://packetstormsecurity.com/files/161582/Microsoft-DirectWrite-fsg_ExecuteGlyph-Buffer-Overflow.html

# 漏洞分析 # windows漏洞 # RCE漏洞
本文为 奇安信代码卫士 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
奇安信代码卫士
奇安信代码卫士 LV.8
国内第一家专注于软件开发安全的产品 https://codesafe.qianxin.com
  • 275 文章数
  • 257 关注者
GitHub Actions 供应链攻击因受陷的 SpotBugs 令牌引起
2025-04-07
奇安信发布《2024中国软件供应链安全分析报告》
2024-09-09
存疑 CVE 漏洞带来无谓压力 热门开源项目开发者归档 GitHub 仓库
2024-07-05
文章目录