← Back to Feed CACHED · 2026-05-31 17:36:10 · CACHE_KEY tenable:106657
tenable:106657 · CWE-79 · Disclosed 2018-01-18

JQuery 1.x < 1.12.0 / 2.x < 2.2.0 XSS

jQuery cross-domain Ajax text/javascript auto-execution XSS

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

Like calling a third-party number and blindly trusting whoever picks up

The finding behind tenable:106657 maps to jQuery’s old habit of auto-executing text/javascript returned from a cross-domain Ajax request when developers used $.ajax() / $.get() without an explicit dataType. Tenable flags jQuery 1.x before 1.12.0 and 2.x before 2.2.0, because the jQuery 1.12.0 / 2.2.0 releases added a mitigation for this behavior. The broader CVE record, CVE-2015-9251, was later scoped as affecting jQuery before 3.0.0, which is why this plugin often under-describes the real version story.

Vendor MEDIUM is fair in a lab, but still a bit generous for enterprise patch triage. The bug is only reachable when the application actually makes a vulnerable cross-origin Ajax call to an attacker-controlled or compromised endpoint, and most sites simply do not do that in a way an external attacker can steer on demand. This is real, but it is heavily conditional, highly application-specific, and a terrible candidate for emergency patching across 10,000 hosts.

"This is a risky jQuery behavior, not a reliable internet-scale compromise path."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Find a jQuery page that makes cross-origin Ajax calls

The attacker first needs a page using vulnerable jQuery plus application code that issues $.ajax() / $.get() to a third-party origin without an explicit dataType. The library version alone is not enough; the application has to exercise the bad pattern. The relevant behavior is documented in jQuery issue #2432 and the later mitigation commit f60729f.
Conditions required:
  • Victim browses an application that ships vulnerable jQuery
  • The application makes a cross-domain Ajax request
  • The request omits an explicit dataType
Where this breaks in practice:
  • Many apps never make cross-origin jQuery Ajax calls at all
  • Modern apps often use fetch, back-end proxies, or explicit JSON APIs instead
  • Static library detection does not prove the dangerous code path exists
Detection/coverage: Version scanners like Nessus/RetireJS catch the library, not the vulnerable runtime call path. SAST/grep for $.ajax(, $.get(, and cross-domain URLs is better for applicability.
STEP 02

Gain control of the remote response

Next the attacker must control the third-party server, compromise it, abuse DNS/redirect behavior, or otherwise influence the response body and Content-Type. The original report demonstrates a malicious endpoint returning text/javascript, which jQuery then treated as executable content.
Conditions required:
  • Attacker controls or can tamper with the cross-origin endpoint
  • Browser can reach that endpoint from the victim context
Where this breaks in practice:
  • This usually requires compromise of a partner API, CDN-like endpoint, or app configuration weakness
  • TLS, pinned destinations, and allowlisted domains make this much less reachable in mature environments
  • If the third-party endpoint is stable and trusted, the attacker has no path
Detection/coverage: Network telemetry may show unexpected third-party requests, but few orgs instrument browser-side Ajax flows well enough to tie them back to this CVE.
STEP 03

Trigger jQuery script auto-execution

On vulnerable versions, jQuery could pass the returned body into jQuery.globalEval() even when the caller did not request script execution. The mitigation commit explicitly added a prefilter to disable auto-execution for crossDomain requests unless dataType: "script" is set.
Conditions required:
  • Returned response is treated as script-capable content
  • The request remains cross-domain in jQuery’s logic
Where this breaks in practice:
  • The exact behavior depends on request options and response headers
  • Applications already using dataType safely or sanitizing flows are not meaningfully exposed
Detection/coverage: Browser DevTools, CSP violation telemetry in some cases, and front-end security testing can expose this. Most infrastructure scanners cannot.
STEP 04

Abuse the victim session in-browser

If execution lands, impact is classic browser-context abuse: session riding, data theft visible to that user, DOM modification, or credential/token capture from the app context. The blast radius is the current user session, not direct server takeover.
Conditions required:
  • Victim must load the affected page and trigger the request
  • The application session has something worth stealing or abusing
Where this breaks in practice:
  • Impact stays inside the browser session unless chained further
  • HttpOnly cookies, strong CSP, and short-lived tokens can reduce practical payoff
Detection/coverage: RUM/browser telemetry or CSP reports can help; EDR on endpoints generally has poor visibility into front-end JavaScript misuse.
03 · Compensating Control

1
Inventory the code path — Within backlog hygiene timing for a LOW issue, confirm whether the app actually performs cross-origin jQuery Ajax without explicit dataType. This is the single fastest way to separate real exposure from scanner noise.
2
Eliminate browser-to-third-party Ajax where possible — Move third-party API calls server-side or through a same-origin proxy so the browser is not executing trust decisions on remote content. For this LOW verdict, fold this into normal engineering backlog rather than emergency change windows.
3
Set explicit dataType on legacy jQuery requests — Where legacy code must stay, force dataType: 'json' or another expected type so script auto-execution is not inferred from response headers. Do this during the next planned maintenance cycle.
4
Harden CSP — Use a stricter script-src and eliminate unnecessary execution allowances to reduce the payoff of browser-side script injection. CSP is not a perfect fix here, but it can reduce downstream abuse while you clean up old front-end code.
What doesn't work
  • A WAF does not reliably help because the bad decision happens inside legitimate browser-side JavaScript after the page loads.
  • Blindly trusting the library version finding does not prove exploitability; this CVE is highly dependent on application behavior.
  • EDR on user endpoints usually has poor visibility into a browser executing allowed JavaScript from an application origin.
04 · Intelligence Metadata

The supporting signals.

Mapped issueCVE-2015-9251; Tenable plugin 106657 specifically keys on the 1.12.0 / 2.2.0 mitigation milestone rather than the broader CVE scope.
In-the-wild statusNo CISA KEV listing and I found no authoritative evidence of active exploitation campaigns tied to this CVE in the sources reviewed.
PoC / weaponizationA public demo exists in jQuery issue #2432 and Packet Storm/RetireJS references are attached from NVD. That makes reproduction easy for testers, but not the same as broad offensive use.
EPSSCurrent public telemetry is elevated for a medium bug: Bitsight shows 18.01% probability over 30 days at the 95th percentile. Tenable’s CVE page also shows FIRST EPSS on the record, which confirms this CVE stays visible in exploit-likelihood feeds.
KEV statusNot present in CISA’s Known Exploited Vulnerabilities Catalog. No due date exists because it is not KEV-listed.
CVSS and meaningNVD scores it 6.1 / MEDIUM with CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N: network reachable, but user interaction is required and impact is browser-session level.
Affected versionsAuthoritative CVE language says jQuery before 3.0.0. The Tenable plugin title is narrower: 1.x < 1.12.0 / 2.x < 2.2.0.
Fixed versionsUpstream mitigation appears in 1.12.0 and 2.2.0 release notes, but Debian notes only 3.0 was fixed upstream for the full CVE because backporting was considered too invasive. Treat 3.0.0+ as the clean answer.
Exposure dataBitsight Groma currently shows about 2,023,513 observations tied to this CVE over the prior 30 days, with 39.02% of observations in the US. That says the library is common, not that each instance is exploitable.
Timeline / attributionThe dangerous behavior was publicly discussed in jQuery issue #2432 on 2015-06-26; the mitigation shipped in the 2016-01-08 jQuery 1.12.0 / 2.2.0 release; NVD published the CVE on 2018-01-18. The GitHub issue was opened by homakov.

Sources.

  1. Tenable plugin 106657
  2. Official jQuery 1.12.0 / 2.2.0 release notes
  3. jQuery issue #2432
  4. jQuery mitigation commit f60729f
  5. NVD CVE-2015-9251
  6. CISA Known Exploited Vulnerabilities Catalog
  7. Bitsight CVE-2015-9251 observation footprint
  8. Debian security tracker for CVE-2015-9251
05 · The Call

Final Verdict
↓ DOWNGRADED to LOW (3.4/10)

Why this verdict

  • Downgrade for prerequisite chain: the attacker does not get value from the library version alone; they need a live cross-origin $.ajax() / $.get() call with no explicit dataType.
  • Downgrade for attacker position: requiring control of a third-party response path implies either compromise of an external service, misrouting, or application-specific endpoint abuse. That is much narrower than unauthenticated direct exploitation.
  • Downgrade for blast radius: successful exploitation lands in the victim browser session, not as direct server-side code execution or broad host compromise.
  • Slight upward pressure: public demos exist and EPSS is higher than many medium-severity bugs, so this is not noise-only.

Why not higher?

There is no direct pre-auth server compromise path here. The exploit chain depends on a fragile combination of front-end coding pattern, cross-origin request behavior, and attacker control of the response source; that is too much real-world friction for MEDIUM-or-higher patch urgency across a large fleet.

Why not lower?

This is still a real client-side execution issue with public repro steps, and some legacy apps absolutely do make unsafe jQuery cross-domain calls. If one of your internet-facing portals relies on this pattern, the result can still be credential theft or session abuse for active users.

06 · Verification

Crowdsourced verification payload.

Run this on the target web server, build artifact directory, or unpacked static-content repo that actually hosts front-end assets. Invoke it as python3 verify_jquery_106657.py /var/www/html or point it at your CI artifact root; no admin rights are required beyond read access to the files.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
# verify_jquery_106657.py
# Purpose: validate whether local jQuery assets match Tenable plugin 106657 conditions.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN/usage error

import os
import re
import sys
from typing import Optional, Tuple

VERSION_RE = re.compile(r"jQuery v?(\d+)\.(\d+)\.(\d+)", re.I)
FILENAME_RE = re.compile(r"jquery(?:[-.]min)?[-.]?(\d+)\.(\d+)\.(\d+)", re.I)
JS_EXT = (".js", ".jsp", ".html", ".htm", ".cshtml", ".php", ".twig")


def parse_version(text: str) -> Optional[Tuple[int, int, int]]:
    m = VERSION_RE.search(text)
    if m:
        return tuple(map(int, m.groups()))
    m = FILENAME_RE.search(text)
    if m:
        return tuple(map(int, m.groups()))
    return None


def classify(ver: Tuple[int, int, int]) -> str:
    major, minor, patch = ver
    # Tenable plugin 106657 logic
    if major == 1 and ver < (1, 12, 0):
        return "VULNERABLE"
    if major == 2 and ver < (2, 2, 0):
        return "VULNERABLE"

    # Important nuance: broader CVE-2015-9251 language says < 3.0.0.
    # Versions 1.12.x and 2.2.x contain the mitigation Tenable is looking for,
    # but may still be treated as affected by broader CVE records.
    if major in (1, 2) and ver >= ((1, 12, 0) if major == 1 else (2, 2, 0)):
        return "UNKNOWN"

    if major >= 3:
        return "PATCHED"

    return "UNKNOWN"


def inspect_file(path: str) -> Optional[Tuple[Tuple[int, int, int], str]]:
    # First try filename
    ver = parse_version(os.path.basename(path))
    if ver:
        return ver, classify(ver)

    # Then read a small chunk from the file body for the jQuery banner
    try:
        with open(path, "r", encoding="utf-8", errors="ignore") as f:
            chunk = f.read(65536)
        ver = parse_version(chunk)
        if ver:
            return ver, classify(ver)
    except Exception:
        return None
    return None


def main() -> int:
    if len(sys.argv) != 2:
        print("UNKNOWN - usage: python3 verify_jquery_106657.py <webroot-or-artifact-dir>")
        return 2

    root = sys.argv[1]
    if not os.path.exists(root):
        print(f"UNKNOWN - path does not exist: {root}")
        return 2

    findings = []
    for base, _, files in os.walk(root):
        for name in files:
            if not name.lower().endswith(JS_EXT):
                continue
            path = os.path.join(base, name)
            result = inspect_file(path)
            if result:
                ver, state = result
                findings.append((path, ver, state))

    if not findings:
        print("UNKNOWN - no identifiable jQuery assets found")
        return 2

    vulnerable = [f for f in findings if f[2] == "VULNERABLE"]
    unknown = [f for f in findings if f[2] == "UNKNOWN"]
    patched = [f for f in findings if f[2] == "PATCHED"]

    if vulnerable:
        path, ver, _ = vulnerable[0]
        print(f"VULNERABLE - found jQuery {ver[0]}.{ver[1]}.{ver[2]} at {path}")
        return 1

    if unknown:
        sample = unknown[0]
        path, ver, _ = sample
        print(f"UNKNOWN - found jQuery {ver[0]}.{ver[1]}.{ver[2]} at {path}; Tenable plugin condition may be mitigated, but broader CVE-2015-9251 scope should be reviewed")
        return 2

    path, ver, _ = patched[0]
    print(f"PATCHED - found jQuery {ver[0]}.{ver[1]}.{ver[2]} at {path}")
    return 0


if __name__ == "__main__":
    sys.exit(main())