Monday, June 22, 2015

How to find outdated joomla versions on your server to reduce the risk of being hacked

How to find outdated joomla versions on your server to reduce the risk of being hacked



Today I want to focus on a topic that can lead to huge problems of hacked accounts, spam mailings etc.: Outdated Joomla installations on your server.
Of course, this is valid for other software, too. The mentioned method should work in a similar way for other software. Since Joomla is widely spread throughout the internet, especially on shared hosting systems CMS (Content Management System), this howto will only cover Joomla so far.

Prerequisites

You will need at least the tools bc and sed that are not always installed by default.

Let us begin

At first there is one question: how can I recognize the joomla version from the installed files?
This depends on the installed version of Joomla. Until now I have found three different files that contain the version information:
/libraries/joomla/version.php
/libraries/cms/version/version.php
/includes/version.php
The most important lines of these files are the version number and the minor version that are contained in the following variables:
var $RELEASE = '1.0';
var $DEV_LEVEL = '12';
In a next step we search for the most recent version of Joomla on the official website. At the time of writing this howto there are three version groups:1.5 (1.5.26), 2.5 (2.5.17) and 3.2 (3.2.1).

Find joomla installations on your server

One thing that all joomla installations have in common is the folder name "components", so we search for all folders with this name. At the same time we ignore all those components folders that are contained in a subfolder "administrator". The base path /var/www has to be adjusted if the websites on your server do not lie in there.
find /var/www/ -type d -name 'components' ! -wholename '**/administrator/components'
This command will give you a list of all those folders. The command dirname is suitable for getting the path that contains the components folder. This is the base path of the Joomla installation.
We do this in a loop for all found components folders:
for L in `find /var/www/ -type d -name 'components' ! -wholename '**/administrator/components'` ; do
    D=`dirname $L` ;
done

Get the major and minor version

To get the version of your installation we will use the combined commands "grep" and "sed".
First we check which of the three files I mentioned earlier exists in the installation path.
F=$D/libraries/joomla/version.php ;
F2=$D/libraries/cms/version/version.php ;
F3=$D/includes/version.php ;
if [[ -e "$F" || -e "$F2" || -e "$F3" ]] ; then
    if [[ -e "$F" ]] ; then
        F=$F ;
    elif [[ -e "$F2" ]] ; then
        F=$F2 ;
    elif [[ -e "$F3" ]] ; then
        F=$F3 ;
    fi
else
    echo "No joomla version file found." ;
fi
Now we read the major and minor version from this file:
VERSION=`grep '$RELEASE' $F | sed -r "s/^.*=\s*'(.*)'.*$/\1/g"` ;
SUBVERSION=`grep '$DEV_LEVEL' $F | sed -r "s/^.*=\s*'(.*)'.*$/\1/g"` ;

Compare versions

Since version numbers are no integers, we cannot compare them inside the bash script using -lt etc. Instead we need to use an external programm called bc:
ISOK=1 ;
if [[ $(echo "if (${VERSION} < 1.5) 1 else 0" | bc) -eq 1 ]] ; then
    # version is lower than 1.5
    ISOK=0 ;
elif [[ $(echo "if (${VERSION} == 1.5) 1 else 0" | bc) -eq 1 && $(echo "if (${SUBVERSION} < 26) 1 else 0" | bc) -eq 1 ]] ; then
    # version is 1.5.x but lower than 1.5.26
    ISOK=0 ;
### and so on - further version checks
else
    ISOK=1 ;
fi

The complete script

Now we are ready to assemble all the parts into a ready-to-use script and add some minor improvements. The script will search for all joomla versions in the given base path and prints information about the status. Of course you cannot speak of a version 1.5.26 as "current" but as it is the most current version of the 1.5 branch and an update to the 2.5 or 3.x branch is very complicated in some cases, this script will mark this version as "OK". You can change this if you like.
Here is a sample output of the script:
[INFO] version 1.5.26 in /var/www/xxx is ok.
[WARN] outdated Joomla version 1.0.12 in /var/www/yyy
[WARN] outdated Joomla version 1.5.14 in /var/www/zzz
[WARN] outdated Joomla version 2.5.8 in /var/www/aaa
[WARN] outdated Joomla version 1.5.10 in /var/www/bbb
And now: the complete script. Just save it as "joomlascan.sh" and call it via
bash joomlascan.sh
If you give a file name as an additional argument (like bash joomlascan.sh list.csv), you get a file called list.csv that contains a list of all outdated installations:
/var/www/yyy;1.0.12;1.5.26
/var/www/zzz;1.5.14;1.5.26
/var/www/aaa;2.5.8;2.5.17
/var/www/bbb;1.5.10;1.5.26
Tip:
If you use ISPConfig 3, you should alter the BASEPATH to BASEPATH="/var/www/clients/client*/web*".
#!/bin/bash

# current version 1.5.x
CUR15=26
# aktuelle version 2.5.x
CUR25=17
# aktuelle version 3.2.x
CUR3=1

#base path of the websites
BASEPATH="/var/www/"

# write to csv file (optional argument)
OUTFILE=$1

if [[ "$OUTFILE" != "" ]] ; then
    # empty CSV file
    echo -n "" > $OUTFILE ;
fi

for L in `find ${BASEPATH} -type d -name 'components' ! -wholename '**/administrator/components' | grep -v '/tmp/'` ; do
    D=`dirname $L` ;
    F=$D/libraries/joomla/version.php ;
    F2=$D/libraries/cms/version/version.php ;
    F3=$D/includes/version.php ;
    ISOK=0 ;
    SHOWNEWEST="" ;
    IMPORTANCE=0 ;
    if [[ -e "$F" || -e "$F2" || -e "$F3" ]] ; then
        if [[ -e "$F" ]] ; then
            F=$F ;
        elif [[ -e "$F2" ]] ; then
            F=$F2 ;
        elif [[ -e "$F3" ]] ; then
            F=$F3 ;
        fi
        VERSION=`grep '$RELEASE' $F | sed -r "s/^.*=\s*'(.*)'.*$/\1/g"` ;
        SUBVERSION=`grep '$DEV_LEVEL' $F | sed -r "s/^.*=\s*'(.*)'.*$/\1/g"` ;
        if [[ $(echo "if (${VERSION} < 1.5) 1 else 0" | bc) -eq 1 ]] ; then
            # version is lower than 1.5
            SHOWNEWEST="1.5.${CUR15}" ;
            IMPORTANCE=3 ;
        elif [[ $(echo "if (${VERSION} == 1.5) 1 else 0" | bc) -eq 1 && $(echo "if (${SUBVERSION} < ${CUR15}) 1 else 0" | bc) -eq 1 ]] ; then
            # version is 1.5.x but not most current version
            SHOWNEWEST="1.5.${CUR15}" ;
            IMPORTANCE=2 ;
        elif [[ $(echo "if (${VERSION} == 1.6) 1 else 0" | bc) -eq 1 ]] ; then
            # version is 1.6
            SHOWNEWEST="2.5.${CUR25}" ;
            IMPORTANCE=2 ;
        elif [[ $(echo "if (${VERSION} == 1.7) 1 else 0" | bc) -eq 1 ]] ; then
            # version is 1.7
            SHOWNEWEST="2.5.${CUR25}" ;
            IMPORTANCE=2 ;
        elif [[ $(echo "if (${VERSION} > 1.7) 1 else 0" | bc) -eq 1 && $(echo "if (${VERSION} < 2.5) 1 else 0" | bc) -eq 1 ]] ; then
            # version is somewhere between 1.7 and 2.5
            SHOWNEWEST="2.5.${CUR25}" ;
            IMPORTANCE=2 ;
        elif [[ $(echo "if (${VERSION} == 2.5) 1 else 0" | bc) -eq 1 && $(echo "if (${SUBVERSION} < ${CUR25}) 1 else 0" | bc) -eq 1 ]] ; then
            # version is 2.5 but lower than current
            SHOWNEWEST="2.5.${CUR25}" ;
            IMPORTANCE=1 ;
        elif [[ $(echo "if (${VERSION} >= 3) 1 else 0" | bc) -eq 1 && $(echo "if (${VERSION} < 3.2) 1 else 0" | bc) -eq 1 ]] ; then
            # version is 3.0 or 3.1
            SHOWNEWEST="3.2.${CUR3}" ;
            IMPORTANCE=2 ;
        elif [[ $(echo "if (${VERSION} == 3.2) 1 else 0" | bc) -eq 1 && $(echo "if (${SUBVERSION} < ${CUR3}) 1 else 0" | bc) -eq 1 ]] ; then
            # version is 3.2 but lower than current
            SHOWNEWEST="3.2.${CUR3}" ;
            IMPORTANCE=1 ;
        else
            ISOK=1 ;
            echo "[INFO] version $VERSION.$SUBVERSION in $D is ok." ;
        fi
    else
        # seems not to bee a joomla version ...
        ISOK=1 ;
    fi ;
    
    if [[ $ISOK -eq 0 ]] ; then
        echo "[WARN] outdated Joomla version $VERSION.$SUBVERSION in $D" ;
        if [[ "$OUTFILE" != "" ]] ; then
            # write CSV file
            echo "\"$D\";$VERSION.$SUBVERSION;$SHOWNEWEST;$IMPORTANCE" >> $OUTFILE ;
        fi
    fi
done

exit 0 ;
There is one known issue whith this script: it cannot recognize Mambo. So it will mark all mambo installations as "OK" regardless which version they have.

No comments: