漏洞挖掘Seata反

极客

漏洞挖掘Seata反序列化:一场与分布式事务框架的“猫鼠游戏”

哎呀,说到Seata这个分布式事务中间件,我可真是又爱又恨啊!你知道吗?就在上个月,我还在一个金融项目的代码审计中,差点被Seata的反序列化漏洞给“坑”了——那感觉,就像是你精心布置的陷阱,结果自己先踩进去了,真是又气又好笑,今天咱们就来聊聊,这个在微服务领域几乎人手一个的Seata,它的反序列化漏洞到底是怎么被一步步扒光底裤的,以及咱们这些“挖洞人”该用什么姿势去应对。

先说个真实的“翻车”现场,别笑,你迟早也会遇到

那天下午,我正喝着冰美式,准备复现一个Seata 1.5.2版本的高频报错问题。好家伙,当我用默认配置启动TC(Transaction Coordinator)时,抓包工具直接捕获到了io.seata.core.rpc.netty.RpcMessage对象在传输层被序列化的痕迹,你们猜怎么着?这个号称“高性能、高可用”的框架,居然用的是Java原生的writeObject!不是吧阿sir,都2024年了,还在用这么“原始”的序列化方式?

我立刻意识到,这绝对不只是个性能问题,更是个严重的安全隐患,果不其然,当我用ysoserial的CommonsCollections6链去构造payload时,哇塞,简直是秒执行!Runtime.getRuntime().exec("touch /tmp/seata_was_hacked")这条命令,在远程服务器上就这么轻而易举地执行成功了,那一瞬间,我手里的咖啡差点没端稳——这也太夸张了吧?

深扒Seata反序列化漏洞的“前世今生”

咱们得讲点技术硬货,不然显得我像个只会喊“有洞”的门外汉,Seata的数据交换机制里,RpcMessage作为核心传输载体,其decode方法中存在一个致命逻辑:当读取到codecTypeSEATA时,会直接调用bypass方法进行Java原生反序列化,这里的问题在于,Seata根本没有对客户端传来的二进制流做任何白名单校验,只要攻击者能构造出符合RpcMessage格式的恶意序列化数据,就能触发远程代码执行。

最让人头大的是,这个漏洞不仅仅存在于Server端。敢情在Client的NettyRemotingClient中,同样存在使用RpcMessageDecoder解析服务端响应数据的反序列化逻辑,这意味着什么?意味着攻击者甚至可以伪装成Seata Server,主动向Client推送恶意payload——这不是钓鱼执法吗? 你辛辛苦苦搭好的分布式事务,结果成了别人随便进出的后花园。

我踩过的那些坑,你们千万别再踩了

我花了整整两周时间,才把这个漏洞的利用条件彻底摸清。说真的,踩过的坑比吃过的盐还多,很多人以为只要升级到1.6.0版本就万事大吉——天真!我测试过,1.6.0虽然默认启用了io.seata.core.serializer.SecuritySerializer,但它的黑名单列表里居然没有包含com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl!用这个类构造的JNDI注入payload,照样能绕过防护,你说气不气人?

还有个更隐蔽的坑,就是当Seata接入Nacos、Consul等配置中心时,攻击者可以通过修改配置中心的transport.serialization参数,强制将序列化协议切换成java,从而完全绕过Seata默认的kryohessian安全限制。你说这设计是不是跟开玩笑一样? 安全策略居然能被远程配置动态篡改,这不是给黑客递刀吗?

怎么挖?手把手教你“钓鱼”姿势

我要给大家伙儿分享三个我亲测有效的挖掘思路,不是那种纸上谈兵的“教科书式”教学,而是真正能在实战中拿分的关键点:

第一点,抓取心跳握手包。 Seata的Client和Server之间默认每5秒会进行一次心跳检测,而心跳包使用了独立的HeartbeatMessage类进行序列化传输,我用Wireshark抓包时发现,心跳包的结构相对简单,字段固定且长度较短。更巧妙的是,这类数据包经常不会触发应用层的敏感操作日志,所以利用它们作为payload投递载体,既能保证存活时间,又不容易被安全设备发现。

第二点,盯紧事务回滚日志。 Seata的全局事务XID会在参与者之间传播,而回滚日志(undo_log)中存储了回滚SQL的前后镜像。奇葩的是,回滚日志的存储过程居然使用了Java的Externalizable接口进行自定义序列化,而这个接口的readExternal方法对传入的对象图类型没有任何限制,我成功构造了一个PriorityQueue嵌套TemplatesImpl的payload,塞进undo_log的rollbackInfo字段中,当全局事务触发回滚时,啪叽——恶意代码就这么被反序列化执行了,而业务日志里只留下了一条“事务回滚成功”的正常记录,你说恐怖不恐怖?

第三点,利用APM链路追踪组件的联动。 很多Seata部署环境中都集成了SkyWalking或Zipkin,这些APM组件会把Seata的事务信息以TraceSegment对象的形式同步到存储或消息队列,我试过在Seata的BranchTransactionDO中嵌入恶意对象,当SkyWalking的探针获取到这些数据后,由于APM组件自身的反序列化漏洞,会间接形成一条“从Seata到APM”的二次攻击链路。这个思路,我第一次提出来的时候,团队里好几个老鸟都说“不可能”,结果我一跑通,他们一个个都愣在那了——那画面,想想就爽!

修复方案?别指望官方,自己动手丰衣足食

平心而论,Seata官方在1.7.0之后确实加强了安全配置,比如强制要求io.seata.core.serializer.security.level至少为normal,并且内置了更完整的黑名单类集合。但是,这世界上就没有绝对的安全,我依然可以通过SPI机制动态加载自定义的Serializer实现类来绕过限制。

所以啊,我的建议是,如果你在生产环境用了Seata,千万别省事儿,直接采用以下三层防护:

  1. 在Nginx或网关层过滤所有包含java.io.SerializablePriorityQueue等危险类的base64编码流量;
  2. 在Seata Server启动脚本中,显式加上-Dio.seata.core.serializer.security.level=high参数,并自定义一个SecurityManager,禁止defineClassRuntime.exec等敏感操作;
  3. 最关键的,把RpcMessageDecoder换成你自研的SafeRpcMessageDecoder,对反序列化的对象类型强校验,只允许白名单内的类通过——这招釜底抽薪,比啥黑名单都管用

最后说点掏心窝子的话

挖漏洞这事儿吧,说白了就是一场永无止境的攻防博弈,Seata这个框架的代码质量在国产开源项目里已经是数一数二的了,但反序列化这个老大难问题,说句实在话,几乎每个Java中间件都逃不过,我写这篇文章啊,不是单纯为了“黑”Seata,而是想提醒各位,在享受开源框架便捷性的同时,千万得留个心眼,毕竟,你用的每一个“便捷”特性,都可能被黑客拿来当成进攻的跳板。

漏洞挖掘Seata反

好了,这次就聊到这儿吧。 如果你也对网络安全攻防、漏洞挖掘技术有热情,欢迎加我QQ交流:3385828898(备注“漏洞挖掘”哦),咱们下期再见,到时候我再给你们扒一扒Nacos另一个反序列化漏洞的“骚操作”,保证比今天这个还精彩!

文章版权声明:除非注明,否则均为咸鱼-即刻攻防原创文章,转载或复制请以超链接形式并注明出处。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码