漏洞的总体情况是,基于“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
根据漏洞报告可知,有远程代码执行漏洞的struts版本号分别是2.3.5, 2.3.31, 2.5和2.5.10。如果在web应用程序中使用了以上版本的struts框架,并使用了struts框架的文件上传功能,那么该web应用程序一定存在远程代码执行漏洞。
步:在pom.xml文件中指定struts2的版本号。Strut2的配置文件中默认struts.multipart.parser=Jakarta。
第二步:在代码中引入struts2包含文件上传功能的包。即“import com.opensymphony.xwork2.ActionSupport;”。
Apache官方文档中给出的struts文件上传使用示例如下:
图.使用struts2文件上传代码样例
在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远程代码执行漏洞。
三个规则分别是:
用户自定义规则统一存放在“Corp”目录下。本文创建的三个规则中查找struts2版本号这个规则是新增的,查找文件上传动作和查找文件执行这两个规则是对原有checkmarx规则的修改,添加部分内容后得到的。三个规则的具体内容,详见第9部分。
图.自定义规则
本文以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")));