Talos Vulnerability Report

TALOS-2017-0283

Pharos PopUp Printer Client DecodeBinary Code Execution Vulnerability

March 7, 2017
CVE Number

CVE-2017-2788

Summary

An exploitable buffer overflow 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 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

10.0 - CVSS:3.0/AV:N/AC:L/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 DecodeBinary function. The packet encodes binary data and adds the length of the data to the beginning. By passing in an overly large length the application can be caused to overflow the buffer with controlled data and an exploitable condition arises. The relevant code is shown below.

__text:00000001000063F5                 mov     rsi, [rbx+PSComDecodePacket.end_of_data]                     [1]
__text:00000001000063FC                 movzx   ecx, byte ptr [rsi]
__text:00000001000063FF                 mov     eax, ecx
__text:0000000100006401                 and     eax, 3Fh
__text:0000000100006404                 cmp     eax, 0Bh
__text:0000000100006407                 jnz     short loc_100006475
__text:0000000100006409                 lea     rax, [rsi+1]
__text:000000010000640D                 mov     [rbx+PSComDecodePacket.end_of_data], rax
__text:0000000100006414                 mov     al, 1
__text:0000000100006416                 test    cl, cl
__text:0000000100006418                 js      short loc_10000644B
__text:000000010000641A                 mov     edx, [rsi+1]                                                 [2]
__text:000000010000641D                 mov     [r15], edx
__text:0000000100006420                 add     rsi, 5
__text:0000000100006424                 mov     [rbx+PSComDecodePacket.end_of_data], rsi
__text:000000010000642B                 mov     [r14], rsi
__text:000000010000642E                 add     [rbx+PSComDecodePacket.end_of_data], rdx
__text:0000000100006435                 test    cl, 40h
__text:0000000100006438                 jz      short loc_100006449
__text:000000010000643A                 add     rbx, 20h
__text:000000010000643E                 mov     rsi, [r14]
__text:0000000100006441                 mov     rdi, rbx
__text:0000000100006444                 call    xor_decode                                                   [3]

Starting at [1], we see the data 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 a call to xor_decode which is of interest because the third argument to that function is EDX. The relevant function code for xor decode is shown below.

__text:0000000100006CC0                 push    rbp
__text:0000000100006CC1                 mov     rbp, rsp
__text:0000000100006CC4                 push    r14
__text:0000000100006CC6                 push    rbx
__text:0000000100006CC7                 mov     cl, [rdi+(PSComDecodePacket.length+0E0h)]
__text:0000000100006CCD                 mov     r9b, [rdi+(PSComDecodePacket.length+0E1h)]
__text:0000000100006CD4                 test    edx, edx                                                [1]
__text:0000000100006CD6                 jz      short loc_100006D2A
__text:0000000100006CD8                 lea     r8d, [rdx+0FFh]
__text:0000000100006CDF                 add     r8b, cl
__text:0000000100006CE2
__text:0000000100006CE2 loc_100006CE2:                          ; CODE XREF: xor_decode+62j
__text:0000000100006CE2                 movzx   ecx, cl
__text:0000000100006CE5                 lea     eax, [rcx+1]
__text:0000000100006CE8                 movzx   r14d, al
__text:0000000100006CEC                 movzx   r10d, byte ptr [rdi+r14]
__text:0000000100006CF1                 movzx   r11d, r10b
__text:0000000100006CF5                 movzx   r9d, r9b
__text:0000000100006CF9                 add     r9d, r11d
__text:0000000100006CFC                 movzx   eax, r9b
__text:0000000100006D00                 mov     bl, [rdi+rax]
__text:0000000100006D03                 mov     [rdi+r14], bl
__text:0000000100006D07                 mov     [rdi+rax], r10b
__text:0000000100006D0B                 movzx   eax, byte ptr [rdi+r14]
__text:0000000100006D10                 add     eax, r10d
__text:0000000100006D13                 movzx   eax, al
__text:0000000100006D16                 mov     al, [rdi+rax]
__text:0000000100006D19                 xor     [rsi], al                                               [2]
__text:0000000100006D1B                 inc     rsi
__text:0000000100006D1E                 inc     cl
__text:0000000100006D20                 dec     edx
__text:0000000100006D22                 jnz     short loc_100006CE2                                     [3]

At the beginning, EDX is being checked against zero, [1], then we subsequently fall into a loop. Some data is then grabbed from the packet structure and used at location [2]. This is XOR'd against user data in an attempt to deobfuscate the packet received. Continuing down a touch further we see an increment to RSI, CL and a decrement to EDX. EDX is then tested against zero and it is clear the loop will continue until EDX reaches zero. This means that the loop will be executed however many times the attacker passed in from the previous function, directly taken from the packet. The code will continue incrementing the packet pointer in RSI, causing a buffer overflow of attacker controlled data and ultimately leading to remote code execution.

Crash Information

TYBOHAN-M-F0VF% ./exc_handler ./psnotifyd
GuardMalloc[exc_handler-95307]: Allocations will be placed on 16 byte boundaries.
GuardMalloc[exc_handler-95307]:  - Some buffer overruns may not be noticed.
GuardMalloc[exc_handler-95307]:  - Applications using vector instructions (e.g., SSE) should work.
GuardMalloc[exc_handler-95307]: version 108
GuardMalloc[psnotifyd-95308]: Allocations will be placed on 16 byte boundaries.
GuardMalloc[psnotifyd-95308]:  - Some buffer overruns may not be noticed.
GuardMalloc[psnotifyd-95308]:  - Applications using vector instructions (e.g., SSE) should work.
GuardMalloc[psnotifyd-95308]: version 108
2017-01-25 10:48:59.209 psnotifyd[95308:9320558] Notify listening thread started
2017-01-25 10:48:59.210 psnotifyd[95308:9320558] Listening on socket 4
2017-01-25 10:48:59.224 psnotifyd[95308:9320552] CFSocketSetAddress bind failure: 48
2017-01-25 10:48:59.225 psnotifyd[95308:9320552] Telling any existing Notify processes that psnotifyd has started up.
2017-01-25 10:49:06.770 psnotifyd[95308:9320558] New notify connection incoming
2017-01-25 10:49:06.770 psnotifyd[95308:9320558] Spawning a new notify request handler thread
2017-01-25 10:49:06.771 psnotifyd[95308:9320558] Listening on socket 4
2017-01-25 10:49:06.771 psnotifyd[95308:9321282] New request handler thread started
2017-01-25 10:49:06.771 psnotifyd[95308:9321282] I got some stuff goin' on

Crashed thread log =

0   psnotifyd                       0x0000000100006d19 0x100000000 + 27929
1   psnotifyd                       0x0000000100006449 0x100000000 + 25673
2   psnotifyd                       0x00000001000063a3 0x100000000 + 25507
3   psnotifyd                       0x0000000100002a4f 0x100000000 + 10831
4   psnotifyd                       0x0000000100002392 0x100000000 + 9106
5   com.apple.Foundation            0x00007fff89e3de64 __NSThread__start__ + 1351
6   libsystem_pthread.dylib         0x00007fff997ec99d _pthread_body + 131
7   libsystem_pthread.dylib         0x00007fff997ec91a _pthread_start + 168
8   libsystem_pthread.dylib         0x00007fff997ea351 thread_start + 13

log name is: ./crashlogs/crashlog.txt
---
exception=EXC_BAD_ACCESS:signal=11:is_exploitable=yes:instruction_disassembly=xorb  %al,(%rsi):instruction_address=0x0000000100006d19:access_type=write:access_address=0x00000001058d5000:
Crash accessing invalid address.

Talos would also like to thank NYU Osiris Lab for helping out with some of the reversing.

Timeline

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

Credit

Tyler Bohan