CSP "script-src 'self' 'unsafe-inline'" policy can be bypassed and inline Javascript payload executed, no violation reported
Categories
(Core :: DOM: Security, defect)
Tracking
()
People
(Reporter: wiseagent, Unassigned, NeedInfo)
References
(Blocks 1 open bug)
Details
(Keywords: reporter-external, Whiteboard: [reporter-external] [client-bounty-form] [verif?])
Attachments
(2 files)
##Firefox V80 Content-Security-Policy bypass
CSP "script-src 'self' 'unsafe-inline'" policy can be bypassed and inline Javascript payload executed, no violation reported
OS: Windows 7 - it can be reproduced in at least Windows 10.
Firefox version: Version 80(64-bit)
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0
###Step to reproduce the unsafe-eval bypass for "script-src 'self' 'unsafe-inline'" policy
- download the attached PoC_unsafe-eval-bypassed.Edge85.html
- hosting in any web server(I'm using nginx), the CSP policy is defined in the poc meta tag
- load the poc file and the result are printed in the console.
- sample results screen capture attached.
The PoC(PoC_unsafe-eval-bypassed.Edge85.html) has presented 10 cases where the CSP has been bypassed and 9 of them have no violation reported
Test case for expected CSP behaviour
TC 1: Expected behaviour - unsafe-eval violation reported
The following test cases observed no CSP voilats warning, payload getting executed. There is no CSP report on the violation sent back
TC 2: Unexpected behaviour - unsafe-eval violation is not reported, payload triggered
TC 3: eval undefined won't voilate CSP unsafe-eval
TC 4: eval null won't voilate CSP unsafe-eval
TC 5: eval NaN won't voilate CSP unsafe-eval
TC 6: eval an expression returns numeric value won't voilate CSP unsafe-eval
TC 7: eval an expression contains string and returns NaN won't voilate CSP unsafe-eval
TC 8: eval function expression won't voilate CSP unsafe-eval
TC 9: eval function pointer expecting args won't voilate CSP unsafe-eval
TC10: eval function pointer variable expecting args won't voilate CSP unsafe-eval
The following test case has payload triggered, but there is CSP violation reported
TC11: Function() payload triggered, should observed alert popup
Function(eval(alert("Function() payload triggered"))) voilation will be reported by CSP!
Summary - unsafe-eval can be bypassed and javascript code block executed by:
- Embeding it in a funcation call, as long as the function is rereturning undefined, null, NaN or numberic
- Embeding it in a expression, which contains a function call, as long as expression results in undefined, null, NaN or numeric
Observation and analysis:
Here is the highlight I hope will help speed up the triage and debugging process:
It appears to me that there is an implicited assumption that:
- It's harmful to eval any string values, including "blank", for obvious reasons
- (implicitly assumed) it's okay to eval() undefined, null, NaN or numeric as it's not a Javascript code block!
Updated•5 years ago
|
Updated•5 years ago
|
Comment 1•5 years ago
|
||
how does it make sense to restrict the use of eval() if you allow all other scripts with unsafe-inline? OK, I can think of one super contrived example that's not humanly realistic (guaranteed injection-free page, so you're not worried about basic XSS, only DOM XSS). I'm surprised the spec doesn't treat unsafe-inline as a superset of unsafe-eval.
Comment 2•5 years ago
|
||
The spec at 4.3. Integration with ECMAScript says
ECMAScript defines a HostEnsureCanCompileStrings() abstract operation which allows the host environment to block the compilation of strings into ECMAScript code. This document defines an implementation of that abstract operation thich examines the relevant CSP list to determine whether such compilation ought to be blocked.
So I can see that this might not be called (intentionally) in many of the cases you list as violations.
It appears to me that there is an implicited assumption that:
- It's harmful to eval any string values, including "blank", for obvious reasons
Yes, the explicit reason we worried about eval() is because of processing user (attacker) input--which is always a string--and accidentally turning it into code. Also same with "eval-like" functions such as setTimeout() where we don't block explicit function calls, but we do treat a string input as an eval call.
How do other browsers behave?
Comment 3•5 years ago
|
||
Looking at the ECMAScript spec, HostEnsureCanCompileStrings() it's used in the PerformEval step in section 18.2.1.1 Runtime Semantics: PerformEval ( x, callerRealm, strictCaller, direct ). We see step 2 "If Type(x) is not String, return x." comes before step 4, "Perform ? HostEnsureCanCompileStrings(callerRealm, evalRealm)" which is the CSP hook.
I believe our behavior is correct. In any case it does not appear to introduce a practical security worry (given unsafe-inline) so I'm un-hiding this bug in case we need to have a wider discussion hashing out the intended behavior of cross-referencing specs that can potentially get out of sync.
In your initial comment you said this meant you could bypass the protection by embedding the string in a function call that returns null, etc. For that to work that function itself would have to contain an eval() call on the string, which would be blocked.
Comment 4•5 years ago
•
|
||
I should note that I couldn't successfully unpack your POC -- my RAR utility thinks your archive is broken. Please just upload the .html file as a non-compressed attachment.
Simple text or image attachments save us from having to be paranoid and run everything through virus checkers. This attachment appears clean, at least: https://www.virustotal.com/gui/file/fe93385bce9e1454b5c3fdaaaea8939c3756cbbae76087a4432f3c4fbfff4ab4/detection
Comment 5•5 years ago
|
||
More success with a different archive program. Here it is
Comment 6•5 years ago
|
||
I see the same results on Chrome and Safari (as I'd expect, given the spec)
Updated•5 years ago
|
Updated•5 years ago
|
Updated•1 year ago
|
Description
•