CVE-2018-1000559: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: qutebrowser version introduced in v0.11.0 (1179ee7a937fb31414d77d9970bac21095358449) contains a Cross Site Scripting (XSS) vulnerability in history command, qute://history page that can result in Via injected JavaScript code, a website can steal the user's browsing history. This attack appear to be exploitable via the victim must open a page with a specially crafted <title> attribute, and then open the qute://history site via the :history command. This vulnerability appears to have been fixed in fixed in v1.3.3 (4c9360237f186681b1e3f2a0f30c45161cf405c7, to be released today) and v1.4.0 (5a7869f2feaa346853d2a85413d6527c87ef0d9f, released later this week).
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 6.1
Published: 2018-06-26T16:29Z
Repository: https://github.com/qutebrowser/qutebrowser
Language: HTML
Files changed: qutebrowser/browser/qutescheme.py, tests/end2end/data/issue4011.html, tests/end2end/features/history.feature
Commit date: 2018-06-21 21:20:19 +0200
Commit message: Fix XSS issue on qute://history

Fixes #4011

Diff: diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py
index 6fc2a8429..7e60b265f 100644
--- a/qutebrowser/browser/qutescheme.py
+++ b/qutebrowser/browser/qutescheme.py
@@ -22,10 +22,11 @@
 Module attributes:
     pyeval_output: The output of the last :pyeval command.
     _HANDLERS: The handlers registered via decorators.
 """
 
+import html
 import json
 import os
 import time
 import textwrap
 import mimetypes
@@ -239,12 +240,13 @@ def history_data(start_time, offset=None):
     else:
         # end is 24hrs earlier than start
         end_time = start_time - 24*60*60
         entries = hist.entries_between(end_time, start_time)
 
-    return [{"url": e.url, "title": e.title or e.url, "time": e.atime}
-            for e in entries]
+    return [{"url": html.escape(e.url),
+             "title": html.escape(e.title) or html.escape(e.url),
+             "time": e.atime} for e in entries]
 
 
 @add_handler('history')
 def qute_history(url):
     """Handler for qute://history. Display and serve history."""
diff --git a/tests/end2end/data/issue4011.html b/tests/end2end/data/issue4011.html
new file mode 100644
index 000000000..488193736
--- /dev/null
+++ b/tests/end2end/data/issue4011.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>&lt;img src=&quot;x&quot; onerror=&quot;console.log('XSS')&quot;&gt;foo</title>
+    </head>
+    <body>
+        foo
+    </body>
+</html>
diff --git a/tests/end2end/features/history.feature b/tests/end2end/features/history.feature
index 2e2e1712a..13a890c10 100644
--- a/tests/end2end/features/history.feature
+++ b/tests/end2end/features/history.feature
@@ -110,5 +110,10 @@ Feature: Page history
         And I open data/numbers/4.txt
         And I open qute:history without waiting
         And I wait until qute://history is loaded
         Then the page should contain the plaintext "3.txt"
         Then the page should contain the plaintext "4.txt"
+
+    Scenario: XSS in :history
+        When I open data/issue4011.html
+        And I open qute://history
+        Then the javascript message "XSS" should not be logged

Vulnerable code:     return [{"url": e.url, "title": e.title or e.url, "time": e.atime}
            for e in entries]
Fixed code: import html
    return [{"url": html.escape(e.url),
             "title": html.escape(e.title) or html.escape(e.url),
             "time": e.atime} for e in entries]
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>&lt;img src=&quot;x&quot; onerror=&quot;console.log('XSS')&quot;&gt;foo</title>
    </head>
    <body>
        foo
    </body>
</html>

    Scenario: XSS in :history
        When I open data/issue4011.html
        And I open qute://history
        Then the javascript message "XSS" should not be logged