阅读: 2

ONC/Sun RPC有个rpcinfo,可以列出向rpcbind注册过的所有动态端口。Java RMI没有官方工具完成类似任务,只有第三方工具或自己编程实现。

1) rmiinfo.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

/*

* javac -encoding GBK -g rmiinfo.java

* java rmiinfo 192.168.65.23 1099

*/

import java.rmi.registry.*;

public class rmiinfo

{

    public static void main ( String[] argv ) throws Exception

    {

        String      addr    = argv[0];

        int         port    = Integer.parseInt( argv[1] );

        Registry    r       = LocateRegistry.getRegistry( addr, port );

        String[]    names   = r.list();

        // for ( int i = 0; i < names.length; i++ )

        // {

        //     System.out.println( names[i] );

        // }

        for ( String name : names )

        {

            System.out.println( name );

        }

    }

}

这是最简单也最无用的工具,只能从RMI Registry中转储所有绑定过的name。

2) rmi-dumpregistry.nse

nmap提供了一个脚本:

https://nmap.org/nsedoc/scripts/rmi-dumpregistry.html

https://svn.nmap.org/nmap/scripts/rmi-dumpregistry.nse

$ nmap -n -Pn -p 1099 --script rmi-dumpregistry.nse 192.168.65.23

PORT     STATE SERVICE

1099/tcp open  java-rmi

| rmi-dumpregistry:

|   any

|      implements java.rmi.Remote, HelloRMIInterface,

|     extends

|       java.lang.reflect.Proxy

|       fields

|           Ljava/lang/reflect/InvocationHandler; h

|             java.rmi.server.RemoteObjectInvocationHandler

|             @192.168.65.23:42524

|             extends

|_              java.rmi.server.RemoteObject

nmap的”-sC”或”–script=default”默认会调用上述脚本。

3) rmiregistry_detect.nasl

Nessus提供了一个插件,可以从RMI周知端口转储那些向之注册过的动态端口信息。

$ nasl -t 192.168.65.23 rmiregistry_detect.nasl

Valid response recieved for port 1099:

0x00:  51 AC ED 00 05 77 0F 01 A6 D3 99 DA 00 00 01 71    Q....w.........q

0x10:  A7 28 23 89 80 05 75 72 00 13 5B 4C 6A 61 76 61    .(#...ur..[Ljava

0x20:  2E 6C 61 6E 67 2E 53 74 72 69 6E 67 3B AD D2 56    .lang.String;..V

0x30:  E7 E9 1D 7B 47 02 00 00 70 78 70 00 00 00 01 74    ...{G...pxp....t

0x40:  00 03 61 6E 79                                     ..any

Here is a list of objects the remote RMI registry is currently
aware of :

rmi://192.168.65.23:42524/any

rmiregistry_detect.nasl的解码能力比nmap脚本rmi-dumpregistry.nse差远了,但
最关键的name与动态端口之间的映射关系被解码后显示出来。

单扫rmiregistry_detect.nasl有点慢,可以自己精简一下插件。此外,NASL语法直白,很容易改写成Python版本。

4) jndiinfo.java

之前以为必须进行socket编程才能从RMI Registry中转储动态端口信息,后来意识到
lookup()返回的其实是RemoteObject在本地的代理,其中必有ref信息。先list(),
再对每个name依次lookup(),然后输出返回的Object的字符串形式。后来发现另一批
API:

com.sun.jndi.rmi.registry.RegistryContext.listBindings()
com.sun.jndi.rmi.registry.BindingEnumeration.hasMore()
com.sun.jndi.rmi.registry.BindingEnumeration.next()

其实就是list()、lookup()的封装。

参看:

http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/com/sun/jndi/rmi/registry/RegistryContext.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

89

90

91

92

93

94

95

96

97

/*

* javac -encoding GBK -g jndiinfo.java

*/

import javax.naming.*;

public class jndiinfo

{

    private static void addspace ( StringBuilder sb, int indent, int spacenum )

    {

        for ( int i = 0; i < indent * spacenum; i++ )

        {

            sb.append( ' ' );

        }

    }

    private static String PrivateFormat ( String sth, int spacenum )

    {

        if ( null == sth || "".equals( sth ) )

        {

            return "";

        }

        StringBuilder   sb      = new StringBuilder();

        char            cur;

        int             indent  = 0;

        for ( int i = 0; i < sth.length(); i++ )

        {

            cur     = sth.charAt( i );

            switch ( cur )

            {

            case '{' :

            case '[' :

                sb.append( '\n' );

                addspace( sb, indent, spacenum );

                sb.append( cur );

                sb.append( '\n' );

                indent++;

                addspace( sb, indent, spacenum );

                break;

            case '}' :

            case ']' :

                sb.append( '\n' );

                indent--;

                addspace( sb, indent, spacenum );

                sb.append( cur );

                break;

            case ',' :

                sb.append( cur );

                sb.append( '\n' );

                addspace( sb, indent, spacenum );

                break;

            case ' ' :

                /*

                 * 相当于删除所有空格

                 */

                break;

            default :

                sb.append( cur );

            }

        }

        return sb.toString();

    }

    /*

     * 这段代码将自身置于极其危险的境地,对不明远程目标使用时至少要在JVM参

     * 数中启用SecurityManager,勿谓言之不预。

     */

    public static void main ( String[] argv ) throws Exception

    {

        /*

         * 保持一般性,使用JNDI,用JVM参数传递env

         */

        Context             ctx         = new InitialContext();

        /*

         * 形参只能传空串

         */

        NamingEnumeration   bindings    = ctx.listBindings( "" );

        while ( bindings.hasMore() )

        {

            Binding bd  = ( Binding )bindings.next();

            System.out.println

            (

                String.format

                (

                    "%s - %s - %s",

                    bd.getName(),

                    bd.getClassName(),

                    PrivateFormat

                    (

                        bd.getObject().toString(),

                        4

                    )

                )

            );

        }

        ctx.close();

    }

}

java \

-Djava.naming.factory.initial=com.sun.jndi.rmi.registry.RegistryContextFactory \

-Djava.naming.provider.url=rmi://192.168.65.23:1099 \

jndiinfo

输出形如:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

any - com.sun.proxy.$Proxy0 - Proxy

[

    HelloRMIInterface,

    RemoteObjectInvocationHandler

    [

        UnicastRef2

        [

            liveRef:

            [

                endpoint:

                [

                    192.168.65.23:42524

                ](remote),

                objID:

                [

                    3e2be595:171a728632c:-7fff,

                    1669433444583981789

                ]

            ]

        ]

    ]

]

保持一般性,使用JNDI,用JVM参数传递env。这样理论上或可用于其他周知端口,比如侦听1050/TCP的orbd:

java \

-Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory \

-Djava.naming.provider.url=iiop://192.168.65.23:1050 \

jndiinfo

输出形如:

any - _HelloRMIInterface_Stub - IOR:...

JNDI封装未对IOR后面的数据解码显示,直接显示16进制字节流,这种场景没啥大用。

还可以试试:

java \

-Djava.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory \

-Djava.naming.provider.url=ldap://192.168.65.23:10389/o=anything,dc=evil,dc=com \

jndiinfo

输出形如:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

cn=any - HelloRMIInterfaceImpl - HelloRMIInterfaceImpl

[

    UnicastServerRef

    [

        liveRef:

        [

            endpoint:

            [

                127.0.0.1:36388

            ](local),

            objID:

            [

                793e717a:171a755a9fc:-7fff,

                6413047511569805657

            ]

        ]

    ]

]

下面这种我没试过,感兴趣者可以自己试:

java \

-cp "wlthint3client.jar:." \

-Djava.naming.factory.initial=weblogic.jndi.WLInitialContextFactory \

-Djava.naming.provider.url=t3://192.168.65.23:7001 \

jndiinfo

4.1) jndiinfo.policy

rmiinfo.java、jndiinfo.java很容易受到恶意服务端的攻击。对不明远程目标使用
时建议在客户端使用虚拟机,至少要在JVM参数中启用SecurityManager。虽然客户端CLASSPATH中不一定存在Gadget链的依赖库,但你不知道有什么0day在远端等着你。

不要只想着搞人,总有被反搞的一天。干咱们这行,在这个恶意满满的时代,未谋胜先谋败。

grant

{

permission java.net.SocketPermission "192.168.65.23:1099", "connect,resolve";

};

java \

-Djava.security.manager \

-Djava.security.policy=jndiinfo.policy \

-Djava.naming.factory.initial=com.sun.jndi.rmi.registry.RegistryContextFactory \

-Djava.naming.provider.url=rmi://192.168.65.23:1099 \

jndiinfo

就这种PoC而言,不太喜欢在在代码中启用SecurityManager。jndiinfo.java中只有必要代码,可以避免初看者失焦。