Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit b39e1d4

Browse files
chirayukbtford
authored andcommitted
fix($parse): disallow passing Function to Array.sort
Fix the following exploit: hasOwnProperty.constructor.prototype.valueOf = valueOf.call; ["a", "alert(1)"].sort(hasOwnProperty.constructor); The exploit: • 1. Array.sort takes a comparison function and passes it 2 parameters to compare. 2. It then calls .valueOf() if the result is not a primitive. • The Function object conveniently accepts two string arguments so we can use this to construct a function. However, this doesn't do much unless we can execute it. • We set the valueOf function on Function.prototype to Function.prototype.call. This causes the function that we constructed to be executed when sort calls .valueOf() on the result of the comparison. The fix is in two parts. • Disallow passing unsafe objects to function calls as parameters. • Do not traverse the Function object when setting a path.
1 parent 5061d2c commit b39e1d4

File tree

2 files changed

+41
-3
lines changed

2 files changed

+41
-3
lines changed

src/ng/parse.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,7 @@ Parser.prototype = {
753753
var context = contextGetter ? contextGetter(scope, locals) : scope;
754754

755755
for (var i = 0; i < argsFn.length; i++) {
756-
args.push(argsFn[i](scope, locals));
756+
args.push(ensureSafeObject(argsFn[i](scope, locals), parser.text));
757757
}
758758
var fnPtr = fn(scope, locals, context) || noop;
759759

@@ -841,13 +841,15 @@ Parser.prototype = {
841841
//////////////////////////////////////////////////
842842

843843
function setter(obj, path, setValue, fullExp, options) {
844+
ensureSafeObject(obj, fullExp);
845+
844846
//needed?
845847
options = options || {};
846848

847849
var element = path.split('.'), key;
848850
for (var i = 0; element.length > 1; i++) {
849851
key = ensureSafeMemberName(element.shift(), fullExp);
850-
var propertyObj = obj[key];
852+
var propertyObj = ensureSafeObject(obj[key], fullExp);
851853
if (!propertyObj) {
852854
propertyObj = {};
853855
obj[key] = propertyObj;
@@ -867,7 +869,6 @@ function setter(obj, path, setValue, fullExp, options) {
867869
}
868870
}
869871
key = ensureSafeMemberName(element.shift(), fullExp);
870-
ensureSafeObject(obj, fullExp);
871872
ensureSafeObject(obj[key], fullExp);
872873
obj[key] = setValue;
873874
return setValue;

test/ng/parseSpec.js

+37
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,43 @@ describe('parser', function() {
10061006
expect(scope.$eval('fn().anotherFn()')).toBe(true);
10071007
});
10081008

1009+
it('should disallow traversing the Function object in a setter: E02', function() {
1010+
expect(function() {
1011+
// This expression by itself isn't dangerous. However, one can use this to
1012+
// automatically call an object (e.g. a Function object) when it is automatically
1013+
// toString'd/valueOf'd by setting the RHS to Function.prototype.call.
1014+
scope.$eval('hasOwnProperty.constructor.prototype.valueOf = 1');
1015+
}).toThrowMinErr(
1016+
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
1017+
'Expression: hasOwnProperty.constructor.prototype.valueOf = 1');
1018+
});
1019+
1020+
it('should disallow passing the Function object as a parameter: E03', function() {
1021+
expect(function() {
1022+
// This expression constructs a function but does not execute it. It does lead the
1023+
// way to execute it if one can get the toString/valueOf of it to call the function.
1024+
scope.$eval('["a", "alert(1)"].sort(hasOwnProperty.constructor)');
1025+
}).toThrow();
1026+
});
1027+
1028+
it('should prevent exploit E01', function() {
1029+
// This is a tracking exploit. The two individual tests, it('should … : E02') and
1030+
// it('should … : E03') test for two parts to block this exploit. This exploit works
1031+
// as follows:
1032+
//
1033+
// • Array.sort takes a comparison function and passes it 2 parameters to compare. If
1034+
// the result is non-primitive, sort then invokes valueOf() on the result.
1035+
// • The Function object conveniently accepts two string arguments so we can use this
1036+
// to construct a function. However, this doesn't do much unless we can execute it.
1037+
// • We set the valueOf property on Function.prototype to Function.prototype.call.
1038+
// This causes the function that we constructed to be executed when sort calls
1039+
// .valueOf() on the result of the comparison.
1040+
expect(function() {
1041+
scope.$eval('' +
1042+
'hasOwnProperty.constructor.prototype.valueOf=valueOf.call;' +
1043+
'["a","alert(1)"].sort(hasOwnProperty.constructor)');
1044+
}).toThrow();
1045+
});
10091046

10101047
it('should call the function once when it is part of the context', function() {
10111048
var count = 0;

0 commit comments

Comments
 (0)