Php Website Scanner - find injected files, added files and updated files | South Coast Web Design Ltd
South Coast Web Design Ltd
Php Website Scanner – find injected files, added files and updated files Mar 25

As website developers,

we all hope the day does not come when a client phones to tell you that their website has been hacked, or weird popups are appearing, or worse still the web hosting company has taken their site offline.

Well recently a clients WordPress site that we have just inherited was hacked, (just to be clear it was not a site we wrote or even host, we have just been given the site when their last developers closed).

So the first thing to do is browse through all the weird .php files that have popped up and insure they are not injected code.

Realistically the site will need to redone completely on the latest version of WordPress or Joomla.

The question is:
What is there to warn us of the possible file injection?

And the answer:
Well not a huge amount. I googled about and a lot of scripts on Github ran from SSH, or others were quite heavy in cost.

As the site was not hosted by us, SSH was out of the question. As the customer was running on a budget the costly versions were also a no.

After a few minutes of searching, I stumbled across a script by Mike Stowe (http://www.mikestowe.com) here

This scans the whole home directory and searches for ‘eval’ and other such injections.

You simply upload the script to the home directory, setup the email and run it manually or via a Cron job.

In our case, I did find another injection which was not found so we add another search for the extra code.

        /* if you are looking for other injected code add here */
        if(preg_match('/x65\/li\x6eweb/' , $contents)){
            $this->infected_files[] = $file;
            echo 'Infected File : '.$file.'<br/>';
        }

This worked perfectly and gave us the results we needed. We were able to scan every hour and catch the code before it did any harm.

However, we decided that there were other things that we may require from the scanner, such as added files, modified files and removed files.

So we pulled the main scan script out and set about building a full class that will do these checks as well as the ‘eval scanner’.

First off we created a serialised array which stores the files in the home directory. This was quite straight forward as we were already grabbing a list of the files within the eval script.

    /**
     * Function will create a file containing a serialiazed array of files scanned
     */
    function writeFile() {
        // debug. Uncomment to get a fresh file list
        // $this->freshclean();

        $data = serialize($this->fileindex);
        $fopen = fopen('scanner-logs/scannerfiles.txt', 'w+');
        fwrite($fopen, $data);
        fclose($fopen);
    }

This serialised data was then written to a file within a scanner-logs folder. We then pull this file and unserialize the text into an array.

    /**
     * Function to read the previously canned filenames and unserialize them to an array
     * It will also return the last time a file list was created
     */
    function readFile() {
        $this->oldfileindex = unserialize(file_get_contents('scanner-logs/scannerfiles.txt'));
        $this->lastscan = filemtime('scanner-logs/scannerfiles.txt');
    }

This file is only rewritten if any files have been updated, added or removed. We can use the date this file was modified as a point to check for modifications. ($this->lastscan)

So we now have 2 arrays, 1 with the new files listed and 1 with the old files. We also have our last modified point to check from.

Whilst scanning the directories for eval injection we check each file modified time against the last modified time of the stored old list.

    /**
     * Function to check the file contents for injections
     * @string $contents Content of the file
     * @string $file     Filename being checked
     */
    function check($contents,$file) {
        $this->scanned_files[] = $file;
        $this->fileindex[]     = $file;

        // check for injection
        if(preg_match('/eval\((base64|eval|\$_|\$\$|\$[A-Za-z_0-9\{]*(\(|\{|\[))/i',$contents)) {
            $this->infected_files[] = $file;
        }

        /* if you are looking for other injected code add here */
        if(preg_match('/x65\/li\x6eweb/' , $contents)){
            $this->infected_files[] = $file;
        }

        // check for modification
        if(filemtime($file) > $this->lastscan){
            $this->modified[] = $file.' modified '.date ("F d Y H:i:s.", filemtime($file));
        }
    }

So to check for added files we do an array_diff on the new list against the old list. Gives us array of added files.

        // files added
        $difference = array_diff($this->fileindex, $this->oldfileindex);
        if(count($difference) > 0){
            // set flag for file write
            $updateList = true;

            $message .= "<br/>== FILES ADDED == <br/><br/>";
            foreach ($difference as $added) {
                $message .= $added."<br/>";
            }
        } else {
            $message .= "<br/>== NO FILES HAVE BEEN ADDED == <br/><br/>";
        }

Next, we do a check for removed files using an array_diff on the old list against the new list. Gives us array of removed files.

        // files Removed
        $removed = array_diff($this->oldfileindex, $this->fileindex);
        if(count($removed) > 0){
            // set flag for file write
            $updateList = true;

            // add results to message
            $message .= "<br/>== FILES REMOVED == <br/><br/>";
            foreach ($removed as $deleted) {
                $message .= $deleted."<br/>";
            }
        } else {
            $message .= "<br/>== NO FILES HAVE BEEN REMOVED == <br/><br/>";
        }

So now we have 4 arrays:

  • One with any injected files listed
  • One with any modified files listed
  • One with any added files listed
  • One with any removed files listed

So now we create a message by looping through the arrays.

We can now send an email to the email list with all of this content.

        $header = "<h2>PHP Web Scanner Results</h2><hr/><br/>";
        $footer .= "<br/><br/><br/>Website was scanned by PHP Web Scanner by <a href=\"https://www.southcoastweb.co.uk\" target=\"_blank\">South Coast Web Design Ltd</a>";

        $headers = "MIME-Version: 1.0" . "\r\n";
        $headers .= "Content-type:text/html;charset=UTF-8" . "\r\n";
        // More headers
        $headers .= 'From: PHP Website Scanner <'.FROM_EMAIL.'>' . "\r\n";

        mail(EMAIL_ALERT, 'Website Scan results', $header.$message.$footer, $headers);

        echo $message;

To add to this we create a log file to keep a track of the results.

    /**
     * Function to log results for future reading
     * @string $message Records a log of the results
     */
    function logResults($message) {
        $fopen = fopen('scanner-logs/scan-results-'.date('H:i').'.log', 'w+');
        fwrite($fopen, $message);
        fclose($fopen);
    }

(if run hourly the files overwrite themselves daily).

So we put all the functions together within our phpWebScanner class.

In our index file, we have a couple of variables to add in and we are complete.

// Setup
define('EMAIL_ALERT','');
define('DOMAIN', '');
define('FROM_EMAIL', '');

Just run the scanner by going to <domain>/php-web-scanner.
or create the cronjob to ‘wget’ the file as often as you wish.

Feel free to download the whole package over at GitHub here

If you wish to edit, improve or help with the script please feel free to do so.

(please leave all original credits in place)

Leave a Response


Top