Talos Vulnerability Report

TALOS-2017-0282

Pharos PopUp Printer Client memcpy Code Execution Vulnerability

March 7, 2017
CVE Number

CVE-2017-2787

Summary

A buffer overflows exists in the psnotifyd application of the Pharos PopUp printer client version 9.0. A specially crafted packet can be sent to the victim's computer and can lead to a heap based buffer overflow resulting in potential remote code execution. This client is always listening, has root privileges, and requires no user interaction to exploit.

Tested Versions

Pharos PopUp Printer Client 9.0

Product URLs

https://pharos.com/products-services/

CVSSv3 Score

9.0 - CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H

CWE

CWE-122 - Heap-based Buffer Overflow

Details

Pharos PopUp Printer client is printing software that is widely used in Universities all over the United States. This client is a way to manage multiple connections to a single printing point and is constantly listening in the background for a packet from the printer. It is also running with root privilege for easy access to any privileged drivers. These all make this an excellent target where a vulnerability could have a high impact.

The vulnerability is located inside of the BlobData function. Blob data is encoded data returned to the client upon making the connection. It is partially controlled by the data passed in from the attacker to start. The packet is continually parsed and used by this blob data function until the packet's end is reached. This code is shown below.

__text:0000000100008698 loc_100008698:                          ; CODE XREF: doBlobDataStuff+B5j
__text:0000000100008698                 mov     rdi, r14        ; a1
__text:000000010000869B                 call    get_blob_var
__text:00000001000086A0                 cmp     byte ptr [rax], 0
__text:00000001000086A3                 jnz     short loc_1000086F2                      [5]
__text:00000001000086A5                 mov     rdi, r14        ; a1
__text:00000001000086A8                 call    get_blob_var
__text:00000001000086AD                 mov     rbx, rax
__text:00000001000086B0                 mov     rdi, r14
__text:00000001000086B3                 call    getPacketLength                          [1]
__text:00000001000086B8                 inc     rbx
__text:00000001000086BB                 dec     eax                                      [2]
__text:00000001000086BD                 mov     rdi, r15        ; a1
__text:00000001000086C0                 mov     rsi, rbx        ; a2
__text:00000001000086C3                 mov     edx, eax        ; length                 [3]
__text:00000001000086C5                 call    doBlobData_
__text:00000001000086CA                 mov     rdi, r14        ; a1
__text:00000001000086CD                 mov     rsi, r15        ; a2
__text:00000001000086D0                 call    assign_new_blob                          [4]
__text:00000001000086D5                 mov     rdi, r15
__text:00000001000086D8                 call    delObj
__text:00000001000086DD                 jmp     short loc_100008698

The length of the packet is calculated, [1] and we see RBX is incremented by one. RBX in this instance points to the data in the structure that was sent via the packet. From here we see EAX, [2], is decremented and is not checked for validity. Finally at [3], EAX is passed into BlobData as the length. Inside of the BlobData function a new Blob structure is created and the length passed in is EAX, one decremented from the length of the previous packet. Then subsequently at [4], the new Blob data pointer is swapped from R15 into R14, the register used for all of our checks.

To recap what we know so far, the blob structure's length is calculated and a new blob structure is created with a length decremented by one. This new blob structure is then used to replace the original blob structure. The vulnerability arises at [5], because the check is incorrect and should be jump if zero not JNZ. This makes the program fall into an infinite loop where the length is ever decrementing. Looking one time into the doBlobData function we can see the crashing point.

__text:0000000100007805                 add     rdi, 4          ; unsigned __int64
__text:0000000100007809                 call    __Znam          ; operator new[](ulong)
__text:000000010000780E                 mov     [r13+PSComDecodePacket.blob_var], rax
__text:0000000100007812                 mov     rdi, r13
__text:0000000100007815                 mov     esi, r12d
__text:0000000100007818                 call    SetSize
__text:000000010000781D                 mov     rdi, [r13+PSComDecodePacket.blob_var] ;
__text:0000000100007821                 mov     edx, r12d       ; size_t                      [1]
__text:0000000100007824                 mov     rsi, r15        ; void *
__text:0000000100007827                 call    _memcpy
__text:000000010000782C                 add     rsp, 8
__text:0000000100007830                 pop     rbx

The size shown at [1] is the passed in EAX from the previous function. Without a way to stop this number gets decremented down until it passes a negative value into the memcpy causing an out of bounds write. Due to the fact that multiple concurrent connections can be made there may be a way to use threads to stop the memcpy and cause an exploitable condition to arise.

Crash Information

./exc_handler ./psnotifyd
2017-01-24 14:29:58.030 psnotifyd[38902:8830625] Notify listening thread started
2017-01-24 14:29:58.031 psnotifyd[38902:8830625] Listening on socket 4
2017-01-24 14:29:58.033 psnotifyd[38902:8830621] CFSocketSetAddress bind failure: 48
2017-01-24 14:29:58.034 psnotifyd[38902:8830621] Telling any existing Notify processes that psnotifyd has started up.
2017-01-24 14:30:10.415 psnotifyd[38902:8830625] New notify connection incoming
2017-01-24 14:30:10.415 psnotifyd[38902:8830625] Spawning a new notify request handler thread
2017-01-24 14:30:10.416 psnotifyd[38902:8830625] Listening on socket 4
2017-01-24 14:30:10.416 psnotifyd[38902:8831088] New request handler thread started
2017-01-24 14:30:10.416 psnotifyd[38902:8831088] I got some stuff goin' on

Crashed thread log =

0   libsystem_platform.dylib        0x00007fff8db24130 _platform_memmove$VARIANT$Haswell + 528
1   psnotifyd                       0x000000010000782c 0x100000000 + 30764
2   psnotifyd                       0x0000000100007580 0x100000000 + 30080
3   psnotifyd                       0x00000001000086ca 0x100000000 + 34506
4   psnotifyd                       0x0000000100002a95 0x100000000 + 10901
5   psnotifyd                       0x0000000100002392 0x100000000 + 9106
6   com.apple.Foundation            0x00007fff89e3de64 __NSThread__start__ + 1351
7   libsystem_pthread.dylib         0x00007fff997ec99d _pthread_body + 131
8   libsystem_pthread.dylib         0x00007fff997ec91a _pthread_start + 168
9   libsystem_pthread.dylib         0x00007fff997ea351 thread_start + 13

log name is: ./crashlogs/crashlog.txt
---
exception=EXC_BAD_ACCESS:signal=11:is_exploitable=yes:instruction_disassembly=.byte 0xc5 #bad opcode:instruction_address=0x00007fff8db24130:access_type=unknown:access_address=0x00000001057fffe1:
Crash accessing invalid address

Timeline

2017-02-07 - Vendor Disclosure
2017-03-07 - Public Release

Credit

Discovered by Tyler Bohan of Cisco Talos. Talos would also like to thank NYU Osiris Lab for helping out with some of the reversing.