ROME改造计划
成果
从ysoserial原本的4000+缩短到1320(Base64+弹计算器)
写在前面
首先非常感谢这次的D^3CTF给我一次学习的机会,两个Java题都挺有意思学到了不同的东西,因为第二个比较简单就不分享了,这里分享一下如何去缩短ROME利用链,本身我也是之前没学习过ROME,这里以一个旁观者的视角来讲述好累,全篇没有各种高级技术不涉及ASM
的改造,仅仅只是一些Trick和利用链的精简,同时非常感谢我的同学@HolaAs以及我的朋友@风潇在我做题过程当中给我的帮助
简单分析
首先看看路由,很简单要求传入字符长度不超过1956
接下来免不了找依赖后面发现了ROME可以用,在ysoserial里面直接食用,可以惊讶的看到这里只有短短的4400那么“短”,痛!太痛了!
哎呀怎么办呢?既然要改造免不了需要先看看调用链
1 | /** |
既然要改造我的思路是,先精简利用链,再减少细节
看到这里我能有个想法就是从调用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也有这个神奇的机制,
为了解决这个问题,我们抽丝剥茧慢慢来啰,下面的只是对后面做铺垫
先来个简单的场景,首先看下面这个代码
1 | HashMap<Object, Object> objectObjectHashMap = new HashMap<>(); |
会觉得他们相同吗,答案很显然
为什么呢,可以看到,由于我们value为空其实就是比较key的hashCode了
1 | public final int hashCode() { |
对于一个String类型其hashCode,考虑两个元素的场景也就是31*val[0]+val[1]=31val[0]+val[1]
,因此第一个元素如果比第二个元素小1,第二个元素就必须比第一个元素大31
现在场景提升
1 | objectObjectHashMap.put("aa","1"); |
仍然相等,对于这个场景里面有两个元素,它会调用父类的java.util.AbstractMap#hashCode
1 | public int hashCode() { |
为了简化理解可以把上面的场景代码简化为(毕竟aa
与bB
相等),这样看是不是就很好理解了
1 | objectObjectHashMap.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))这里=>
表示对应
1 | map1.put("aa",templates); |
因此安这个思路我们可以得到
痛!太痛了!不过还是缩了一千多了?
仔细一想罪魁祸首就是Gadgets.createTemplatesImpl(command);
Step2–超级小Trick
那我们来看看这个ysoserial生成的类是啥样子
这里很多东西我们都可以改,啥serialVersionUID
、Pwner311912468728708
、等等这些都可以拿下
但是你以为这样就ok了,给大家看个骚的
没有trycatch
,没有实现抽象类的方法,这怎么实现的!!!
我们平时javac编译的时候,同样的代码都会报错
那上面这个咋搞的嘞,而且不报错,那就是javassist啰,不用ASM去操作好极了
现在再看看长度嘞,1324
小草莓坏笑
测试下嘞ok计算器来了,记得url编码一下哦!
最终代码
Rome.java
1 | import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; |
GetTemplatesImpl.java
1 | import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; |
GenerateEvilByJavaassist.java
1 | import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; |