CVE-2017-2818
An exploitable heap overflow vulnerability exists in the image rendering functionality of Poppler-0.53.0. A specifically crafted PDF can cause an overly large number of color components during image rendering, resulting in heap corruption. An attacker controlled PDF file can be used to trigger this vulnerability.
Poppler-0.53.0
https://poppler.freedesktop.org/
7.5 - CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE-122: Heap-based Buffer Overflow
Poppler is a shared library for displaying PDF files, used as middleware within different enterprise and opensource solutions alike (e.g. Gimp). It is forked off of XPDF, and is a complete implementation of the PDF ISO standard.
The Poppler library, by default, uses a private implementation of reading and rendering images. There is a compilation option for libjpeg support, but the flag is not enabled by default. This private implementation contains assumptions about the JPEG file headers that can lead to heap corruption when broken.
This vulnerability was formerly found (CVE-2005-3627) with a fix applied to DCTStream::readBaselineSOF, however the bug was not also fixed in the readProgressiveSOF function. A look at the two functions highlights the vulnerability:
There should be a check for: if (numComps <= 0 || numComps > 4)
at [0]
GBool DCTStream::readBaselineSOF() {
int length;
int prec;
int i;
int c;
length = read16();
prec = str->getChar();
height = read16();
width = read16();
numComps = str->getChar();
if (numComps <= 0 || numComps > 4) {
error(errSyntaxError, getPos(), "Bad number of components in DCT stream");
numComps = 0;
return gFalse;
if (prec != 8) {
error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec);
return gFalse;
//...
GBool DCTStream::readProgressiveSOF() {
int length;
int prec;
int i;
int c;
length = read16();
prec = str->getChar();
height = read16();
width = read16();
numComps = str->getChar();
// [0]
if (prec != 8) {
error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec);
return gFalse;
As there is no check on the numComps variable, the subsequent loop in DCTStream::readProgressiveSOF
can then write past the intended bounds of compInfo[3], and into heap metadata
for (i = 0; i < numComps; ++i) {
compInfo[i].id = str->getChar();
c = str->getChar();
compInfo[i].hSample = (c >> 4) & 0x0f;
compInfo[i].vSample = c & 0x0f;
compInfo[i].quantTable = str->getChar();
if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 ||
compInfo[i].vSample < 1 || compInfo[i].vSample > 4) {
error(errSyntaxError, getPos(), "Bad DCT sampling factor");
return gFalse;
if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) {
error(errSyntaxError, getPos(), "Bad DCT quant table selector");
return gFalse;
RAX: 0x7f8c6dfbaf50 --> 0x7f8c6dcb2760 (:~DCTStream()>: 0x530030b6c9058b48)
RBX: 0x142dd00 --> 0x1
RCX: 0x8
RDX: 0xffffffff
RSI: 0x0
RDI: 0x142cf50 --> 0x7f8c6dfbaf50 --> 0x7f8c6dcb2760 (:~DCTStream()>: 0x530030b6c9058b48)
RBP: 0x142de00 --> 0x100000001
RSP: 0x7ffce0c46010 --> 0x142e450 --> 0x7f8cfffffffd
RIP: 0x7f8c6dcb15f8 (:close()+40>: 0xe808c383483b8b48)
R8 : 0x3
R9 : 0x142c280 --> 0x142c660 --> 0x0
R10: 0x7f8c6d31bbe0 --> 0x0
R11: 0x1
R12: 0x142e100 --> 0x0
R13: 0x142e100 --> 0x0
R14: 0x142cf50 --> 0x7f8c6dfbaf50 --> 0x7f8c6dcb2760 (:~DCTStream()>: 0x530030b6c9058b48)
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x7f8c6dcb15e9 <DCTStream::close()+25>: mov r12,r13
0x7f8c6dcb15ec <DCTStream::close()+28>: lea rbp,[rbx+0x100]
0x7f8c6dcb15f3 <DCTStream::close()+35>: nop DWORD PTR [rax+rax*1+0x0]
=> 0x7f8c6dcb15f8 <DCTStream::close()+40>: mov rdi,QWORD PTR [rbx]
0x7f8c6dcb15fb <DCTStream::close()+43>: add rbx,0x8
0x7f8c6dcb15ff <DCTStream::close()+47>: call 0x7f8c6dbfd7a0 <gfree@plt>
0x7f8c6dcb1604 <DCTStream::close()+52>: mov QWORD PTR [rbx-0x8],0x0
0x7f8c6dcb160c <DCTStream::close()+60>: cmp rbx,rbp
[------------------------------------stack-------------------------------------]
0000| 0x7ffce0c46010 --> 0x142e450 --> 0x7f8cfffffffd
0008| 0x7ffce0c46018 --> 0x0
0016| 0x7ffce0c46020 --> 0x142e468 --> 0x8
0024| 0x7ffce0c46028 --> 0x1
0032| 0x7ffce0c46030 --> 0x0
0040| 0x7ffce0c46038 --> 0x7f8c6dc9bdf7 (:getChar(bool)+55>: 0xfff650e1e8e7894c)
0048| 0x7ffce0c46040 --> 0x142e450 --> 0x7f8cfffffffd
0056| 0x7ffce0c46048 --> 0x1429d98 --> 0xd ('\r')
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
2017-05-17 - Vendor Disclosure
2017-07-07 - Public Release
Discovered by Lilith Wyatt of Cisco Talos.