Changeset 94828 in webkit


Ignore:
Timestamp:
Sep 8, 2011 7:40:09 PM (13 years ago)
Author:
dbates@webkit.org
Message:

XSS filter bypass via non-standard URL encoding
https://bugs.webkit.org/show_bug.cgi?id=66588

Reviewed by Adam Barth.

Source/WebCore:

Tests: http/tests/security/xssAuditor/script-tag-with-16bit-unicode-surrogate-pair.html

http/tests/security/xssAuditor/script-tag-with-16bit-unicode.html
http/tests/security/xssAuditor/script-tag-with-16bit-unicode2.html
http/tests/security/xssAuditor/script-tag-with-16bit-unicode3.html
http/tests/security/xssAuditor/script-tag-with-16bit-unicode4.html
http/tests/security/xssAuditor/script-tag-with-16bit-unicode5.html
http/tests/security/xssAuditor/script-tag-with-three-times-url-encoded-16bit-unicode.html
http/tests/security/xssAuditor/window-open-without-url-should-not-assert.html

Implement support for decoding non-standard 16-bit Unicode escape sequences of
the form %u26C4 as described in <http://www.w3.org/International/iri-edit/draft-duerst-iri.html#anchor29>.

See also <http://en.wikipedia.org/wiki/Percent-encoding#Non-standard_implementations>.

  • GNUmakefile.list.am: Added DecodeEscapeSequences.h.
  • WebCore.gypi: Ditto.
  • WebCore.pro: Ditto.
  • WebCore.vcproj/WebCore.vcproj: Ditto.
  • WebCore.xcodeproj/project.pbxproj: Ditto.
  • html/parser/XSSAuditor.cpp:

(WebCore::decode16BitUnicodeEscapeSequences): Added.
(WebCore::decodeStandardURLEscapeSequences): Added.
(WebCore::fullyDecodeString): Modified to call decode16BitUnicodeEscapeSequences().
(WebCore::XSSAuditor::init): Modified to return early when the URL of the document
is the empty string. This can happen when opening a new browser window or calling
window.open("").

  • platform/KURL.cpp:

(WebCore::decodeURLEscapeSequences): Abstracted code into template-function decodeEscapeSequences().
This function just calls decodeEscapeSequences<URLEscapeSequence>().

  • platform/text/DecodeEscapeSequences.h: Added.

(WebCore::Unicode16BitEscapeSequence::findInString):
(WebCore::Unicode16BitEscapeSequence::matchStringPrefix):
(WebCore::Unicode16BitEscapeSequence::decodeRun):
(WebCore::URLEscapeSequence::findInString):
(WebCore::URLEscapeSequence::matchStringPrefix):
(WebCore::URLEscapeSequence::decodeRun):
(WebCore::decodeEscapeSequences):

LayoutTests:

Add tests for decoding non-standard 16-bit Unicode escape sequences.

Also add a test to ensure that we don't cause an assertion failure when
calling window.open("").

  • http/tests/security/xssAuditor/resources/echo-intertag-decode-16bit-unicode.pl: Added.

(isUTF16Surrogate):
(decodeRunOf16BitUnicodeEscapeSequences):
(decode16BitUnicodeEscapeSequences):

  • http/tests/security/xssAuditor/script-tag-with-16bit-unicode-expected.txt: Added.
  • http/tests/security/xssAuditor/script-tag-with-16bit-unicode-surrogate-pair-expected.txt: Added.
  • http/tests/security/xssAuditor/script-tag-with-16bit-unicode-surrogate-pair.html: Added.
  • http/tests/security/xssAuditor/script-tag-with-16bit-unicode.html: Added.
  • http/tests/security/xssAuditor/script-tag-with-16bit-unicode2-expected.txt: Added.
  • http/tests/security/xssAuditor/script-tag-with-16bit-unicode2.html: Added.
  • http/tests/security/xssAuditor/script-tag-with-16bit-unicode3-expected.txt: Added.
  • http/tests/security/xssAuditor/script-tag-with-16bit-unicode3.html: Added.
  • http/tests/security/xssAuditor/script-tag-with-16bit-unicode4-expected.txt: Added.
  • http/tests/security/xssAuditor/script-tag-with-16bit-unicode4.html: Added.
  • http/tests/security/xssAuditor/script-tag-with-16bit-unicode5-expected.txt: Added.
  • http/tests/security/xssAuditor/script-tag-with-16bit-unicode5.html: Added.
  • http/tests/security/xssAuditor/script-tag-with-fancy-unicode-expected.txt: Updated expected

result since we now pass this test. We should rename this file to something more descriptive,
see <https://bugs.webkit.org/show_bug.cgi?id=67818>.

  • http/tests/security/xssAuditor/script-tag-with-three-times-url-encoded-16bit-unicode-expected.txt: Added.
  • http/tests/security/xssAuditor/script-tag-with-three-times-url-encoded-16bit-unicode.html: Added.
  • http/tests/security/xssAuditor/window-open-without-url-should-not-assert-expected.txt: Added.
  • http/tests/security/xssAuditor/window-open-without-url-should-not-assert.html: Added.
Location:
trunk
Files:
18 added
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r94827 r94828  
     12011-09-08  Daniel Bates  <dbates@webkit.org>
     2
     3        XSS filter bypass via non-standard URL encoding
     4        https://bugs.webkit.org/show_bug.cgi?id=66588
     5
     6        Reviewed by Adam Barth.
     7
     8        Add tests for decoding non-standard 16-bit Unicode escape sequences.
     9
     10        Also add a test to ensure that we don't cause an assertion failure when
     11        calling window.open("").
     12
     13        * http/tests/security/xssAuditor/resources/echo-intertag-decode-16bit-unicode.pl: Added.
     14        (isUTF16Surrogate):
     15        (decodeRunOf16BitUnicodeEscapeSequences):
     16        (decode16BitUnicodeEscapeSequences):
     17        * http/tests/security/xssAuditor/script-tag-with-16bit-unicode-expected.txt: Added.
     18        * http/tests/security/xssAuditor/script-tag-with-16bit-unicode-surrogate-pair-expected.txt: Added.
     19        * http/tests/security/xssAuditor/script-tag-with-16bit-unicode-surrogate-pair.html: Added.
     20        * http/tests/security/xssAuditor/script-tag-with-16bit-unicode.html: Added.
     21        * http/tests/security/xssAuditor/script-tag-with-16bit-unicode2-expected.txt: Added.
     22        * http/tests/security/xssAuditor/script-tag-with-16bit-unicode2.html: Added.
     23        * http/tests/security/xssAuditor/script-tag-with-16bit-unicode3-expected.txt: Added.
     24        * http/tests/security/xssAuditor/script-tag-with-16bit-unicode3.html: Added.
     25        * http/tests/security/xssAuditor/script-tag-with-16bit-unicode4-expected.txt: Added.
     26        * http/tests/security/xssAuditor/script-tag-with-16bit-unicode4.html: Added.
     27        * http/tests/security/xssAuditor/script-tag-with-16bit-unicode5-expected.txt: Added.
     28        * http/tests/security/xssAuditor/script-tag-with-16bit-unicode5.html: Added.
     29        * http/tests/security/xssAuditor/script-tag-with-fancy-unicode-expected.txt: Updated expected
     30        result since we now pass this test. We should rename this file to something more descriptive,
     31        see <https://bugs.webkit.org/show_bug.cgi?id=67818>.
     32        * http/tests/security/xssAuditor/script-tag-with-three-times-url-encoded-16bit-unicode-expected.txt: Added.
     33        * http/tests/security/xssAuditor/script-tag-with-three-times-url-encoded-16bit-unicode.html: Added.
     34        * http/tests/security/xssAuditor/window-open-without-url-should-not-assert-expected.txt: Added.
     35        * http/tests/security/xssAuditor/window-open-without-url-should-not-assert.html: Added.
     36
    1372011-09-08  Fumitoshi Ukai  <ukai@chromium.org>
    238
  • trunk/LayoutTests/http/tests/security/xssAuditor/script-tag-with-fancy-unicode-expected.txt

    r78776 r94828  
    1 ALERT: /XSS/
     1CONSOLE MESSAGE: line 1: Refused to execute a JavaScript script. Source code of script found within request.
    22
     3
  • trunk/Source/WebCore/ChangeLog

    r94825 r94828  
     12011-09-08  Daniel Bates  <dbates@webkit.org>
     2
     3        XSS filter bypass via non-standard URL encoding
     4        https://bugs.webkit.org/show_bug.cgi?id=66588
     5
     6        Reviewed by Adam Barth.
     7
     8        Tests: http/tests/security/xssAuditor/script-tag-with-16bit-unicode-surrogate-pair.html
     9               http/tests/security/xssAuditor/script-tag-with-16bit-unicode.html
     10               http/tests/security/xssAuditor/script-tag-with-16bit-unicode2.html
     11               http/tests/security/xssAuditor/script-tag-with-16bit-unicode3.html
     12               http/tests/security/xssAuditor/script-tag-with-16bit-unicode4.html
     13               http/tests/security/xssAuditor/script-tag-with-16bit-unicode5.html
     14               http/tests/security/xssAuditor/script-tag-with-three-times-url-encoded-16bit-unicode.html
     15               http/tests/security/xssAuditor/window-open-without-url-should-not-assert.html
     16
     17        Implement support for decoding non-standard 16-bit Unicode escape sequences of
     18        the form %u26C4 as described in <http://www.w3.org/International/iri-edit/draft-duerst-iri.html#anchor29>.
     19
     20        See also <http://en.wikipedia.org/wiki/Percent-encoding#Non-standard_implementations>.
     21
     22        * GNUmakefile.list.am: Added DecodeEscapeSequences.h.
     23        * WebCore.gypi: Ditto.
     24        * WebCore.pro: Ditto.
     25        * WebCore.vcproj/WebCore.vcproj: Ditto.
     26        * WebCore.xcodeproj/project.pbxproj: Ditto.
     27        * html/parser/XSSAuditor.cpp:
     28        (WebCore::decode16BitUnicodeEscapeSequences): Added.
     29        (WebCore::decodeStandardURLEscapeSequences): Added.
     30        (WebCore::fullyDecodeString): Modified to call decode16BitUnicodeEscapeSequences().
     31        (WebCore::XSSAuditor::init): Modified to return early when the URL of the document
     32        is the empty string. This can happen when opening a new browser window or calling
     33        window.open("").
     34        * platform/KURL.cpp:
     35        (WebCore::decodeURLEscapeSequences): Abstracted code into template-function decodeEscapeSequences().
     36        This function just calls decodeEscapeSequences<URLEscapeSequence>().
     37        * platform/text/DecodeEscapeSequences.h: Added.
     38        (WebCore::Unicode16BitEscapeSequence::findInString):
     39        (WebCore::Unicode16BitEscapeSequence::matchStringPrefix):
     40        (WebCore::Unicode16BitEscapeSequence::decodeRun):
     41        (WebCore::URLEscapeSequence::findInString):
     42        (WebCore::URLEscapeSequence::matchStringPrefix):
     43        (WebCore::URLEscapeSequence::decodeRun):
     44        (WebCore::decodeEscapeSequences):
     45
    1462011-09-08  Adam Barth  <abarth@webkit.org>
    247
  • trunk/Source/WebCore/GNUmakefile.list.am

    r94694 r94828  
    28222822        Source/WebCore/platform/text/BidiResolver.h \
    28232823        Source/WebCore/platform/text/BidiRunList.h \
     2824        Source/WebCore/platform/text/DecodeEscapeSequences.h \
    28242825        Source/WebCore/platform/text/Hyphenation.cpp \
    28252826        Source/WebCore/platform/text/Hyphenation.h \
  • trunk/Source/WebCore/WebCore.gypi

    r94783 r94828  
    847847            'platform/text/BidiContext.h',
    848848            'platform/text/BidiResolver.h',
     849            'platform/text/DecodeEscapeSequences.h',
    849850            'platform/text/LineBreakIteratorPoolICU.h',
    850851            'platform/text/LineEnding.h',
  • trunk/Source/WebCore/WebCore.pro

    r94694 r94828  
    21132113    platform/text/Base64.h \
    21142114    platform/text/BidiContext.h \
     2115    platform/text/DecodeEscapeSequences.h \
    21152116    platform/text/Hyphenation.h \
    21162117    platform/text/QuotedPrintable.h \
  • trunk/Source/WebCore/WebCore.vcproj/WebCore.vcproj

    r94656 r94828  
    3155031550                                </File>
    3155131551                                <File
     31552                                        RelativePath="..\platform\text\DecodeEscapeSequences.h"
     31553                                        >
     31554                                </File>
     31555                                <File
    3155231556                                        RelativePath="..\platform\text\Hyphenation.h"
    3155331557                                        >
  • trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj

    r94699 r94828  
    54505450                CEA3949C11D45CDA003094CF /* StaticHashSetNodeList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CEA3949A11D45CDA003094CF /* StaticHashSetNodeList.cpp */; };
    54515451                CEA3949D11D45CDA003094CF /* StaticHashSetNodeList.h in Headers */ = {isa = PBXBuildFile; fileRef = CEA3949B11D45CDA003094CF /* StaticHashSetNodeList.h */; };
     5452                CECCFC3B141973D5002A0AC1 /* DecodeEscapeSequences.h in Headers */ = {isa = PBXBuildFile; fileRef = CECCFC3A141973D5002A0AC1 /* DecodeEscapeSequences.h */; };
    54525453                CEF418CE1179678C009D112C /* ViewportArguments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CEF418CC1179678C009D112C /* ViewportArguments.cpp */; };
    54535454                CEF418CF1179678C009D112C /* ViewportArguments.h in Headers */ = {isa = PBXBuildFile; fileRef = CEF418CD1179678C009D112C /* ViewportArguments.h */; settings = {ATTRIBUTES = (Private, ); }; };
     
    1220912210                CEA3949A11D45CDA003094CF /* StaticHashSetNodeList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticHashSetNodeList.cpp; sourceTree = "<group>"; };
    1221012211                CEA3949B11D45CDA003094CF /* StaticHashSetNodeList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StaticHashSetNodeList.h; sourceTree = "<group>"; };
     12212                CECCFC3A141973D5002A0AC1 /* DecodeEscapeSequences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DecodeEscapeSequences.h; sourceTree = "<group>"; };
    1221112213                CEF418CC1179678C009D112C /* ViewportArguments.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ViewportArguments.cpp; sourceTree = "<group>"; };
    1221212214                CEF418CD1179678C009D112C /* ViewportArguments.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewportArguments.h; sourceTree = "<group>"; };
     
    1838518387                                B2C3D9F40D006C1D00EF6F26 /* BidiResolver.h */,
    1838618388                                A8C402921348B2220063F1E5 /* BidiRunList.h */,
     18389                                CECCFC3A141973D5002A0AC1 /* DecodeEscapeSequences.h */,
    1838718390                                375CD231119D43C800A2A859 /* Hyphenation.h */,
    1838818391                                A5ABB78613B904BC00F197E3 /* LineBreakIteratorPoolICU.h */,
     
    2347723480                                1A927FD31416A15B003A83C8 /* npruntime.h in Headers */,
    2347823481                                1A927FD41416A15B003A83C8 /* nptypes.h in Headers */,
     23482                                CECCFC3B141973D5002A0AC1 /* DecodeEscapeSequences.h in Headers */,
    2347923483                        );
    2348023484                        runOnlyForDeploymentPostprocessing = 0;
  • trunk/Source/WebCore/html/parser/XSSAuditor.cpp

    r94225 r94828  
    11/*
    22 * Copyright (C) 2011 Adam Barth. All Rights Reserved.
     3 * Copyright (C) 2011 Daniel Bates (dbates@intudata.com).
    34 *
    45 * Redistribution and use in source and binary forms, with or without
     
    2930#include "Console.h"
    3031#include "DOMWindow.h"
     32#include "DecodeEscapeSequences.h"
    3133#include "Document.h"
    3234#include "DocumentLoader.h"
     
    116118}
    117119
     120static inline String decode16BitUnicodeEscapeSequences(const String& string)
     121{
     122    // Note, the encoding is ignored since each %u-escape sequence represents a UTF-16 code unit.
     123    return decodeEscapeSequences<Unicode16BitEscapeSequence>(string, UTF8Encoding());
     124}
     125
     126static inline String decodeStandardURLEscapeSequences(const String& string, const TextEncoding& encoding)
     127{
     128    // We use decodeEscapeSequences() instead of decodeURLEscapeSequences() (declared in KURL.h) to
     129    // avoid platform-specific URL decoding differences (e.g. KURLGoogle).
     130    return decodeEscapeSequences<URLEscapeSequence>(string, encoding);
     131}
     132
    118133static String fullyDecodeString(const String& string, const TextResourceDecoder* decoder)
    119134{
     135    const TextEncoding& encoding = decoder ? decoder->encoding() : UTF8Encoding();
    120136    size_t oldWorkingStringLength;
    121137    String workingString = string;
    122138    do {
    123139        oldWorkingStringLength = workingString.length();
    124         workingString = decodeURLEscapeSequences(workingString);
     140        workingString = decode16BitUnicodeEscapeSequences(decodeStandardURLEscapeSequences(workingString, encoding));
    125141    } while (workingString.length() < oldWorkingStringLength);
    126     if (decoder) {
    127         CString workingStringUTF8 = workingString.utf8();
    128         String decodedString = decoder->encoding().decode(workingStringUTF8.data(), workingStringUTF8.length());
    129         if (!decodedString.isEmpty())
    130             workingString = decodedString;
    131     }
     142    ASSERT(!workingString.isEmpty());
    132143    workingString.replace('+', ' ');
    133144    workingString = canonicalize(workingString);
     
    169180
    170181    const KURL& url = m_parser->document()->url();
     182
     183    if (url.isEmpty()) {
     184        // The URL can be empty when opening a new browser window or calling window.open("").
     185        m_isEnabled = false;
     186        return;
     187    }
    171188
    172189    if (url.protocolIsData()) {
  • trunk/Source/WebCore/platform/KURL.cpp

    r94640 r94828  
    2727#include "KURL.h"
    2828
     29#include "DecodeEscapeSequences.h"
    2930#include "TextEncoding.h"
    3031#include <stdio.h>
     
    252253}
    253254
    254 static inline int hexDigitValue(UChar c)
    255 {
    256     ASSERT(isASCIIHexDigit(c));
    257     if (c < 'A')
    258         return c - '0';
    259     return (c - 'A' + 10) & 0xF; // handle both upper and lower case without a branch
    260 }
    261 
    262255// Copies the source to the destination, assuming all the source characters are
    263256// ASCII. The destination buffer must be large enough. Null characters are allowed
     
    934927}
    935928
    936 String decodeURLEscapeSequences(const String& str)
    937 {
    938     return decodeURLEscapeSequences(str, UTF8Encoding());
    939 }
    940 
    941 String decodeURLEscapeSequences(const String& str, const TextEncoding& encoding)
    942 {
    943     StringBuilder result;
    944 
    945     CharBuffer buffer;
    946 
    947     unsigned length = str.length();
    948     unsigned decodedPosition = 0;
    949     unsigned searchPosition = 0;
    950     size_t encodedRunPosition;
    951     while ((encodedRunPosition = str.find('%', searchPosition)) != notFound) {
    952         // Find the sequence of %-escape codes.
    953         unsigned encodedRunEnd = encodedRunPosition;
    954         while (length - encodedRunEnd >= 3
    955                 && str[encodedRunEnd] == '%'
    956                 && isASCIIHexDigit(str[encodedRunEnd + 1])
    957                 && isASCIIHexDigit(str[encodedRunEnd + 2]))
    958             encodedRunEnd += 3;
    959         searchPosition = encodedRunEnd;
    960         if (encodedRunEnd == encodedRunPosition) {
    961             ++searchPosition;
    962             continue;
    963         }
    964 
    965         // Decode the %-escapes into bytes.
    966         unsigned runLength = (encodedRunEnd - encodedRunPosition) / 3;
    967         buffer.resize(runLength);
    968         char* p = buffer.data();
    969         const UChar* q = str.characters() + encodedRunPosition;
    970         for (unsigned i = 0; i < runLength; ++i) {
    971             *p++ = (hexDigitValue(q[1]) << 4) | hexDigitValue(q[2]);
    972             q += 3;
    973         }
    974 
    975         // Decode the bytes into Unicode characters.
    976         String decoded = (encoding.isValid() ? encoding : UTF8Encoding()).decode(buffer.data(), p - buffer.data());
    977         if (decoded.isEmpty())
    978             continue;
    979 
    980         // Build up the string with what we just skipped and what we just decoded.
    981         result.append(str.characters() + decodedPosition, encodedRunPosition - decodedPosition);
    982         result.append(decoded);
    983         decodedPosition = encodedRunEnd;
    984     }
    985 
    986     result.append(str.characters() + decodedPosition, length - decodedPosition);
    987 
    988     return result.toString();
     929String decodeURLEscapeSequences(const String& string)
     930{
     931    return decodeEscapeSequences<URLEscapeSequence>(string, UTF8Encoding());
     932}
     933
     934String decodeURLEscapeSequences(const String& string, const TextEncoding& encoding)
     935{
     936    return decodeEscapeSequences<URLEscapeSequence>(string, encoding);
    989937}
    990938
Note: See TracChangeset for help on using the changeset viewer.