# CVE-2025-68147: Stored Cross-Site Scripting (XSS) in OpenSourcePOS

| Metadata | Details |
| --- | --- |
| **CVE ID** | **[CVE-2025-68147](https://cve.mitre.org/cgi-bin/cvename.cgi%3Fname%3DCVE-2025-68147)** |
| **Severity** | **High** `CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:H/I:H/A:N` |
| **Vulnerability Type** | Stored Cross-Site Scripting (CWE-79) |
| **Affected Versions** | OpenSourcePOS v3.4.0, v3.4.1 |
| **Patched Version** | **v3.4.2** |
| **Vulnerable Component** | Store Configuration Module (`Return Policy` field) |
| **Reporter** | [Aditya Singh (Nixon-H)](https://github.com/Nixon-H) |

---

## 📝 Executive Summary

A **Stored Cross-Site Scripting (XSS)** vulnerability was discovered in the **Store Configuration** module of OpenSourcePOS. The application failed to properly sanitize user-supplied input in the "Return Policy" field before storing it in the `ospos_app_config` database table.

This flaw allowed an authenticated attacker with configuration privileges (or an attacker exploiting a separate CSRF chain) to inject arbitrary JavaScript payloads. Because the "Return Policy" is dynamically rendered on every sales receipt, the injected payload executes automatically in the browser of **any user**—including low-privileged cashiers, other administrators, or customers—whenever a receipt is generated or viewed.

---

## 🕵️‍♂️ Technical Root Cause Analysis

### 1. The Vulnerable Logic

The vulnerability resides in the receipt view template: `app/Views/sales/receipt_default.php`.

The application retrieves the "Return Policy" string from the global configuration array (`$this->config['return_policy']`) and prepares it for display.

* **The Mistake:** The developers used the native PHP function `nl2br()` to convert newlines to HTML line breaks (`<br>`).
* **The Failure:** `nl2br()` **does not sanitize** HTML special characters. It leaves tags like `<script>`, `<iframe>`, and `onload` attributes completely intact.

**Vulnerable Code (Before Patch):**

```php
<div id="sale_return_policy">
    <?php echo nl2br($this->config['return_policy']); ?>
</div>

```

### 2. Database Persistence

When an admin saves the configuration, the payload is stored **raw** in the database.

* **Table:** `ospos_app_config`
* **Key:** `return_policy`
* **Value:** `Policy Text... <script>alert('XSS')</script>`

Because there is no input sanitization on the controller side (`Config.php`) and no output escaping on the view side (`receipt_default.php`), the application is vulnerable to Stored XSS.

---

## 💥 Proof of Concept (PoC)

### Attack Vector

The attack targets the **Store Configuration** panel but impacts the **Sales/Receipt** module.

1. **Entry Point:** `http://[TARGET]/config` (POST request to save config)
2. **Execution Point:** `http://[TARGET]/sales/receipt/[SALE_ID]`

### Step-by-Step Exploitation

**Step 1: The Injection**
We logged in as an Administrator and navigated to **Store Configuration** -> **General**. In the **"Return Policy"** text area, we injected the following specific payload:

```html
Standard Return Policy: No Refunds.
<script>alert('XSS_BY_NIXON_SUCCESSFUL')</script>

```

**Step 2: Persistence**
Upon clicking "Submit", the application sent a POST request to `/config/save`. The payload was successfully committed to the database.

**Step 3: The Trigger**
To verify the impact on other users:

1. We logged out and logged back in as a **Cashier** (low-privilege account).
2. We navigated to the **Sales** module.
3. We added a test item to the cart and clicked **"Complete Sale"**.
4. **Result:** The application redirects to the receipt view (e.g., `http://localhost/sales/receipt/1`). The browser parses the `return_policy` div, encounters the `<script>` tag, and immediately executes the JavaScript.

**Observed Result:**
A browser alert box appeared with the message: **`XSS_BY_NIXON_SUCCESSFUL`**.

### 📷 Media Proof

**Screenshot 1: [The Alert Trigger](https://github.com/Nixon-H/CVE-2025-68147-OSPOS-Stored-XSS/blob/main/POC/Alert_Trigger.png)**

**Screenshot 2: [Payload in Configuration](https://github.com/Nixon-H/CVE-2025-68147-OSPOS-Stored-XSS/blob/main/POC/Payload_in_Configuration.png)**

**🎥 Video Demonstration:**
[Click to Download / Watch the PoC Video](https://github.com/Nixon-H/CVE-2025-68147-OSPOS-Stored-XSS/raw/refs/heads/main/POC/STORED_XSS_POC.mp4)



---

## ⚠️ Impact Scenarios

This is a **Scope Changed (S:C)** vulnerability because the attack is stored on the server but executes in the victim's browser context.

1. **Privilege Escalation & Account Takeover:**
* **Scenario:** An attacker injects a JavaScript payload designed to steal session cookies.
* **Execution:** A Super Administrator views a past receipt (e.g., `/sales/receipt/105`). The script executes silently, sending `document.cookie` (containing the `ospos_session` ID) to the attacker's server.
* **Impact:** The attacker hijacks the Super Admin's session, gaining full control over the system without needing a password.


2. **Wormable Admin Creation:**
* **Scenario:** The payload is a script that uses AJAX (XMLHttpRequest) to perform administrative actions.
* **Execution:** When a logged-in Admin views the receipt, the script forces their browser to send a hidden POST request to `/employees/save`.
* **Impact:** A new Administrator account (e.g., `hacker / password123`) is created instantly in the background. The victim sees nothing but the receipt, while the attacker gains a permanent backdoor.


3. **Defacement & Phishing:**
* **Scenario:** The attacker modifies the DOM to alter the receipt's financial information.
* **Execution:** The "Return Policy" script rewrites the footer or payment instructions.
* **Impact:** Receipts display fraudulent bank details or crypto wallet addresses ("Please transfer payment to..."), leading to financial fraud against customers or the business.



---

## 🛡️ Remediation

The vulnerability was fixed in **OpenSourcePOS v3.4.2**.

The maintainer applied a patch that implements **Context-Aware Output Encoding**. The configuration value is now wrapped in CodeIgniter's global `esc()` helper function before being passed to `nl2br()`.

**The Patch (Commit `22297a`):**

```php
<div id="sale_return_policy">
    <?php echo nl2br(esc($this->config['return_policy'])); ?>
</div>

```

**Verification:**
After upgrading to v3.4.2, the same payload is rendered as harmless text:

> `Standard Return Policy: No Refunds. &lt;script&gt;alert('XSS_BY_NIXON_SUCCESSFUL')&lt;/script&gt;`

---

## 📅 Disclosure Timeline

* **2025-12-12:** Vulnerability discovered by **Aditya Singh (Nixon-H)** during a manual security audit.
* **2025-12-13:** Responsible disclosure report sent to maintainer (**Jeroen Peelaerts**) via email/GitHub.
* **2025-12-14:** Vulnerability confirmed by maintainer. **CVE-2025-68147** reserved.
* **2025-12-14:** Patch developed (Commit `22297a`) and verified by researcher.
* **2025-12-19:** Patch released in version **3.4.2**.
* **2025-12-19:** Public disclosure via GitHub Security Advisory.

---

### 🔗 References

* **Advisory:** [GHSA-xgr7-7pvw-fpmh](https://github.com/opensourcepos/opensourcepos/security/advisories/GHSA-xgr7-7pvw-fpmh)
* **Patch:** [Commit 22297a](https://github.com/opensourcepos/opensourcepos/commit/22297a)
* **Reporter:** [Aditya Singh (Nixon-H)](https://github.com/Nixon-H)
