You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[vulnerability][acl] ACL is skipped for super classes/objects
Root Cause
Chained super classes and objects are not tracked for ACL except for new C() and super()
Important Notes for Fix
Defined ACL policies for global objects/classes must also guard chained global objects/classes properly since Policy.defaultAcl() is applied only to untracked objects and classes
With Policy.globalAcl(), many formerly unrecognized global objects may be found and their access should be denied. ALL 3rd PARTY APIs NEED THEIR DEDICATED ACLs.
With Policy.globalAcl() and Policy.defaultAcl(), tracking the prototype chain is stopped once a named ACL (acl['name']) is found and applied in order to avoid redundant application of chained ACLs.
Reproducible Code Example
There are other ways of circumventing ACL for chained classes/objects
{classFakeClass{};letfakeObject=newFakeClass();Object.setPrototypeOf(Object.getPrototypeOf(fakeObject),window);// still (fakeObject instanceof FakeClass) === trueReflect.get(fakeObject,'caches');// Illegal invocation; not Permission Denied}
Fix
Policy.defaultAcl() ACL callback function
Applied to
acl[S_DEFAULT][S_DEFAULT] for
Non-global objects
Instances of non-global classes
acl.Function[S_PROTOTYPE][S_INSTANCE][S_DEFAULT] for
Stop tracking the chain if a named ACL is found and applied
TODO: Handling primitives properly
To be handled in a separate issue
Policy.globalAcl() ACL callback function for global objects with no dedicated ACL
Replacing Policy.avoidGlobalClone()
Note: Many formerly unrecognized global objects may be found and their access should be denied by Policy.globalAcl(). ALL 3rd PARTY APIs NEED THEIR DEDICATED ACLs.
The difference between Policy.globalAcl() and Policy.avoidGlobalClone() is similar to that between strict and targeted policy in SELinux
Policy.globalAcl() does not allow writing to global objects unless dedicated ACLs are defined
Policy.avoidGlobalClone() allow writing to no-dedicated ACL global objects if they are not clones of other global objects. Targeted objects with dedicated ACLs can deny writing access to them.
Applied to
acl[S_GLOBAL][S_DEFAULT] for
Global objects with no dedicated ACLs
acl[S_GLOBAL][S_PROTOTYPE][S_DEFAULT] for
Instances of global classes with no dedicated ACLs
acl[mainGlobalObjectName][S_DEFAULT] for
Global properties with no dedicatd ACLs in acl[mainGlobalObjectName]
TODO: When to apply acl[mainGlobalObjectName][S_DEFAULT] is to be cleared, as it is currently ambiguous.
name is empty in Permission Denied: Cannot access *** error if Policy.defaultAcl() is applied and permission denied
Status: Fixed
The effective target is now exposed to the root applyAcl() caller, i.e.,__hook__
Note: The normalizedArgs argument used for an object property collectionnormalizedArgs.result = [name, ...] Array object, which conveys the real object name that denied access, is created as an array literal ([element0, element1,...]) on each __hook__ call and is discarded just after the call to __hook__ returns and onThrow handles the exception from the permission error. Therefore, the normalizedArgs.result = [name, ...] object on each denied __hook__ call DOES NOT HAVE ANY SIDE EFFECTS on its following processes.
Delete normalizedArgs.result = [name, ...] property just in case
Status: Fixed in 0.4.0-alpha.10
Note: SRI (subresource integrity) in Chrome has a tricky bug that fails to verify certain seemingly unpredictable hash values, whose workaround is to insert a syntactically meaningless whitespace in a comment of the target file whose integrity value cannot be verified. In this failure case in no-hook-authorization.js after the fix for the used result property, a space character is appended in a comment, which is effective.
Compatibility with 0.4.0-alpha.8
Status: Under investigation
As ACL has become more comprehensive and more rigid, there can be some incompatibility issues
USER FEEDBACKS ARE CRITICAL TO SUCH ISSUES even if I try hard to keep maximum compatibility.
Notes for planning
I am going to call the current policy as "policy-v0"
"policy-v1" with completely new design will be sought
No milestones for now since removing vulnerabilities has higher priority than performance
[vulnerability][acl] ACL is skipped for super classes/objects
Root Cause
new C()andsuper()Important Notes for Fix
Policy.defaultAcl()is applied only to untracked objects and classesPolicy.globalAcl(), many formerly unrecognized global objects may be found and their access should be denied. ALL 3rd PARTY APIs NEED THEIR DEDICATED ACLs.Policy.globalAcl()andPolicy.defaultAcl(), tracking the prototype chain is stopped once a named ACL (acl['name']) is found and applied in order to avoid redundant application of chained ACLs.Reproducible Code Example
Fix
Policy.defaultAcl()ACL callback functionacl[S_DEFAULT][S_DEFAULT]foracl.Function[S_PROTOTYPE][S_INSTANCE][S_DEFAULT]for'r'(read) operations -> track chain if necessarythis.hasOwnProperty('property')-> access allowedsuper.hasOwnProperty('property') ->applyAcl(super, 'property')`!Reflect.has(this, 'property')-> property undefined -> access allowed'w'(write) operations -> access allowed as the operation is forthisthis.hasOwnProperty('property')-> access allowedsuper.hasOwnProperty('property')thisobjectthisobject's own property, which is harmless to super'x'(execute) operations -> track chain if necessarythis.hasOwnProperty('property')-> access allowedsuper.hasOwnProperty('property') ->applyAcl(super, 'property')`!Reflect.has(this, 'property')-> property undefined -> access allowed_globalMethods.get(this.property)->applyAcl(this.property)'R'(get descriptor) -> access allowed forthisobject'W'(define property) -> access allowed forthisobjectS_ALLproperty -> track chain ->applyAcl(super, S_ALL)Policy.globalAcl()ACL callback function for global objects with no dedicated ACLPolicy.avoidGlobalClone()Policy.globalAcl(). ALL 3rd PARTY APIs NEED THEIR DEDICATED ACLs.Policy.globalAcl()andPolicy.avoidGlobalClone()is similar to that between strict and targeted policy in SELinuxPolicy.globalAcl()does not allow writing to global objects unless dedicated ACLs are definedPolicy.avoidGlobalClone()allow writing to no-dedicated ACL global objects if they are not clones of other global objects. Targeted objects with dedicated ACLs can deny writing access to them.acl[S_GLOBAL][S_DEFAULT]foracl[S_GLOBAL][S_PROTOTYPE][S_DEFAULT]foracl[mainGlobalObjectName][S_DEFAULT]foracl[mainGlobalObjectName]acl[mainGlobalObjectName][S_DEFAULT]is to be cleared, as it is currently ambiguous.'r'(read) operations -> track chain if necessarythis.hasOwnProperty('property')-> access allowedsuper.hasOwnProperty('property') ->applyAcl(super, 'property')`!Reflect.has(this, 'property')-> property undefined -> access allowed'w'(write) operations -> instance access allowed; class access deniedthisno-acl global object -> access deniedthisno-acl global object -> access allowedthisno-acl global object -> access denied'x'(execute) operations -> track chain if necessarythis.hasOwnProperty('property')-> access allowedsuper.hasOwnProperty('property') ->applyAcl(super, 'property')`!Reflect.has(this, 'property')-> property undefined -> access allowed_globalMethods.get(this.property)->applyAcl(this.property)'R'(get descriptor) -> instance access allowed; class access deniedthisno-acl global object -> access deniedthisno-acl global object -> access allowedthisno-acl global object -> access denied'W'(define property) -> instance access allowed; class access deniedthisno-acl global object -> access deniedthisno-acl global object -> access allowedthisno-acl global object -> access deniedS_ALLproperty -> track chain ->applyAcl(super, S_ALL)Policy.patternAcl({ r, w : (name, prop) => true, x, R, W })ACL callback functionopTypePolicy.globalAcl()is applied for otheropTypesfirebase-auth.jsdefineswindow.closure_lm_{random number}property'@firebase_auth_closure_global_variable_writer': Policy.patternAcl({ w: (name, prop) => name === 'window' && typeof prop === 'string' && prop.startsWith('closure_') })acl[S_GLOBAL]['@firebase_auth_closure_global_variable_writer']acl[mainGlobalObjectName]['@firebase_auth_closure_global_variable_writer']detectName(target)utility function_globalObjects.get(target)acl.Window[S_PROTOTYPE]acl.Window[S_PROTOTYPE][S_INSTANCE]and chain it toaclhookBenchmark()to work with the current version__hook__aclas the default callback__hook__aclis highly recommended over__hook__with more overheadsIssues
Object.prototypemust be handled properlyacl[property]has to be applied instead ofacl.window[S_DEFAULT]forObject.assign(window, {property: value})detectName()?: Some prototype objects are mistreated as instance objects of their chain._globalObjects.get(EventTarget)returnsSet([ "EventTarget", "constructor" ])window -> Window.prototype -> WindowProperties.prototype -> EventTarget.prototype -> Object.prototype -> nullwindow.constructor = Window.prototype.constructor === Windownameis empty inPermission Denied: Cannot access ***error ifPolicy.defaultAcl()is applied and permission deniedapplyAcl()caller, i.e.,__hook__Note: The
normalizedArgsargument used for an object property collectionnormalizedArgs.result = [name, ...]Array object, which conveys the real object name that denied access, is created as an array literal ([element0, element1,...]) on each__hook__call and is discarded just after the call to__hook__returns andonThrowhandles the exception from the permission error. Therefore, thenormalizedArgs.result = [name, ...]object on each denied__hook__call DOES NOT HAVE ANY SIDE EFFECTS on its following processes.normalizedArgs.result = [name, ...]property just in caseno-hook-authorization.jsafter the fix for the used result property, a space character is appended in a comment, which is effective.Notes for planning
#70 Benchmarks with hookBenchmark()