内容索引:
- 5.3. Java
- 5.3.1. 基本概念
- 5.3.1.1. JVM
- 5.3.1.2. JDK
- 5.3.1.3. JMX
- 5.3.1.4. OGNL
- 5.3.1.5. IO模型
- 5.3.1.5.1. BIO
- 5.3.1.5.2. NIO
- 5.3.1.5.3. AIO
- 5.3.2. 框架
- 5.3.2.1. Servlet
- 5.3.2.1.1. 简介
- 5.3.2.1.2. 生命周期为
- 5.3.2.1.3. 接口
- 5.3.2.2. Struts 2
- 5.3.2.2.1. 简介
- 5.3.2.2.2. 请求流程
- 5.3.2.2.3. 相关CVE
- 5.3.2.3. Spring
- 5.3.2.3.1. 简介
- 5.3.2.3.2. Spring MVC
- 5.3.2.3.3. Spring Boot
- 5.3.2.3.4. 请求流程
- 5.3.3. 容器
- 5.3.3.1. Tomcat
- 5.3.3.1.1. 相关CVE
- 5.3.3.2. Weblogic
- 5.3.3.2.1. 简介
- 5.3.3.2.2. 相关CVE
- 5.3.3.3. JBoss
- 5.3.3.3.1. 简介
- 5.3.3.3.2. 相关CVE
- 5.3.3.4. Jetty
- 5.3.3.4.1. 简介
- 5.3.4. 沙箱
- 5.3.4.1. 简介
- 5.3.4.2. 相关CVE
- 5.3.5. 反序列化
- 5.3.5.1. 简介
- 5.3.5.1.1. 序列数据结构
- 5.3.5.1.2. 序列化流程
- 5.3.5.1.3. 反序列化流程
- 5.3.5.2. 漏洞利用
- 5.3.5.2.1. 存在危险的基础库
- 5.3.5.3. 漏洞修复和防护
- 5.3.5.3.1. Hook resolveClass
- 5.3.5.3.2. ValidatingObjectInputStream
- 5.3.5.3.3. ObjectInputFilter(JEP290)
- 5.3.6. RMI
- 5.3.6.1. 简介
- 5.3.6.2. 调用步骤
- 5.3.6.3. 样例
- 5.3.6.3.1. Person接口定义
- 5.3.6.3.2. 使用PersonServer实现Person
- 5.3.6.3.3. 使用Person_Stub实现Person
- 5.3.6.3.4. Skeleton的实现
- 5.3.6.3.5. Client实现
- 5.3.6.4. T3协议
- 5.3.6.5. JRMP
- 5.3.7. JNDI
- 5.3.7.1. 简介
- 5.3.7.2. JNDI注入
- 5.3.7.3. 攻击载荷
- 5.3.7.3.1. RMI Remote Object
- 5.3.7.3.2. RMI + JNDI Reference
- 5.3.7.3.3. LDAP + JNDI Reference
- 5.3.8. JDK
- 5.3.8.1. JDK 6
- 5.3.8.1.1. 6u45
- 5.3.8.1.2. 6u141
- 5.3.8.1.3. 6u211
- 5.3.8.2. JDK 7
- 5.3.8.2.1. 7u40
- 5.3.8.2.2. 7u122
- 5.3.8.2.3. 7u201
- 5.3.8.3. JDK 8
- 5.3.8.3.1. 8u113
- 5.3.8.3.2. 8u121
- 5.3.8.3.3. 8u191
- 5.3.9. 常见Sink
- 5.3.9.1. 命令执行/注入
- 5.3.9.2. XXE
- 5.3.9.3. SSRF
- 5.3.9.4. 反序列化
- 5.3.9.4.1. 相关Sink函数
- 5.3.9.4.2. Magic Call
- 5.3.9.4.3. 主流JSON库
- 5.3.10. WebShell
- 5.3.10.1. BCEL字节码
- 5.3.10.2. 自定义类加载器
- 5.3.10.3. 执行命令变式
- 5.3.10.4. 其他Shell变式
- 5.3.11. 参考链接
- 5.3.11.1. 官方文档
- 5.3.11.2. 反序列化
- 5.3.11.3. 沙箱
- 5.3.11.4. 框架
- 5.3.11.5. RMI
- 5.3.11.6. JNDI
- 5.3.11.7. WebShell
5.3. Java
5.3.1. 基本概念
5.3.1.1. JVM
JVM是Java平台的核心,以机器代码来实现,为程序执行提供了所需的所有基本功能,例如字节码解析器、JIT编译器、垃圾收集器等。由于它是机器代码实现的,其同样受到二进制文件受到的攻击。
JCL是JVM自带的一个标准库,含有数百个系统类。默认情况下,所有系统类都是可信任的,且拥有所有的特权。
5.3.1.2. JDK
Java开发工具包(Java Development Kit,JDK)是Oracle公司发布的Java平台,有标准版(Standard Edition,Java SE)、企业版(Enterprise Edition,Java EE)等版本。
在最开始,JDK以二进制形式发布,而后在2006年11月17日,Sun以GPL许可证发布了Java的源代码,于是之后出现了OpenJDK。
5.3.1.3. JMX
JMX(Java Management Extensions,Java管理扩展)是一个为应用程序植入管理功能的框架。
5.3.1.4. OGNL
OGNL(Object-Graph Navigation Language,对象导航语言)是一种功能强大的表达式语言,通过简单一致的表达式语法,提供了存取对象的任意属性、调用对象的方法、遍历整个对象的结构图、实现字段类型转化等功能。
Struts2中使用了OGNL,提供了一个ValueStack类。ValueStack分为root和context两部分。root中是当前的action对象,context中是ActionContext里面所有的内容。
5.3.1.5. IO模型
Java 对操作系统的各种 IO 模型进行了封装,形成了不同的API。
5.3.1.5.1. BIO
BIO (Blocking I/O) 是同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。
5.3.1.5.2. NIO
NIO (New I/O) 是一种同步非阻塞的I/O模型,在Java 1.4中引入,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。
5.3.1.5.3. AIO
AIO (Asynchronous I/O) 在 Java 7 中引入,是NIO的改进版,是异步非阻塞的IO模型,基于事件和回调机制实现。
5.3.2. 框架
5.3.2.1. Servlet
5.3.2.1.1. 简介
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,是用Java编写的服务器端程序,主要功能在于交互式地浏览和修改数据,生成动态Web内容。
狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
5.3.2.1.2. 生命周期为
- 客户端请求该 Servlet
- 加载 Servlet 类到内存
- 实例化并调用init()方法初始化该 Servlet
- service()(根据请求方法不同调用
doGet()
/doPost()
/ … /destroy()
5.3.2.1.3. 接口
init()
在 Servlet 的生命期中,仅执行一次 init() 方法,在服务器装入 Servlet 时执行。
service()
service() 方法是 Servlet 的核心。每当一个客户请求一个HttpServlet对象,该对象的 service()
方法就要被调用,而且传递给这个方法一个"请求"(ServletRequest)对象和一个"响应"(ServletResponse)对象作为参数。
5.3.2.2. Struts 2
5.3.2.2.1. 简介
Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。
5.3.2.2.2. 请求流程
- 客户端发送请求的tomcat服务器
- 请求经过一系列过滤器
- FilterDispatcher调用ActionMapper来决定这个请求是否要调用某个Action
- ActionMppaer决定调用某个ActionFilterDispatcher把请求给ActionProxy
- ActionProxy通过Configuration Manager查看structs.xml,找到对应的Action类
- ActionProxy创建一个ActionInvocation对象
- ActionInvocation对象回调Action的execute方法
- Action执行完毕后,ActionInvocation根据返回的字符串,找到相应的result,通过HttpServletResponse返回给服务器
5.3.2.2.3. 相关CVE
- CVE-2016-3081 (S2-032)
- CVE-2016-3687 (S2-033)
- CVE-2016-4438 (S2-037)
- CVE-2017-5638
- CVE-2017-7672
- CVE-2017-9787
- CVE-2017-9793
- CVE-2017-9804
- CVE-2017-9805
- CVE-2017-12611
- CVE-2017-15707
- CVE-2018-1327
- CVE-2018-11776
5.3.2.3. Spring
5.3.2.3.1. 简介
Spring一般指的是Spring Framework,一个轻量级Java应用程序开源框架,提供了简易的开发方式。
5.3.2.3.2. Spring MVC
Spring MVC根据Spring的模式设计的MVC框架,主要用于开发Web应用,简化开发。
5.3.2.3.3. Spring Boot
Spring在推出之初方案较为繁琐,因此提供了Spring Boot作为自动化配置工具,降低项目搭建的复杂度。
5.3.2.3.4. 请求流程
- 用户发送请求给服务器
- 服务器收到请求,使用DispatchServlet处理
- Dispatch使用HandleMapping检查url是否有对应的Controller,如果有,执行
- 如果Controller返回字符串,ViewResolver将字符串转换成相应的视图对象
- DispatchServlet将视图对象中的数据,输出给服务器
- 服务器将数据输出给客户端
5.3.3. 容器
常见的Java服务器有Tomcat、Weblogic、JBoss、GlassFish、Jetty、Resin、IBM Websphere等,这里对部分框架做一个简单的说明。
5.3.3.1. Tomcat
Tomcat是一个轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,用于开发和调试JSP程序。
在收到请求后,Tomcat的处理流程如下:
- 客户端访问Web服务器,发送HTTP请求
- Web服务器接收到请求后,传递给Servlet容器
- Servlet容器加载Servlet,产生Servlet实例后,向其传递表示请求和响应的对象
- Servlet实例使用请求对象得到客户端的请求信息,然后进行相应的处理
- Servlet实例将处理结果通过响应对象发送回客户端,容器负责确保响应正确送出,同时将控制返回给Web服务器
Tomcat服务器是由一系列可配置的组件构成的,其中核心组件是Catalina Servlet容器,它是所有其他Tomcat组件的顶层容器。
5.3.3.1.1. 相关CVE
- CVE-2020-1938
- https://www.freebuf.com/vuls/228108.html
- CVE-2019-0232
- 远程代码执行
- https://github.com/pyn3rd/CVE-2019-0232/
- CVE-2017-12615
- 任意文件写入
- https://mp.weixin.qq.com/s?__biz=MzI1NDg4MTIxMw==&mid=2247483659&idx=1&sn=c23b3a3b3b43d70999bdbe644e79f7e5
- CVE-2013-2067
- CVE-2012-4534
- CVE-2012-4431
- CVE-2012-3546
- CVE-2012-3544
- CVE-2012-2733
- CVE-2011-3375
- CVE-2011-3190
- CVE-2008-2938
5.3.3.2. Weblogic
5.3.3.2.1. 简介
WebLogic是美国Oracle公司出品的一个Application Server,是一个基于Java EE架构的中间件,WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。其将Java的动态功能和Java Enterprise标准的安全性引入大型网络应用的开发、集成、部署和管理之中。
WebLogic对业内多种标准的全面支持,包括EJB、JSP、Servlet、JMS、JDBC等。
5.3.3.2.2. 相关CVE
- CVE-2019-2725
- wls-wsat 反序列化远程代码执行
- CVE-2019-2658
- CVE-2019-2650
- CVE-2019-2649
- CVE-2019-2648
- CVE-2019-2647
- CVE-2019-2646
- CVE-2019-2645
- CVE-2019-2618
- https://github.com/jas502n/cve-2019-2618/
- CVE-2019-2615
- CVE-2019-2568
- CVE-2018-3252
- CVE-2018-3248
- CVE-2018-3245
- CVE-2018-3201
- CVE-2018-3197
- CVE-2018-3191
- https://github.com/voidfyoo/CVE-2018-3191
- https://github.com/Libraggbond/CVE-2018-3191
- CVE-2018-2894
- 任意文件上传
- https://xz.aliyun.com/t/2458
- CVE-2018-2893
- 反序列化
- https://www.freebuf.com/vuls/178105.html
- CVE-2018-2628
- https://mp.weixin.qq.com/s/nYY4zg2m2xsqT0GXa9pMGA
- CVE-2018-1258
- CVE-2017-10271
- XMLDecoder 反序列化漏洞
- http://webcache.googleusercontent.com/search?q=cache%3AsH7j8TF8uOIJ%3Awww.freebuf.com%2Fvuls%2F160367.html
- CVE-2017-3248
- CVE-2016-3510
- CVE-2015-4852
- https://github.com/roo7break/serialator
5.3.3.3. JBoss
5.3.3.3.1. 简介
JBoss是一个基于J2EE的管理EJB的容器和服务器,但JBoss核心服务不包括支持servlet/JSP的WEB容器,一般与Tomcat或Jetty绑定使用。
5.3.3.3.2. 相关CVE
- CVE-2017-12149
- 反序列化漏洞
- 访问
/invoker/readonly
,页面存在即有反序列化漏洞
5.3.3.4. Jetty
5.3.3.4.1. 简介
Jetty是一个开源的servlet容器。
5.3.4. 沙箱
5.3.4.1. 简介
Java实现了一套沙箱环境,使远程的非可信代码只能在受限的环境下执行。
5.3.4.2. 相关CVE
- CVE-2012-0507
- CVE-2012-4681
- CVE-2017-3272
- CVE-2017-3289
5.3.5. 反序列化
5.3.5.1. 简介
序列化就是把对象转换成字节流,便于保存在内存、文件、数据库中;反序列化即逆过程,由字节流还原成对象。一般用于远程调用、通过网络将对象传输至远程服务器、存储对象到数据库或本地等待重用等场景中。Java中的 ObjectOutputStream
类的writeObject()
方法可以实现序列化,类ObjectInputStream
类的readObject()
方法用于反序列化。如果要实现类的反序列化,则是对其实现 Serializable
接口。
当远程服务接受不可信的数据并进行反序列化且当前环境中存在可利用的类时,就认为存在反序列化漏洞。
5.3.5.1.1. 序列数据结构
0xaced
魔术头
5.3.5.1.2. 序列化流程
- ObjectOutputStream实例初始化时,将魔术头和版本号写入bout (BlockDataOutputStream类型) 中
- 调用ObjectOutputStream.writeObject()开始写对象数据
- ObjectStreamClass.lookup()封装待序列化的类描述 (返回ObjectStreamClass类型) ,获取包括类名、自定义serialVersionUID、可序列化字段 (返回 ObjectStreamField类型) 和构造方法,以及writeObject、readObject方法等
- writeOrdinaryObject()写入对象数据
- 写入对象类型标识
- writeClassDesc()进入分支writeNonProxyDesc()写入类描述数据
- 写入类描述符标识
- 写入类名
写入SUID (当SUID为空时,会进行计算并赋值) - 计算并写入序列化属性标志位
- 写入字段信息数据
- 写入Block Data结束标识
- 写入父类描述数据
- writeSerialData()写入对象的序列化数据
- 若类自定义了writeObject(),则调用该方法写对象,否则调用defaultWriteFields()写入对象的字段数据 (若是非原始类型,则递归处理子对象)
5.3.5.1.3. 反序列化流程
- ObjectInputStream实例初始化时,读取魔术头和版本号进行校验
- 调用ObjectInputStream.readObject()开始读对象数据
- 读取对象类型标识
- readOrdinaryObject()读取数据对象
- readClassDesc()读取类描述数据
- 读取类描述符标识,进入分支readNonProxyDesc()
- 读取类名
- 读取SUID
- 读取并分解序列化属性标志位
- 读取字段信息数据
- resolveClass()根据类名获取待反序列化的类的Class对象,如果获取失败,则抛出ClassNotFoundException
- skipCustomData()循环读取字节直到Block Data结束标识为止
- 读取父类描述数据
- initNonProxy()中判断对象与本地对象的SUID和类名 (不含包名) 是否相同,若不同,则抛出InvalidClassException
- ObjectStreamClass.newInstance()获取并调用离对象最近的非Serializable的父类的无参构造方法 (若不存在,则返回null) 创建对象实例
- readSerialData()读取对象的序列化数据
- 若类自定义了readObject(),则调用该方法读对象,否则调用defaultReadFields()读取并填充对象的字段数据
- readClassDesc()读取类描述数据
5.3.5.2. 漏洞利用
5.3.5.2.1. 存在危险的基础库
commons-fileupload 1.3.1
commons-io 2.4
commons-collections 3.1
commons-logging 1.2
commons-beanutils 1.9.2
org.slf4j:slf4j-api 1.7.21
com.mchange:mchange-commons-java 0.2.11
org.apache.commons:commons-collections 4.0
com.mchange:c3p0 0.9.5.2
org.beanshell:bsh 2.0b5
org.codehaus.groovy:groovy 2.3.9
org.springframework:spring-aop 4.1.4.RELEASE
5.3.5.3. 漏洞修复和防护
5.3.5.3.1. Hook resolveClass
在使用 readObject()
反序列化时会调用 resolveClass
方法读取反序列化的类名,可以通过hook该方法来校验反序列化的类,一个Demo如下
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
if (!desc.getName().equals(SerialObject.class.getName())) {
throw new InvalidClassException(
"Unauthorized deserialization attempt",
desc.getName());
}
return super.resolveClass(desc);
}
以上的Demo就只允许序列化 SerialObject
,通过这种方式,就可以设置允许序列化的白名单,来防止反序列化漏洞被利用。SerialKiller/Jackson/Weblogic等都使用了这种方式来防御。
5.3.5.3.2. ValidatingObjectInputStream
Apache Commons IO Serialization包中的 ValidatingObjectInputStream
类提供了accept
方法,可以通过该方法来实现反序列化类白/黑名单控制,一个demo如下
private static Object deserialize(byte[] buffer) throws IOException, ClassNotFoundException , ConfigurationException {
Object obj;
ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
ValidatingObjectInputStream ois = new ValidatingObjectInputStream(bais);
ois.accept(SerialObject.class);
obj = ois.readObject();
return obj;
}
5.3.5.3.3. ObjectInputFilter(JEP290)
Java 9提供了支持序列化数据过滤的新特性,可以继承 java.io.ObjectInputFilter
类重写checkInput
方法来实现自定义的过滤器,并使用 ObjectInputStream
对象的 setObjectInputFilter
设置过滤器来实现反序列化类白/黑名单控制。这个机制本身是针对Java 9的一个新特性,但是随后官方突然决定向下引进该增强机制,分别对JDK 6,7,8进行了支持。这个机制主要描述了如下的机制:
- 提供一个限制反序列化类的机制,白名单或者黑名单
- 限制反序列化的深度和复杂度
- 为RMI远程调用对象提供了一个验证类的机制
- 定义一个可配置的过滤机制,比如可以通过配置properties文件的形式来定义过滤器
5.3.6. RMI
5.3.6.1. 简介
RMI(Remote Method Invocation,远程方法调用)能够让在客户端Java虚拟机上的对象像调用本地对象一样调用服务端Java虚拟机中的对象上的方法。其中RMI标准实现是Java RMI,之外还有Weblogic RMI、Spring RMI等不同的实现。
RMI中比较重要的两个概念是Stub和Skeleton,Stub和Skeleton对同一套接口进行实现,其中Stub由Client端调用,并不进行真正的实现,而是和Server端通信。Skeleton是Server端,监听来自Stub的连接,根据Stub发送的数据进行真正的操作。
5.3.6.2. 调用步骤
- 客户调用客户端辅助对象Stub上的方法
- 客户端辅助对象Stub打包调用信息(变量,方法名),通过网络发送给服务端辅助对象Skeleton
- 服务端辅助对象Skeleton将客户端辅助对象发送来的信息解包,找出真正被调用的方法以及该方法所在对象
- 调用真正服务对象上的真正方法,并将结果返回给服务端辅助对象Skeleton
- 服务端辅助对象将结果打包,发送给客户端辅助对象Stub
- 客户端辅助对象将返回值解包,返回给调用者
- 客户获得返回值
5.3.6.3. 样例
一份代码样例如下(来自《Enterprise JavaBeans》):
5.3.6.3.1. Person接口定义
public interface Person {
public int getAge() throws Throwable;
public String getName() throws Throwable;
}
5.3.6.3.2. 使用PersonServer实现Person
public class PersonServer implements Person {
private int age;
private String name;
public PersonServer(String name, int age) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
}
5.3.6.3.3. 使用Person_Stub实现Person
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.net.Socket;
public class Person_Stub implements Person {
private Socket socket;
public Person_Stub() throws Throwable {
// connect to skeleton
socket = new Socket("computer_name", 9000);
}
public int getAge() throws Throwable {
// pass method name to skeleton
ObjectOutputStream outStream =
new ObjectOutputStream(socket.getOutputStream());
outStream.writeObject("age");
outStream.flush();
ObjectInputStream inStream =
new ObjectInputStream(socket.getInputStream());
return inStream.readInt();
}
public String getName() throws Throwable {
// pass method name to skeleton
ObjectOutputStream outStream =
new ObjectOutputStream(socket.getOutputStream());
outStream.writeObject("name");
outStream.flush();
ObjectInputStream inStream =
new ObjectInputStream(socket.getInputStream());
return (String)inStream.readObject();
}
}
5.3.6.3.4. Skeleton的实现
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.net.ServerSocket;
public class Person_Skeleton extends Thread {
private PersonServer myServer;
public Person_Skeleton(PersonServer server) {
// get reference of object server
this.myServer = server;
}
public void run() {
try {
// new socket at port 9000
ServerSocket serverSocket = new ServerSocket(9000);
// accept stub's request
Socket socket = serverSocket.accept();
while (socket != null) {
// get stub's request
ObjectInputStream inStream =
new ObjectInputStream(socket.getInputStream());
String method = (String)inStream.readObject();
// check method name
if (method.equals("age")) {
// execute object server's business method
int age = myServer.getAge();
ObjectOutputStream outStream =
new ObjectOutputStream(socket.getOutputStream());
// return result to stub
outStream.writeInt(age);
outStream.flush();
}
if(method.equals("name")) {
// execute object server's business method
String name = myServer.getName();
ObjectOutputStream outStream =
new ObjectOutputStream(socket.getOutputStream());
// return result to stub
outStream.writeObject(name);
outStream.flush();
}
}
} catch(Throwable t) {
t.printStackTrace();
System.exit(0);
}
}
public static void main(String args []) {
// new object server
PersonServer person = new PersonServer("Richard", 34);
Person_Skeleton skel = new Person_Skeleton(person);
skel.start();
}
}
5.3.6.3.5. Client实现
public class PersonClient {
public static void main(String [] args) {
try {
Person person = new Person_Stub();
int age = person.getAge();
String name = person.getName();
System.out.println(name + " is " + age + " years old");
} catch(Throwable t) {
t.printStackTrace();
}
}
}
5.3.6.4. T3协议
T3协议是用于在WebLogic服务器和其他类型的Java程序之间传输信息的协议,是Weblogic对RMI规范的实现。简单来说,可以把T3视为暴露JDNI给用户调用的接口。
5.3.6.5. JRMP
Java远程方法协议(Java Remote Method Protocol,JRMP)是特定于Java技术的、用于查找和引用远程对象的协议。这是运行在Java远程方法调用(RMI)之下、TCP/IP之上的线路层协议。
JRMP是一个Java特有的、适用于Java之间远程调用的基于流的协议,要求客户端和服务器上都使用Java对象。
5.3.7. JNDI
5.3.7.1. 简介
JNDI(Java Naming and Directory Interface,Java命名和目录接口)是为Java应用程序提供命名和目录访问服务的API,允许客户端通过名称发现和查找数据、对象,用于提供基于配置的动态调用。这些对象可以存储在不同的命名或目录服务中,例如RMI、CORBA、LDAP、DNS等。
其中Naming Service类似于哈希表的K/V对,通过名称去获取对应的服务。Directory Service是一种特殊的Naming Service,用类似目录的方式来存取服务。
5.3.7.2. JNDI注入
JNDI注入是2016年由pentester在BlackHat USA上的 A Journey From JNDI LDAP Manipulation To RCE
议题提出的。
其攻击过程如下
1、攻击者将Payload绑定到攻击者的命名/目录服务中
2、攻击者将绝对URL注入易受攻击的JNDI查找方法
3、应用程序执行查找
4、应用程序连接到攻击者控制的JNDI服务并返回Payload
5、应用程序解码响应并触发有效负载
5.3.7.3. 攻击载荷
JDNI主要有几种攻击载荷:
- CORBA
- IOR
- JNDI Reference
- JNDI Reference
- LDAP
- Remote Location
- Remote Object
- RMI
- Serialized Object
5.3.7.3.1. RMI Remote Object
攻击者实现一个RMI恶意远程对象并绑定到RMI Registry上,将编译后的RMI远程对象类放在HTTP/FTP/SMB等服务器上。其中Codebase地址由远程服务器的 java.rmi.server.codebase
属性设置,供受害者的RMI客户端远程加载。
利用条件如下:
- RMI客户端的上下文环境允许访问远程Codebase。
- 属性 java.rmi.server.useCodebaseOnly 的值为false。
其中JDK 6u45、7u21后,java.rmi.server.useCodebaseOnly
的值默认为true。
5.3.7.3.2. RMI + JNDI Reference
攻击者通过RMI服务返回一个JNDI Naming Reference,受害者解码Reference时会去攻击者指定的远程地址加载Factory类。这种方式原理上并非使用RMI Class Loading机制,因此不受java.rmi.server.useCodebaseOnly
系统属性的限制。但是在JDK 6u132, JDK 7u122, JDK 8u113 后限制了Naming/Directory服务中JNDI Reference远程加载Object Factory类的特性。系统属性 com.sun.jndi.rmi.object.trustURLCodebase
、 com.sun.jndi.cosnaming.object.trustURLCodebase
的默认值变为false,即默认不允许从远程的Codebase加载Reference工厂类。
5.3.7.3.3. LDAP + JNDI Reference
Java的LDAP可以在属性值中存储特定的Java对象,且LDAP服务的Reference远程加载Factory类不受 com.sun.jndi.rmi.object.trustURLCodebase
、com.sun.jndi.cosnaming.object.trustURLCodebase
等属性的限制,适用范围更广。
5.3.8. JDK
5.3.8.1. JDK 6
5.3.8.1.1. 6u45
- java.rmi.server.useCodebaseOnly 默认为 true,禁用自动加载远程类文件
5.3.8.1.2. 6u141
com.sun.jndi.rmi.object.trustURLCodebase
默认为falsecom.sun.jndi.cosnaming.object.trustURLCodebase
默认为false
5.3.8.1.3. 6u211
- LDAP远程Reference代码默认不信任,影响LDAP远程Reference代码攻击方式
5.3.8.2. JDK 7
5.3.8.2.1. 7u40
java.io.File
类中添加了isInvalid
方法,检测文件名中是否包含空字节
5.3.8.2.2. 7u122
com.sun.jndi.rmi.object.trustURLCodebase
默认为falsecom.sun.jndi.cosnaming.object.trustURLCodebase
默认为false
5.3.8.2.3. 7u201
- LDAP远程Reference代码默认不信任,影响LDAP远程Reference代码攻击方式
5.3.8.3. JDK 8
sun.net.www.protocol
不再支持gopher协议
5.3.8.3.1. 8u113
com.sun.jndi.rmi.object.trustURLCodebase
默认为falsecom.sun.jndi.cosnaming.object.trustURLCodebase
默认为false
5.3.8.3.2. 8u121
- RMI加入了反序列化白名单机制
- RMI远程Reference代码默认不信任,影响RMI远程Reference代码攻击方式
5.3.8.3.3. 8u191
- LDAP远程Reference代码默认不信任,影响LDAP远程Reference代码攻击方式
5.3.9. 常见Sink
5.3.9.1. 命令执行/注入
- java.lang.Runtime.getRuntime().exec()
- java.lang.ProcessBuilder
5.3.9.2. XXE
- javax.xml.parsers.DocumentBuilderFactory
- javax.xml.parsers.SAXParser
- javax.xml.transform.TransformerFactory
- javax.xml.validation.Validator
- javax.xml.validation.SchemaFactory
- javax.xml.transform.sax.SAXTransformerFactory
- javax.xml.transform.sax.SAXSource
- org.xml.sax.XMLReader
- org.xml.sax.helpers.XMLReaderFactory
- org.dom4j.io.SAXReader
- org.jdom.input.SAXBuilder
- org.jdom2.input.SAXBuilder
- javax.xml.bind.Unmarshaller
- javax.xml.xpath.XpathExpression
- javax.xml.stream.XMLStreamReader
- org.apache.commons.digester3.Digester
5.3.9.3. SSRF
- HttpClient.execute
- HttpClients.execute
- HttpURLConnection.getInputStream
- ImageIO.read
- OkHttpClient.newCall.execute
- Request.Get.execute
- Request.Post.execute
- URL.openStream
- URLConnection.getInputStream
5.3.9.4. 反序列化
5.3.9.4.1. 相关Sink函数
ObjectInputStream.readObject
ObjectInputStream.readUnshared
XMLDecoder.readObject
Yaml.load
XStream.fromXML
ObjectMapper.readValue
JSON.parseObject
5.3.9.4.2. Magic Call
以下的魔术方法都会在反序列化过程中被自动的调用。
readObject
readExternal
readResolve
readObjectNoData
validateObject
finalize
5.3.9.4.3. 主流JSON库
主流的JSON库有Gson、Jackson、Fastjson等,因为JSON常在反序列化中使用,所以相关库都有较大的影响。
其中Gson默认只能反序列化基本类型,如果是复杂类型,需要程序员实现反序列化机制,相对比较安全。
Jackson除非指明@jsonAutoDetect,Jackson不会反序列化非public属性。在防御时,可以不使用enableDefaultTyping方法。相关CVE有CVE-2017-7525、CVE-2017-15095。
FastJson是阿里巴巴的开源JSON解析库,支持将Java Bean序列化为JSON字符串,也支持从JSON字符串反序列化到Java Bean,相关CVE有CVE-2017-18349等。
FastJson常见的Sink点有:
JSON.toJSONString
JSON.parseObject
JSON.parse
5.3.10. WebShell
5.3.10.1. BCEL字节码
String bcelCode = "...";
response.getOutputStream().write(String.valueOf(new ClassLoader().loadClass(bcelCode).getConstructor(String.class).newInstance(request.getParameter("cmd")).toString()).getBytes());
5.3.10.2. 自定义类加载器
response.getOutputStream().write(new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.contains("shell")) {
return findClass(name);
}
return super.loadClass(name);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] bytes = Base64.getDecoder().decode("...");
PermissionCollection pc = new Permissions();
pc.add(new AllPermission());
ProtectionDomain protectionDomain = new ProtectionDomain(new CodeSource(null, (Certificate[]) null), pc, this, null);
return this.defineClass(name, bytes, 0, bytes.length, protectionDomain);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
}.loadClass("shell").getConstructor(String.class).newInstance(request.getParameter("cmd")).toString().getBytes());
%>
5.3.10.3. 执行命令变式
- java.lang.ProcessBuilder
- MethodAccessor.invoke
- Method.invoke
- TemplatesImpl
5.3.10.4. 其他Shell变式
- ScriptEngine.eval
- URLClassLoader
- ToolProvider.getSystemJavaCompiler
- jdk.nashorn.internal.runtime.ScriptLoader
- ObjectInputStream.resolveClass
5.3.11. 参考链接
5.3.11.1. 官方文档
- ognl
- Java SE Security Guide
- Java RMI Release Notes for JDK 6
- Java Release Notes for JDK 7
5.3.11.2. 反序列化
- Java反序列化漏洞从入门到深入
- Java反序列化漏洞通用利用分析
- JRE8u20反序列化漏洞分析
- WebLogic反序列化漏洞漫谈
- 从WebLogic看反序列化漏洞的利用与防御
- JSON反序列化之殇
- 浅析Java序列化和反序列化
- Commons Collections Java反序列化漏洞深入分析
- FAR SIDES OF JAVA REMOTE PROTOCOLS
- SpringBootVulExploit SpringBoot 相关漏洞学习资料,利用方法和技巧合集,黑盒安全评估 checklist
5.3.11.3. 沙箱
- Java Sandbox Escape
5.3.11.4. 框架
- Struts
- Struts Examples
- Eclipse Jetty
5.3.11.5. RMI
- Java RMI与RPC的区别
- Remote Method Invocation (RMI)
- Java 中 RMI、JNDI、LADP、JRMP、JMX、JMS那些事儿
- Oracle: Developing T3 Clients
5.3.11.6. JNDI
- Overview of JNDI
- 关于 JNDI 注入
- A Journey From JNDI LDAP Manipulation To RCE
- 如何绕过高版本JDK的限制进行JNDI注入https://www.freebuf.com/column/207439.html
5.3.11.7. WebShell
- 各种姿势jsp webshell