标签 GenericObjectSerDe 下的文章


Apache NiFi GetAsanaObject 处理器反序列化漏洞(CVE-2025-66524)

漏洞描述

file-20260112204532791.png



简单看就是在GetAsanaObject处理器中可以反序列化任意类,影响版本为Apache NiFi 1.20.0至2.6.0

利用条件:

GetAsanaObject 处理器依赖可配置的 Distribute Map Cache Client Service 来存储和检索状态信息。

该处理器在处理缓存中的状态数据时,使用了 未经过滤的 Java 对象序列化与反序列化

攻击者若能向缓存服务器中写入恶意构造的状态数据,便可触发反序列化漏洞

环境搭建

在官网下载 Apache NiFi 2.6.0,下载地址:https://archive.apache.org/dist/nifi/2.6.0/

file-20260112204811665.png



下载解压后看到有个 lib 目录,里面是 nifi 的主要依赖。不过这里面有些 jar 包是在 nar 中不方便我们待会调试,还需要先写个脚本把 jar 提取出来

全部提取到新的 lib 目录后新建一个 java 项目把 lib 添加为依赖,配置远程调试信息

file-20260112205232708.png



然后在 nifi-2.6.0\conf\bootstrap.conf 中添加远程调试信息

file-20260112205342299.png



最后在 bin 目录下执行 nifi.cmd start 即可启动程序

漏洞分析&复现

运行后访问 https://localhost:8443/nifi/ 进入页面我们新建一个 GetAsanaObject 处理器,

file-20260112205700915.png



然后配置其属性,Asana Client Service|只有一个选项直接选就可以了,然后Distributed Cache Service 根据漏洞描述需要选择 map 那个,

file-20260112205723091.png



继续配置这两个 service,首先看 StandardAsanaClientProviderService 2.6.0,需要我们访问 asana 平台注册个账号配置 workspace 和 token,值得注意的是这个 workspace 要填 name 而不是 gid

file-20260112205952307.png



接着就是MapCacheClientService 2.6.0 了,只需要添加个 hostname 就可以了

file-20260112210202219.png



最后还要配置个 MapCacheServer,需要和 MapCacheClientService 相对应,这个 sevice 我们只用填 port 和 Persistence Directory 这两个属性就可以了,这样待会的缓存数据就会保存到 E:\tmp\test\test2 目录下面。

file-20260112210353755.png



启动 GetAsanaObject 处理器,这个处理器会不停的从 Asana 平台拉取指定类型的对象数据,并转换成 NiFi 的 FlowFile 供后续处理,而在这拉取数据写到缓存服务器和从缓存服务器读取数据的时候这里就发生了序列化和反序列化。

file-20260112210452875.png



根据 https://github.com/apache/nifi/pull/10599/commits/12351ba71bb32b5d725060e0c0236478ccdf4b05 ,定位到 GetAsanaObject 类,在其 recoverState 方法下上断点,会先调用到 client.get 方法读取缓存服务器数据,

file-20260112211050019.png



跟进来到 MapCacheClientService 的 get 方法

file-20260112211140116.png



赋值inboundAdapterValueInboundAdapter 对象,valueDeserializerValueInboundAdapter 对象的 deserializer 属性,

file-20260112211333866.png



继续跟进这个 get 方法,调用了valueAdapter.getResult() 方法

file-20260112211435002.png



最后会一直调用到 getValue 方法,看到其实这个 value 是上面 deserializer.deserialize(bytes); 获得的

file-20260112211947956.png



而这个 deserializer 其实是上面的 STATE_MAP_VALUE_SERIALIZER 值也就是 GenericObjectSerDe 对象,所以最后会来到 GenericObjectSerDe.deserialize 方法,在这里进行了反序列化,

file-20260112212138766.png



反序列化数据为缓存服务器中的 value 也就是对应的 hashmap 部分,

file-20260112212209545.png


file-20260112213223986.png



以上就是反序列化流程了,当然为了控制输入流我们还需要看看写的流程,定位 MapCacheClientService#put 方法,看到就是直接对 key 和 value 进行序列化然后写入

file-20260112211600961.png


file-20260112211647772.png



所以我们自己写到缓存服务器的数据也要同时对 key 和 value 都进行序列化才能让后面 GetAsanaObject 处理器成功读取到我们自己写入的数据实现任意反序列化。

让 ai 写个脚本通过 java 代码来进行写入,注意缓存服务器地址要对应,

这里的 key 可以在 statushistory 里面看到,

file-20260112213535782.png



运行后成功写入数据

file-20260112213637554.png



file-20260112213648634.png



然后重新启动 GetAsanaObject 处理器,看到成功反序列化到了我们写入的数据,实现了可以反序列化任意类的利用

file-20260112213746146.png


file-20260112213807808.png



漏洞修复

file-20260112215755380.png



GenericObjectSerDe 换为了 StringSerDeMapStringSerDe 对象,

file-20260112215603629.png



然后 deserialize 方法改为了直接返回字符串,

file-20260112215944740.png



参考

https://github.com/apache/nifi/pull/10599/commits/12351ba71bb32b5d725060e0c0236478ccdf4b05

https://avd.aliyun.com/detail?id=AVD-2025-66524