CVE-2019-9877: Invalid memory access in TextPage::findGaps() – xpdf-4.01

Invalid memory access in TextPage::findGaps() – xpdf-4.01

13 March, 2019

CVE Number

CVE-2019-9877

CWE

CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer

Product Details

Xpdf is a free PDF viewer and toolkit, including a text extractor, image converter, HTML converter, and more. Most of the tools are available as open source.
URL: https://www.xpdfreader.com/download.html

Vulnerable Versions

4.01

Vulnerability Details

During our research we discovered invalid memory access vulnerability in function TextPage::findGaps() located at TextOutputDev.c in xpdf-4.01. The same be triggered by sending a crafted pdf file to the pdftops binary. It allows an attacker to cause Denial of Service (Segmentation fault) or possibly have unspecified other impact.

SYNOPSIS

In Progress

vulnerable Source code
for (y = yMinI2; y <= yMaxI2; ++y) {
     ++horizProfile[y - yMinI];
   }
Analysis

DEBUG:
GDB :

[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x0000601e00028878  →  0x0000000000000000
$rbx   : 0x000060300001ef40  →  0x0000606000002600  →  0xbebebebebebebebe
$rcx   : 0x0               
$rdx   : 0xfffffffe00001888
$rsp   : 0x00007fffffffd580  →  0x00007fffffffd6e8  →  0xfff8000000000000
$rbp   : 0x00007fffffffd660  →  0x00007fffffffd7c0  →  0x00007fffffffd850  →  0x00007fffffffd910  →  0x00007fffffffd9b0  →  0x00007fffffffd9d0  →  0x00007fffffffda10  →  0x00007fffffffdb00
$rsi   : 0x34              
$rdi   : 0x000060300001efa0  →  0x0000625000005100  →  0x000060800000bf20  →  0x0000000100000041  →  0x0000000000000000
$rip   : 0x0000000000449cee  →   mov edx, DWORD PTR [rax]
$r8    : 0x00007ffff7fd3778  →  0x0000000000000000
$r9    : 0x15440           
$r10   : 0x0000602000026fd0  →  0x0000003400000000  →  0x0000000000000000
$r11   : 0x00007ffff7f22250  →  0x0000000000000000
$r12   : 0x00007fffffffdab0  →  0xbebebebe0000000d
$r13   : 0x00007fffffffde30  →  0x0000000000000012
$r14   : 0x0               
$r15   : 0x0               
$eflags: [CARRY PARITY adjust zero sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000 
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffd580│+0x0000: 0x00007fffffffd6e8  →  0xfff8000000000000     ← $rsp
0x00007fffffffd588│+0x0008: 0x00007fffffffd6e0  →  0xfff8000000000000
0x00007fffffffd590│+0x0010: 0x00007fffffffd6d8  →  0xfff8000000000000
0x00007fffffffd598│+0x0018: 0x00000000000080a0
0x00007fffffffd5a0│+0x0020: 0x000060300001efa0  →  0x0000625000005100  →  0x000060800000bf20  →  0x0000000100000041  →  0x0000000000000000
0x00007fffffffd5a8│+0x0028: 0x0000611000009280  →  0x0000000000000003
0x00007fffffffd5b0│+0x0030: 0x000060800000ba20  →  0x0000000e00000046  →  0x0000000000000000
0x00007fffffffd5b8│+0x0038: 0x0000062100000005  →  0x0000000000000000
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
     0x449cdf  lea    rdx, [rax*4+0x0]
     0x449ce7  mov    rax, QWORD PTR [rbp-0x38]
     0x449ceb  add    rax, rdx
→   0x449cee  mov    edx, DWORD PTR [rax]
     0x449cf0  add    edx, 0x1
     0x449cf3  mov    DWORD PTR [rax], edx
     0x449cf5  add    DWORD PTR [rbp-0x90], 0x1
     0x449cfc  jmp    0x449cc3 
     0x449cfe  mov    eax, DWORD PTR [rbp-0xa8]
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── source:/home/aceteam/Desktop/xpdf-4.01/xpdf/TextOutputDev.cc+3097 ────
   3092           yMinI2 = (int)floor(ch->yMin / splitPrecision);
   3093           yMaxI2 = (int)floor(ch->yMax / splitPrecision);
   3094           break;
   3095         }
   3096         for (y = yMinI2; y <= yMaxI2; ++y) {
        // horizProfile=0x00007fffffffd628  →  [...]  →  0x0000000000000000, yMinI=0x7fffffff, y=0x621
→ 3097           ++horizProfile[y - yMinI];
   3098         }
   3099         for (x = xMinI2; x <= xMaxI2; ++x) {
   3100           ++vertProfile[x - xMinI];
   3101         }
   3102       }
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "pdftotext", stopped, reason: SIGSEGV
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x449cee → TextPage::findGaps(this=0x611000009280, charsA=0x60300001efa0, rot=0x0, xMinOut=0x7fffffffd6d8, yMinOut=0x7fffffffd6e0, xMaxOut=0x7fffffffd6e8, yMaxOut=0x7fffffffd6f0, avgFontSizeOut=0x7fffffffd6f8, horizGaps=0x60300001ef70, vertGaps=0x60300001ef40)
[#1] 0x44841e → TextPage::split(this=0x611000009280, charsA=0x60300001efa0, rot=0x0)
[#2] 0x44818d → TextPage::splitChars(this=0x611000009280, charsA=0x603000001870)
[#3] 0x443fb4 → TextPage::writePhysLayout(this=0x611000009280, outputStream=0x61600000f380, outputFunc=0x44f988 , uMap=0x60600000d520, space=0x7fffffffd970 " \331\377\377\377\177", spaceLen=0x1, eol=0x7fffffffd990 "\n", eolLen=0x1)
[#4] 0x443932 → TextPage::write(this=0x611000009280, outputStream=0x61600000f380, outputFunc=0x44f988 )
[#5] 0x44ff13 → TextOutputDev::endPage(this=0x610000007f40)
[#6] 0x479901 → Gfx::~Gfx(this=0x60f00000e9b0, __in_chrg=)
[#7] 0x4e319f → Page::displaySlice(this=0x60800000bfa0, out=0x610000007f40, hDPI=72, vDPI=72, rotate=0x0, useMediaBox=0x0, crop=0x0, sliceX=0xffffffff, sliceY=0xffffffff, sliceW=0xffffffff, sliceH=0xffffffff, printing=0x0, abortCheckCbk=0x0, abortCheckCbkData=0x0)
[#8] 0x4e2b57 → Page::display(this=0x60800000bfa0, out=0x610000007f40, hDPI=72, vDPI=72, rotate=0x0, useMediaBox=0x0, crop=0x1, printing=0x0, abortCheckCbk=0x0, abortCheckCbkData=0x0)
[#9] 0x4e7bb7 → PDFDoc::displayPage(this=0x60700000dfb0, out=0x610000007f40, page=0x1, hDPI=72, vDPI=72, rotate=0x0, useMediaBox=0x0, crop=0x1, printing=0x0, abortCheckCbk=0x0, abortCheckCbkData=0x0)

gef➤  ptype ++horizProfile[y - yMinI]
type = int
gef➤  ptype horizProfile
type = int * 
gef➤  p/d y
$24 = 1569
gef➤  p/d yMinI
$25 = 2147483647
gef➤  x ++horizProfile[y - yMinI]
0x1:    Cannot access memory at address 0x1

Proof of Concept

pdftotext -f 1 -l 4 -layout -simple -table -lineprinter -raw -fixed 2 -clip -nodiag -upw rome $POC out.txt
POC FILE: REPRODUCER
Vendor Disclosure: 2019-3-13
Public Disclosure: 2019-3-20

Credit

Discovered by ACE Team – Loginsoft