Hey! and welcome to another THEY BURNED MY BUG episode. This time, we introduce CVE-2025-25257. An SQLi that I spotted back in Feb.

This is a pre-auth SQLi bug that can be leveraged to an RCE in FortiWeb.

Description

in httpd.conf, there’s a native Apache handler:

<Location "/api/fabric/device/status">
    SetHandler fabric_device_status-handler
</Location>

Looking at its code reveals that it is responsible for parsing user-supplied headers such as Authorization. The Authorization header goes into the char token[] variable, as is, without validation or sanitation.

In get_fabric_user_by_token(), the token is concatenated to a SQL query:

ah, classic.

PoC

GET /api/fabric/device/status HTTP/1.1
Host: 192.168.10.144
Authorization: Bearer 123' or 'x'='x

Getting RCE

So, how can we escalate from SQLi to a pre-auth RCE? To do this, we will use MySQL’s INTO OUTFILE statement, which allows you to write files to the filesystem(SELECT 'hello' INTO OUTFILE '/tmp/foo.txt').

NOTE

Note: The INTO OUTFILE is available to high-privileged MySQL user, such as root, but, guess what? they are running the mysql queries as root haha)

How can we execute code? well, there’s a file called ml-draw.py, it’s a cgi script contains the following imports and accessible without auth:

import os
import sys
import cgi
import cgitb; cgitb.enable()
import time
from datetime import datetime
import matplotlib
import pylab
import numpy as np
from numpy import array

I found that it is possible to override the 3rd party module by making a file in the core modules directory:

/var/log/lib/python3.10/matplotlib.py

Then, we send an HTTP request to the ml-draw.py file it import the library triggers our code :D demo.gif

There were a few obstacles to exploiting this such as length of payload, encoding issues, and more. I’d elaborate about it but it turns out WatchTowr wrote an analysis SO FAST and it literally includes every step of the same methods I dealt with the constraints lmao. So make sure to read their writeup :^) SynSynology is a legend.

Full Exploit

  • The full exploit can be found here: https://github.com/0xbigshaq/CVE-2025-25257
  • The signed version I tweeted about(8fc4ca6426ae50c7673326eacb6644a8b361ad1051138d04cbd9da8b807a0973) can also be found in the repo under signed.py (At this point of the research I only leveraged it to file read/write primitive, and after a day or two I found the RCE gadget.)
ptr@nix:~/repos/CVE-2025-25257 (main) $ sha256sum signed.py 
8fc4ca6426ae50c7673326eacb6644a8b361ad1051138d04cbd9da8b807a0973  signed.py

I hope you enjoyed reading this! :D stay tuned because in the next few weeks I’m dropping another bug(it took them a long time to fix it so apologies for postponing it every time. It’s worth it tho).