Website Security

CSV Formula Injection: The Spreadsheet That Opens a Command Prompt

2MNY Admin October 13, 2026 4 min read 3 views

Your export endpoint writes user-controlled strings into CSV cells. When a cell starts with =, @, +, -, or a tab, Excel and friends treat it as code — and a crafted cell can chain DDE into a command prompt on the analyst's machine. Here's the two-sided detection: planted formula cells already present, and the reflection vector that lets attackers plant them — proven without sending a single formula.

Of all the injection families, CSV formula injection has the most cinematic payload: a spreadsheet cell that opens a command prompt on the machine of whoever opened the file. The cell looks like data. The spreadsheet application — Excel, LibreOffice, Google Sheets on import — sees a leading = and evaluates it as a formula. And formulas can do more than math: with DDE (Dynamic Data Exchange) syntax like =cmd|'/C calc'!A0, a formula can ask the operating system to run a program.

The attack needs one thing from your application: an export endpoint that writes strings you don't fully control — usernames, profile fields, ticket titles, product names — into CSV cells without neutralizing formula prefixes. The attacker sets their display name to =cmd|'/C powershell ...' and waits for your staff member to export the user list and open it. Your server stored the string; their spreadsheet executed it.

Two Different Risk Questions

Most write-ups conflate two separable findings, and the distinction drives both severity and the fix:

Risk A — planted cells already present (HIGH). The export file, as served right now, contains cells beginning with formula-meaningful characters: = followed by function syntax; @ (legacy XLS function syntax); a tab or CR (prefix smuggling across cell boundaries); or +/- followed by DDE chain indicators (|, =, !). This means someone has already injected formula-shaped content into your data, and any spreadsheet that opens the export will evaluate it. This is not a theoretical vector — the payload is in production data, in a file you serve.

Risk B — the reflection vector (MEDIUM). The export reflects request input into cell position without RFC 4180 quoting. If ?q=2mnyCHECK comes back as a bare 2mnyCHECK cell (unquoted), then ?q==HYPERLINK(...) would come back as a bare formula too. The vector is proven; the payload is up to the attacker's imagination.

Detecting It Without Ever Sending a Formula

The safety line for this class is absolute: a scanner must never store a payload on the target and never send an actual formula. Our csv_formula_injection scanner honors both with a two-phase design:

Phase 1 — export discovery and cell audit. The scanner crawls the site surface for export-shaped endpoints — paths and parameters matching csv/export/download/report/format/excel/spreadsheet, and .csv/.tsv/.xls/.xlsx extensions — fetches each (capped at 5,000 cells and 512KB per file), parses the rows, and flags any existing cell whose value starts with a formula prefix. Planted cells are reported as evidence, never executed — the report shows the cell position and a redacted prefix, and the finding quotes the path.

Phase 2 — reflection check with a harmless marker. The export is requested with a purely alphanumeric parameter: ?q=2mny plus a random nonce. Alphanumerics can't be a formula, can't escape a cell, can't alter a spreadsheet's behavior — they can only prove one thing: whether the value lands in a cell unquoted. If the nonce appears bare in a cell, the export reflects request input without quoting (the exact precondition an attacker needs), and the medium-severity reflection finding fires. If the export quotes it ("2mny...") or ignores unknown parameters, there's no vector — nothing was sent that could have been one.

No =, +, -, @, tab, or CR is ever transmitted by the scanner. The proof of the vector is the reflection of a string that is provably inert.

The Fix: Quote Like RFC 4180 Means It

The cure is unglamorous and absolute: neutralize formula prefixes at write time. When emitting a CSV cell whose value begins with =, +, -, @, tab, or CR, prefix the value with a single quote (') or wrap and escape per RFC 4180 with the leading apostrophe guard — Excel and LibreOffice render the result as text, not a formula. Apply this to every field in every export, not just the ones that "face users," because data migrates: today's product-name column is tomorrow's finance-team export.

Then harden the consumption side as defense in depth: LibreOffice and recent Excel builds prompt (or refuse) on DDE and external content — don't disable those prompts, and teach staff that a spreadsheet asking to "run an external program" is an incident, not an inconvenience.

Finally, verify from outside: the free 2MNY scan discovers export endpoints on your site, audits served files for planted formula cells, and proves (or disproves) the reflection vector with an inert marker — the full two-sided check, GET-only, zero payloads stored, in every report.

A CSV export is a promise that the file contains data. Formula injection breaks that promise on the analyst's desk. Quote your cells, and keep the command prompts where they belong.

Is your website affected?

Find out in minutes — free scan, professional report, no credit card.

Start Free Scan