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

Security Fix for Cross Site Scripting - huntr.dev #3782

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from

Conversation

huntr-helper
Copy link

@huntr-helper huntr-helper commented Jun 15, 2020

https://huntr.dev/app/users/Asjidkalam has fixed the Cross Site Scripting vulnerability πŸ”¨. Asjidkalam has been awarded $25 for fixing the vulnerability through the huntr bug bounty program πŸ’΅. Think you could fix a vulnerability like this?

Get involved at https://huntr.dev/

Q | A
Version Affected | ALL
Bug Fix | YES
Original Pull Request | 418sec#1
GitHub Issue URL | #3719
Vulnerability README | https://github.com/418sec/huntr/blob/master/bounties/npm/summernote/1/README.md

User Comments:

πŸ“Š Metadata *

Please enter the direct URL for this bounty on huntr.dev. This is compulsory and will help us process your bounty submission quicker.

Bounty URL: https://www.huntr.dev/bounties/1-npm-summernote

βš™οΈ Abstract

Affected versions of this package are vulnerable to Cross-site Scripting (XSS). It is possible to inject JavaScript with object decoding such as <script>alert(1)</script> resulting in XSS. The fix is to sanitize the input on the package before further processing/executing.

❓ Technical description

Issue: #3719
From the issue, the package doesn't sanitize any HTML entities thrown at it. It is unsafe and causes reflected XSS in all cases. However, filtering out the tags which causes XSS from the DOM input can mitigate the issue.
The Fix is implemented in the dom_value function, which returns the input value (val) from the DOM. val variable is sanitized by replacing tags like &, >, < with &amp;, &lt; and &gt; respectively. So i made a callback prototype function to achieve this:

String.prototype.escape = function() {
    var tagsToReplace = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;'
    };
    return this.replace(/[&<>]/g, function(tag) {
        return tagsToReplace[tag] || tag;
    });
};

πŸ› Proof of Vulnerability (PoV)

Open the editor, go to the Code View section and enter the following payload:
<script>alert(1)</script>, now deactivate the Code View to trigger the XSS.

Before patch:
before-patch

πŸ”₯ Proof of Fix (PoF)

After patch:
after-patch

It successfully sanitizes the input and deactivates from the Code View without triggering XSS.
The debug messages in the console shows the actual input and filtered input.

index.html file used in the POC:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" />
    <script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>

    <link href="summernote.css" rel="stylesheet">
    <script src="summernote.js"></script>
    
    <title>Summernote - XSS Fix POC</title>
</head>
<body>
    <div id="summernote">Hello summernote</div>
    <script>
        $(document).ready(function() {
            $('#summernote').summernote();
        });
    </script>
</body>
</html>

dist/summernote.js Outdated Show resolved Hide resolved
@R2116
Copy link

R2116 commented Jun 29, 2020

Switching between the regular editor and the Code View back and forth escapes the Code View again and again for me:
All three tags to replace include an & which is one of the tags to replace - the & gets endlessly escaped.
Removing the first line of the tagsToReplace array solves it; but I'm sure it has some use?

Added a different regex on summernote.js to fix the issue

dist/summernote-lite.js Outdated Show resolved Hide resolved
dist/summernote-bs4.js Outdated Show resolved Hide resolved
JamieSlome and others added 3 commits June 30, 2020 11:34
Co-authored-by: R2116 <15418932+R2116@users.noreply.github.com>
Co-authored-by: R2116 <15418932+R2116@users.noreply.github.com>
Co-authored-by: R2116 <15418932+R2116@users.noreply.github.com>
@JamieSlome
Copy link

@R2116 - the requested changes have been made!

Thanks! 🍰

@Augustukas
Copy link

@lqez @easylogic @hackerwins bump

@JamieSlome
Copy link

@R2116 @lqez @easylogic @hackerwins - any updates on this?

Cheers! 🍰

@DennisSuitters
Copy link
Member

DennisSuitters commented Nov 26, 2020

@R2116 The changes to fix this vulnerability have been made to the wrong files, please do NOT edit the files in the dist/ folder, but rather the relevant files within the src/ folder, thank you.

@DennisSuitters
Copy link
Member

I've just been reviewing this patch, there is an issue where the > and < in element tags are being converted, so for e.g. images are being displayed as <img src="image.jpg"> in the visual editor, but show with the entities in code view. We need to filter out elements so they are not affected by the patch. I'll have more of a play here in the meantime, but can you please update this to reflect the changes in the src/ folder, not the dist/ folder, thank you.
@R2116

@DennisSuitters
Copy link
Member

Um, I remember in the settings there's options to filter the codeview data, which has options that this PR is trying to fix, and works.

    codeviewFilter: false,
    codeviewFilterRegex: /<\/*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|ilayer|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|t(?:itle|extarea)|xml)[^>]*?>/gi,
    codeviewIframeFilter: true,
    codeviewIframeWhitelistSrc: [],
    codeviewIframeWhitelistSrcBase: [
      'www.youtube.com',
      'www.youtube-nocookie.com',
      'www.facebook.com',
      'vine.co',
      'instagram.com',
      'player.vimeo.com',
      'www.dailymotion.com',
      'player.youku.com',
      'v.qq.com',
    ],

@DennisSuitters
Copy link
Member

@ivanovych666 I agree, except for the XSS issues while using the editor in the Browser, sanitising data before editing and database/other storage should take place.

Similar options like the codeview options should be implemented, I could do some changes as an experiment and see how that performs, I am mainly waiting through the discussion to determine what our best options for this are.

@DennisSuitters
Copy link
Member

Looking at the changes in this PR, it looks like another PR should probably be made to reflect the changes without the dist/ file edits. We can continue to discuss best options or new ideas to accomplish this here for the time being.

@nuclearghost
Copy link

I'm actively dealing with a XSS attack in our app using summernote. I'd like to ensure this test case is covered for pasting into code view: '"><img src=x onerror=alert(document.domain)>. This doesn't hit any of our code before it renders, so not sure how best to insert a fix. I've tried enabling codeviewFilter: true, but it did not resolve this.

Screen Shot 2021-01-28 at 9 38 30 AM
Screen Shot 2021-01-28 at 9 38 38 AM

@ivanovych666
Copy link

ivanovych666 commented Jan 29, 2021

alert(document.domain)

@nuclearghost you can insert this into dev tools console and you will have the same result. This is not an issue. The issue if this script can be executed for another user. But this is not a WYSIWYG editor issue, it is up to developer to properly escape values before displaying to users or storring somewhere. BTW, we use this editor on a project and successfully passed a professional security audit without any issues. So it is up to you of how to use it. But I'm agree, that the ability to filter values immediatelly will be an awesome feature.

@nuclearghost
Copy link

Thanks for the feedback @ivanovych666 . We're probably outside the scope for this PR, but this seems to be the active XSS thread.

I understand there are different type of attacks for XSS. There's stored XSS where I totally agree it's not the responsibility of the WYSIWYG editor. I'm also concerned about the case where a non-technical user pastes in some HTML into the code view and then views it in the normal editor mode. I'll attempt to add a callback where this can be handled in a separate PR.

@MrGoatsy
Copy link

MrGoatsy commented Feb 6, 2021

I managed to somewhat fix it by having my code like this:

<textarea name="description" id="description"></textarea>
<script>
    $('#description').summernote({
        height: 250,
        codeviewFilter: false,
        codeviewIframeFilter: true,
        // toolbar
        toolbar: [
            ['font', ['bold', 'italic', 'underline', 'clear']],
            ['color', ['color']],
            ['para', ['ul', 'ol', 'paragraph']],
            ['view', ['fullscreen', 'codeview', 'help']]
        ],
    }).on("summernote.enter", function(we, e) {
        $(this).summernote('pasteHTML', '<br />&VeryThinSpace;');
        e.preventDefault();
    });
    $("#description").summernote("code", "<p>&lt;script&gt;alert('123');&lt;/script&gt;<br /></p>");
</script>

This still doesn't fix it if you try type '".

PHP Tidy might be worth looking into:
https://www.php.net/tidy

I found a solution by using addslashes($string) with PHP, so this would be the code:

<?php
    $string = <<<EOD
        '"
    EOD;
?>
$("#description").summernote("code", "<?php echo addslashes($string); ?>");

The $string can also be fetched information from a database.

The result:
asdasd

@DennisSuitters
Copy link
Member

I think we need a solution that doesn't require external libraries, we need to consider that not everyone uses PHP.

@MrGoatsy
Copy link

MrGoatsy commented Feb 6, 2021

I think we need a solution that doesn't require external libraries, we need to consider that not everyone uses PHP.

With Javascript you can also use a heredoc like this:

<textarea name="description" id="description"></textarea>
<script>
   var noXSS = (function() {/*This works
'"`
    */}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1];

    $('#description').summernote({
        height: 250,
        codeviewFilter: false,
        codeviewIframeFilter: true,
        // toolbar
        toolbar: [
            ['font', ['bold', 'italic', 'underline', 'clear']],
            ['color', ['color']],
            ['para', ['ul', 'ol', 'paragraph']],
            ['view', ['fullscreen', 'codeview', 'help']]
        ],
    }).on("summernote.enter", function(we, e) {
        $(this).summernote('pasteHTML', '<br />&VeryThinSpace;');
        e.preventDefault();
    });
    $("#description").summernote("code", noXXS);
</script>

@gabuchoripper
Copy link

for some people looking this on php, i was using some regex to find scripts tags and codes inside and delete them...
would be like code below:

        $code = preg_replace('/<script>[\s\S]+<\/script>/gm' , '' , $_POST['code']);

@DennisSuitters
Copy link
Member

A better option when outputting to the browser using PHP, is to use a proven library like HTMLPurifier, it's easy, and properly sanitizes the output, esp. for XSS vulnerabilities. However, as I stated above, we really need a solution that works inside Summernote to be able to cover every programming language scenario, and mainly as new coders often lack the knowledge to sanitize data properly.

@DennisSuitters
Copy link
Member

It's been brought to my attention, that the dropzone is a potential XSS security risk, with the reporter indicating the following like could be the culprit:

$(content).each((idx, item) => {

I don't think it's the code in the line per see, but rather that that function doesn't sanitize the data before calling the insertNode for the item.
I think we need to revisit this issue, and possibly create a working sanitation function that can be called to sanitize data before insertion.

@bananabr
Copy link

It's been brought to my attention, that the dropzone is a potential XSS security risk, with the reporter indicating the following like could be the culprit:

$(content).each((idx, item) => {

I don't think it's the code in the line per see, but rather that that function doesn't sanitize the data before calling the insertNode for the item.
I think we need to revisit this issue, and possibly create a working sanitation function that can be called to sanitize data before insertion.

I am the reporter for this issue. $(content) is also vulnerable, as giving an external user control over the Jquery global function call parameter is an XSS vector. content would need to be sanitized before the $() function is called.

@bananabr
Copy link

this.context.invoke('editor.pasteHTML', content);
is vulnerable for another reason. In this case, the user input eventually reaches
const contentsContainer = $('<div></div>').html(markup)[0];
and the non-sanitized markup argument is the culprit.

@stevenluengo
Copy link

@DennisSuitters
Copy link
Member

That's ok, no one has come up with a workable solution so far. Been a while too.

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

Successfully merging this pull request may close these issues.

None yet