Collax Security Gateway: anonymous path traversal in the logout handler deletes the two-factor token store

An unauthenticated attacker deletes the appliance-wide two-factor token store and other www-data-writable files through a crafted logout cookie, with a single GET request.

Advisory ID: TP-2026-063
Product: Collax Security Gateway (German UTM, firewall, mail and SSL-VPN appliance; also affects Collax Business Server and Groupware Suite)
Vulnerability type: Path traversal (CWE-22)
CVE: pending (MITRE CNA-LR)
CVSS 3.1: 7.3 (High) · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L
Vendor advisory: Collax release notes CSG 7.2.48
Affected versions: 7.2.46 and earlier
Fixed in: 7.2.48
Reported: 01 September 2026

Summary

Collax Security Gateway is a German UTM, firewall and mail appliance. The anonymous logout endpoint /.loggedOut (cgi-bin/logout-admin.cgi) reads the request cookie 2FA_Auth and places its value into a filesystem path with no validation. The handler builds Mojo::File->new(sprintf('/var/lib/apache_2fa/state/%s', )) and calls unlink() when the target file does not parse as JSON. Because the endpoint is served with 'Require all granted', an unauthenticated attacker deletes any www-data-writable file, including the two-factor token store tokens.json. The preceding check also acts as an existence and JSON-type oracle for arbitrary paths.

Root cause

logout-admin.cgi:50 builds the path with Mojo::File->new(sprintf('/var/lib/apache_2fa/state/%s', $q->cookie('2FA_Auth'))) directly from the attacker-controlled cookie, with no .. rejection, canonicalisation or basename reduction.
logout-admin.cgi:58 calls unlink($stateFile) whenever the slurped file fails JSON decoding, so any non-JSON target under the traversal path is removed.
The writer of the same token (apache_2fa/auth:244) uses the safe Mojo::File->child($key), which rejects / and .., while the logout handler uses ->new(sprintf) and drops that check.
A valid token is an opaque 100-character [A-Z0-9] id that can never contain a path separator, so traversal input is out of contract.
The block is entered anonymously because the Apache configuration sets 'Require all granted' plus SetEnv AuthContext apache, satisfying the AuthContext guard at logout-admin.cgi:40 with no login.

Proof of Concept

Schematically: a single GET request, no login, no interaction.

GET /.loggedOut HTTP/1.1
Host: gw.example.com
Cookie: 2FA_Auth=<traversal path to a www-data-writable non-JSON file>

The traversal path in the cookie leaves the 2FA state directory. Pointed at tokens.json it wipes the appliance-wide 2FA store; pointed at a file outside the www-data-writable set, the mere presence or absence of the removal cookie discloses its existence. Both the deletion and the oracle path were verified as uid 33 (www-data) on 2026-09-01.

Impact

  • Anonymous deletion of the appliance-wide 2FA store tokens.json, removing every TOTP secret and forcing global re-enrolment.
  • Anonymous deletion of any other www-data-writable non-JSON file under /var/lib/apache_2fa/.
  • Unauthenticated existence and JSON-type oracle across arbitrary filesystem paths, including outside the writable set.
  • No authentication, no interaction, one GET request; reachable in the default configuration.

References

Is Something Like This in Your Software?

Our team found this vulnerability during its work. Have your applications tested by the same specialists, with a penetration test from turingpoint.