Introduction
InvoicePlane is a self-hosted, open-source web application designed to manage quotes, invoices, clients, and payments. It is used by small businesses and freelancers as an invoicing and billing solution.
Issue
Multiple security vulnerabilities were identified in InvoicePlane that affect the version "1.6.3". These issues include unauthenticated file read, authenticated SQL injection, and authenticated arbitrary PHP file upload, which may lead to remote code execution (RCE).
At the time of discovery, InvoicePlane does not implement role separation: the only available user role is an administrator role. While this limits privilege escalation scenarios, the impact of authenticated vulnerabilities remains significant due to the high level of access granted by default.
Timeline
| Date | Description |
|---|---|
| 18/08/2025 | Reporting vulnerabilities through the GitHub project |
| 21/08/2025 | Response from the publisher and start of corrections |
| 01/12/2025 | v1.6.4-beta-1 fixes vulnerabilities |
| 06/01/2026 | CVE identifiers have been reserved |
Technical details
Unauthenticated File Read / CVE-2025-67083
An unauthenticated attacker can read files from the server through a directory traversal vulnerability. The extent of file access depends on the web server configuration and filesystem permissions.
The vulnerable endpoint is:
/guest/get/get_file/<filename>
public function get_file($filename): void
{
$filename = urldecode($filename);
if ( ! file_exists($this->targetPath . $filename)) {
$ref = isset($_SERVER['HTTP_REFERER']) ? ', Referer:' . $_SERVER['HTTP_REFERER'] : '';
$this->respond_message(404, 'upload_error_file_not_found', $this->targetPath . $filename . $ref);
}
$path_parts = pathinfo($this->targetPath . $filename);
$file_ext = mb_strtolower($path_parts['extension'] ?? '');
$ctype = $this->content_types[$file_ext] ?? $this->ctype_default;
$file_size = filesize($this->targetPath . $filename);
header('Expires: -1');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Type: ' . $ctype);
header('Content-Length: ' . $file_size);
readfile($this->targetPath . $filename);
}
The filename parameter is used without proper sanitization or validation, allowing traversal sequences (e.g. ../) to be injected. This enables an attacker to access arbitrary files on the server, subject to web server restrictions.
This vulnerability may allow attackers to read sensitive configuration files, logs, or other application data.
Authenticaded SQL injection / CVE-2025-67082
An authenticated user can inject arbitrary SQL commands via vulnerable POST parameters in the reporting functionality.
The issue occurs in the file Mdl_reports.php, where the minQuantity and maxQuantity parameters are directly embedded into an SQL query without proper sanitization or the use of prepared statements.
$this->db->where('
(
SELECT SUM(amounts.invoice_total) FROM ip_invoice_amounts amounts
WHERE amounts.invoice_id IN
(
SELECT inv.invoice_id FROM ip_invoices inv
WHERE inv.client_id=ip_clients.client_id
AND ' . $this->db->escape($from_date) . ' <= inv.invoice_date_created
AND ' . $this->db->escape($to_date) . ' >= inv.invoice_date_created
AND ' . $minQuantity . ' <=
(
SELECT SUM(amounts2.invoice_total) FROM ip_invoice_amounts amounts2
WHERE amounts2.invoice_id IN
(
SELECT inv2.invoice_id FROM ip_invoices inv2
WHERE inv2.client_id=ip_clients.client_id
AND ' . $this->db->escape($from_date) . ' <= inv2.invoice_date_created
AND ' . $this->db->escape($to_date) . ' >= inv2.invoice_date_created
)
)
)
) <>0');
Because these parameters are not safely handled, an attacker can manipulate the SQL query, potentially leading to:
- Disclosure of sensitive database information
- Modification or deletion of data
- Abuse of database functionality depending on permissions
Authenticated Arbitrary PHP File Upload / CVE-2025-67084
An authenticated user can upload arbitrary PHP files through the attachment upload functionality, ultimately leading to remote code execution.
In the upload handler Upload.php, a MIME type check is partially implemented:
$tempFile = $_FILES['file']['tmp_name'];
if (extension_loaded('fileinfo')) {
$this->validate_mime_type(mime_content_type($tempFile));
}
However, the function is based on an array containing the authorised content types, including text/plain. It is then possible to upload a PHP file while declaring a benign MIME type such as text/plain.
Once uploaded, these PHP files can be accessed and executed by the server, allowing an attacker to execute arbitrary code in the context of the web application.
