Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: insert scripts on Firefox is not executed on Slack (Bypass CSP enforced using HTML meta tags) #549

Closed
1 of 4 tasks
SethFalco opened this issue Apr 18, 2023 · 12 comments

Comments

@SethFalco
Copy link

SethFalco commented Apr 18, 2023

Describe your issue?

I'm using the ← Insert Scripts rule to inject JavaScript into Slack on Firefox.

In Requestly I can see that the conditions were met, and that the script was injected. I can also see the script in the <head> of the document via Inspect Element. Despite scattering console.log all over the script, none of the logs are printed under the Console tab.

This script works exactly as intended on Chromium, and I am able to inject scripts on other sites like Google on Firefox.

Repro steps

  1. Open the Requestly app
  2. Create a new ← Insert Scripts rule
  3. For the condition, use:
    • HOST
    • Equals
    • app.slack.com
  4. For the script settings, use:
    • Language: JS
    • Code Source: CODE
    • Insert: After Page Load
  5. Paste the following script (do skim it first!):
Click to expand!
// Site-specific configuration.

/** 
 * Key/value pairs for filtering outgoing transmissions. If any match, the
 * outgoing transmission is dropped.
 * 
 * In each nested array, index 0 represents the property key, and index 1
 * represents the value it must equal to be considered a match.
 * 
 * @type {string[][]}
 */
const FILTER_CONDITIONS = [
  ["type", "user_typing"]
];

// Generic script for blocking outgoing WebSocket messages.

/**
 * The original {@link WebSocket#send} implementation.
 * 
 * @type {function}
 */
const SEND = window.WebSocket.prototype.send;

/**
 * Wrapper around the built-in {@link WebSocket#send} function, but filtering
 * events against {@link FILTER_CONDITIONS}.
 * 
 * @param {string | ArrayBufferLike | Blob | ArrayBufferView} data
 */
function preprocessedSend(data) {
  if (typeof data !== 'string') {
    console.log('Received non-string data, forwarding without check.');
    return SEND.apply(this, [data]);
  }

  try {
    const parsed = JSON.parse(data);

    for (const pair of FILTER_CONDITIONS) {
      if (parsed[pair[0]] === pair[1]) {
        return;
      }
    }

    return SEND.apply(this, [data]);
  } catch (err) {
    if (err instanceof SyntaxError) {
      console.log('Received non-JSON data, forwarding without check.');
      return SEND.apply(this, [data]);
    }

    throw err;
  }
}

window.WebSocket.prototype.send = preprocessedSend;

The result should be that all outgoing WebSocket messages should go through except user_typing which is instead dropped. This results in Slack and other users not getting my typing events. i.e. "Seth is typing…"

image

My configuration for this rule.

What Requestly tool were you using?

  • Extension
  • Desktop-App
  • Android SDK
  • Selenium

Your Environment

Debian 11, Firefox 112.0 (64-bit)

Requestly Version

v23.4.5

Error screenshot

No response

@sagarsoni7 sagarsoni7 self-assigned this Apr 18, 2023
@sagarsoni7
Copy link
Member

Thanks @SethFalco for the detailed info.
app.slack.com has CSP that prevents execution of inline JS
Screenshot 2023-04-19 at 1 26 42 AM

@sagarsoni7
Copy link
Member

Screenshot 2023-04-19 at 1 29 39 AM

@SethFalco
Copy link
Author

Ahh, that's my bad. I was under the impression that extensions, bookmarklets, etc could bypass CSP, but I can see other extensions have the same problem on Firefox.

I was exploring if I could do this through a user-script instead, but that isn't going to work either. 🤔

Relevant bug on Firefox:

Interesting reads:

Not sure if much can be done here then, or if this should be closed and we can just wait for Mozilla to address it in Firefox, though it's been 7 years already. ^-^'

@sagarsoni7
Copy link
Member

sagarsoni7 commented Apr 18, 2023

Bypassing CSP is possible for extensions if it is enforced via a HTTP header (since extensions can modify headers).

https://app.slack.com/client/ is enforcing CSP via a HTML meta tag. Extensions cannot modify responses of such requests.

But what if you have a proxy server in the middle that does the modification of this HTML meta tag and then only forward response to the browser? Browser would no longer be aware of any CSP policy.

Try Requestly Desktop App. Close your existing Firefox instance and reopen via Connected Apps in Requestly App.

  • See if it proxy is working fine and you see network traffic intercepted in Requestly App

Screenshot 2023-04-19 at 3 37 43 AM

  • Create a Modify Response Rule which replaces/removes/invalidates Content-Security-Policy meta tag in HTML

image

  • Create a Inject Script Rule as required

image

Voila!
image




Screenshot 2023-04-19 at 3 46 38 AM

Not very convenient but opens up a lot of possibilities

@sachinjain024
Copy link
Contributor

@sagarsoni7 This is very interesting! Good solution, we should update our existing article https://requestly.io/blog/learn-and-bypass-content-security-policy-http-response-header and mention this approach too.

@sachinjain024
Copy link
Contributor

@sagarsoni7 @SethFalco -- Just wondering if using the Insert script feature if we remote the meta tag that matches the content-security-policy, does Firefox still apply the CSP restrictions?

@sagarsoni7
Copy link
Member

@sachinjain024 - No, it doesn't apply restrictions then. ^^ Updated the screenshot with script injection working.

@sachinjain024
Copy link
Contributor

@sagarsoni7 Then it should be completely solvable with just the browser extension without the need of using desktop app? Basically, then we can create two blocks (pairs) of scripts

  • The First block removes the meta tag
  • Second block inserts the actual script

Thoughts?

@SethFalco
Copy link
Author

SethFalco commented Apr 19, 2023

Bypassing CSP is possible for extensions if it is enforced via a HTTP header (since extensions can modify headers).

Right, but what I meant is that the CSP specification dictates this shouldn't be require at all. I'm assuming this is why it works in Chromium for example. That's why this is considered a bug in Firefox.

Policy enforced on a resource SHOULD NOT interfere with the operation of user-agent features like addons, extensions, or bookmarklets. These kinds of features generally advance the user’s priority over page authors, as espoused in [HTML-DESIGN].

https://www.w3.org/TR/CSP3/#extensions

Create a Modify Response Rule which replaces/removes/invalidates Content-Security-Policy meta tag in HTML

If this will be documented anywhere, instead of advising to remove the CSP outright, perhaps it's better to use a regex to replace only the script-src, style-src, and default-src portion of the CSP when and as necessary?

What users do in practice is entirely up to them! But I think a formal article should try to minimize advising to undermine the browsers built-in security features.

For example, something like the following for scripts:

const CSP = "<meta http-equiv=\"Content-Security-Policy\" content=\"base-uri 'self'; script-src 'none'; object-src 'none'; default-src 'none'\">";
CSP.replace(/\s*(script|default)-src.+?(;|(?="))/g, "");

// Input:  <meta http-equiv="Content-Security-Policy" content="base-uri 'self'; script-src 'none'; object-src 'none'; default-src 'none'">
// Output: <meta http-equiv="Content-Security-Policy" content="base-uri 'self'; object-src 'none';">

It might not be perfect as I rushed this up. Also, the content of the meta[http-equiv=Content-Securiy-Policy] element should be selected, overridden, and inserted again to avoid accidentally replacing other arbitrary content in the DOM. Even this is aggressive. Ideally, all that has to happen is to remove none if it's there, and include unsafe-inline. This proposal is just to keep it as a one-liner.

Then it should be completely solvable with just the browser extension without the need of using desktop app? Basically, then we can create two blocks (pairs) of scripts

I would be surprised if this would work tbh. If CSP is blocking scripts, I don't see how one could use a script to remove the meta tag in the first place to execute the next script.

I did do an experiment where I tried to make uBlock Origin remove the CSP through a custom filter:

app.slack.com##meta[http-equiv=Content-Security-Policy]

However, it wasn't removed at all. Probably because uBlock Origin doesn't remove the actual content from the DOM, so that was a bust.

@sagarsoni7
Copy link
Member

@sachinjain024 - If you mean to remove meta like with a script injection like

function deleteMetaTag() {
  const metaTags = document.getElementsByTagName('meta');

  for (let i = 0; i < metaTags.length; i++) {
    if (metaTags[i].getAttribute('http-equiv') === 'Content-Security-Policy') {
      metaTags[i].parentNode.removeChild(metaTags[i]);
      break;
    }
  }
}

deleteMetaTag();

it won't work since even this script won't get executed since CSP is restricting in-line script in OP's case.
I'm yet to see if it works in case CSP is restricting other domains but not in-line scripts.

@sagarsoni7
Copy link
Member

If this will be documented anywhere, instead of advising to remove the CSP outright, perhaps it's better to use a regex to replace only the script-src, style-src, and default-src portion of the CSP when and as necessary?

What users do in practice is entirely up to them! But I think a formal article should try to minimize advising to undermine the browsers built-in security features.

Totally agreed!

@wrongsahil wrongsahil changed the title bug: insert scripts on Firefox is not executed on Slack bug: insert scripts on Firefox is not executed on Slack (CSP using meta tags) Aug 23, 2023
@wrongsahil wrongsahil changed the title bug: insert scripts on Firefox is not executed on Slack (CSP using meta tags) bug: insert scripts on Firefox is not executed on Slack (Bypass CSP enforced using HTML meta tags) Aug 23, 2023
@wrongsahil
Copy link
Member

@Kanishkrawatt @sagarsoni7 How to bypass CSP enforced using HTML meta tag.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants