← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
tenable:56818 · CWE-352 · Disclosed 2011-11-17

CGI Generic Cross-Site Request Forgery Detection

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This finding is a smoke alarm tripped by seeing a stove, not by seeing a fire

Tenable plugin 56818 is not tied to a named product, version range, or CVE. It fires when Nessus crawls HTML forms and sees requests that *appear* to lack random anti-CSRF tokens; Tenable explicitly says the scanner did not exploit anything, cannot tell whether the action is sensitive, and requires manual review of the application code and behavior.

Vendor MEDIUM overstates operational urgency for patch teams. In the real world, exploitation already requires an authenticated victim, a reachable state-changing action, browser cookie behavior that still permits the request, and an application that lacks compensating checks such as framework CSRF middleware, Origin / Referer validation, SameSite cookies, or user-interaction gates. That makes this a weak, compliance-oriented signal with a high false-positive rate, not a patch-now event.

"This is a heuristic form check, not a proven bug; treat it like appsec triage, not emergency patching."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Find a logged-in victim path

The attacker first needs a user who is already authenticated to the target web app and who can reach a state-changing function. Typical delivery is phishing, chat, or a malicious site that the victim visits while their session is active. Tooling is trivial: any browser-hosted HTML form or JavaScript can be used as the launcher.
Conditions required:
  • A victim must be logged in to the target app
  • The victim must have privileges for a meaningful action
  • The target function must be reachable from the victim browser
Where this breaks in practice:
  • No authenticated victim means no exploit
  • Low-privilege users often cannot trigger high-impact actions
  • Session timeouts, re-auth prompts, and MFA step-up checks break the chain
Detection/coverage: Nessus cannot validate this prerequisite. Email security, browser isolation, secure web gateways, and phishing-resistant MFA reduce reachability but are outside plugin coverage.
STEP 02

Forge a cross-site request

The attacker crafts a request that matches the target endpoint, usually a POST form or a top-level navigation. Common weaponization is a hidden HTML form with auto-submit JavaScript or a lure button on an attacker-controlled page.
Conditions required:
  • The app must expose a state-changing endpoint via browser-reachable requests
  • The attacker must know the parameter names and request shape
Where this breaks in practice:
  • Modern frameworks often add CSRF tokens by default
  • Unsafe actions behind XHR-only APIs often require custom headers that a simple cross-site form cannot send
  • Many apps no longer use legacy browser-submittable patterns for admin actions
Detection/coverage: This plugin only infers absence of token-like fields in crawled forms. It does not prove the endpoint accepts the forged request.
STEP 03

Get the browser to carry authentication

For CSRF to land, the browser must attach session material on the forged request. That usually means a cookie-based session that is allowed on cross-site traffic, or a very narrow top-level navigation case under SameSite=Lax behavior.
Conditions required:
  • The app must rely on ambient browser credentials such as cookies
  • Cookie attributes and browser policy must still allow the request
Where this breaks in practice:
  • SameSite cookie behavior blocks many classic CSRF patterns
  • APIs using bearer tokens in headers are not trivially CSRFable from a foreign site
  • Secure cookies plus HTTPS-only paths narrow useful attack surface
Detection/coverage: Nessus plugin 56818 does not model the target's actual cookie attributes or browser-specific behavior during exploitation.
STEP 04

Land a state-changing action

The target endpoint must accept the request without token validation, origin checking, referer validation, secondary confirmation, or re-authentication. Impact is bounded by whatever the victim account can do, from benign preference changes to materially sensitive administrative actions.
Conditions required:
  • No effective server-side CSRF defense on the target action
  • The action must actually mutate state or expose a sensitive side effect
Where this breaks in practice:
  • Tenable states it cannot identify whether the action is sensitive
  • Read-only forms and harmless workflows inflate findings without real impact
  • User-confirmation dialogs, anti-automation, and transaction signing stop the last mile
Detection/coverage: This is the decisive gap: the plugin reports a *potential* weakness, not a verified exploit path. Manual app testing or code review is required.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo product-specific exploitation evidence is attached to plugin 56818. This is a generic heuristic finding, not a tracked exploited CVE.
Proof-of-concept availabilityGeneric CSRF PoCs are trivial to build with an HTML form or JavaScript, but there is no product-specific exploit package because the plugin does not identify a concrete vulnerable product.
EPSSNot applicable. EPSS is CVE-based, and this Tenable plugin is not mapped to a CVE.
KEV statusNot applicable. CISA KEV tracks CVEs; plugin 56818 is a generic detection with no CVE identifier.
Vendor severity / scoreTenable labels it MEDIUM, but no vendor CVSS score or vector is published on the plugin page.
CVSS interpretationNo CVSS vector is assigned. That is a tell: this is closer to a compliance/control gap heuristic than a confirmed remotely exploitable software flaw.
Affected versionsNo affected product or version range exists. Any web application page with crawled HTML forms may match if Tenable sees no obvious random token.
Fixed versionsNo patched version exists because this is not tied to a vendor advisory. Remediation is application-level: framework CSRF middleware, Origin / Referer validation, SameSite cookies, or explicit transaction confirmation.
Scanning / exposure dataShodan/Censys/FOFA-style internet counts are not meaningful here. The finding is not fingerprintable as a product exposure and Nessus itself says it cannot identify sensitive actions.
Disclosure / sourcePlugin published 2011-11-17, updated 2021-01-19, and attributed to Tenable Nessus research logic under file pci_dss_potential_xsrf.nasl.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to LOW (2.9/10)

The decisive downgrade factor is that this is not a verified vulnerability; it is a generic crawler heuristic that only notices forms without obvious anti-CSRF tokens. Every meaningful exploitation step depends on additional conditions outside the plugin's proof, especially an authenticated victim, a truly state-changing action, and the absence of modern browser and framework defenses.

HIGH Downgrade from vendor `MEDIUM` to operational `LOW`
MEDIUM Any single flagged endpoint being truly exploitable

Why this verdict

  • Not a proven bug: Tenable explicitly says it did not exploit the issue and cannot determine whether the form performs a sensitive action.
  • Attacker position is post-user-context: exploitation requires an already authenticated victim and therefore assumes a successful social-engineering or browsing-lure stage before the bug matters.
  • Reachability is narrowed by modern defaults: SameSite cookie handling, framework CSRF middleware, custom-header APIs, Origin checks, and re-auth flows routinely break classic CSRF chains.
  • No product/version scope: there is no vendor patch, no affected version range, and no blast-radius estimate suitable for enterprise patch operations.

Why not higher?

A higher severity would require either verified exploitability on a sensitive action or strong evidence of active abuse against a well-scoped product population. This finding has neither. It is a pattern match on forms, with no proof that the request succeeds, no proof that the action matters, and no evidence of exploitation at scale.

Why not lower?

I am not calling it IGNORE because a manually validated CSRF on an administrative workflow can still be serious, especially in line-of-business apps with cookie-based auth. The right posture is to remove it from emergency patch queues but keep it in appsec validation and engineering backlog triage.

05 · Compensating Control

What to do — in priority order.

  1. Validate one representative hit manually — Use a browser and proxy to test whether the flagged form performs a state-changing action without a CSRF token, Origin / Referer check, or secondary confirmation. For a LOW verdict there is no emergency clock; do this as backlog hygiene in the next normal appsec review cycle so you can close noise fast or open a real defect with evidence.
  2. Enforce framework-native CSRF defenses — Prefer built-in middleware over custom token logic so protections apply consistently across forms and endpoints. For LOW, roll this into the normal engineering release train rather than treating it as incident-driven work.
  3. Set session cookies defensively — Use SameSite=Lax or Strict where the workflow permits, plus Secure and HttpOnly, to reduce ambient credential leakage on cross-site requests. This is a low-urgency hardening task and belongs in standard platform baseline work.
  4. Validate Origin or Referer on unsafe methods — For POST, PUT, PATCH, and DELETE, reject cross-site origins unless explicitly expected. This is a cheap compensating layer when token coverage is incomplete and should be folded into normal web platform standards.
What doesn't work
  • WAF-only protection is not enough because CSRF requests often look like legitimate user traffic and lack exploit signatures.
  • MFA at login does not stop a forged in-session request once the victim is already authenticated.
  • Hiding the endpoint URL does not help; attackers can learn request shapes from the browser, source, or normal user workflows.
06 · Verification

Crowdsourced verification payload.

Run this from an auditor workstation against a *representative authenticated page* that contains the flagged form, not from the server itself. Invoke it as python3 csrf_check.py https://app.example.com/admin/settings --cookie "SESSIONID=abc123"; no privileged OS access is needed, but you may need a valid low-risk test account cookie to reach the workflow Nessus flagged.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
# Heuristic verifier for Tenable plugin 56818-style CSRF findings.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN, 3=usage/error

import argparse
import re
import sys
from html.parser import HTMLParser
from urllib.parse import urljoin

try:
    import requests
except Exception:
    print('UNKNOWN - requests module not installed')
    sys.exit(2)

TOKEN_PATTERNS = [
    re.compile(p, re.I) for p in [
        r'csrf', r'xsrf', r'authenticity', r'nonce', r'_token', r'requestverificationtoken'
    ]
]
UNSAFE_METHODS = {'post', 'put', 'patch', 'delete'}

class FormParser(HTMLParser):
    def __init__(self):
        super().__init__()
        self.forms = []
        self._current = None

    def handle_starttag(self, tag, attrs):
        attrs = dict(attrs)
        if tag.lower() == 'form':
            self._current = {
                'method': attrs.get('method', 'get').lower(),
                'action': attrs.get('action', ''),
                'inputs': []
            }
        elif tag.lower() == 'input' and self._current is not None:
            self._current['inputs'].append({
                'type': attrs.get('type', 'text').lower(),
                'name': attrs.get('name', ''),
                'id': attrs.get('id', ''),
                'value': attrs.get('value', '')
            })

    def handle_endtag(self, tag):
        if tag.lower() == 'form' and self._current is not None:
            self.forms.append(self._current)
            self._current = None


def looks_like_token(name_or_id, value):
    s = f"{name_or_id} {value}".strip()
    return any(p.search(s) for p in TOKEN_PATTERNS)


def get_cookie_samesite_flags(resp):
    raw = []
    try:
        raw = resp.raw.headers.get_all('Set-Cookie') or []
    except Exception:
        header = resp.headers.get('Set-Cookie')
        if header:
            raw = [header]
    flags = []
    for c in raw:
        m = re.search(r'(?i)\bSameSite\s*=\s*([^;]+)', c)
        flags.append(m.group(1).strip() if m else 'ABSENT')
    return flags


def main():
    ap = argparse.ArgumentParser(description='Heuristic CSRF checker for a single page')
    ap.add_argument('url', help='Target URL to inspect')
    ap.add_argument('--cookie', default='', help='Optional Cookie header, e.g. "SESSIONID=abc123; other=value"')
    ap.add_argument('--timeout', type=int, default=15, help='HTTP timeout in seconds')
    args = ap.parse_args()

    sess = requests.Session()
    headers = {'User-Agent': 'noisgate-csrf-check/1.0'}
    if args.cookie:
        headers['Cookie'] = args.cookie

    try:
        resp = sess.get(args.url, headers=headers, timeout=args.timeout, allow_redirects=True, verify=True)
    except Exception as e:
        print(f'UNKNOWN - request failed: {e}')
        sys.exit(2)

    ctype = resp.headers.get('Content-Type', '')
    if 'html' not in ctype.lower() and '<form' not in resp.text.lower():
        print('UNKNOWN - target did not return HTML form content')
        sys.exit(2)

    parser = FormParser()
    parser.feed(resp.text)
    forms = parser.forms
    if not forms:
        print('UNKNOWN - no forms found on supplied page')
        sys.exit(2)

    samesite = get_cookie_samesite_flags(resp)
    all_have_protection = True
    found_suspicious = []

    for form in forms:
        method = form['method'] or 'get'
        action = urljoin(resp.url, form['action'] or resp.url)
        token_found = False
        for inp in form['inputs']:
            if inp['type'] == 'hidden' and (looks_like_token(inp['name'], inp['value']) or looks_like_token(inp['id'], inp['value'])):
                token_found = True
                break

        if method in UNSAFE_METHODS:
            if not token_found:
                all_have_protection = False
                found_suspicious.append(f'{method.upper()} {action} (no obvious token)')
        elif method == 'get':
            # GET forms are only suspicious if they appear action-oriented.
            joined = ' '.join([inp.get('name', '') for inp in form['inputs']]).lower() + ' ' + action.lower()
            if any(x in joined for x in ['delete', 'remove', 'update', 'save', 'create', 'admin', 'password', 'role', 'user']):
                all_have_protection = False
                found_suspicious.append(f'GET {action} (appears state-changing)')

    # Decision logic:
    # VULNERABLE: unsafe/state-changing-looking form with no obvious token AND cookie SameSite absent/None or no cookies seen.
    # PATCHED: all unsafe forms have obvious token indicators OR no unsafe/state-changing-looking forms are present and cookies are restrictive.
    # UNKNOWN: mixed/inconclusive.
    restrictive_cookie = any(str(x).lower() in ('lax', 'strict') for x in samesite)
    permissive_cookie = any(str(x).lower() in ('none', 'absent') for x in samesite) or len(samesite) == 0

    if found_suspicious and permissive_cookie:
        print('VULNERABLE - suspicious forms found and session cookie policy appears permissive: ' + '; '.join(found_suspicious))
        sys.exit(1)

    if all_have_protection:
        print('PATCHED - all unsafe forms found on this page have obvious anti-CSRF token indicators')
        sys.exit(0)

    if found_suspicious and restrictive_cookie:
        print('UNKNOWN - suspicious forms found, but cookie SameSite appears restrictive; manual proxy test still required: ' + '; '.join(found_suspicious))
        sys.exit(2)

    print('UNKNOWN - inconclusive heuristic result; perform authenticated proxy-based validation')
    sys.exit(2)

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print('UNKNOWN - interrupted')
        sys.exit(2)
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning, do not send this to the infrastructure patch queue as if it were a verified software vuln. Tag plugin 56818 findings as appsec validation items, sample a few high-value authenticated workflows first, and either close them as non-actionable noise or convert them into real engineering defects with evidence; for a LOW verdict there is noisgate mitigation SLA and noisgate remediation SLA beyond backlog hygiene, so document the downgrade rationale now and handle confirmed issues in the normal web-platform hardening cycle.

Sources

  1. Tenable Nessus Plugin 56818
  2. Tenable Web Application Tests Settings dependents
  3. Tenable Nessus Assessment Settings
  4. Tenable Preconfigured Assessment Settings
  5. OWASP CSRF Prevention Cheat Sheet
  6. MITRE CWE-352
  7. MDN Set-Cookie / SameSite
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.