{"id":"CVE-2026-41890","aliases":["EUVD-2026-28292","GHSA-vgrf-pr28-vf98"],"status":"published","title":"CI4MS: Arbitrary Database Table Drop via Theme deleteProcess","description":"CI4MS is a CodeIgniter 4-based CMS skeleton that delivers a production-ready, modular architecture with RBAC authorization and theme support. From version 0.31.1.0 to before version 0.31.8.0, the 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. The 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. This issue has been patched in version 0.31.8.0.","severity":{"version":"4.0","score":6.9,"vector":"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","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-09","score":0.00409,"percentile":0.35457,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-20"],"published":"2026-05-04T20:50:10Z","modified":"2026-10-28T02:30:23Z","fixes":[{"ecosystem":"Packagist","package":"ci4-cms-erp/ci4ms","fixed_in":["0.31.8.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Packagist","package":"ci4-cms-erp/ci4ms","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.31.1.0"},{"last_affected":"0.31.7.0"},{"fixed":"0.31.8.0"}]}]},{"source":"osv","ecosystem":"Packagist","package":"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"]}],"products":[{"vendor":"ci4-cms-erp","product":"ci4ms"}],"references":[{"url":"https://github.com/ci4-cms-erp/ci4ms/security/advisories/GHSA-vgrf-pr28-vf98","tags":["x_refsource_CONFIRM","exploit","advisory","web"]},{"url":"https://github.com/ci4-cms-erp/ci4ms/releases/tag/0.31.8.0","tags":["x_refsource_MISC","web"]},{"url":"https://github.com/ci4-cms-erp/ci4ms/commit/2f38284281ce6b435ea42003951f14109ac2cea7","tags":["web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-41890","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-vgrf-pr28-vf98"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/41xxx/CVE-2026-41890.json","tags":["advisory"]},{"url":"https://github.com/ci4-cms-erp/ci4ms","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-41890","title":"CI4MS: Arbitrary Database Table Drop via Theme deleteProcess","description":"CI4MS is a CodeIgniter 4-based CMS skeleton that delivers a production-ready, modular architecture with RBAC authorization and theme support. From version 0.31.1.0 to before version 0.31.8.0, the 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. The 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. This issue has been patched in version 0.31.8.0.","status":"published","published":"2026-05-07T03:23:31.339Z","modified":"2026-05-07T13:42:35.44Z","cvss":[{"version":"4.0","score":6.9,"vector":"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","level":"medium","assessor":"cna"}],"cwes":["CWE-20"],"updated_at":"2026-11-02T13:41:08.498858Z"}],"euvd":[{"id":"EUVD-2026-28292","description":"CI4MS is a CodeIgniter 4-based CMS skeleton that delivers a production-ready, modular architecture with RBAC authorization and theme support. From version 0.31.1.0 to before version 0.31.8.0, the 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. The 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. This issue has been patched in version 0.31.8.0.","status":"published","published":"2026-05-07T03:23:31Z","modified":"2026-05-07T13:42:35Z","cvss":[{"version":"4.0","score":6.9,"vector":"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","level":"medium"}],"updated_at":"2026-11-02T19:05:15.366699Z"}],"ghsa":[{"id":"GHSA-vgrf-pr28-vf98","title":"CI4MS Vulnerable to Arbitrary Database Table Drop via Theme deleteProcess","description":"### 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) \u0026\u0026 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.","status":"published","published":"2026-05-04T20:50:10Z","modified":"2026-05-08T20:14:37Z","cvss":[{"version":"4.0","score":6.9,"vector":"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","level":"medium"}],"cwes":["CWE-20"],"updated_at":"2026-11-02T14:40:04.762828Z"}],"nvd":[{"id":"CVE-2026-41890","description":"CI4MS is a CodeIgniter 4-based CMS skeleton that delivers a production-ready, modular architecture with RBAC authorization and theme support. From version 0.31.1.0 to before version 0.31.8.0, the 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. The 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. This issue has been patched in version 0.31.8.0.","status":"published","published":"2026-05-07T04:16:33.74Z","modified":"2026-10-26T19:10:00.163Z","cvss":[{"version":"4.0","score":6.9,"vector":"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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"medium","assessor":"secondary"}],"cwes":["CWE-20"],"updated_at":"2026-11-02T14:12:17.842772Z"}],"osv":[{"id":"CVE-2026-41890","title":"CI4MS: Arbitrary Database Table Drop via Theme deleteProcess","description":"CI4MS is a CodeIgniter 4-based CMS skeleton that delivers a production-ready, modular architecture with RBAC authorization and theme support. From version 0.31.1.0 to before version 0.31.8.0, the 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. The 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. This issue has been patched in version 0.31.8.0.","status":"published","published":"2026-05-07T03:23:31.339Z","modified":"2026-10-28T02:30:23.57613712Z","cvss":[{"version":"4.0","score":6.9,"vector":"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","level":"medium"}],"cwes":["CWE-20"],"updated_at":"2026-11-02T15:45:03.764807Z"},{"id":"GHSA-vgrf-pr28-vf98","title":"CI4MS Vulnerable to Arbitrary Database Table Drop via Theme deleteProcess","description":"### 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) \u0026\u0026 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.","status":"published","published":"2026-05-04T20:50:10Z","modified":"2026-05-08T20:33:36.536502Z","cvss":[{"version":"4.0","score":6.9,"vector":"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","level":"medium"}],"cwes":["CWE-20"],"updated_at":"2026-11-02T15:46:57.348035Z"}]}}
