GHSA-vgrf-pr28-vf98
GHSA-vgrf-pr28-vf98 is a Improper Input Validation vulnerability in ci4-cms-erp/ci4ms. O3 Security confirms whether GHSA-vgrf-pr28-vf98 is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.
CI4MS Vulnerable to Arbitrary Database Table Drop via Theme deleteProcess
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
Blast Radius
ci4-cms-erp/ci4msReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Packagist packages — download data is not available via public APIs for these ecosystems.
Description
Summary
The deleteProcess() action accepts a POST parameter tables[] containing arbitrary table names. These are passed directly to $forge->dropTable() 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 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".
Details
Location
modules/Theme/Controllers/Theme.php :: deleteProcess() ~line 147
Vulnerable Code
public function deleteProcess(string $slug)
{
$themeName = $slug;
$activeTheme = setting('App.siteTheme');
if ($activeTheme === $themeName) {
return redirect()->route('templateSettings')...;
}
$tablesToDrop = $this->request->getPost('tables'); // ← user-supplied, unvalidated
if (!empty($tablesToDrop) && is_array($tablesToDrop)) {
$forge = \Config\Database::forge();
$db = \Config\Database::connect();
foreach ($tablesToDrop as $table) {
if ($db->tableExists($table)) {
$forge->dropTable($table, true); // ← no whitelist check
}
}
}
PoC
- Authenticate to the backend (any user with theme.delete permission)
- POST to
/backend/themes/delete-process/<any_non_active_theme_slug> - Include
tables[]=<any_table>in POST body - The named tables are dropped without validation
Impact
- Dropped
ci4ms_blog(confirmed in test) - Dropped
ci4ms_users+ci4ms_auth_identitiessimultaneously — disables all authentication (confirmed) - Any table in the database can be targeted
Additional note
Quick 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.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐘Packagist | ci4-cms-erp/ci4ms | ≥ 0.31.1.0&&< 0.31.8.0 | 0.31.8.0 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for ci4-cms-erp/ci4ms. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update ci4-cms-erp/ci4ms to 0.31.8.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-vgrf-pr28-vf98 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-vgrf-pr28-vf98 is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-vgrf-pr28-vf98. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-vgrf-pr28-vf98 in your dependencies?
O3 detects GHSA-vgrf-pr28-vf98 across Packagist dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.