Struts2命令执行漏洞分析与复现

  • A+
所属分类:安全文章

Apache Struts是美国阿帕奇(Apache)软件基金会负责维护的一个开源项目,是一套用于创建企业级Java Web 应用的开源MVC框架(Model View Controller),Struts2作为控制器(Controller)来建立模型与视图的数据交互,它本质上相当于一个servlet(使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页)。目前,Struts框架广泛应用于政府、公安、交通、金融行业和运营商的网站建设,作为网站开发的底层模板使用,是应用最广泛的Web应用框架之一。


漏洞介绍


在主流的struts2漏洞利用工具上,主要的RCE漏洞如上图所示,一看这么多的编号,都排到57了,就像道哥说的一样如果一个软件出现的漏洞较多,那么说明代码维护者的安全意识与安全经验有所欠缺,同时由于破窗效应,这个软件未来往往出现更多的漏洞。
那么我们先挑选其中一个s2-045来进行复现和详细的分析。
Struts2命令执行漏洞分析与复现
#S2-045分析与复现
ApacheStruts 2.3.5 – 2.3.31版本及2.5 –2.5.10版本存在远程代码执行漏洞(CVE-2017-5638)。该漏洞是由于上传功能的异常处理函数没有正确处理用户输入的错误信息。导致远程攻击者可通过发送恶意的数据包,利用该漏洞在被攻击服务器上执行任意命令。
具体是由于当数据包content-type中出现”multipart/form_data”时,会被认为有文件上传,恶意用户可在上传文件时通过修改HTTP请求头中的Content-Type值来触发该漏洞,从而调用struts2默认的上传文件组件Jakarta,通过组件漏洞载入OGNL表达式并执行,从而达到代码执行的目的。漏洞的点在于,由于Strus2对错误消息处理时,出现了疏忽。
实验环境:WIN7,装有Tomcat服务,部署有s2-045漏洞的web服务

1.漏洞成因


我们下载了2个版本的源码,分别是2.3.31有漏洞的版本,另一个是2.3.32修复后的版本,我们使用工具来比较2个版本的文件,找到补丁修改的代码。

Struts2命令执行漏洞分析与复现

我们可以使用Beyond Compare 4 这个工具来对所有文件进行对比,找到修改的文件。
右击struts-STRUTS_2_3_31目录,选择select left folder for Compare

Struts2命令执行漏洞分析与复现

然后右击struts-STRUTS_2_3_32目录,选择Compareto”struts-STRUTS_2_3_31”来与之前的文件夹里所有文件进行对比

Struts2命令执行漏洞分析与复现

然后在菜单栏点击 edit,选择Select All或者直接按Ctrl+A 选中所有文件进行对比

Struts2命令执行漏洞分析与复现

然后点击菜单栏的Actions,选择Compare Contents

Struts2命令执行漏洞分析与复现

弹出如下对话框,   比较方式选择Binary comparison,把show results dialog 勾选上,然后点击Start等待比较完成。

Struts2命令执行漏洞分析与复现

比较完成后如下图所示,下面显示的红色的为有修改的,一共有57个文件内容不同。

Struts2命令执行漏洞分析与复现

通过点开修改的文件查看可知好多文件只是修改了版本号
Struts2命令执行漏洞分析与复现
重要的是下面三个修改的文件
Struts2命令执行漏洞分析与复现
我们点击进入第一个,点击左侧导航栏位置的红色部分可以直接跳转到2个文件不相同的地方。
Struts2命令执行漏洞分析与复现
可以看到是在buildErrorMessage 函数里面,更新后的版本在调用LocalizedTextUtil.findText 的时候后面的参数发生了变化。其他2个文件修改的内容也类似。

Struts2命令执行漏洞分析与复现

使用弹计算器的POC,在源码中这个位置加断点之后当代码执行完这行的时候,对应的我们的命令也就执行了。
Struts2命令执行漏洞分析与复现
那么咱们就打开有漏洞的那一版本的源代码,跟进LocalizedTextUtil.findText,因为java支持方法重载,所以在找findText的时候可能会有多个findText函数,我们根据这里的传参个数,以及参数的类型,就可以找到这里实际调用的函数,我们可以在xwork-coresrcmainjavacomopensymphonyxwork2utilLocalizedTextUtil.java里找到这个方法。

Struts2命令执行漏洞分析与复现

在注释中写着Message中${}里的代码会被当作OGNL 表达式执行,当没有Message时,通过defaultMessage来获取message,该函数继续调用了findText方法,同样根据函数参数来跟踪调用的函数是424行的函数
Struts2命令执行漏洞分析与复现
然后往下看 发现调用了getDefaultMessage函数
Struts2命令执行漏洞分析与复现
再跟进getDefaultMessage函数,在该函数里调用了TextParseUtil.translateVariables
Struts2命令执行漏洞分析与复现
可见漏洞产生的关键点是在处理 error message产生了问题,message拼接了用户的“输入”,而这个message参数会传递到transalteVariables()这个函数。该方法的源码在xwork-coresrcmainjavacomopensymphonyxwork2utilTextParseUtil.java

里面,找到translateVariables方法并跟踪,可以看到最终调用com.opensymphony.xwork2.util.OgnlTextParser.evaluate(char[],String,ParsedValueEvaluator, int),导致了ognl命令执行。

Struts2命令执行漏洞分析与复现
expression是要被用作ognl表达式执行的,这样就产生命令执行了。

evaluate函数内部能够动态执行Java代码 例如:

public String Evaluate(String S){...}

S="return 5 ";
Evaluate(S) 返回 5
Struts2命令执行漏洞分析与复现
这样我们就分析出了漏洞产生的原因和位置。


2.漏洞利用


然后来看下怎么利用,我们可以查找所有buildErrorMessage 函数的调用,也可以从框架入口开始跟踪输入流。
我们选择从框架入口开始跟踪输入流,Struts2默认入口是StrutsPrepareAndExecuteFilter,可以在web.xml里查看

   <filter>       <filter-name>StrutsFilter</filter-name>       <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>   </filter>   <filter-mapping>       <filter-name>StrutsFilter</filter-name>       <url-pattern>/*</url-pattern>   </filter-mapping>

源码路径:

coresrcmainjavaorgapachestruts2dispatcherngfilterStrutsPrepareAndExecuteFilter.java首先对输入请求对象requests进行了封装
Struts2命令执行漏洞分析与复现
跟进处理函数,在coresrcmainjavaorgapachestruts2dispatcherDispatcher.java的wrapRequest的函数里,首先判断content type是否为空,判断是不是POST表单,判断是不是有multipart/form-data 这个字符串,如果是则建立一个multiPartRequest 的实例,默认返回JakartaMultiPartRequest类。并且建立MultiPartRequestWrapper。

Struts2命令执行漏洞分析与复现

在建立MultiPartRequestWrapper时解析requests。通过设置struts.multipart.parser的值,可以指定不同的解析类,struts.multipart.parser的值有多个,而默认的是jakarta,在default.properties里面可以看到。

Struts2命令执行漏洞分析与复现

在struts-2.3.31-allstruts-2.3.31lib 下找到struts2-core-2.3.31.jar包。default.properties在struts2-core-2.3.31.jar包里面的orgapachestruts2路径下,如果想查看,可以直接把struts2-core-2.3.31.jar包解压。

Struts2命令执行漏洞分析与复现

解压完以后查看 default.properties内容如下图所示
在default.properties文件中,struts.multipart.parser的值有两个选择,分别是jakarta和pell。其中的jakarta解析器是Struts


2框架的标准组成部分。默认情况下jakarta是启用的,所以该漏洞的严重性需要得到正视。

Struts2命令执行漏洞分析与复现

也就是调用了org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest类的parse方法,可以继续跟进processUpload,最终跟踪到该函数发生了异常,在调用里面可以看到,当处理 multipart 的时候发生了异常,进入了parse的异常分支,然后调用了前面存在漏洞的方法。


所以我们只要构造特殊的content-type值,由于头字段Content-Type的内容是非法的,不符合格式的,所以上传肯定是会出错的在该值里面包含OGNL表达式就可以达到远程命令执行了,content-type里必须包含multipart/form-data,并不需要真正的上传文件就可以利用。然后commons-fileupload组件的ServletFileUpload#parseRequest(RequestContext)方法会抛出异常,其中异常信息就包含ognl格式的字符串例如:%{#[email protected]@DEFAULT_MEMBER_ACCESS,@[email protected]().exec('calc')};,然后struts2会解析这个符合ognl语法格式的字符串,命令也就可以成功执行。


3.漏洞测试


我们已经搭建好了struts  S2-045的漏洞环境Struts2命令执行漏洞分析与复现


接下里我们使用如下POC进行验证本机是否存在漏洞Struts2命令执行漏洞分析与复现
我们可以看到POC中Content-Type 里包有
#nike='multipart/form-data'   就是使content_type.contains("multipart/form-data")判断为true。如果该POC运行成功会执行ipconfig命令。同时要求在POC同目录下,必须存在test.txt,这个文件名和路径可以自行修改,只要保证存在即可。
然后我们运行POC脚本,成功执行了ipconfig

Struts2命令执行漏洞分析与复现

漏洞修复


因为Struts2默认受本漏洞的影响,要利用该漏洞也不需要找到上传文件的地方,且POC中的命令可以修改成其他的,危害极大。
1.我们可以通过修改struts.multipart.parser属性值可以消除漏洞的影响,
2.过滤http请求头中content-type的内容也可以达到临时缓解的效果
3.应该尽快升级到最新版本


发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: