What is the vulnerability?
Invalid Memory Access vulnerability is discovered in the pdofo (0.9.6 - Trunk r1952). The same can be triggered by sending a crafted pdf file to the podofocrop binary. It allows an attacker to cause Denial of Service (Segmentation fault) or possibly have unspecified other impact when a victim opens a specially crafted file.
Version - 0.9.6 -Trunk r1952
Tested environment - 32-bit ubuntu 16.04 LTS
Command - podofocrop $POC test.pdf
vulnerable code -
void crop_page( PdfPage* pPage, const PdfRect & rCropBox )
{
PdfVariant var;
/*
printf("%f %f %f %f\n",
rCropBox.GetLeft(),
rCropBox.GetBottom(),
rCropBox.GetWidth(),
rCropBox.GetHeight());
*/
rCropBox.ToVariant( var );
pPage->GetObject()->GetDictionary().AddKey( PdfName("MediaBox"), var );
}
Synopsis -
As per our research, the vulnerability exits in function crop_page() in podofocrop.cpp. When the line pPage->GetObject()->GetDictionary().AddKey( PdfName("MediaBox"), var ); executes, it calls the function GetObject() located in PdfElement.h, which returns m_pObjects, located at location [DWORD ESP+0X4].
inline PdfObject* PdfElement::GetObject()
{
return m_pObject;
}
Valid scenario -
Gdb:
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0xb4e00f10 → 0x081d4958 → 0x081342a0 → <PoDoFo::PdfInfo::~PdfInfo()+0> push ebp
$ebx : 0xb4e00f10 → 0x081d4958 → 0x081342a0 → <PoDoFo::PdfInfo::~PdfInfo()+0> push ebp
$ecx : 0x0
$edx : 0x7
$esp : 0xbffff188 → 0xbffff318 → 0xbffff338 → 0xbffff3d8 → 0xbffff418 → 0xbffff568 → 0x00000000
$ebp : 0xbffff188 → 0xbffff318 → 0xbffff338 → 0xbffff3d8 → 0xbffff418 → 0xbffff568 → 0x00000000
$esi : 0xb4403bc0 → 0x081dad68 → 0x08177cde → <PoDoFo::PdfParserObject::~PdfParserObject()+0> push ebp
$edi : 0xb74e8000 → 0x001b1db0
$eip : 0x080f7253 → <PoDoFo::PdfElement::GetObject()+3> mov eax, DWORD PTR [ebp+0x8]
$eflags: [carry parity ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 IDENTIFICATION]
$cs: 0x0073 $ss: 0x007b $ds: 0x007b $es: 0x007b $fs: 0x0000 $gs: 0x0033
───────────────────────────────────────────────────────────────────────────────────────── stack ────
0xbffff188│+0x0000: 0xbffff318 → 0xbffff338 → 0xbffff3d8 → 0xbffff418 → 0xbffff568 → 0x00000000 ← $esp, $ebp
0xbffff18c│+0x0004: 0x08134434 → <PoDoFo::PdfInfo::Init(int)+312> add esp, 0x10
0xbffff190│+0x0008: 0xb4e00f10 → 0x081d4958 → 0x081342a0 → <PoDoFo::PdfInfo::~PdfInfo()+0> push ebp
0xbffff194│+0x000c: 0x081d48df → "ModDate"
0xbffff198│+0x0010: 0x00000006
0xbffff19c│+0x0014: 0x00000000
0xbffff1a0│+0x0018: 0xb4601a50 → 0x081c2f94 → 0x080f7e9c → <PoDoFo::PdfDictionary::~PdfDictionary()+0> push ebp
0xbffff1a4│+0x001c: 0x00000000
─────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0x80f724f nop
0x80f7250 <PoDoFo::PdfElement::GetObject()+0> push ebp
0x80f7251 <PoDoFo::PdfElement::GetObject()+1> mov ebp, esp
→ 0x80f7253 <PoDoFo::PdfElement::GetObject()+3> mov eax, DWORD PTR [ebp+0x8]
0x80f7256 <PoDoFo::PdfElement::GetObject()+6> mov eax, DWORD PTR [eax+0x4]
0x80f7259 <PoDoFo::PdfElement::GetObject()+9> pop ebp
0x80f725a <PoDoFo::PdfElement::GetObject()+10> ret
0x80f725b nop
0x80f725c <std::vector<PoDoFo::PdfRect,+0> push ebp
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── source:/home/loginsoft/podofo-code-r1952-podofo-trunk/src/doc/PdfElement.h+180 ────
178 inline PdfObject* PdfElement::GetObject()
179 {
→ 180 return m_pObject;
181 }
──────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "podofocrop", stopped, reason: BREAKPOINT
───────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x80f7253 → PoDoFo::PdfElement::GetObject(this=0xb4e00f10)
[#1] 0x8134434 → PoDoFo::PdfInfo::Init(this=0xb4e00f10, eInitial=0x2)
[#2] 0x8134279 → PoDoFo::PdfInfo::PdfInfo(this=0xb4e00f10, pObject=0xb4402a40, eInitial=0x2)
[#3] 0x81357d9 → PoDoFo::PdfMemDocument::InitFromParser(this=0xbffff464, pParser=0xb4003e40)
[#4] 0x8135b9d → PoDoFo::PdfMemDocument::Load(this=0xbffff464, pszFilename=0xbffff776 "crash1", bForUpdate=0x0)
[#5] 0x80f6592 → main(argc=0x3, argv=0xbffff614)
─────────────────────────────────────────────────────────────────────────────
gef➤ p m_pObject
$15 = (PoDoFo::PdfObject *) 0xb4402a40
gef➤ p $eax+0x4
$16 = 0xb4e00f14
gef➤ x/8w $eax+0x4
0xb4e00f14: 0xb4402a40 0x0 0x0 0x2
0xb4e00f24: 0x4ffffff 0xc 0x30800002 0x81c4a60
Vulnerable scenario -
When a crafted pdf is passed to the binary, it changes the return address m_pObjects of the function PdfElement::GetObject(), leading to an Invalid Memory Access vulnerability.
GDB : 180 return m_pObject; [ Legend: Modified register | Code | Heap | Stack | String] ────────────────────────────────────────────────────────────────────────────────────────────────────── registers ──── $eax : 0x0 $ebx : 0x2 $ecx : 0x0 $edx : 0x8 $esp : 0xbffff388 → 0xbffff418 → 0xbffff568 → 0x00000000 $ebp : 0xbffff388 → 0xbffff418 → 0xbffff568 → 0x00000000 $esi : 0xb74e8000 → 0x001b1db0 $edi : 0xb74e8000 → 0x001b1db0 $eip : 0x080f7253 → <PoDoFo::PdfElement::GetObject()+3> mov eax, DWORD PTR [ebp+0x8] $eflags: [carry parity ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 IDENTIFICATION] $cs: 0x0073 $ss: 0x007b $ds: 0x007b $es: 0x007b $fs: 0x0000 $gs: 0x0033 ───────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ──── 0xbffff388│+0x0000: 0xbffff418 → 0xbffff568 → 0x00000000 ← $esp, $ebp 0xbffff38c│+0x0004: 0x080f5e6b → <crop_page(PoDoFo::PdfPage*,+0> add esp, 0x10 0xbffff390│+0x0008: 0x00000000 0xbffff394│+0x000c: 0x081c281b → "MediaBox" 0xbffff398│+0x0010: 0x00000005 0xbffff39c│+0x0014: 0x00000000 0xbffff3a0│+0x0018: 0x00000000 0xbffff3a4│+0x001c: 0xb5a01420 → 0x2a000006 → 0x00000000 ────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ──── 0x80f724f nop 0x80f7250 <PoDoFo::PdfElement::GetObject()+0> push ebp 0x80f7251 <PoDoFo::PdfElement::GetObject()+1> mov ebp, esp → 0x80f7253 <PoDoFo::PdfElement::GetObject()+3> mov eax, DWORD PTR [ebp+0x8] 0x80f7256 <PoDoFo::PdfElement::GetObject()+6> mov eax, DWORD PTR [eax+0x4] 0x80f7259 <PoDoFo::PdfElement::GetObject()+9> pop ebp 0x80f725a <PoDoFo::PdfElement::GetObject()+10> ret 0x80f725b nop 0x80f725c <std::vector<PoDoFo::PdfRect,+0> push ebp ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── source:/home/loginsoft/podofo-code-r1952-podofo-trunk/src/doc/PdfElement.h+180 ──── 178 inline PdfObject* PdfElement::GetObject() 179 { → 180 return m_pObject; 181 } ──────────────────────────────────────── threads ──── [#0] Id 1, Name: "podofocrop", stopped, reason: BREAKPOINT ─────────────────────────────────────────────────────────────────────────────── trace ──── [#0] 0x80f7253 → PoDoFo::PdfElement::GetObject(this=0x0) [#1] 0x80f5e6b → crop_page(pPage=0x0, rCropBox=@0xb5001760) [#2] 0x80f66ba → main(argc=0x3, argv=0xbffff614) ───────────────────────────────────────────────────────────── gef➤ p m_pObject Cannot access memory at address 0x4 gef➤ p $eax+0x4 $19 = 0x4 gef➤ x/w $eax+0x4 0x4: Cannot access memory at address 0x4
Tickets: #1
Tickets: #2
Tickets: #3
Tickets: #4
Tickets: #5
The security vulnerability isn't in PdfElement::GetObject() because it can't be fixed there: calling a method from a nullptr object is undefined behaviour - nothing in the method can change that. Therefore, the usage of pPage in the function crop_page() of podofocrop.cpp needs a nullptr check.
Right! Now it makes sense. We tried to analyse what payload in the PDF causing this issue.
Thanks for the clarification.
IMHO the fix for this is so simple that testing with GCC 4.9.2 is enough (sorry, I don't have a newer one handy these days), which I have done, so I'm closing here. Further independent corroboration (or showing flaws) is still welcomed though, including with differences in build configuration (mine isn't with full library support, and I've checked only the shared build, please speak up e.g. on the mailing list when you've got reasoning/evidence this isn't enough for this simple case).
We have tested in GCC version 5.5.0 and able to reproduce the issue by configuring full Library. I missed the point here, do you mean that we need to compile with older versions of GCC ?
Curious to know, can't this issue be fixed by having a NULL check against 'pPage', which solves the issue though the user is using latest GCC version?
Did you use the latest revision of PoDoFo, i.e. svn r1954 which does contain the NULL check on pPage? I had hoped this issue isn't dependent on the compiler version (nor library configuration) ... I'll have to check with a newer GCC version (could be newer than 5.5.0 though, IIRC GCC 7, currently 7.3, is supported upstream and in the newest Ubuntu LTS, 18.04, section main).
Last edit: Matthew Brincke 2018-11-27
We now confirm that the issue has been fixed in svn r1954.
FTR, this has been given CVE-2018-20751