GitHub Security Advisory: GHSA-ph84-r98x-2j22
Package: admidio/admidio (COMPOSER)
Severity: MODERATE (CVSS 4.5)
CWE: CWE-352: Cross-Site Request Forgery (CSRF)
Vulnerable versions: < 5.0.8
Patched in: 5.0.8

Summary: Admidio has Missing CSRF Protection on Registration Approval Actions

Details:
## 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->checkRolesR