Talos Vulnerability Report

TALOS-2017-0281

Pharos PopUp Printer Client DecodeString Code Execution Vulnerability

March 7, 2017
CVE Number

CVE-2017-2786

Summary

A denial of service vulnerability 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 an out of bounds read causing a crash and a denial of service.

Tested Versions

Pharos PopUp Printer Client 9.0

Product URLs

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

CVSSv3 Score

5.3 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

CWE

CWE-125 - Out-of-bounds Read

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 begins inside of the DecodeString function. The packet strings are sent to the program encoded in an encoding format. This function parses the string from the packet and increments the packet pointer to the next data. It does this by reading in the length of the string from the packet just before the string to be parsed. It then adds this value to its current location to position itself at the next data to be parsed. This code is shown below.

__text:0000000100005E21                 mov     rsi, [rbx+PSComDecodePacket.end_of_str]     [1]
__text:0000000100005E28                 movzx   ecx, byte ptr [rsi]
__text:0000000100005E2B                 mov     eax, ecx
__text:0000000100005E2D                 and     eax, 3Fh
__text:0000000100005E30                 cmp     eax, 10
__text:0000000100005E33                 jnz     short loc_100005E98
__text:0000000100005E35                 lea     rax, [rsi+1]
__text:0000000100005E39                 mov     [rbx+PSComDecodePacket.end_of_str], rax
__text:0000000100005E40                 mov     al, 1
__text:0000000100005E42                 test    cl, cl
__text:0000000100005E44                 js      short loc_100005E74
__text:0000000100005E46                 mov     edx, [rsi+1]                                [2]
__text:0000000100005E49                 add     rsi, 5
__text:0000000100005E4D                 mov     [rbx+PSComDecodePacket.end_of_str], rsi
__text:0000000100005E54                 mov     [r14], rsi
__text:0000000100005E57                 add     [rbx+PSComDecodePacket.end_of_str], rdx ;   [3]
__text:0000000100005E5E                 test    cl, 40h
__text:0000000100005E61                 jz      short loc_100005E72

Starting at [1], we see the string location being loaded from a struct and moved into RSI. A few checks are made on the data which is controlled by the attacker and can be easily bypassed. Then at [2], we see some data being moved directly from the attacker controlled packet and into EDX. Further down, [3], we see this attacker controlled length directly added to the pointer. Later in the code a call to CheckPacketEnd is made and the pointer is used again. The code is below.

__text:0000000100006194 CheckPacketEnd  proc near               ; CODE XREF: DecodeString+24p
__text:0000000100006194                                         ; DecodeBinary+2Ap ...
__text:0000000100006194
__text:0000000100006194                 push    rbp
__text:0000000100006195                 mov     rbp, rsp
                                        ...
__text:00000001000061B7                 mov     rbx, [r15+PSComDecodePacket.end_of_str]             [1]
__text:00000001000061BE                 cmp     byte ptr [rbx], 0                                   [2]

At location [1], the pointer for the end of the string is moved into RBX. It is then immediately dereferenced and compared against zero, [2]. This pointer is never validated and indeed points out of bounds after being manipulated in the previous function. This leads to an out of bounds access and a denial of service condition.

Crash Information

./exc_handler ./psnotifyd
2017-01-24 13:57:10.602 psnotifyd[32275:8783881] Notify listening thread started
2017-01-24 13:57:10.603 psnotifyd[32275:8783881] Listening on socket 4
2017-01-24 13:57:10.607 psnotifyd[32275:8783877] CFSocketSetAddress bind failure: 48
2017-01-24 13:57:10.607 psnotifyd[32275:8783877] Telling any existing Notify processes that psnotifyd has started up.
2017-01-24 13:57:17.372 psnotifyd[32275:8783881] New notify connection incoming
2017-01-24 13:57:17.372 psnotifyd[32275:8783881] Spawning a new notify request handler thread
2017-01-24 13:57:17.372 psnotifyd[32275:8783881] Listening on socket 4
2017-01-24 13:57:17.372 psnotifyd[32275:8784052] New request handler thread started
2017-01-24 13:57:17.372 psnotifyd[32275:8784052] I got some stuff goin' on

Crashed thread log =

0   psnotifyd                       0x00000001000061be 0x100000000 + 25022
1   psnotifyd                       0x0000000100005e21 0x100000000 + 24097
2   psnotifyd                       0x00000001000029c4 0x100000000 + 10692
3   psnotifyd                       0x0000000100002392 0x100000000 + 9106
4   com.apple.Foundation            0x00007fff89e3de64 __NSThread__start__ + 1351
5   libsystem_pthread.dylib         0x00007fff997ec99d _pthread_body + 131
6   libsystem_pthread.dylib         0x00007fff997ec91a _pthread_start + 168
7   libsystem_pthread.dylib         0x00007fff997ea351 thread_start + 13

log name is: ./crashlogs/lollipo.crashlog.txt
---
exception=EXC_BAD_ACCESS:signal=11:is_exploitable= no:instruction_disassembly=cmpb  $CONSTANT,(%rbx):instruction_address=0x00000001000061be:access_type=read:access_address=0x00000002004fa582:
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.