froxlor/froxlor: Froxlor 2.3.13 - IDOR in Email Template Editor (Information Disclosure)
What fixes it
- 2.3.14froxlor/froxlorGitHub
A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.
Description
Affected Software
- Software: Froxlor (open-source hosting control panel)
- Version: 2.3.13 (latest release at the time of writing)
- Repository: https://github.com/Froxlor/Froxlor
- Tested source revision: Git tag
2.3.13(= commit9051a8c3222c2d1338738a3d2b1fa0f7e0be110a)
Vulnerability Description
The email template editor in the Froxlor admin area (admin_templates.php?page=email&action=edit) renders a template's subject and body into an edit form. Both row ids, subjectid and mailbodyid, are taken directly from the request. Email templates are per-admin resources: every other query touching the panel_templates table in this file — the template list, the delete action, the save action, and even the subject lookup in this very branch — is scoped to the owning account's adminid. The query that fetches the mail body, however, filters by id alone.
As a result, any authenticated low-privileged admin (a reseller) can read the email templates of any other admin, including the full administrator, by requesting the edit page with their own subjectid (which passes the ownership check) combined with a foreign mailbodyid. The ids are sequential, so all admins' templates can be enumerated.
Root Cause
admin_templates.php takes both ids straight from the request; mailbodyid is the user-controlled lookup key for the read below:
$subjectid = intval(Request::any('subjectid'));
$mailbodyid = intval(Request::any('mailbodyid'));In the action == 'edit' branch, the subject row is looked up correctly scoped to the requesting admin's adminid:
$result_stmt = Database::prepare("
SELECT `language`, `varname`, `value` FROM `" . TABLE_PANEL_TEMPLATES . "`
WHERE `adminid` = :adminid AND `id` = :subjectid");
Database::pexecute($result_stmt, [
'adminid' => $userinfo['adminid'],
'subjectid' => $subjectid
]);A few lines below, in the same branch (the GET path that renders the edit form), the mail body row is fetched by id alone — the adminid condition is missing, and the fetched value is what gets rendered into the edit form:
$result_stmt = Database::prepare("
SELECT `language`, `varname`, `value`
FROM `" . TABLE_PANEL_TEMPLATES . "`
WHERE `id` = :id");
Database::pexecute($result_stmt, [
'id' => $mailbodyid
]);
$result = $result_stmt->fetch(PDO::FETCH_ASSOC);
// ...
$mailbody = $result['value'];The save path (the POST branch of the same page) updates both rows with WHERE adminid = :adminid AND id = :id, which is why this is read-only disclosure — writes to foreign templates are silently dropped.
Proof of Concept
Log in as a low-privileged admin (reseller) account:
curl -s -c cookies.txt \ -d 'loginname=<reseller_username>' -d 'password=<reseller_password>' -d 'dologin=1' \ 'https://<your-froxlor-install>/index.php'
Still as the reseller, create an email template via *Email & file templates* → *Add template* and note the ids from its edit link (action=edit&subjectid=...&mailbodyid=...). The victim admin's mailbodyid can be any sequential row id belonging to another admin.
Send the unauthorized read, combining the reseller's own subjectid with the victim's mailbodyid:
curl -s -b cookies.txt \ 'https://<your-froxlor-install>/admin_templates.php?page=email&action=edit&subjectid=<your_own_subjectid>&mailbodyid=<victim_mailbodyid>'
The edit form's *Mail body* field contains the victim admin's template body.
Actual execution output (from a local test instance): the request GET /admin_templates.php?page=email&action=edit&subjectid=11&mailbodyid=10 — where subjectid=11 was the reseller's own template and mailbodyid=10 belonged to the full admin — returned:
- HTTP 200,
Content-Type: text/html; charset=UTF-8 - Mail body field value:
BODY-VB-VICTIM-20260907-0421-x1(the admin's template body, which had been marked beforehand with this unique string) - Subject field value:
Subj-VB-ATK-20260907-0421-x2(the reseller's own subject)
The admin-owned body, rendered inside the reseller's session, confirms the read is not scoped to the owner.
Impact
An authenticated reseller — a restricted admin account that is only supposed to manage its own customers — can enumerate the sequential mailbodyid values and read the subject and body of every email template owned by any other admin, including the super-admin. These templates hold the exact texts Froxlor sends to customers (welcome mails, account/FTP/database creation notices, password reset mails), and admins routinely customize them with host-specific wording and internal details not intended for lower-privileged staff. The save path enforces ownership, so templates cannot be modified this way; the issue is information disclosure.
Suggested Fix
Add adminid = :adminid to the mail body lookup in the edit branch, matching the subject lookup, so the query is scoped to the requesting admin.
Text from GitHub Advisory Database
Affected versions 1
- froxlor/froxlor2.3.13GitHub