slab / quill

Quill is a modern WYSIWYG editor built for compatibility and extensibility
https://quilljs.com
BSD 3-Clause "New" or "Revised" License
42.64k stars 3.34k forks source link

Security Issue CVE-2021-3163 #3364

Open hieroshima opened 3 years ago

hieroshima commented 3 years ago

Hi.

I would like to raise a security issue which is described in CVE-2021-3163. Is there any fix for that or do someone know an ETA when that security issue will be fixed?

Thanks in advance.

arktronic commented 3 years ago

Has anyone been able to reproduce this vulnerability? I tried using various setups without luck. The originally referenced issue (#3273) has been deleted. The only way I've been able to see the XSS in action is on the CodePen Playground on the bottom of https://quilljs.com/docs/delta/ but that's because, for the right side of the playground, they're assigning directly to .innerHTML without escaping (thus, that particular instance is not itself a QuillJS issue).

polyatail commented 3 years ago

I have not been able to reproduce this vulnerability either. The closest I've come is editing the contents of the Quill <div> as HTML and forcing something like <div><img src="foobar.jpg" onclick="alert(1234)">foobar</div> to exist on the page. Even then, copying and pasting that image within the editor, the pasted image box is stripped of the onclick property.

polyatail commented 3 years ago
  1. Save/persist contents of Quill

So to clarify, this issue affects only cases where un-sanitized contents are loaded into a Quill editor via e.g., an API call to the server that reads the contents from a database. It is not possible to directly input this attack into the editor. Correct?

polyatail commented 3 years ago

Perhaps a solution here is to pipe the initial input of the editor through the same mechanism that sanitizes clipboard pasting, since that seems to be effectively removing these problematic element attributes.

polyatail commented 3 years ago

That sounds sensible to me. Will you be taking a look at this? If not, I may do a pull-request in the near future.

Feel free to give it a go and tag me for a review.

arktronic commented 3 years ago

@alexcodito Thank you for following up on that.

HamzaAnis commented 3 years ago

Has anyone been able to reproduce this vulnerability? I tried using various setups without luck. The originally referenced issue (#3273) has been deleted. The only way I've been able to see the XSS in action is on the CodePen Playground on the bottom of https://quilljs.com/docs/delta/ but that's because, for the right side of the playground, they're assigning directly to .innerHTML without escaping (thus, that particular instance is not itself a QuillJS issue).

So is this a valid vulnerability? If not then the a lot of people will be getting false potential security vulnerabilities alerts.

samcambridge commented 3 years ago

I noticed that the blog post revealing this "exploit" (https://burninatorsec.blogspot.com/2021/04/cve-2021-3163-xss-slab-quill-js.html) mentioned a html editor. Please correct me if i'm wrong but Quill doesn't provide this functionality out of the box.

The only way I've managed to replicate this is using a https://github.com/benwinding/quill-html-edit-button, which describes itself as a Module which allows you to quickly view/edit the HTML in the editor.

Pasting the HTML from the blog post into the HTML editor available in this demo https://benwinding.github.io/quill-html-edit-button/src/demos/javascript/demo.html reproduces the issue. Still, that does not make it a Quill issue.

If anyone has had any success reproducing it without custom modules can they let me know how?

pauldraper commented 3 years ago

So is this a valid vulnerability?

I have been able to reproduce this by loading the unsanitized payload into Quill.

Yes, this is a valid vulnerability if untrusted content is loaded.

For example, Alice submits content with XSS attack on a website (say, via its API). Bob opens a Quill editor on that website with Alice's content and is attacked.

HamzaAnis commented 3 years ago

Is anyone planning for a patch on this security Issue ?

So is this a valid vulnerability?

I have been able to reproduce this by loading the unsanitized payload into Quill.

Yes, this is a valid vulnerability if untrusted content is loaded.

For example, Alice submits content with XSS attack via a website's API. Bob opens a Quill editor with Alice's content and is attacked.

jkneepkens commented 3 years ago

So is this a valid vulnerability?

I have been able to reproduce this by loading the unsanitized payload into Quill.

Yes, this is a valid vulnerability if untrusted content is loaded.

For example, Alice submits content with XSS attack on a website (say, via its API). Bob opens a Quill editor on that website with Alice's content and is attacked.

But shouldn't this be handled by the backend/API which is serving this XSS content to the Quill editor to remove already this malicious content? Otherwise it would be really nice if it gets fixed in a patch!

ndtx commented 2 years ago

Some feedback regarding plans to address this would be appreciated. quill package being flagged as vulnerable we have to document why this is not an issue if it is not being fixed or replace if it is not maintained.

image

einfallstoll commented 2 years ago

I had a look at the source code and there aren't many places which allow XSS using .innerHTML. From the blog post my best guess is the reporter just included an XSS payload in the editor div (just like documented in the Quickstart) and then the XSS will execute:

<!-- Include Quill stylesheet -->
<link href="dist/quill.snow.css" rel="stylesheet">

<!-- Create the toolbar container -->
<div id="toolbar">
  <button class="ql-bold">Bold</button>
  <button class="ql-italic">Italic</button>
</div>

<!-- Create the editor container -->
<div id="editor">
  <image src=validateNonExistantImage.png onerror=alert(1337)> hey girl hey
</div>

<!-- Include the Quill library -->
<script src="dist/quill.js"></script>

<!-- Initialize Quill editor -->
<script>
  var editor = new Quill('#editor', {
    modules: { toolbar: '#toolbar' },
    theme: 'snow'
  });
</script>

If this is the actual XSS, the risk is fairly low, because that's not a vulnerability in Quill but intended behavior in ... a browser. To mitigate this:

To me this risk is acceptable and I can understand why no fix is available or will be available.

snoopysecurity commented 2 years ago

Hi, I've contacted the CVE database to revoke this CVE with the evidence provided. I will also get this removed from the Snyk Security Database. https://snyk.io/vuln/SNYK-JS-QUILL-1245047. Hopefully that will reduce the noise

AM1988 commented 2 years ago

Hi, I've contacted the CVE database to revoke this CVE with the evidence provided. I will also get this removed from the Snyk Security Database. https://snyk.io/vuln/SNYK-JS-QUILL-1245047. Hopefully that will reduce the noise

Hi. Any updates on this? Seems still treated as a vulnerable package.

pauldraper commented 2 years ago

To clarify for all the visitors not understanding if this applies to them:

If you save and load shared Quill content without server-side filtering, you have XSS.

For example:

Alice submits malicious Quill-ish content to a website via direct HTTP request. The website saves this content verbatim. When Bob edits/views that content on the website with Quill editor, Bob is attacked.

Recurse-blip commented 2 years ago

As I understand this is a non-issue and the CVE will be revoked. Is there any progress on this matter?

twocs commented 2 years ago

Hi. Any updates on this? Seems still treated as a vulnerable package. and As I understand this is a non-issue and the CVE will be revoked. Is there any progress on this matter?

It is still indicated as vulnerable. Github advisory (powers npm audit) https://github.com/advisories/GHSA-4943-9vgg-gr5r The CVE advisory is marked as disputed https://nvd.nist.gov/vuln/detail/CVE-2021-3163 However, the SNYK advisory was revoked https://security.snyk.io/vuln/SNYK-JS-QUILL-1245047

EricDitp commented 2 years ago

fix would be appreciated.

oalexdoda commented 2 years ago

Still getting this as a vulnerability alert. What's the resolution here?

pauldraper commented 2 years ago

The resolution is to scrub the data from quill before saving in a shared place.

Nyamkhuub commented 2 years ago

Still getting this as a vulnerability alert. How to solve it guys?

Sliffcak commented 1 year ago

Still getting this as a vulnerability alert

deepesh146 commented 1 year ago

still getting error any fix?

pauldraper commented 1 year ago

No. You can tell by the way this issue is still open, and the alert doesn't include a fix version

deepesh146 commented 1 year ago

please suggest any other text editor actually i have purchased a theme and quill is built in ,i m building a enterprise app security is most important for company

burninatorsec2 commented 1 year ago

Hi, this is how to reproduce the issue - you need to edit the traffic with an interception proxy when storing the comment, in order to put this payload in:<div><image src=validateNonExistantImage.png onloadstart=alert(1337)> thepayload </div>

Then once it's stored, any other user to visit the page with that payload will get the XSS to pop. This vulnerability is very much still active, I've used it in multiple environments I've pentested or done bug bounties for since reporting it back in 2021 (here https://github.com/quilljs/quill/issues/3273, and here: https://github.com/quilljs/quill/issues/3558).

EDIT: spelling and wording

burninatorsec2 commented 1 year ago

The resolution is to scrub the data from quill before saving in a shared place.

Yes, this is one way to remediate it, in lieu of Quill releasing a security patch.

burninatorsec2 commented 1 year ago

Perhaps a solution here is to pipe the initial input of the editor through the same mechanism that sanitizes clipboard pasting, since that seems to be effectively removing these problematic element attributes.

Correct, this same sanitation needs to be done server-side/API side, not just in the UI.

gustaff-weldon commented 1 year ago

Perhaps a solution here is to pipe the initial input of the editor through the same mechanism that sanitizes clipboard pasting, since that seems to be effectively removing these problematic element attributes.

That sounds reasonable. Any chance this can be implemented? Or if that's a public method users can call it on their own?

burninatorsec2 commented 1 year ago

Perhaps a solution here is to pipe the initial input of the editor through the same mechanism that sanitizes clipboard pasting, since that seems to be effectively removing these problematic element attributes.

That sounds reasonable. Any chance this can be implemented? Or if that's a public method users can call it on their own?

Maybe! I know a lot of text editor wysiwyg widgets have issues with XSS, so they all have different solutions that they've implemented different ways. For example, CKEditor introduced a setting "script mode" (or something like that), to either disallow or allow tags, because allowing script tags would open it up any client using that widget to attacks. Perhaps something similar can be done here.

polyatail commented 1 year ago

Hi, this is how to reproduce the issue - you need to edit the traffic with an interception proxy when storing the comment, in order to put this payload in:<div><image src=validateNonExistantImage.png onloadstart=alert(1337)> thepayload </div>

Then once it's stored, any other user to visit the page with that payload will get the XSS to pop. This vulnerability is very much still active, I've used it in multiple environments I've pentested or done bug bounties for since reporting it back in 2021 (here https://github.com/quilljs/quill/issues/3273, and here: #3558).

EDIT: spelling and wording

@burninatorsec2 As described, this is strictly not an issue with QuillJS. If you are able to get a backend to store un-sanitized input, that is an issue with the backend. Blaming Quill for displaying what it's given is like blaming SQL for interpreting Robert'); DROP TABLE Students; such that it allows an injection attack. You are describing an issue with the way Quill is being used in specific applications, rather than an issue with the software.

Asking for a "script mode" to be implemented is a feature request to unburden developers of having to sanitize input on their own, not a bugfix.

burninatorsec2 commented 1 year ago

@polyatail The burden is first with QuillJS, since other similar text editors found ways to work around the introduced vulnerability. QuillJS could do the same, or find a way to end the dependence on <div> tags for their functionality. So, I disagree. However, you're right that developers of the client apps that use Quill and the developers of Quill should be sanitizing both on the client side and the server side. This applies for any security issue, whether it's command injection, or XSS, or XXE, and that would make a great workaround in the meantime.

Think about in layers of protection - if we can trust a 3rd party like Quill to filter inappropriate input AND have each of the developers who use Quill to add their own sanitization, we stand a much better chance of preventing an attack. But the common denominator is Quill here, so that's where the responsibility lies. Usually vendors jumped on fixing security issues that I've reported, because their product has lots of clients that depend on them, and they realize the effect they would have on the client apps and the people that use it. So they implement a fix.

I like the idea of the SQL Injection metaphor, but it doesn't really work here, because no one is blaming how JavaScript works, just like how no reasonable person would ever blame SQL itself for SQL injections. We all already know JavaScript has a lot of problems, so we've been designing around JavaScript security issues for years. Making a library like Quill that relies on raw HTML tags is not a good idea because of stuff like this. They could encode the tags, for example. There are lots of ways to remediate this, that's just one.

If you want to make a metaphor about preventing this sort of thing for SQL Injections, how about something like this... we, as 3rd party developers, cannot assume that a client is going to be filtering dangerous SQL commands in their client app, or using a WAF. So we do the due diligence and write code with parameterized SQL queries to prevent introducing risk to the client. That's what I would expect from Quill. I realize that the nature of the design may make it difficult to remediate this, and I'm happy to help brainstorm ways to do it because I have experience as both a software engineer and a pen tester.

EDIT: So this is funny - GitHub properly sanitized the div tag I wrote above, instead of allowing it to be displayed. That's exactly what I'm talking about! :) So I added backticks so it can show up. Good job, GitHub!

burninatorsec2 commented 1 year ago

Personally, I'm not a fan of the "script mode" feature in CKEditor either, since elective security isn't really security (since it makes it a choice instead of a default - maybe make "script mode off" the default?) But it sounds like there's plenty of concerned users in this thread that would use something like that in Quill if it existed to help mitigate the problem.

Saduff commented 1 year ago

As described, this is strictly not an issue with QuillJS. If you are able to get a backend to store un-sanitized input, that is an issue with the backend.

This absolutely is an issue with Quill. The backend must store "un-sanitized" input, otherwise it would be violating data integrity. XSS is not an input problem, it's an output encoding problem (e.g. it's under the "V5.3 Output Encoding and Injection Prevention" subsection in the OWASP ASVS). As a pentester, I often see developers get confused by this.

For example, let's say I want to share an XSS payload here on GitHub. If GitHub "sanitized" the input before saving it to the database then it wouldn't be able to display the payload (in its original form) later, i.e. data integrity has been violated. "Sanitizing" before saving to the database is always an incorrect defense and often creates problems of its own, not just data integrity, but sometimes even security problems. Data is data. What if instead of my XSS payload being displayed on a HTML page, I use GitHub API to retrieve it in JSON instead, what threat does it pose there? It's an output encoding problem, meaning user input must be encoded for the context in which it's used, whether that's HTML, part of an URL, JSON etc. If user input is properly encoded, there is no XSS vulnerability, despite an XSS payload being saved in the database.

Now, as for Quill, it's a WYSIWYG editor which means HTML cannot simply be encoded or the editor wouldn't work properly (you'd no longer "see what you get"). In this case it comes down to this: should Quill support JavaScript execution or not? I would argue that in almost all cases, WYSIWYG editors should not support JS execution. If it's required, it should be configurable and disabled by default. If JS execution should not be supported, but is possible, then that's a bug and not just any bug, a security vulnerability. In this case, Quill should (actually) sanitize the HTML so that JS execution is not possible, similar to what Angular does with its [innerHTML] binding. Angular lets you enter certain HTML tags such as <h1>, <img>, but good luck trying to get JS execution. An alternative would be for the developer to sanitize the HTML using e.g. DOMPurify before giving the HTML as input to Quill, but that should be Quill's job. In addition to preventing JS execution, taking over display of the entire page using CSS in an element's style attribute should be prevented, either by not allowing style attributes at all or by whitelisting certain CSS.

So this is funny - GitHub properly sanitized the div tag I wrote above, instead of allowing it to be displayed.

The opposite actually, GitHub did not sanitize the div tag, which is the reason it didn't show up as text. That's because some HTML tags are allowed in Markdown. Did you think GitHub violated data integrity by removing it?

--> There's a <div> in the DOM here, inspect this element. Other surrounding tags are <p> tags. <--

If GitHub sanitized the div tag above, it would not be in the DOM and would show up as the following text instead: ```
--> There's a <div> in the DOM here, inspect this element. Other surrounding tags are <p> tags. <--
``` Why this is not an issue for GitHub and _not_ an XSS vulnerability is because this is a feature. However, there's no feature for executing JavaScript by any means or taking over display of the entire page using CSS in an element's `style` attribute. --- After some further testing, it seems there is actually no issue with Quill. What I wrote above still applies, but it seems Quill has already properly taken care of it. I tried to reproduce the issue found in a pentest which seemed to be due to Quill, but to my surprise couldn't reproduce it. After some further investigation, it turned out the issue is not due to Quill, but because the developers are doing this with jQuery after Quill has been initialized: ```js $('.ql-editor').html(); ```
burninatorsec2 commented 1 year ago

Yes, as far as the GitHub example goes - HTML injection by design != XSS, we're in agreement there.

As for Quill:

but it seems Quill has already properly taken care of it.

Can you point out the commit or version where Quill has implemented a fix? That would be great news and I would love to be able to tell clients that they can upgrade to a version that isn't vulnerable to XSS.

Saduff commented 1 year ago

See here for a PoC which proves Quill is not vulnerable: https://codepen.io/Saduff/pen/PoejxjN

Alert is shown only once and Quill created DOM looks like this: image

Notice how the <p> tag has no style attribute nor event handlers and the <img> tag has no onerror event handler.

If you have another PoC which shows Quill is vulnerable then please share it.

Also, I'm not familiar with Quill, but from what I could tell by reading the docs, there's no method for setting the editor's HTML content (e.g. something like quill.setHtmlContent('<div>...'). I assumed there was such a method. If I had known there wasn't, I wouldn't have commented here at all. The only way to set HTML content is to initialize Quill with an existing DOM node. Therefore, any XSS issues that exist there have nothing to do with Quill as they're already in the DOM before Quill is even initialized.

burninatorsec2 commented 1 year ago

Yep, the original payload from the blog still works. Though, the one you sent in that link works too, in the latest Firefox browser. So maybe that isn't a very good test, if neither is working for you?

burninatorsec2 commented 1 year ago

PS is everyone ok with me taking screenshots of this thread for a DEFCON presentation because this would make an amazing talk about how the responsible vulnerability disclosure process works (and sometimes doesn't work)... if not I can redact your usernames, thanks in advance!

Saduff commented 1 year ago

Yep, the original payload from the blog still works. Though, the one you sent in that link works too, in the latest Firefox browser. So maybe that isn't a very good test, if neither is working for you?

I'm also using latest Firefox. So when you say it "works" for you, you see two 'img' alerts pop up in that codepen?

burninatorsec2 commented 1 year ago

Yep, the original payload from the blog still works. Though, the one you sent in that link works too, in the latest Firefox browser. So maybe that isn't a very good test, if neither is working for you?

I'm also using latest Firefox. So when you say it "works" for you, you see two 'img' alerts pop up in that codepen?

Yes, because I added my own payload as well. But like I said, your code pen probably not a great test because it’s all client side, and the stored XSS payload is allowed by Quill and then later displayed by Quill (and thus the alert is popped).

I’m trying to decide if it’s worth the time and effort to build a test environment like the web apps I’ve been exploiting it in, with a database and backend, or if it would be more convenient to just come across it again in the wild since it’s been so prevalent lately (and make a redacted video).

Saduff commented 1 year ago

Yes, because I added my own payload as well. But like I said, your code pen probably not a great test because it’s all client side, and the stored XSS payload is allowed by Quill and then later displayed by Quill (and thus the alert is popped).

Quill is all client side, what's your point? There's no stored XSS here, there can only be DOM XSS, which there isn't. You can add as many "payloads" as you want and pop as many alerts as you want and then remove Quill and nothing changes.

I’m trying to decide if it’s worth the time and effort to build a test environment like the web apps I’ve been exploiting it in, with a database and backend, or if it would be more convenient to just come across it again in the wild since it’s been so prevalent lately (and make a redacted video).

That's completely out of scope and has nothing to do with Quill. The XSS you may have seen where Quill is used is caused by something else and would still be there if Quill is completely removed. Like in my case, the cause was jQuery.html(<user input>).

burninatorsec2 commented 1 year ago

That's completely out of scope and has nothing to do with Quill. The XSS you may have seen where Quill is used is caused by something else and would still be there if Quill is completely removed. Like in my case, the cause was jQuery.html(<user input>).

Sorry, but that's not correct. These apps don't have XSS in any other fields except for the Quill text editor field. When they remove Quill it removes this problem.

Saduff commented 1 year ago

Sure. Produce a PoC to prove it then.

burninatorsec2 commented 1 year ago

Yes that’s right, two alert dialog boxes.

On Tue, Sep 20, 2022 at 6:12 PM, Saduff @.***> wrote:

Yep, the original payload from the blog still works. Though, the one you sent in that link works too, in the latest Firefox browser. So maybe that isn't a very good test, if neither is working for you?

I'm also using latest Firefox. So when you say it "works" for you, you see two 'img' alerts pop up in that codepen?

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>

BarkovskiyMaxim commented 1 year ago

Hi! I have solved this issue on the client side with iframe with sandbox attribute, because it can block event execution. To solve I insert html content to iframe with sandbox attribute and remove all the attributes which start with 'on' from the elements. Then I remove all script tags from iframe content. As a result I take html from iframe without scripts and events and use this html in quill.


    function removeSubscriptions(element: HTMLElement) {
        if(!!element.attributes) {
            for(let i = 0; i < element.attributes.length; i++) {
                if(element.attributes[i].name.indexOf('on') === 0) {
                    element.removeAttribute(element.attributes[i].name);
                }
            }
        }
        if(!!element.childNodes) {
            for(let i = 0; i < element.childNodes.length; i++) {
                removeSubscriptions(element.childNodes[i] as HTMLElement);
            }
        }
    }

    function fixHtmlXSS(value: string): string {
        let frame = document.getElementById('my_frame') as HTMLIFrameElement;
        if(!frame) {
            frame = document.createElement('iframe');
            frame.style.display = 'none';
            (frame as any)['sandbox'] = 'allow-same-origin';
            frame.id = 'my_frame';
            document.body.appendChild(frame);
        }
        frame.contentWindow.document.open();
        frame.contentWindow.document.write(
            `<head>
            </head>
            <body>
                ${value}
            </body>`
        );
        frame.contentWindow.document.close();
        removeSubscriptions(frame.contentWindow.document.body);
        let scripts = frame.contentWindow.document.body.getElementsByTagName('script');
        for(let i = 0; i < scripts.length; i++) {
            scripts[i].remove();
        }
        return frame.contentWindow.document.body.innerHTML;
    }

Maybe this solution can help someone.

Saduff commented 1 year ago

This can be trivially bypassed, see this PoC: https://codepen.io/Saduff/pen/xxjMaQy

I've got a much better solution for everyone: stop including user-generated HTML on your pages! Or if you really must, use a proven solution such as DOMPurify instead of reinventing the wheel. In the future when more browsers support it by default, you can use the browser's built-in HTML Sanitizer API. However, to use Quill safely, you don't need DOMPurify or anything like that.


Yes that’s right, two alert dialog boxes.

And for those addicted to alert boxes, here's another PoC without any alerts that once again proves Quill is not vulnerable: https://codepen.io/Saduff/pen/poVGOYr

And before you can say "but of course there are no alerts, because it's in a sandboxed iframe", let me stop you right there. That's not the point at all. The point is this: image

As you can see, Quill created DOM contains no JavaScript that was included in the iframe srcdoc! Quill added the <img> tag without the onerror event handler, stripped the <script> tag, leaving only the text content and replaced the <a> tag href attribute with about:blank.

BarkovskiyMaxim commented 1 year ago

Thank you for a example with iframe, but its works good in quill, because quill can sanitize this code. Your example with srcdoc works because you use iframe. If you check console, you can see this: image Quill try execute this events, but can't do this in iframe and sanitize template in next step. Any way, solution with iframe works fine.

Saduff commented 1 year ago

Quill try execute this events, but can't do this in iframe and sanitize template in next step.

Quill is not executing these events, the browser is. Quill has not been initialized at that point.

Here's an even better example using <template> instead of a sandboxed iframe: https://codepen.io/Saduff/pen/rNvbWzv

After Quill has initialized inside the <template>, the template content is added to the DOM. Notice how no JavaScript execution (i.e. XSS) takes place.

tjad commented 1 year ago

Perhaps this could be cleared up with better documentation.

If you use Quill instance's getText() method, you'll receive a WYSIWYG version of the contents. If you want completely santised text, you can use the Quill instance's root property to get the innerHTML, which is fully sanitised.

@snoopysecurity