GitHub Security Advisory: GHSA-m3vp-3jjm-gpmx
Package: admidio/admidio (COMPOSER)
Severity: MODERATE (CVSS 6.5)
CWE: CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Vulnerable versions: <= 5.0.8
Patched in: 5.0.9

Summary: Admidio has Path Traversal in ECard Preview that Allows Reading Arbitrary Server Files Including Database Credentials

Details:
## Summary

The `ecard_preview.php` endpoint does not validate that the `ecard_template` POST parameter is a safe filename before passing it to `ECard::getEcardTemplate()`. An authenticated user can supply a path traversal payload (e.g., `../config.php`) to read arbitrary files accessible to the web server process, including `adm_my_files/config.php` which contains database credentials.

## Details

**Root Cause:** The `ecard_template` parameter is a select box whose value is only sanitized via `strStripTags()` during form validation, which does not restrict path traversal characters. Unlike `ecard_send.php` which explicitly validates the template name as a safe filename, `ecard_preview.php` omits this check entirely.

**Code Path:**

1. `modules/photos/ecards.php:143-152` — The form creates a select box with template filenames from `adm_my_files/ecard_templates/`. The form object is stored in the session.

2. `modules/photos/ecard_preview.php:33-34` — The POST request is validated against the stored form object:
```php
$categoryEditForm = $gCurrentSession->getFormObject($_POST['adm_csrf_token']);
$formValues = $categoryEditForm->validate($_POST);
```

3. `src/UI/Presenter/FormPresenter.php:2190-2243` — The `validate()` method applies `StringUtils::strStripTags()` to all values and performs type-specific checks for `captcha`, `date`, `editor`, `email`, `number`, `url`, and `uuid` — but has **no validation case for select box values**. The attacker-controlled value `../config.php` passes through unchanged.

4. `modules/photos/ecard_preview.php:48` — The unvalidated value is passed directly to `getEcardTemplate()`:
```php
$ecardDataToParse = $funcClass->getEcardTemplate($formValues['ecard_template']);
```

5. `src/Photos/ValueObject/ECard.php:67-77` — The filename is concatenated into the path and opened:
```php
public function getEcardTemplate(string $tplFilename, string $tplFolder = ''): ?string
{
    if ($tplFolder === '') {
        $tplFolder = ADMIDIO_PATH . FOLDER_DATA . '/ecard_templates/';
    }
    // ...
    $fileHandle = @fopen($tplFolder . $tplFilename, 'rb');
```

With `$tplFilename = '../config.php'`, this resolves to `ADMIDIO_PATH/adm_my_files/ecard_templates/../config.php` → `ADMIDIO_PATH/adm_my_files/config.php`.

**Why `ecard_send.php` is NOT vulnerable:** At line 35, it independently validates the template name:
```php
$postTemplateName = admFuncVariableIsValid($_POST, 'ecard_template', 'file', array('requireValue' => true));
```
This calls `strIsValidFileName()` which checks `basename($filename) !== $filename`, blocking any path traversal. The preview endpoint lacks this check.

## PoC

```bash
# Step 1: Log in and visit the ecard form to create a session with a form object
# Navigate to: /modules/photos/ecards.php?photo_uuid=<valid_album_uuid>&photo_nr=1
# Extract the adm_csrf_token from the rendered form HTML

# Step 2: Send path traversal payload to read config.php (contains DB credentials)
curl -b 'PHPSESSID=<session_cookie>