TSSA-2011-01 xpdf : multiple vulnerabilities allow remote code execution



---------------------------------------------------------------------------
* xpdf : multiple vulnerabilities in t1lib *
* allow arbitrary remote code execution *
-
---------------------------------------------------------------------------




- --[ Vulnerability Summary:

Date Published: 28/03/2011
Last Update: 28/03/2011
Advisory ID: TSSA-2011-01
CVE Name: CVE-2011-0764 (previously known as VU#376500)
Title: xpdf : multiple vulnerabilities in t1lib
Remotely Exploitable: Yes
Locally Exploitable: No
Impact: Arbitrary code execution
Advisory URL: http://www.toucan-system.com/advisories/tssa-2011-01.txt


- --[ Introduction:

Following 3 paragraphs taken from the vendors' documentation:

Xpdf is an open source viewer for Portable Document Format (PDF)
files. (These are also sometimes also called 'Acrobat' files, from
the name of Adobe's PDF software.) The Xpdf project also includes a
PDF text extractor, PDF-to-PostScript converter, and various other
utilities.

Xpdf runs under the X Window System on UNIX, VMS, and OS/2. The non-X
components (pdftops, pdftotext, etc.) also run on Win32 systems and
should run on pretty much any system with a decent C++ compiler.

Xpdf is designed to be small and efficient. It can use Type 1 or
TrueType fonts.


- --[ Synopsis:

The linux version of xpdf is linked against t1lib, which is vulnerable
to multiple vulnerabilities including off by ones, integer overflows
and heap corruptions. At least one of those is exploitable and allows
arbitrary code to be executed on the target machine when opening a
specially crafted pdf file.


- --[ Vulnerabilities overview:

When parsing specially crafted Type 1 fonts, the t1lib library
is subject to several memory corruption vulnerabilities. We will
exemplify only a few of them : t1lib being decomissioned by xpdf
anyways, it will probably never get fixed.

[*] Invalid memory reads (off by few):

The following valgrind trace exemplifies an invalid read from
t1lib:

==24009== Invalid read of size 8
==24009== at 0x406364A: ??? (in /usr/lib/libt1.so.5.1.2)
==24009== by 0x4068A0D: ??? (in /usr/lib/libt1.so.5.1.2)
==24009== by 0x4068BEC: ??? (in /usr/lib/libt1.so.5.1.2)
==24009== by 0x4069052: Type1Char (in /usr/lib/libt1.so.5.1.2)
==24009== by 0x40540F3: fontfcnB (in /usr/lib/libt1.so.5.1.2)
==24009== by 0x4077DDC: T1_SetChar (in /usr/lib/libt1.so.5.1.2)
==24009== by 0x407CE88: T1_AASetChar (in /usr/lib/libt1.so.5.1.2)
==24009== by 0x810C95A: ??? (in /usr/bin/xpdf.bin)
==24009== by 0x810BE1E: ??? (in /usr/bin/xpdf.bin)
==24009== by 0x80FA588: ??? (in /usr/bin/xpdf.bin)
==24009== by 0x80C729F: ??? (in /usr/bin/xpdf.bin)
==24009== by 0x8063A91: ??? (in /usr/bin/xpdf.bin)
==24009== by 0x806452E: ??? (in /usr/bin/xpdf.bin)
==24009== by 0x806224C: ??? (in /usr/bin/xpdf.bin)
==24009== by 0x8062589: ??? (in /usr/bin/xpdf.bin)
==24009== by 0x80A690A: ??? (in /usr/bin/xpdf.bin)
==24009== by 0x80AB754: ??? (in /usr/bin/xpdf.bin)
==24009== by 0x80ACF46: ??? (in /usr/bin/xpdf.bin)
==24009== by 0x80E23D6: ??? (in /usr/bin/xpdf.bin)
==24009== by 0x80A7BB0: ??? (in /usr/bin/xpdf.bin)
==24009== by 0x80EE5B9: ??? (in /usr/bin/xpdf.bin)
==24009== by 0x80DEB0F: ??? (in /usr/bin/xpdf.bin)
==24009== by 0x80F1B32: ??? (in /usr/bin/xpdf.bin)
==24009== by 0x458DB55: (below main) (libc-start.c:220)

Note: This given vulnerability cannot execute arbitrary code :
it only allows a remote denial of service of the xpdf reader.

[*] Invalid memory writes:

In the same fashion, the following trace exemplifies an invalid
memory write, dur to a use after free():

==23165== Invalid write of size 2
==23165== at 0x405606C: t1_Bresenham (in /usr/lib/libt1.so.5.1.2)
==23165== by 0x405627E: t1_StepLine (in /usr/lib/libt1.so.5.1.2)
==23165== by 0x405B6E5: t1_Interior (in /usr/lib/libt1.so.5.1.2)
==23165== by 0x405441B: fontfcnB (in /usr/lib/libt1.so.5.1.2)
==23165== by 0x4077DDC: T1_SetChar (in /usr/lib/libt1.so.5.1.2)
==23165== by 0x407CE88: T1_AASetChar (in /usr/lib/libt1.so.5.1.2)
==23165== by 0x810C95A: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x810BE1E: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80FA588: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80C729F: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x8063A91: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x806452E: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x806224C: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x8062589: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80A690A: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80AB754: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80ACF46: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80E23D6: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80A7BB0: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80EE5B9: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80DEB0F: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80F1B32: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x458DB55: (below main) (libc-start.c:220)
==23165== Address 0x4b7c978 is 19,560 bytes inside a block of size
26,624 free'd
==23165== at 0x4024886: free (vg_replace_malloc.c:325)
==23165== by 0x4069226: Type1Char (in /usr/lib/libt1.so.5.1.2)
==23165== by 0x40540F3: fontfcnB (in /usr/lib/libt1.so.5.1.2)
==23165== by 0x4077DDC: T1_SetChar (in /usr/lib/libt1.so.5.1.2)
==23165== by 0x407CE88: T1_AASetChar (in /usr/lib/libt1.so.5.1.2)
==23165== by 0x810C95A: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x810BE1E: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80FA588: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80C729F: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x8063A91: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x806452E: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x806224C: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x8062589: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80A690A: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80AB754: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80ACF46: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80E23D6: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80A7BB0: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80EE5B9: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80DEB0F: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x80F1B32: ??? (in /usr/bin/xpdf.bin)
==23165== by 0x458DB55: (below main) (libc-start.c:220)

Note: This given vulnerability cannot execute arbitrary code :
it only allows a remote denial of service of the xpdf reader.

[*] Off by one leading to invalid memory read, then integer overflow:

A more interresting case arrises in the following trace, for which
the pointer being dereferenced suggests the presence of an integer
overflow:

==24080== Invalid read of size 8
==24080== at 0x4063409: ??? (in /usr/lib/libt1.so.5.1.2)
==24080== by 0x40686A5: ??? (in /usr/lib/libt1.so.5.1.2)
==24080== by 0x4068BEC: ??? (in /usr/lib/libt1.so.5.1.2)
==24080== by 0x4069052: Type1Char (in /usr/lib/libt1.so.5.1.2)
==24080== by 0x40540F3: fontfcnB (in /usr/lib/libt1.so.5.1.2)
==24080== by 0x4077DDC: T1_SetChar (in /usr/lib/libt1.so.5.1.2)
==24080== by 0x407CE88: T1_AASetChar (in /usr/lib/libt1.so.5.1.2)
==24080== by 0x810C95A: ??? (in /usr/bin/xpdf.bin)
==24080== by 0x810BE1E: ??? (in /usr/bin/xpdf.bin)
==24080== by 0x80FA588: ??? (in /usr/bin/xpdf.bin)
==24080== by 0x80C729F: ??? (in /usr/bin/xpdf.bin)
==24080== by 0x8063A91: ??? (in /usr/bin/xpdf.bin)
==24080== by 0x806452E: ??? (in /usr/bin/xpdf.bin)
==24080== by 0x806224C: ??? (in /usr/bin/xpdf.bin)
==24080== by 0x8062589: ??? (in /usr/bin/xpdf.bin)
==24080== by 0x80A690A: ??? (in /usr/bin/xpdf.bin)
==24080== by 0x80AB754: ??? (in /usr/bin/xpdf.bin)
==24080== by 0x80ACF46: ??? (in /usr/bin/xpdf.bin)
==24080== by 0x80E23D6: ??? (in /usr/bin/xpdf.bin)
==24080== by 0x80A7BB0: ??? (in /usr/bin/xpdf.bin)
==24080== by 0x80EE5B9: ??? (in /usr/bin/xpdf.bin)
==24080== by 0x80DEB0F: ??? (in /usr/bin/xpdf.bin)
==24080== by 0x80F1B32: ??? (in /usr/bin/xpdf.bin)
==24080== by 0x458DB55: (below main) (libc-start.c:220)
==24080== Address 0xffffff38 is not stack'd, malloc'd or (recently)
free'd

In fact, there is an off by one in t1lib responsible for the wrong
reading
of 4 null bytes from a mapped (t1lib's data) location at:
=> 0x4005f348: mov DWORD PTR [ebx+0x32b8],eax

Later on, this memory location is used to initialize eax with the value
of a (wrong) function pointer :
=> 0x4005f86a: imul eax,DWORD PTR [ebx+0x32b8],0x68
(at this point, eax will be NULL).

Then a fixed offset is substrated to eax (0 here), resulting in the
integer
overflow by wrap around:
=> 0x4005f885: sub eax,0xd0
(eax now worhes 0 - 0xd = 0xffffff30)

This pointer is finally used in floating point arithmetic to read a
pointer
into the FPU stack from an unmapped location, resulting in an
unvalid pointer
dereference and in fine in a segmentation fault:

Program received signal SIGSEGV, Segmentation fault.

- -------------------------------------------------------------------------[
regs]
eax:FFFFFF30 ebx:40085FF4 ecx:0000658C edx:000000BB
eflags:00010286
esi:00000008 edi:00000002 esp:BFFFEA40 ebp:BFFFEAB8
eip:4005F89A
cs:0073 ds:007B es:007B fs:0000 gs:0033 ss:007B o d I t
S z a P c

[0073:4005F89A]---------------------------------------------------------[ code]
--[ Instruction:
=> 0x4005f89a: fld QWORD PTR [eax+0x8]

Note: this vulnerability results in a Denial of Service but cannot
be used
to execute arbitrary code on the target.

[*] Arbitrary code execution:

In the following case, register ecx is user controled and allows
direct modification of the control flow:

gdb $ r ./testz.2184122398.pdf
Error (817488): Dictionary key must be a name object
Error (817492): Dictionary key must be a name object
Error (37300): Unknown operator 'lc'
Error (37385): Unknown operator 'lh'
Error (37504): Too few (5) args to 'c' operator
Error (37514): Too few (5) args to 'c' operator
Error (37543): Too few (5) args to 'c' operator
Error (37550): Unknown operator 'mh'
Error (37586): Too few (5) args to 'c' operator
Error (37603): Too few (5) args to 'c' operator
Error (37633): Too few (5) args to 'c' operator
Error (37669): Too few (5) args to 'c' operator
Error (37701): Too few (5) args to 'c' operator
Error (37733): Too few (5) args to 'c' operator
Error (37830): Too few (5) args to 'c' operator
Error (37839): Too few (5) args to 'cm' operator

Program received signal SIGSEGV, Segmentation fault.

- -------------------------------------------------------------------------[
regs]
eax:08234138 ebx:00000000 ecx:AA55AA54 edx:00000054
eflags:00010246
esi:081CB298 edi:0821F2F0 esp:BFFFEF30 ebp:BFFFEFB8
eip:080C512B
cs:0073 ds:007B es:007B fs:0000 gs:0033 ss:007B o d I t
s Z a P c

[0073:080C512B]---------------------------------------------------------[ code]
--[ Instruction:
=> 0x80c512b: call DWORD PTR [ecx+0x28]

By having ecx point 0x28 bytes to a location in the heap and
dereferencing a
pointer to this very same location, an attacker can execute
arbitrary shellcode,
hence performing arbitrary remote code execution on the target
machine uppon
opening of a given pdf file.

Reliability considerations: this particular bug is dependant on an
invalid
pointer dereferencing null data from a valid memory region. In order for
this to be performed without resulting in a segmentation fault, the
t1lib
library must read from a region wich is actually mapped, which
empirically
occures with a probability of around 10% of the cases on a machine
with ASLR
enabled.

- --[ Mitigation:

Instead of fixing the Type 1 library, the xpdf team has decided to
end support for this library and encourages to link xpdf against the
truetype library only. Versions starting with 3.02pl6 will therefor
deprecate the use of t1lib completely.

When compiling older versions manually, the CERT offers the following
compilation options, which mitigate the vulnerability by not linking
xpdf against the Type 1 library:

To build Xpdf without t1lib, add the "--with-t1-library=no" flag to the
configure command:

./configure --with-t1-library=no .....

To double-check, run "xpdf --help". The "-freetype" option should be
listed, and the "-t1lib" option should NOT be listed. That indicates
that Xpdf was built with FreeType and without t1lib.

With this setting, Xpdf will use FreeType instead of t1lib to rasterize
Type 1 fonts. With recent versions of FreeType, the Type 1 quality is
as good or better than t1lib, so this should not present any problems.

- --[ Vulnerable versions:

Vulnerable : Xpdf up to (including) version 3.02pl5.
Non vulnerable (non linked to t1lib) : Xpdf version 3.02pl6.


- --[ Disclosure timeline:

* 05/01/2011: Contact Vendor and CERT, providing full description
and PoC samples to audit the issue.

* 21/03/2011: The vendor releases a new version of the xpdf package,
decomissioning the t1lib entirely, hence mitigating the problem.
The CERT publishes a vulnerability note at the url:
http://www.kb.cert.org/vuls/id/376500

* 24/03/2011: The CERT assigns CVE number CVE-2011-0764 to this
vulnerability.

* 28/03/2011: Public disclosure.


- --[ Credits:

Those vulnerabilities were discovered by Jonathan Brossard from
Toucan System.


- --[ About Toucan System:

Toucan System is a French computer security company providing
cutting edge
research and security consulting to Fortune 500 as well as smaller companies
globally, thanks to a wide range of expertise ranging from Reverse
Engineering
and binary analysis to cryptography and Risk Management.



Relevant Pages