{"id":"GHSA-vgrf-pr28-vf98","summary":"CI4MS Vulnerable to Arbitrary Database Table Drop via Theme deleteProcess","details":"### Summary\nThe `deleteProcess()` action accepts a POST parameter `tables[]` containing arbitrary table names. These are passed directly to `$forge-\u003edropTable()` without validating that the tables belong to the theme being deleted.\n\nThe `deleteConfirm` view correctly populates `tables[]` from the theme's own migration files, but the server-side `deleteProcess` does not verify the received values against those files. An authenticated admin can craft a POST request with arbitrary table names and drop any table in the database.\n\nThis is a real bug even within the admin trust model: the action should be scoped to the theme's own tables. The permission grants  delete this theme's data\", not \"drop any table\".\n\n### Details\n\n### Location\n`modules/Theme/Controllers/Theme.php` :: `deleteProcess()` ~line 147\n\n### Vulnerable Code\n```php\npublic function deleteProcess(string $slug)\n{\n    $themeName = $slug;\n    $activeTheme = setting('App.siteTheme');\n    if ($activeTheme === $themeName) {\n        return redirect()-\u003eroute('templateSettings')...;\n    }\n\n    $tablesToDrop = $this-\u003erequest-\u003egetPost('tables');  // ← user-supplied, unvalidated\n    if (!empty($tablesToDrop) && is_array($tablesToDrop)) {\n        $forge = \\Config\\Database::forge();\n        $db    = \\Config\\Database::connect();\n        foreach ($tablesToDrop as $table) {\n            if ($db-\u003etableExists($table)) {\n                $forge-\u003edropTable($table, true);  // ← no whitelist check\n            }\n        }\n    }\n```\n\n### PoC\n1. Authenticate to the backend (any user with theme.delete permission)\n2. POST to `/backend/themes/delete-process/\u003cany_non_active_theme_slug\u003e`\n3. Include `tables[]=\u003cany_table\u003e` in POST body\n4. The named tables are dropped without validation\n\n### Impact\n- Dropped `ci4ms_blog` (confirmed in test)\n- Dropped `ci4ms_users` + `ci4ms_auth_identities` simultaneously — disables all authentication (confirmed)\n- Any table in the database can be targeted\n\n### Additional note\nQuick note on the design intent for deleteProcess — I noticed delete_confirm.php scopes the checkboxes to the theme's own migration files, and the CHANGELOG confirms the selective deletion was intentional (admins can choose which tables to keep). The server-side deleteProcess already has all the information it needs to validate the input — deleteConfirm derives the valid table set from the migration files, deleteProcess just needs to do the same before acting on the POST. Happy to clarify if useful.","aliases":["CVE-2026-41890"],"modified":"2026-05-08T20:33:36.536502Z","published":"2026-05-04T20:50:10Z","database_specific":{"github_reviewed_at":"2026-05-04T20:50:10Z","nvd_published_at":"2026-05-07T04:16:33Z","cwe_ids":["CWE-20"],"severity":"MODERATE","github_reviewed":true},"references":[{"type":"WEB","url":"https://github.com/ci4-cms-erp/ci4ms/security/advisories/GHSA-vgrf-pr28-vf98"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-41890"},{"type":"WEB","url":"https://github.com/ci4-cms-erp/ci4ms/commit/2f38284281ce6b435ea42003951f14109ac2cea7"},{"type":"PACKAGE","url":"https://github.com/ci4-cms-erp/ci4ms"},{"type":"WEB","url":"https://github.com/ci4-cms-erp/ci4ms/releases/tag/0.31.8.0"}],"affected":[{"package":{"name":"ci4-cms-erp/ci4ms","ecosystem":"Packagist","purl":"pkg:composer/ci4-cms-erp/ci4ms"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.31.1.0"},{"fixed":"0.31.8.0"}]}],"versions":["0.31.1.0","0.31.2.0","0.31.3.0","0.31.4.0","0.31.5.0","0.31.6.0","0.31.7.0"],"database_specific":{"last_known_affected_version_range":"\u003c= 0.31.7.0","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/05/GHSA-vgrf-pr28-vf98/GHSA-vgrf-pr28-vf98.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V4","score":"CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N"}]}