CVE-2026-34384: Admidio/admidio Admidio Has Missing CSRF Protection Vulnerability

MODERATE
Advisory Identifier: GHSA-ph84-r98x-2j22
CVSS 4.5
CWE-352
Published
Advisory Summary

CVE-2026-34384 is a MODERATE (CVSS 4.5) admidio has missing csrf protection vulnerability (CWE-352: Cross-Site Request Forgery (CSRF)) affecting Admidio/admidio libraries in the Packagist ecosystem. The issue involves admidio has missing csrf protection on registration approval actions. Remediation is available by upgrading affected packages to version 5.0.8.

CVE-2026-34384 at a Glance

CVE Identifier CVE-2026-34384
GHSA Identifier GHSA-ph84-r98x-2j22
Severity MODERATE
CVSS v3.1 Base Score 4.5
CWE Classification CWE-352: Cross-Site Request Forgery (CSRF)
Affected Product Admidio/admidio
Package Ecosystem Packagist
Published Date
Last Updated

Security Impact

CVSS: 4.5

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:N/I:H/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required High (PR:H)
User Interaction Required (UI:R)
Scope Unchanged (S:U)
Confidentiality None (C:N)
Integrity High (I:H)
Availability None (A:N)

How to Fix CVE-2026-34384

To remediate this vulnerability, upgrade all affected Admidio/admidio dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

admidio/admidio fixed: 5.0.8
Upgrade admidio/admidio to 5.0.8
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for admidio/admidio

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-34384, inspect your project dependencies for Admidio/admidio:

inspect dependencies for admidio/admidio

Analyze the terminal output to locate any version of Admidio/admidio in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 5.0.8), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-352: Cross-Site Request Forgery (CSRF), where the web application does not, or cannot, sufficiently verify whether a request was intentionally provided by the user who sent the request, which could have originated from an unauthorized actor. In Admidio/admidio, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary The create_user, assign_member, and assign_user action modes in modules/registration.php approve pending user registrations via GET request without validating a CSRF token. Unlike the delete_user mode in the same file (which correctly validates the token), these three approval actions read their parameters from $_GET and perform irreversible state changes without any protection. An attacker who has submitted a pending registration can extract their own user UUID from the registration confirmation email URL, then trick any user with the rol_approve_users right into visiting a crafted URL that automatically approves the registration. This bypasses the manual registration approval workflow entirely. ## Details ### CSRF Protection Is Present for delete_user but Absent for Approval Modes File: modules/registration.php, lines 90-128 The delete_user mode validates the CSRF token (line 99), but the three approval modes do not: ```php // assign_member and assign_user: no CSRF check } elseif (in_array($getMode, array('assign_member', 'assign_user'))) { $registrationService = new RegistrationService($gDb, $getUserUUID); $message = $registrationService->assignRegistration($getUserUUIDAssigned, $getMode === 'assign_member'); $gMessage->setForwardUrl($message['forwardUrl']); $gMessage->show($message['message']); // create_user: no CSRF check } elseif ($getMode === 'create_user') { $registrationUser->acceptRegistration(); if ($gCurrentUser->isAdministratorRoles()) { admRedirect(SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES.'/profile/roles.php', array('accept_registration' => true, 'user_uuid' => $getUserUUID))); } // delete_user: CSRF IS validated } elseif ($getMode === 'delete_user') { SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']); // <-- protected $registrationUser->delete(); } ``` The three approval modes read both UUIDs exclusively from $_GET (lines 41-43): The approve action modes accept $_GET parameters `user_uuid` and `user_uuid_assigned` without any POST body or CSRF token. Both parameters pass through `admFuncVariableIsValid()` with `uuid` type validation, which prevents SQL injection but provides no CSRF protection. ### User UUID Is Known to the Attacker from Registration Email File: `D:/bugcrowd/admidio/repo/src/Infrastructure/Service/RegistrationService.php`, lines 154-157 When a user submits a registration, Admidio sends a confirmation email containing a URL of the form: ``` https://TARGET/adm_program/modules/registration.php?id=VALIDATION_ID&user_uuid=REGISTRANT_UUID ``` The `user_uuid` in this URL is the registrant's own UUID. The attacker has this UUID because they received the confirmation email for their own registration. ### isAdministratorRegistration() Is a Delegated Right File: `D:/bugcrowd/admidio/repo/src/Users/Entity/User.php`, lines 1603-1606 ```php public function isAdministratorRegistration(): bool { return $this->checkRolesRight('rol_approve_users'); } ``` The `rol_approve_users` right is a delegated organizational privilege, not full system administrator access. Any member designated to review registrations -- for example, a membership secretary or club administrator -- is a valid CSRF victim. ## PoC **Scenario: Attacker bypasses manual registration approval** Prerequisites: (1) Manual registration approval is enabled. (2) The attacker submits a registration form and receives a confirmation email with their `user_uuid`. (3) After clicking the confirmation link, their registration enters the pending queue. **Step 1: Attacker extracts their own user_uuid from the registration email** The confirmation email contains a link of the form: ``` https://TARGET/adm_program/modules/registration.php?id=VALIDATION_ID&user_uuid=ATTACKER_UUID ``` The `ATTACKER_UUID` is visible to the attacker from their own email. **Step 2: CSRF auto-approval via image tag** The attacker hosts a page that the victim (admin with `rol_approve_users` right) visits: ```html <img src="https://TARGET/adm_program/modules/registration.php?mode=create_user&user_uuid=ATTACKER_UUID" width="1" height="1"> ``` When the victim loads this page, Admidio silently accepts the attacker registration and assigns default organization roles. No confirmation or token is required. **Step 3: Force-assign registration to an existing account (account takeover)** If the attacker knows the UUID of an existing member (obtainable from profile page URLs when the user list is visible) and has a pending registration: ```html <img src="https://TARGET/adm_program/modules/registration.php?mode=assign_user&user_uuid=ATTACKER_REG_UUID&user_uuid_assigned=EXISTING_USER_UUID" width="1" height="1"> ``` This merges the pending registration into the existing account, replacing that account login credentials with the attacker credentials. ## Impact - **Manual Approval Bypass:** An attacker with a pending registration can force auto-approval without waiting for an administrator to manually review it. This grants them organization membership, including access to events, documents, mailing lists, and other role-restricted features. - **Account Takeover via assign_user CSRF:** If the attacker knows any member UUID (visible in profile page URLs), the `assign_user` mode merges the attacker registration into that member account, replacing the existing member login with the attacker credentials. This is a full account takeover requiring only that the victim admin visit a crafted URL. - **Low Attack Complexity:** The attacker only needs their own registration email to get their UUID. The CSRF payload is a plain GET request via an image tag -- no JavaScript required. - **Delegated Right:** The required victim right (`rol_approve_users`) is a common delegation target in organizations with membership approval workflows. ## Recommended Fix Add `SecurityUtils::validateCsrfToken($_POST["adm_csrf_token"])` at the beginning of each approval action, consistent with how `delete_user` is already protected in the same file. ```php // File: modules/registration.php } elseif (in_array($getMode, array('assign_member', 'assign_user'))) { // ADD: validate CSRF token SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']); $registrationService = new RegistrationService($gDb, $getUserUUID); $message = $registrationService->assignRegistration($getUserUUIDAssigned, $getMode === 'assign_member'); ... } elseif ($getMode === 'create_user') { // ADD: validate CSRF token SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']); $registrationUser->acceptRegistration(); ... } elseif ($getMode === 'delete_user') { SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']); // already protected $registrationUser->delete(); } ``` Additionally, convert the approval action URLs from GET-based links to POST-form buttons (with the CSRF token in a hidden field). The existing `delete_user` button uses `callUrlHideElement()` which already sends the token in the POST body -- use the same pattern for approval buttons.

CVE-2026-34384 Affected Admidio/admidio Versions

1 component(s)
Fixed Versions:
5.0.8
Product / Package Ecosystem Affected Versions Fixed Version
admidio/admidio
Packagist
< 5.0.8
5.0.8

References

Frequently Asked Questions

What is CVE-2026-34384?
CVE-2026-34384 is a security vulnerability in Admidio/admidio involving Admidio has Missing CSRF Protection on Registration Approval Actions under CWE-352: Cross-Site Request Forgery (CSRF).
Which Admidio/admidio versions are affected?
Affected versions include releases prior to 5.0.8 across affected packages: admidio/admidio.
What version fixes CVE-2026-34384?
Upgrading to 5.0.8 (or the latest patched release for your respective branch) resolves the vulnerability.
Is admidio/admidio affected?
Yes, admidio/admidio is affected in releases prior to 5.0.8. Upgrade your dependency to 5.0.8 or later.
What is the CVSS score of CVE-2026-34384?
CVE-2026-34384 has a CVSS v3.1 base score of 4.5 (MODERATE) with vector string CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:N/I:H/A:N.
What CWE is associated with CVE-2026-34384?
CVE-2026-34384 is classified under CWE-352: Cross-Site Request Forgery (CSRF) (The web application does not, or cannot, sufficiently verify whether a request was intentionally provided by the user who sent the request, which could have originated from an unauthorized actor.).
How can I check Packagist dependencies for CVE-2026-34384?
Run the dependency tree command: 'inspect dependencies for admidio/admidio'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-ph84-r98x-2j22",
  "modified" : "2026-03-31T23:11:24Z",
  "published" : "2026-03-31T23:11:24Z",
  "aliases" : [ "CVE-2026-34384" ],
  "summary" : "Admidio has Missing CSRF Protection on Registration Approval Actions",
  "details" : "## Summary\n\nThe create_user, assign_member, and assign_user action modes in modules/registration.php approve pending user registrations via GET request without validating a CSRF token. Unlike the delete_user mode in the same file (which correctly validates the token), these three approval actions read their parameters from $_GET and perform irreversible state changes without any protection. An attacker who has submitted a pending registration can extract their own user UUID from the registration confirmation email URL, then trick any user with the rol_approve_users right into visiting a crafted URL that automatically approves the registration. This bypasses the manual registration approval workflow entirely.\n\n## Details\n\n### CSRF Protection Is Present for delete_user but Absent for Approval Modes\n\nFile: modules/registration.php, lines 90-128\n\nThe delete_user mode validates the CSRF token (line 99), but the three approval modes do not:\n\n```php\n// assign_member and assign_user: no CSRF check\n} elseif (in_array($getMode, array('assign_member', 'assign_user'))) {\n    $registrationService = new RegistrationService($gDb, $getUserUUID);\n    $message = $registrationService->assignRegistration($getUserUUIDAssigned, $getMode === 'assign_member');\n    $gMessage->setForwardUrl($message['forwardUrl']);\n    $gMessage->show($message['message']);\n\n// create_user: no CSRF check\n} elseif ($getMode === 'create_user') {\n    $registrationUser->acceptRegistration();\n    if ($gCurrentUser->isAdministratorRoles()) {\n        admRedirect(SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES.'/profile/roles.php',\n            array('accept_registration' => true, 'user_uuid' => $getUserUUID)));\n    }\n\n// delete_user: CSRF IS validated\n} elseif ($getMode === 'delete_user') {\n    SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']); // <-- protected\n    $registrationUser->delete();\n}\n```\n\nThe three approval modes read both UUIDs exclusively from $_GET (lines 41-43):\n\nThe approve action modes accept $_GET parameters `user_uuid` and `user_uuid_assigned` without any POST body or CSRF token. Both parameters pass through `admFuncVariableIsValid()` with `uuid` type validation, which prevents SQL injection but provides no CSRF protection.\n\n### User UUID Is Known to the Attacker from Registration Email\n\nFile: `D:/bugcrowd/admidio/repo/src/Infrastructure/Service/RegistrationService.php`, lines 154-157\n\nWhen a user submits a registration, Admidio sends a confirmation email containing a URL of the form:\n\n```\nhttps://TARGET/adm_program/modules/registration.php?id=VALIDATION_ID&user_uuid=REGISTRANT_UUID\n```\n\nThe `user_uuid` in this URL is the registrant's own UUID. The attacker has this UUID because they received the confirmation email for their own registration.\n\n### isAdministratorRegistration() Is a Delegated Right\n\nFile: `D:/bugcrowd/admidio/repo/src/Users/Entity/User.php`, lines 1603-1606\n\n```php\npublic function isAdministratorRegistration(): bool\n{\n    return $this->checkRolesRight('rol_approve_users');\n}\n```\n\nThe `rol_approve_users` right is a delegated organizational privilege, not full system administrator access. Any member designated to review registrations -- for example, a membership secretary or club administrator -- is a valid CSRF victim.\n\n## PoC\n\n**Scenario: Attacker bypasses manual registration approval**\n\nPrerequisites: (1) Manual registration approval is enabled. (2) The attacker submits a registration form and receives a confirmation email with their `user_uuid`. (3) After clicking the confirmation link, their registration enters the pending queue.\n\n**Step 1: Attacker extracts their own user_uuid from the registration email**\n\nThe confirmation email contains a link of the form:\n\n```\nhttps://TARGET/adm_program/modules/registration.php?id=VALIDATION_ID&user_uuid=ATTACKER_UUID\n```\n\nThe `ATTACKER_UUID` is visible to the attacker from their own email.\n\n**Step 2: CSRF auto-approval via image tag**\n\nThe attacker hosts a page that the victim (admin with `rol_approve_users` right) visits:\n\n```html\n<img src=\"https://TARGET/adm_program/modules/registration.php?mode=create_user&user_uuid=ATTACKER_UUID\" width=\"1\" height=\"1\">\n```\n\nWhen the victim loads this page, Admidio silently accepts the attacker registration and assigns default organization roles. No confirmation or token is required.\n\n**Step 3: Force-assign registration to an existing account (account takeover)**\n\nIf the attacker knows the UUID of an existing member (obtainable from profile page URLs when the user list is visible) and has a pending registration:\n\n```html\n<img src=\"https://TARGET/adm_program/modules/registration.php?mode=assign_user&user_uuid=ATTACKER_REG_UUID&user_uuid_assigned=EXISTING_USER_UUID\" width=\"1\" height=\"1\">\n```\n\nThis merges the pending registration into the existing account, replacing that account login credentials with the attacker credentials.\n\n## Impact\n\n- **Manual Approval Bypass:** An attacker with a pending registration can force auto-approval without waiting for an administrator to manually review it. This grants them organization membership, including access to events, documents, mailing lists, and other role-restricted features.\n- **Account Takeover via assign_user CSRF:** If the attacker knows any member UUID (visible in profile page URLs), the `assign_user` mode merges the attacker registration into that member account, replacing the existing member login with the attacker credentials. This is a full account takeover requiring only that the victim admin visit a crafted URL.\n- **Low Attack Complexity:** The attacker only needs their own registration email to get their UUID. The CSRF payload is a plain GET request via an image tag -- no JavaScript required.\n- **Delegated Right:** The required victim right (`rol_approve_users`) is a common delegation target in organizations with membership approval workflows.\n\n## Recommended Fix\n\nAdd `SecurityUtils::validateCsrfToken($_POST[\"adm_csrf_token\"])` at the beginning of each approval action, consistent with how `delete_user` is already protected in the same file.\n\n```php\n// File: modules/registration.php\n\n} elseif (in_array($getMode, array('assign_member', 'assign_user'))) {\n    // ADD: validate CSRF token\n    SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']);\n    $registrationService = new RegistrationService($gDb, $getUserUUID);\n    $message = $registrationService->assignRegistration($getUserUUIDAssigned, $getMode === 'assign_member');\n    ...\n\n} elseif ($getMode === 'create_user') {\n    // ADD: validate CSRF token\n    SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']);\n    $registrationUser->acceptRegistration();\n    ...\n\n} elseif ($getMode === 'delete_user') {\n    SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']); // already protected\n    $registrationUser->delete();\n}\n```\n\nAdditionally, convert the approval action URLs from GET-based links to POST-form buttons (with the CSRF token in a hidden field). The existing `delete_user` button uses `callUrlHideElement()` which already sends the token in the POST body -- use the same pattern for approval buttons.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:N/I:H/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Packagist",
      "name" : "admidio/admidio"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "5.0.8"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/Admidio/admidio/security/advisories/GHSA-ph84-r98x-2j22"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-34384"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/Admidio/admidio/commit/707171c188b3e8f36007fc3f2bccbfac896ed019"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/Admidio/admidio"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-352" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-03-31T23:11:24Z",
    "nvd_published_at" : "2026-03-31T21:16:30Z"
  }
}