基于CVE-2017-5638报告使用Checkmarx自定义规则查找Struts2远程代码执行漏洞

发布时间: 2017-03-24      作者:并擎科技

漏洞的总体情况是,基于“Jakarta Multipart parser”上传文件时,可以执行远程代码。

表1.1 漏洞总结一览表

Who should read this

All Struts 2 developers and users

Impact of vulnerability

Possible RCE when performing file upload based on Jakarta Multipart parser

Maximum security rating

High

Recommendation

Upgrade to Struts 2.3.32 or Struts 2.5.10.1

Affected Software

Struts 2.3.5 - Struts 2.3.31, Struts 2.5 - Struts 2.5.10

Reporter

Nike Zheng <nike dot zheng at dbappsecurity dot com dot cn>

CVE Identifier

CVE-2017-5638

原文链接: https://cwiki.apache.org/confluence/display/WW/S2-045

研究目的

根据CVE-2017-5638报告,使用checkmarx并自定义规则,查找使用了Struts2框架的Web应用程序源代码中的远程代码执行安全漏洞。

研究工具和方法

研究工具有checkmarx CxSAST测试版,CxAuditor。

研究方法:根据checkmarx提供的CxQL API文档,在CxAuditor中编写自定义规则,并执行扫描验证。

Struts2文件上传代码链接:

http://www.mkyong.com/wp-content/uploads/2010/06/Struts2-File-Upload-Example.zip

CVE-2017-5638报告分析

根据漏洞报告可知,有远程代码执行漏洞的struts版本号分别是2.3.5, 2.3.31, 2.5和2.5.10。如果在web应用程序中使用了以上版本的struts框架,并使用了struts框架的文件上传功能,那么该web应用程序一定存在远程代码执行漏洞。

Web应用程序如何使用struts文件上传功能

步:在pom.xml文件中指定struts2的版本号。Strut2的配置文件中默认struts.multipart.parser=Jakarta。

第二步:在代码中引入struts2包含文件上传功能的包。即“import com.opensymphony.xwork2.ActionSupport;”

Apache官方文档中给出的struts文件上传使用示例如下:

图.使用struts2文件上传代码样例

 
如何查找使用了struts2有远程命令执行漏洞的函数库的代码
  • 根据pom.xml查找strut2的版本号。
  • 在web应用程序源代码中查找使用进行文件上传的用户输入
  • 在web应用程序源代码中查找文件上传执行,进行数据流分析。
使用CxAuditor自定义规则查找struts2漏洞详解
  • 查看与远程代码执行漏洞相关的checkmarx原有规则

在CxAuditor中可以自定义规则,从而替换和扩充checkmarx原有规则。在CxAuditor中,位于“Cx”下的规则为checkmarx原有规则,用户只可查看,不可编辑。

在“Cx”下查看Java语言的高危漏洞(Java_High_Risk)下的命令注入(Command_Injection)漏洞的查询规则。

图. checkmarx查询规则导航栏

图.Command_Injection查询规则定义

 

由于struts2的CVE-2017-5638号漏洞是新发现的漏洞,checkmarx CxSAST不能直接扫描出来。可以通过CxAuditor来修改和扩充checkmarx规则,来找到这种新发现的漏洞。

 

自定义规则

上海并擎根据checkmarx原有规则,自定义了如下三个规则,用以查找struts2远程代码执行漏洞。

三个规则分别是:

  1. 查找strtus2的版本号的规则(Find_Struts_Version)
  2. 查找文件上传动作的规则(Find_Interactive_Inputs),即用户交互式输入
  3. 查找文件上传执行的规则(Find_Command_Injection_Outputs)。

用户自定义规则统一存放在“Corp”目录下。本文创建的三个规则中查找struts2版本号这个规则是新增的,查找文件上传动作和查找文件执行这两个规则是对原有checkmarx规则的修改,添加部分内容后得到的。三个规则的具体内容,详见第9部分。

图.自定义规则

 
使用自定义规则的扫描结果
  • Struts2版本号扫描结果

本文以Maven构建工程的代码为例,只考虑用Struts2上传文件的功能。Maven工程pom.xml文件中定义了Struts2的版本号。如图7.4所示,dependency标签中定义了Struts2的版本。

图.pom.xml中定义的struts2版本号

命令注入扫描结果

Struts2中的远程命令执行漏洞对应于Checkmarx中的命令注入(Command_Injection)漏洞。

扫描结果如下列图例所示。左侧窗格显示查询结果的代码,并以黄色高亮形势显示漏洞代码。右侧窗格以图表形式显示有漏洞的代码元素,以数据流形势显示整个攻击路径。右侧窗格中的代码元素链接到左侧窗格中的代码,点击右侧窗格代码元素光标会自动跳左侧窗格中对应代码处。

文件上传从页面开始,如图7.5。在图中,左侧窗格显示fileupload.jsp文件的源代码。<s:file/>标签对应为页面上文件上传图标。

图.页面上选取文件(fileupload.jsp文件中的<s:file/>)

图.文件对象流动到setFileUpload函数的参数中

下图为文件对象流动到后端java程序。在FileUploadAction.java文件中,引入struts2的文件上传功能组件(“import com.opensymphony.xwork2.ActionSupport”)。然后使用该组件。文件对象先流动到setFileUpload函数的参数中。

图.文件对象流动到函数体中

图.文件流动到FileUploadAction类对象中

总结

本文首先分析了CVE-2017-5638报告,介绍了web应用程序如何使用struts2文件上传功能。然后,从总体上概括了如何查找报告中提到的相应版本struts2的漏洞的代码。最后,详细描述了使用CxAuditor创建三个自定义规则,并使用自定义规则对文件上传代码扫描,查找到web应用程序源代码中与远程代码执行漏洞相对应的checkmarx命令注入(Command_Injection)漏洞。

至此,完成了使用checkmarx并自定义规则,查找使用了Struts2框架的Web应用程序源代码中的远程代码执行安全漏洞。

 

附件(自定义规则函数详解)

1.自定义规则一(查找Struts2版本号)

规则名称:Find_Struts_Version

规则内容:

/// Checks the Version of Struts

/// Starts by checking the Maven configuration file

///

/// It returns an empty or single Node CxList with one of two types:

/// - StringLiteral: can be obtained the version with .GetName()

/// - Comment: an ( .data.GetByIndex(0) as CSharpGraph).FullName should be used

 

// Maven

CxList pomData = Find_Pom_Config();

if(pomData.Count != 0){

  CxList literals = pomData.FindByType(typeof(StringLiteral));

  CxList memberAccess = pomData.FindByType(typeof(MemberAccess));

 

  CxList springLiterals = literals.FindByShortName("*struts*", false);

  CxList assignExpr = springLiterals.GetFathers();

  CxList leftSide = memberAccess.FindByFathers(assignExpr).FindByAssignmentSide(CxList.AssignmentSide.Left);

  CxList pomDependency = pomData.FindByShortName("dependency", false);

  CxList pomDependencyIf = leftSide.GetAncOfType(typeof(IfStmt)).GetFathers().GetFathers();

  pomDependencyIf = pomDependency.FindByFathers(pomDependencyIf).GetFathers();

  CxList pomVersion = memberAccess.FindByShortName("version", false);

  pomVersion = pomVersion.GetByAncs(pomDependencyIf);

  CxList springVersion = literals.FindByFathers(pomVersion.GetFathers());

 

  string maxVersion = "";

  foreach(CxList v in springVersion){

         string versionName = v.GetName();

         if(!versionName.StartsWith("$")){

                if(versionName.CompareTo(maxVersion) > 0){

                       maxVersion = versionName;

                       result = v.Clone();

                }

         } else {

                string propertyName = versionName.Trim(new char[]{'$','{','}'});

                String fullName = "*.properties." + propertyName.Replace("-", "_") + ".text";

                CxList realNameVersion = memberAccess.FindByName(fullName, false);

                CxList realVersion = literals.FindByFathers(realNameVersion.GetFathers());

                versionName = realVersion.GetName();

                if(versionName.CompareTo(maxVersion) > 0){

                       maxVersion = versionName;

                       result = realVersion;

                }

         }

  }

}

 

2. 自定义规则二(交互式输入)

规则名称:Find_Interactive_Inputs

规则内容:

result = base.Find_Interactive_Inputs();

result.Add(All.FindByName("com.mkyong.common.action.FileUploadAction") +

All.FindAllReferences(All.FindDefinition(All.FindByName("com.mkyong.common.action.FileUploadAction"))) +

All.FindAllReferences(All.FindByName("com.mkyong.common.action.FileUploadAction")));

 

3. 自定义规则三(命令注入输出)

规则名称:Find_Command_Injection_Outputs

规则内容:

result = base.Find_Command_Injection_Outputs();

result.Add(All.FindByName("com.mkyong.common.action.FileUploadAction.execute") +

All.FindAllReferences(All.FindDefinition(All.FindByName("com.mkyong.common.action.FileUploadAction.execute"))) +

All.FindAllReferences(All.FindByName("com.mkyong.common.action.FileUploadAction.execute"))+

All.FindByMemberAccess("FileUploadAction.execute"));

result.Add(All.FindByName("com.mkyong.common.action.FileUploadAction") +

All.FindAllReferences(All.FindDefinition(All.FindByName("com.mkyong.common.action.FileUploadAction"))) +

All.FindAllReferences(All.FindByName("com.mkyong.common.action.FileUploadAction")));