3漏洞利用升级
漏洞特征描述:
一、前置条件
原理是构造恶意webp类型图像,在需要将webp解码为其他类型文件的地方造成哈夫曼编码的堆缓冲区溢出。
可以用c代码构造哈夫曼编码频率或者python代码构造哈夫曼编码二级表,精心设计一个存在堆缓冲区溢出的webp类型图像bad.webp,在溢出区域写入了4字节数据,偏移量为152字节。
事先在Ubuntu 20.04上下载Google(2023年9月18日前发布的版本)的libwebp(<1.3.2)源码,利用其中的dwebp解码程序对bad.webp进行测试,输出报错堆溢出并打印出具体的溢出位置。
二、漏洞利用过程
将bad.webp图像保存到手机系统自带的相册,也可通过邮件发送bad.webp使得对方下载到本地。选择用其他利用打开选择qq(最新版V 8.9.83.12605)或者pixlr打开都会出现闪退和死机,或者上传到googlelens识图会导致崩溃,手机系统自带的相册也会出现闪退和卡机。并且这种危害是不可逆的,也就是自从在发送bad.webp图像导致闪退后,应用在之后的日常使用中也会不自觉的闪退。
三、漏洞影响
漏洞影响范围很大,只要应用软件底层调用了Google的libwebp库(如Chrome、Firefox、微信、钉钉、QQ、Edge、Brave、Signal、1Password 等)都会受影响,并且由于写入了4字节数据覆盖堆内存,尝试在某同一处多次打开图像会导致该软件闪退和崩溃
四、漏洞升级前后构造代码对比:
(即对比文档1和2中的craft.c和此文档3中的poc.py)
相同点:构造使得哈夫曼编码很长(树很大)使得超过设定的缓冲区范围导致溢出
不同点:
craft.c:构造频率,抓住哈夫曼建树特点,对15个原子分量精心设计频率,间接导致编码长度
poc.py:直接构造哈夫曼编码
五、漏洞升级(写入数据)原理:
堆缓冲区溢出
由于哈夫曼编码的特点,频率大的编码长,对应的叶子在整个树的位置就越深,树很高空间大,使得哈夫曼树溢出,
一个图像由5个维度的哈夫曼编码树构成,分别是RHBA和distance,要使得通过安全检查,这五个哈夫曼表不能中断,思路是让前四个哈夫曼表构建达到最大,给最后一个distance表溢出的前提机会。
确定在写入样本:由于每个哈夫曼编码结构体的特点,每个HuffmanCode占4-bytes大小的内存空间,结构上分为两部分,编码长度和实际编码值,bits的取值是受到前缀码长度限制的,其取值范围为[1, 15];而value是当前HuffmanCode的实际编码值,该编码值是可以由攻击者控制的,其取值范围取决于编码对象的取值范围,例如对于RGB颜色编码,其取值范围为[0, 255]。在distance通道构造溢出样本,其编码编码符号数为40,因此,我们可以做到写入一个4字节的对象,其中高地址的2字节可控(value部分),取值范围是[0, 39]。
确定写入位置:哈夫曼编码并非顺序写入哈夫曼表,根据哈夫曼表的特点,分为根表和二级表,这也是用哈夫曼编码文件的原因查找更快且省空间,根据编码的键值在根表中找到索引,根据索引指示的位置写入表中。所以可以构造4个15位的哈夫曼编码,使得可以第4个哈夫曼编码写入到对应的索引上,使其能够覆盖下一个堆块的数据。(前面说4字节,后面说15位,因为根表保存在1~8,二级表保存在9~15)
每个HuffmanCode的结构如下:
typedef struct {
uint8_t bits; // number of bits used for this symbol
uint16_t value; // symbol value or table offset
} HuffmanCode;
该结构体的内存分布如下:
| bits (1 byte) | padding (1 byte) | value (2 bytes) |
其中,bits字段表示当前HuffmanCode的编码长度,value字段表示当前HuffmanCode的值,每个HuffmanCode占4-bytes大小的内存空间。其中,bits的取值是受到前缀码长度限制的,其取值范围为[1, 15];而value是当前HuffmanCode的实际编码值,该编码值是可以由攻击者控制的,其取值范围取决于编码对象的取值范围,例如对于RGB颜色编码,其取值范围为[0, 255]。
由于我们是在distance通道构造溢出样本,其编码编码符号数为40,因此,我们可以做到写入一个4字节的对象,其中高地址的2字节可控,取值范围是[0, 39]。
可以发现,HuffmanCode并非是以顺序写入霍夫曼表,其index是通过reversed prefix code计算得到的。为了能够控制写入的index,我们计算了在15 bits的前缀码长度下(编码域为2^(15-8)=128),其二级霍夫曼表中每个HuffmanCode的index顺序为:
0x0 0x40 0x20 0x60 0x10 0x50 0x30 0x70 0x8 0x48 0x28 0x68 0x18 0x58 0x38 0x78
0x4 0x44 0x24 0x64 0x14 0x54 0x34 0x74 0xc 0x4c 0x2c 0x6c 0x1c 0x5c 0x3c 0x7c
0x2 0x42 0x22 0x62 0x12 0x52 0x32 0x72 0xa 0x4a 0x2a 0x6a 0x1a 0x5a 0x3a 0x7a
0x6 0x46 0x26 0x66 0x16 0x56 0x36 0x76 0xe 0x4e 0x2e 0x6e 0x1e 0x5e 0x3e 0x7e
0x1 0x41 0x21 0x61 0x11 0x51 0x31 0x71 0x9 0x49 0x29 0x69 0x19 0x59 0x39 0x79
0x5 0x45 0x25 0x65 0x15 0x55 0x35 0x75 0xd 0x4d 0x2d 0x6d 0x1d 0x5d 0x3d 0x7d
0x3 0x43 0x23 0x63 0x13 0x53 0x33 0x73 0xb 0x4b 0x2b 0x6b 0x1b 0x5b 0x3b 0x7b
0x7 0x47 0x27 0x67 0x17 0x57 0x37 0x77 0xf 0x4f 0x2f 0x6f 0x1f 0x5f 0x3f 0x7f
我们的想法是:在溢出的霍夫曼表中构造4个15-bit的编码,使得其第4个HuffmanCode写入到0x60的index上,使其能够覆盖下一个堆块的数据。另外,我们还可以构造多个9-bit的编码(其二级霍夫曼表的大小为2),使得该index能够以2为单位进行调整,从而实现index可控。
参考文献:Exploiting the libwebp Vulnerability, Part 1: Playing with Huffman Code | DARKNAVY
这是我到11月底看过写的最好的一篇技术文档!真的膜拜orz!关于webp漏洞深蓝写了两part。虽然第二part对我来说读的有些吃力了...但是anyway!好东西就要分享出来!强烈安利!!
感谢阅读!这应该是CVE-2023-4863最后一篇博客了!感谢您宝贵的时间!
插播一个小插曲hhh
我现在上传博客的同时正在上某专选的最后一节课(台上轮流同学做汇报,今天主题是web安全)然后老师问我们学了web木有,台上同学说:学了,等于没学。全班大笑hhh,我正好坐老师后面,老师问我那门计网课的情况hhh。看来我们专业的计网日后可能只能靠自学了…说实话越往后学越感觉web无处不在真的好重要(我自我感觉比更底层的编译和体系结构日常能接触的概率更多)