--------------------------------------------------------------------------- * 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.