GHSA-hgjx-r89m-m7v4
## Summary `FacturaScripts\Core\UploadedFile::move($destiny, $destinyName)` concatenates `$destiny` and `$destinyName` without normalizing the resulting path. Every caller in the codebase passes `UploadedFile::getClientOriginalName()` — the unsanitized client-supplied filename — as `$destinyName`, so an authenticated user submitting a filename containing `../` segments can write the uploaded content to any directory writable by the web-server user, escaping the intended `MyFiles/` location. Because the shipped `htaccess-sample` (the documented production Apache configuration) excludes `Dinamic/Assets/` and `node_modules/` from the `index.php` rewrite, files written into those directories are served directly by Apache. Combined with `.htaccess` not being in `BLOCKED_EXTENSIONS`, the primitive escalates from arbitrary file write to remote code execution. ## Vulnerable Code `Core/UploadedFile.php`: ```php private const BLOCKED_EXTENSIONS = ['phar', 'php', 'php3', 'php4', 'php5', 'php7', 'php8', 'pht', 'phtml', 'phps']; public function move(string $destiny, string $destinyName): bool { if (!$this->isValid()) { return false; } if (substr($destiny, -1) !== DIRECTORY_SEPARATOR) { $destiny .= DIRECTORY_SEPARATOR; } return $this->test ? rename($this->tmp_name, $destiny . $destinyName) : move_uploaded_file($this->tmp_name, $destiny . $destinyName); } public function getClientOriginalName(): string { return $this->name ?? ''; } ``` `isValid()` only checks the extension blocklist, the upload error code, and `is_uploaded_file()` — it never inspects the filename for directory separators or `..` segments. Six call sites pass the raw client filename straight into `move()`: - `Core/Controller/ApiUploadFiles.php:58` — `POST /api/3/uploadfiles` - `Core/Controller/ApiAttachedFiles.php:136` — `POST /api/3/attachedfiles` - `Core/Lib/Widget/WidgetFile.php:84` — every form using a file widget - `Core/Lib/Widget/WidgetLibrary
Properties
- ghsa_id
- GHSA-hgjx-r89m-m7v4
- severity
- critical
- summary
- FacturaScripts: Path traversal in UploadedFile::move() via getClientOriginalName() — arbitrary file write outside MyFiles/ leading to RCE
- cvss_score
- 9.9
- cve_id
- GHSA-hgjx-r89m-m7v4
- cvss_vector
- CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
- is_ghsa_only
- true
- ghsa_published
- 2026-07-14T20:52:00Z
- source_url
- https://github.com/advisories/GHSA-hgjx-r89m-m7v4
- ghsa_updated
- 2026-07-14T20:52:00Z
Related Entities (5)
VULNERABLE_TO (1)
AFFECTS (1)
HAS_WEAKNESS (2)
REPORTED_BY (1)
Explore deeper with Ninja Signal's threat intelligence graph