Vulnerabilities > CVE-2017-2822 - Improper Restriction of Operations within the Bounds of a Memory Buffer vulnerability in Lexmark Perceptive Document Filters 11.3.0.2400
Attack vector
NETWORK Attack complexity
LOW Privileges required
NONE Confidentiality impact
HIGH Integrity impact
HIGH Availability impact
HIGH Summary
An exploitable code execution vulnerability exists in the image rendering functionality of Lexmark Perceptive Document Filters 11.3.0.2400. A specifically crafted PDF can cause a function call on a corrupted DCTStream to occur, resulting in user controlled data being written to the stack. A maliciously crafted PDF file can be used to trigger this vulnerability.
Vulnerable Configurations
Part | Description | Count |
---|---|---|
Application | 1 |
Common Weakness Enumeration (CWE)
Common Attack Pattern Enumeration and Classification (CAPEC)
- Buffer Overflow via Environment Variables This attack pattern involves causing a buffer overflow through manipulation of environment variables. Once the attacker finds that they can modify an environment variable, they may try to overflow associated buffers. This attack leverages implicit trust often placed in environment variables.
- Overflow Buffers Buffer Overflow attacks target improper or missing bounds checking on buffer operations, typically triggered by input injected by an attacker. As a consequence, an attacker is able to write past the boundaries of allocated buffer regions in memory, causing a program crash or potentially redirection of execution as per the attackers' choice.
- Client-side Injection-induced Buffer Overflow This type of attack exploits a buffer overflow vulnerability in targeted client software through injection of malicious content from a custom-built hostile service.
- Filter Failure through Buffer Overflow In this attack, the idea is to cause an active filter to fail by causing an oversized transaction. An attacker may try to feed overly long input strings to the program in an attempt to overwhelm the filter (by causing a buffer overflow) and hoping that the filter does not fail securely (i.e. the user input is let into the system unfiltered).
- MIME Conversion An attacker exploits a weakness in the MIME conversion routine to cause a buffer overflow and gain control over the mail server machine. The MIME system is designed to allow various different information formats to be interpreted and sent via e-mail. Attack points exist when data are converted to MIME compatible format and back.
Seebug
bulletinFamily | exploit |
description | ### Summary An exploitable code execution vulnerability exists in the image rendering functionality of Lexmark Perceptive Document Filters 11.3.0.2400. A specifically crafted PDF can cause a function call on a corrupted DCTStream to occur, resulting in user controlled data being written to the stack. A maliciously crafted PDF file can be used to trigger this vulnerability. ### Tested Versions Lexmark Perceptive Document Filters 11.3.0.2400 ### Product URLs http://www.lexmark.com/en_us/partners/enterprise-software/technology-partners/oem-technologies/document-filters.html ### CVSSv3 Score 7.5 CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H ### CWE CWE-121 - Stack-Based Buffer Overflow ### Details Lexmark Perceptive Document Filters is an SDK used for inspection, conversion, and viewing for a multitude of different file formats. Developers can utilize either the built binaries or shared libraries of this product in order to manipulate common file types including PDFs. It should be noted that Marklogic uses this product for rendering purposes. Lexmark Perceptive Document Filters statically links various code with a modified version of the open source Poppler library for parsing and rendering PDF files, resulting in a shared library called libISYSpdf6.so. For rendering certain types of images within the PDF, libISYSpdf6 uses a modified version of the DCTStream class, called IGRStream. An interesting thing to note about the DCTStream class, and by extension the IGRStream class, is that there are two buffers within the class that are used for storing image data, however upon initialization, only one of these buffers is allocated and overwritten, depending on the type of the image (interleaved || progressive). A mini-bug in the class occurs in that one can switch the image type after the allocation has occurred, forcing the DCTStream to interact with the un-allocated buffer. Due to the above issue and the fact that the IGRStream doesn't clear a few variables correctly, ([IGRStream+0xda8], [IGRStream+0xdb0] respectively designated blockBuf and blockBufEnd), if the heap is manipulated correctly, on initialization of the IGRStream object, the blockBuf and blockBufEnd pointers can be controlled by the user. It should be noted that the image type needs to be set to progressive at first, and then swapped to interleaved after the memory allocation occurs, such that the blockBuf and blockBufEnd pointers do not get overwritten with malloc pointers. This issue will become relevant soon, but in summary, IGRStream+0xda8 and IGRStream+0xdb0 can both be user controlled. Below is the source code from the poppler-0.53.0 library. It is not 100% accurate with regards to the Lexmark ISYSpdf6.so code, but it serves as a good base. Our corrupted IGR stream is returned from a lookup, and two functions are called upon it: [0] fontDict->lookup is returning our corrupted IGRStream, which passes isStream() check [1] Something different in the ISYSpdf6.so binary. Inside of GfxFont.cc: ``` CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits, CharCodeToUnicode *ctu) { GooString *buf; Object obj1;l if (!fontDict->lookup("ToUnicode", &obj1)->isStream()) { // [0] obj1.free(); return NULL; } buf = new GooString(); obj1.getStream()->fillGooString(buf); // [1] obj1.streamClose(); obj1.free(); ``` Although the above poppler source is not 100% what occurs within the Lexmark code, it's a good approximation. The actual assembly is listed below, in which our unintended object has two methods called upon it, that result in the functions DCTStream::reset [0] and DCTStream::getBlock [1] being called, the latter of which is a Lexmark-only function. ``` 0x7f9b7da5f2bf <_ZN7GfxFont17readToUnicodeCMapEP4DictiP17CharCodeToUnicodePb+95>: mov rdi,QWORD PTR [rsp+0x1008] 0x7f9b7da5f2c7 <_ZN7GfxFont17readToUnicodeCMapEP4DictiP17CharCodeToUnicodePb+103>: mov rax,QWORD PTR [rdi] 0x7f9b7da5f2ca <_ZN7GfxFont17readToUnicodeCMapEP4DictiP17CharCodeToUnicodePb+106>: call QWORD PTR [rax+0x18] // [0] // Ö 0x7f9b7da5f2f0 <_ZN7GfxFont17readToUnicodeCMapEP4DictiP17CharCodeToUnicodePb+144>: call QWORD PTR [rax+0x40] // [1] Guessed arguments: arg[0]: 0xff2130 --> 0x7f9b7dd76870 --> 0x7f9b7dab2d10 --> 0x5c8948f8246c8948 arg[1]: 0x7ffe879972c0 --> 0x0 arg[2]: 0x1000 ``` The DCTStream::getBlock function seems to be a Lexmark replacement for the implementation of reading non-progressive and interleaved JPEG images. It reads the current stream in chunks <= 0x1000 bytes for the image data and copies it onto the stack, with a call to memcpy. The arguments to memcpy end up being as such: ``` Destination : Buffer on the stack, size 0x1018, (rsp before call to DCTStream::getBlock()) Source: *[DCTStream+0xdb0] Size : *[DCTStream+0xdb0] ñ *[DCTStream+0xda8] ``` Since DCTStream+0xdb0 and DCTStream+0xda8 are user controlled (remember IGRStream::blockBuf and IGRStream::blockBufEnd?), the end result is a call to memcpy with a user-controlled source, count, and a destination on the stack. The only thing protecting from an easy mode return address overwrite is a cmovg instruction as shown below: ``` [0] Corrupted Qword (blockBuf) loaded into RSI [1] Corrupted Qword (blockBufEnd) loaded into rax [2] Under flow possible here [3] EBX == 0x1000, and the buffer for this memcpy is > 0x1000. [4] The sign extend move makes underflow even larger 0x00007fbe4b9251b8 <+120>: mov rsi,QWORD PTR [rbp+0xda8] // [0] 0x00007fbe4b9251bf <+127>: cmp rsi,QWORD PTR [rbp+0xdb0] => 0x00007fbe4b9251c6 <+134>: jne 0x7fbe4b925180 <_ZN9DCTStream8getBlockEPci+64> // Ö 0x00007fbe4b925180 <+64>: mov rax,QWORD PTR [rbp+0xdb0] // [1] 0x00007fbe4b925187 <+71>: mov ebx,r13d 0x00007fbe4b92518a <+74>: movsxd rdi,r14d 0x00007fbe4b92518d <+77>: sub ebx,r14d 0x00007fbe4b925190 <+80>: sub eax,esi // [2] 0x00007fbe4b925192 <+82>: cmp ebx,eax => 0x00007fbe4b925194 <+84>: cmovg ebx,eax // [3] 0x00007fbe4b925197 <+87>: add rdi,r15 0x00007fbe4b92519a <+90>: movsxd r12,ebx // [4] 0x00007fbe4b92519d <+93>: add r14d,ebx 0x00007fbe4b9251a0 <+96>: mov rdx,r12 0x00007fbe4b9251a3 <+99>: call 0x7fbe4b8356c0 <memcpy@plt> ``` It should be noted that an integer underflow can occur when calculating the distance between IGRStream::blockBuf and IGRStream::blockBufEnd, which bypasses the cmovg check with 0x1000, forcing the size of the memcopy to be larger than the static buffer of 0x1000 bytes, resulting in a stack-based overflow. ### Crash Information ``` [----------------------------------registers-----------------------------------] RAX: 0x7ffe879972c0 --> 0x0 RBX: 0xd8f0047d RCX: 0x200 RDX: 0xffffffffd8f0047d RSI: 0xf1093ede1d170e06 RDI: 0x7ffe879972c0 --> 0x0 RBP: 0xff2130 --> 0x7f9b7dd76870 --> 0x7f9b7dab2d10 --> 0x5c8948f8246c8948 RSP: 0x7ffe87997278 --> 0x7f9b7dab91a8 --> 0x4500000da8a5014c RIP: 0x7f9b809964a0 --> 0x48f88949066f0ff3 R8 : 0x1059a80 --> 0xe6166ad5556c558e R9 : 0x104d670 --> 0xaa2a74d82e7a6c4f R10: 0x10 R11: 0x1 R12: 0xffffffffd8f0047d R13: 0x1000 R14: 0xd8f0047d R15: 0x7ffe879972c0 --> 0x0 EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x7f9b80996498 <__memmove_ssse3_back+72>: lea rdx,[r11+rdx*1] 0x7f9b8099649c <__memmove_ssse3_back+76>: jmp rdx 0x7f9b8099649e <__memmove_ssse3_back+78>: ud2 => 0x7f9b809964a0 <__memmove_ssse3_back+80>: movdqu xmm0,XMMWORD PTR [rsi] 0x7f9b809964a4 <__memmove_ssse3_back+84>: mov r8,rdi 0x7f9b809964a7 <__memmove_ssse3_back+87>: and rdi,0xfffffffffffffff0 0x7f9b809964ab <__memmove_ssse3_back+91>: add rdi,0x10 0x7f9b809964af <__memmove_ssse3_back+95>: mov r9,rdi [------------------------------------stack-------------------------------------] 0000| 0x7ffe87997278 --> 0x7f9b7dab91a8 --> 0x4500000da8a5014c 0008| 0x7ffe87997280 --> 0x7ffe879982c0 --> 0x8 0016| 0x7ffe87997288 --> 0x7f9b7db18625 ("ydieresis") 0024| 0x7ffe87997290 --> 0xff1fa0 --> 0x0 0032| 0x7ffe87997298 --> 0xfef940 --> 0x0 0040| 0x7ffe879972a0 --> 0x7ffe879982c0 --> 0x8 0048| 0x7ffe879972a8 --> 0x8 0056| 0x7ffe879972b0 --> 0x7ffe879987cf --> 0xfec16001 [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV ``` ### Timeline * 2017-04-24 - Vendor Disclosure * 2017-08-28 - Public Release ### CREDIT * Discovered by Marcin Noga and Lilith Wyatt of Cisco Talos. |
id | SSV:96449 |
last seen | 2017-11-19 |
modified | 2017-09-12 |
published | 2017-09-12 |
reporter | Root |
title | Lexmark LibISYSpdf Image Rendering DCTStream::getBlock() Code Execution Vulnerability(CVE-2017-2822) |
Talos
id | TALOS-2017-0323 |
last seen | 2019-05-29 |
published | 2017-08-28 |
reporter | Talos Intelligence |
source | http://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0323 |
title | Lexmark LibISYSpdf Image Rendering DCTStream::getBlock() Code Execution Vulnerability |