CVE-2024-2961

本文最后更新于 2024年11月19日 下午

参考链接:

【翻译】从设置字符集到RCE:利用 GLIBC 攻击 PHP 引擎(篇一) - 先知社区

Nepnep Web 组第一周队内分享哔哩哔哩_bilibili

该漏洞已在国内大型的ctf赛事活跃,博主本人却是一直不会,在此好好学习一番内容。

复现环境:

vulhub/php/CVE-2024-2961 at master · vulhub/vulhub

环境搭建

直接环境git下来然后docker起一下就行了,我使用的默认源码(用的小水滴起的内容)

1
2
3
<?php    
$data = file_get_contents($_POST['file']);
echo "File contents: $data";

访问index.php传入file参数就能读取文件

2024-11-17172325

原理

闲言碎语

如上文的file_get_contents($_POST['file']);我们可以利用file传值进行读取,比如就像上方的读取/etc/passwd,但是php还支持其他协议比如http://google.com,这样就可以获取google的首页了,也可以使用ftp来获取内容,当然最主要的内容是php协议还自定义了phar://协议。

phar://可以读取PHAR存档的全部内容,PHAR 代表 PHP 存档,是一组文件,比如:

  • 源代码
  • 资源
  • 序列化的元数据

该协议是常见php泄漏点,当使用它访问一个PHAR文件时,其元数据会被反序列化。

随着版本的推移,利用点变少,但是phar://并非攻击者唯一有用的协议;另一个名为php://filter的协议同样取得了显著成效。那么就先把视角专业到php过滤器上面。

php://filter这一PHP特有的协议,它提供了一种在返回流之前对其应用转换的方法。其语法如下:

1
php://filter/[filters...]/resource=[resource]

比如我们可以通过convert.base64-encode这个很常见的过滤器来读取/etc/passwd来进行

2024-11-17221641

理所当然也可以进行两次convert.base64-encode

除此之外还有很多过滤器,比如:

  • string.upper ,将字符串转换为大写
  • string.lower ,将字符串转换为小写
  • string.rot13 ,执行一些 BC 加密
  • convert.iconv.X.Y ,将字符集从 X 转换为

最后一个过滤器: convert.iconv.X.Y 。假设需要将文件从 UTF8 转换为 UTF16,可以用

1
php://filter/convert.iconv.UTF-8.UTF-16/resource=/etc/passwd

当然过滤器也出现了filterchain的考察点,这里不多述了。

cve知识点

这边不多叙述,pwn知识我了解较少,原文博主的pwn水平非常之高。主要的内容是

glibc中iconv()函数将一些数据转换成ISO-2022-CN-EXT格式时,会有1-3字节的溢出。

iconv() API

一般情况下,每个 char 类型的字符占一个字节,但在iconv编码转义的时候则可能转义为多个字节,不过这依旧是在缓冲区预期之内的

当PHP从一个字符集转换到另一个字符集时,它使用 iconv,这是一个用于“使用转换描述符将输入缓冲区中的字符转换为输出缓冲区”的API。在Linux系统上,这个API由 glibc实现API非常简单。首先,打开一个转换描述符,该描述符指定了输入和输出字符集.

1
iconv_t iconv_open(const char *tocode, const char *fromcode);

然后,使用iconv()将输入缓冲区inbuf转换为输出缓冲区outbuf中的新字符集

1
2
3
size_t iconv(iconv_t cd,
char **restrict inbuf, size_t *restrict inbytesleft,
char **restrict outbuf, size_t *restrict outbytesleft);

如果输出缓冲区不够大,iconv() 将返回一个错误指示此情况,可以通过重新分配 outbuf 并再次调用 iconv() 来继续转换。

ISO-2022-CN-EXT

在将数据转换为 ISO-2022-CN-EXT 字符集时,iconv可能在写入输出缓冲区之前未能检查是否有足够的空间剩余

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 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) // <-------------------- 检查点
{
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;

// <-------------------- 无检查点

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;

// <-------------------- 无检查点

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);
}
}

每个块向outbuf(由outptr指向)写入不同的转义序列。

在第一个if[1]里,前面有一个额外的if()块来检查输出缓冲区是否足够大以容纳四个字符。

而其他两个if[2][3]则没有对 outptr 进行检查。因此,转义序列可能会越界写入。,一般的常规汉字没有溢出的效果,但是利用如: 湿这种字符,在iconv()转义时会导致1到3字节的溢出。具体不演示

既然能实现缓冲区溢出,那就能实现rce

Poc分析

核心代码在

1
2
3
4
def run(self) -> None:
self.check_vulnerable()
self.get_symbols_and_addresses()
self.exploit()

check_vulnerable方法仅用于检测相关的 wrapper 是否可用,需要获取完整的回显,如果回显经过了 aes 或者 md5 之类的加密则这个检测方法不可用

get_symbols_and_addresses是用来获取 /proc/self/mapslibc.so.6

exploit方法就是pwn。

复现

需要python310环境和linux环境

2024-11-17215020

访问shell.php即可


这类题最关键的还是如何修改脚本,需要好好学习学习。

相关例题可以参考2024年羊城杯,2024年鹏程杯等等。稍微学习一下怎么修改,这个知识点大概就没什么问题了。


CVE-2024-2961
https://0ran9ewww.github.io/2024/11/17/cve复现/CVE-2024-2961/
作者
orange
发布于
2024年11月17日
许可协议