ソースを参照

address multi-byte codepoint truncation, allowing DOMPurify bypass

kudos @avlidienbrunn
El RIDO 3 週間 前
コミット
3d4d6bf0e8
2 ファイル変更32 行追加23 行削除
  1. 31 22
      js/privatebin.js
  2. 1 1
      lib/Configuration.php

+ 31 - 22
js/privatebin.js

@@ -1060,13 +1060,9 @@ jQuery.PrivateBin = (function($) {
          */
         function arraybufferToString(messageArray)
         {
-            const array = new Uint8Array(messageArray);
-            let message = '',
-                i       = 0;
-            while(i < array.length) {
-                message += String.fromCharCode(array[i++]);
-            }
-            return message;
+            return Array.from(new Uint8Array(messageArray))
+                .map(byte => String.fromCharCode(byte))
+                .join('');
         }
 
         /**
@@ -1082,11 +1078,10 @@ jQuery.PrivateBin = (function($) {
          */
         function stringToArraybuffer(message)
         {
-            const messageArray = new Uint8Array(message.length);
-            for (let i = 0; i < message.length; ++i) {
-                messageArray[i] = message.charCodeAt(i);
-            }
-            return messageArray;
+            return Uint8Array.from(
+                message,
+                character => character.charCodeAt(0)
+            );
         }
 
         /**
@@ -2959,10 +2954,10 @@ jQuery.PrivateBin = (function($) {
          function getBlobUrl(data, mimeType)
          {
             // Transform into a Blob
-            const buf = new Uint8Array(data.length);
-            for (let i = 0; i < data.length; ++i) {
-                buf[i] = data.charCodeAt(i);
-            }
+            const buf = Uint8Array.from(
+                data,
+                character => character.charCodeAt(0)
+            );
             const blob = new window.Blob(
                 [buf],
                 {
@@ -3025,12 +3020,26 @@ jQuery.PrivateBin = (function($) {
             // right-clicks/long-taps and opens the SVG in a new tab - prevented
             // in the preview by use of an img tag, which disables scripts, too
             if (mimeType.startsWith('image\/svg')) {
-                const sanitizedData = DOMPurify.sanitize(
-                    decodedData,
-                    purifySvgConfig
-                );
-                sanitizedMimeType = 'image/svg+xml';
-                blobUrl = getBlobUrl(sanitizedData, sanitizedMimeType);
+                try {
+                    const decodedBuffer = Uint8Array.from(
+                        decodedData,
+                        character => character.charCodeAt(0)
+                    );
+                    const svgText = new TextDecoder(
+                        'utf-8',
+                        {fatal: true}
+                    ).decode(decodedBuffer);
+                    const sanitizedData = DOMPurify.sanitize(
+                        svgText,
+                        purifySvgConfig
+                    );
+                    sanitizedMimeType = 'image/svg+xml';
+                    blobUrl = getBlobUrl(sanitizedData, sanitizedMimeType);
+                } catch {
+                    // Invalid or non-UTF-8 SVG: download only, no preview.
+                    sanitizedMimeType = 'application/octet-stream';
+                    blobUrl = getBlobUrl(decodedData, sanitizedMimeType);
+                }
             }
 
             template.removeClass('hidden');

+ 1 - 1
lib/Configuration.php

@@ -122,7 +122,7 @@ class Configuration
             'js/kjua-0.10.0.js'      => 'sha512-BYj4xggowR7QD150VLSTRlzH62YPfhpIM+b/1EUEr7RQpdWAGKulxWnOvjFx1FUlba4m6ihpNYuQab51H6XlYg==',
             'js/legacy.js'           => 'sha512-RQEo1hxpNc37i+jz/D9/JiAZhG8GFx3+SNxjYnI7jUgirDIqrCSj6QPAAZeaidditcWzsJ3jxfEj5lVm7ZwTRQ==',
             'js/prettify.js'         => 'sha512-puO0Ogy++IoA2Pb9IjSxV1n4+kQkKXYAEUtVzfZpQepyDPyXk8hokiYDS7ybMogYlyyEIwMLpZqVhCkARQWLMg==',
-            'js/privatebin.js'       => 'sha512-XkiRov+DDF3Un0/+xqnGV8OEzXB6/6XispcDGFPCO3ZtvRsJwoEasfZf3DI2a2eo06zkGl/rn6Yb5SChRNtYSw==',
+            'js/privatebin.js'       => 'sha512-QIra+y54toe5mFbF2a5qvirz3HkBPlrkmLqsU9hH+j2XltSeXorcN5/SjjowNYQy0iru+F9IYEQzlO2304oOow==',
             'js/purify-3.4.1.js'     => 'sha512-280a/Vb6fVFsYaeRrkuDp4EDmdYlt2XS+dlDEO/U9qljPrAraA2bIzHTNmP+9dpwPDDwTML+RS+h5iaagPwTzA==',
             'js/showdown-2.1.0.js'   => 'sha512-WYXZgkTR0u/Y9SVIA4nTTOih0kXMEd8RRV6MLFdL6YU8ymhR528NLlYQt1nlJQbYz4EW+ZsS0fx1awhiQJme1Q==',
             'js/zlib-1.3.2.js'       => 'sha512-RAhJgxg9siMIA8ky4c10Rc2zUgnK80olHB8Tt1IOYWY4Eh1WmrviQkDn+sgBlb38ZHq3tzufGC41kP360gmosQ==',