CVE-2026-45263
FacturaScripts: CSV formula injection in CSVExport allows authenticated low-priv users to plant payloads that execute when an admin opens the export
Summary
## Summary > **Live PoC verified 2026-04-30** against a stock FacturaScripts master at `127.0.0.1:8081`. A low-privilege user (`lowpriv`) created a customer with `nombre = "=SUM(1+1)*cmd|/c calc!A1"`. An admin then exported `ListCliente` to CSV via `?action=export&option=CSV`. The downloaded file contains the raw payload as the first cell of the `nombre` column, with no leading single quote and no escape. Excel and LibreOffice will execute the formula on open, including DDE invocations such as `=cmd|'/c calc'!A1` that spawn arbitrary processes on the admin workstation. `Core/Lib/Export/CSVExport.php::writeData()` wraps every cell in the configured delimiter (`"`) and concatenates without inspecting the first character of the value. Spreadsheet applications interpret a cell that starts with `=`, `+`, `-`, `@`, `\t`, or `\r` as a formula. Because FacturaScripts only sanitises HTML metacharacters (` " '`) on save via `Tools::noHtml()` (`Core/Tools.php:45`), the formula prefix characters survive untouched all the way to the export. The downstream consumer is the admin who triggered the export, giving the attacker a reliable handoff: low-priv user plants a payload in any text field that ships in the default list export, admin downloads the CSV (all normal billing/sales/purchasing workflow), and the admin's spreadsheet client runs the formula in the admin's local context. ## Details ### the export does not neutralise leading meta-characters `Core/Lib/Export/CSVExport.php:253-267`: ```php public function writeData(array $data, array $fields = []) { if (!empty($fields)) { $this->writeHeader($fields); } foreach ($data as $row) { $line = []; foreach ($row as $cell) { $line[] = is_string($cell) ? $this->getDelimiter() . $cell . $this->getDelimiter() : $cell; } $this->csv[] = implode($this->separator, $line); } } ``` A string cell is rendered as `" "`. There is no `str_starts_with(...)` check for the formula trigger characters (`= + - @ \t \r` per OWASP CSV-injection guidance) and no leading single-quote guard that Excel and LibreOffice both treat as "force as text". The same writer is reused for every list and document export through `addListModelPage()`, `addModelPage()`, `addBusinessDocPage()`, and `addTablePage()`. ### the upstream sanitiser only scrubs HTML, not formulas `Core/Tools.php:45-46`: ```php const HTML_CHARS = [' ', '"', "'"]; const HTML_REPLACEMENTS = ['<', '>', '"', ''']; ``` `Tools::noHtml()` is the canonical input filter that models call from their `test()` method (e.g. `Cliente::test()` at `Core/Model/Cliente.php:319-321`). It strips angle brackets and quotes, none of which collide with the formula-injection alphabet. As a result, the `=`, `+`, `-`, `@` characters reach storage verbatim and the export pipeline emits them verbatim. ### the export controller hands the payload back to whoever triggers it `Core/Lib/Export/CSVExport.php:240-245`: ```php public function show(Response &$response) { $response->headers->set('Content-Type', 'text/csv; charset=utf-8'); $response->headers->set('Content-Disposition', 'attachment; filename=' . $this->getFileName() . '.csv'); $response->setContent($this->getDoc()); } ``` `Content-Disposition: attachment` plus an `.csv` extension causes browsers to save the file. Double-clicking the downloaded file opens it in Excel/LibreOffice/Numbers, which honour formula execution by default. The attacker payload runs on the admin's machine with the admin's privileges, in a security context entirely outside the FacturaScripts blast radius. ### default permission model exposes the data ingestion A user role only needs `Update`/`Insert` access to one of the default exportable models (`Cliente`, `Proveedor`, `Producto`, `Variante`, `Contacto`, etc.) to plant a payload. The admin export surface is reachable on the same controllers via `?action=export&option=CSV`. There is no s
Affected packages
| Ecosystem | Package | Affected versions | Fixed versions |
|---|---|---|---|
| Packagist | facturascripts/facturascripts | — | 2026.2 |
Remediation: Upgrade to 2026.2 or later.
References
Includes data from the GitHub Advisory Database, licensed under CC-BY 4.0.
CVE® is a registered trademark of The MITRE Corporation. CVE content reproduced under the CVE Terms of Use; copyright designation © MITRE.