CVE-2025-49113 漏洞分析与利用方式

CVE-2025-49113 漏洞分析与利用方式

原创 4uuu Nya 奇安信天工实验室 2025-06-10 07:06

一、前  言

二、版本diff

三、复现环境

四、漏洞分析

五、总  结

前  言

2025年6月2日公开了一个RoundCube邮件系统中的一个RCE漏洞,信息如下:

screenshot (1).png

影响版
本<1.5.10 <1.6.11



是一个认证后才可以触发的漏洞,但是评分却达到了9.9,接下来根据公开的漏洞信息对该漏洞进行分析与复现。

版本diff

在本地下载1.6.10版本代码,diff 1.6.11版本发现主要是对
type
字段进行了过滤,
type
字段来源于 
_form
 和漏洞信息也能对应上:

1.png

复现环境

由于RC官方提供了各个版本的docker镜像,所以只需要再简单配置一个dovecot和mysql用于正常登录即可,配置环境如下。

01

docker-compose.yml

screenshot (2).png

02

dovecot.conf

screenshot (3).png

03

users

screenshot (4).png

漏洞分析

01

漏洞入口

根据漏洞公开信息可以知道漏洞入口点位于 
program/actions/settings/upload.php
,整体流程比较简单:

screenshot (5).png

在函数开始会对 
_from
 参数进行一些处理之后才赋值给 
type
,这些处理并不会对参数进行任何安全的过滤,继续向下可以看到处理了一下上传文件,在文件被成功上传后会调用一次 
$this->session_append
,并且这里带入了可控参数,后续没有了对这个字段的处理,所以可以推断出漏洞应该就出现在对session的反序列化上。

跟入 
append
 函数,这是抽象类 
rcube_session
 中的函数:

screenshot (6).png

这里的 
$this->get_node
 函数第一个参数将传入的 
$path
 使用 
.
 进行了分割,也就是说会把传入的 
$type.files
 分割成 
$type
 和 
files
,跟入这个函数:

screenshot (7).png

这里的第二个参数传入的是 
$_SESSION
,也就是说如果当前session中不存在 
$type.files
 的键就会进行创建出来 
$_SESSION[$type][‘files’]
 这类的键(由于获取 
$type
 参数的时候将
.
转换成了

,导致无法无限制的向下创建任意键),之后再次向 
$type.files
 后添加了一层嵌套,key为上传文件处理后的 
attachment_id
,内容为处理后的上传信息,大概内容信息如下:

screenshot (8).png

此时因为

$type
 的原因,我们拥有了一次控制顶层key的能力,但此时还无法控制这个key的内容。

02

session处理

rcube_session
 是roundcube自定义的一个对于session处理相关的类,在这里面hook了原生的几个方法:

screenshot (9).png

主要看到其中的 
sess_write

screenshot (10).png

由于漏洞是认证后,所以一定存在旧数据,那么继续跟入到存在缓存的情况,跟入 
_fixvars

screenshot (11).png

这里在对旧数据进行处理后,合并了已存储的session数据与当前的session数据,此时调用了一个自定义的 
unserialize
 对当前的session进行了一次反序列化处理,跟入到这个函数的定义:

screenshot (12).png

这里注意到对两个符号有特殊处理,一个是 
|
 一个是 
!
,首先 
|
 的作用主要是用于分隔开key和value,当前session序列化后的值为 
key|value;key|value
 形式的键值对,而 
!
 则是将被这个符号标记的key值直接处理成 
s:len:”key”;N;
,但是这里存在一个问题,如果这个key本身就是一个数组或者对象,在对其添加 
N;
 之后仍然还有这个对象本身序列化后的值,而这些值将会被当做一个新的key直到遇到下一个 
|
,从而造成逃逸的效果,以一个RC系统中部分session内容为例,其存储的序列化字符串如下:

screenshot (13).png

被RC自定义的序列化处理后正常应该会是下面的结果:

screenshot (14).png

但是如果将其中的一个key增加
!
标记,变成下面的情况:

screenshot (15).png

再经过自定义的反序列化处理,结果将会和预期出现很大的出入:

screenshot (16).png

可以发现,本身
应该是 
language
 这个键的值 
5:”en_US”
 被当做了一个新的key的起始字符串,并且这个key长度为24,再将其继续进行原生反序列化函数进行处理后会得到下面的值:

screenshot (17).png

而如果language这个key是我们可控的值,并且在其之后还存在可控的值,就可以通过这次逃逸,将后续可控但并不在顶层的key逃逸出来,实现位于顶层的key、value都完全可控,此时再寻找一个对 
$_SESSION[‘key’]
 进行原生反序列化操作的地方,即可实现一次任意对象的反序列化攻击。

在 
program/actions/settings/upload.php
 的上传中,我们可以控制 
$_SESSION[$type]
 这个顶层的key,并且后续内容中的文件名同样没有过滤导致数据可控,如下所示:

2.png

通过修改
_from
参数,尝试对其进行逃逸操作,实现控制顶层key与value,如下所示:

3.png

至此拥有了任意对象反序列化的能力,在系统中存在一个使用原生反序列化处理session顶层key的地方 
program/include/rcmail.php#logout
_actions

screenshot (18).png

所以需要在构造payload的时候,逃逸出来一个位于顶层的 
preferences
 键,再寻找到一个合适的gadget进行利用即可,但是需要注意的是由于文件名这个字段的特殊性,我们无法构造出带有路径符号的payload导致寻找gadget的时候有一些受限。

03

pop chain

由于RC使用了 
guzzle
 理所当然想到 
FileCookieJar
 和 
FnStream
,但是由于这两个类需要带入命名空间,受文件名的影响这两个gadget只能放弃,但是全局搜索后发现存在一个不需要命名空间并且析构函数中存在命令执行操作的类 
Crypt_GPG_Engine

screenshot (19).png

所以这里可以直接构造出来 
$this->_gpgconf
 然后使用 
proc_open
 进行命令执行,比较简单的链,但是由于文件名处的干扰,这里需要做一下绕过。

使用 
proc_open
 执行 
echo $0
 之后发现是 
sh
,那么不用考虑这里没给 
proc_open
 传入包含 
PATH
 的环境变量,可以直接使用各个命令用于绕过,比如下面的命令:

screenshot (20).png

至此完成了反序列化的利用工作,通过配合logout中的 
unserialize($_SESSION[‘preferences’])
,构造逃逸出一个 
preferences
 键即可。

04

利用截图

4.png

总  结

本文从CVE-2025-49113的漏洞描述信息为入口点,找到RoundCube中对session进行自定义反序列化时会出现的问题,最终实现反序列化攻击导致RCE。这个漏洞的利用比较巧妙,同时也比较
巧合
,比如
刚好存
在一个没有命名空间又同时可以做命令注入

 Crypt_GPG_Engine


,也存在直接对session某项数据进行原生反序列化的地方。

最终漏洞修复方式为对
 $type

参数进行了一次
 rcube_utils::is_simple_string

的过滤,其中只允许存在 \w.-

的存在,无法再通过传入
 !

进行逃逸

【版权说明】

【版权说明】

本作品著作权归4uuu Nya
所有

未经作者同意,不得转载

头像.png

4uuu Nya

天工实验室安全研究员,Nu1L Team,Web安全与代码审计。

往期回顾

01

CVE-2024-47575 漏洞分析及三种利用方式

02

通用Linux x64内核态shellcode编写技巧

03

基于路由转发导致的权限认证绕过漏洞分析

04

隐匿与追踪:Rootkit检测与绕过技术分析

CVE-2025-49113 漏洞分析与利用方式

每周三更新一篇技术文章  点击关注我们吧!