4

I have a virus that has infected thousands of files on one of my client's server.

Fortunately, I have dealt with a lot of other malware on this guy's server and this one looks easy to do simple regex on (he put all his websites on the same account :( but I'm working with him to resolve that).

Basically though, unlike most malware I have seen where it injects php BEFORE the closing ?> of the GOOD code (making it very hard to determine whats good code/bad code), this current malware ALWAYS adds a new <?php ... malware ... ?>.

So basically, say there's good code here:

<?php
require('./wp-blog-header.php'); 
?>

Instead of adding some kind of base64_decode eval immediately after the require statement but before the ?> (which can make removal difficult when the page happens to end in a conditional/complex statement), this will always add the following code with a NEW <?php ... ?> like so:

<?php
require('./wp-blog-header.php'); 
?><?php ... malware ...?>

I don't want to put any malicious code up here but, this is how the malicious code always starts:

<?php @error_reporting(0); if (!isset($eva1fYlbakBcVSir)) {$eva1fYlbakBcVSir = "tons and tons of characters";$eva1tYlbakBcVSir = "\x6335\1443\3x6f\1534\x70\170\x65";$SNIPSNIPSNIPSNIP;} ?>

I'd like to search every file for <?php @error_reporting(0); if (!isset and if it's the last PHP statement on the page, then delete everything within the

hakre
  • 193,403
  • 52
  • 435
  • 836
Tallboy
  • 12,847
  • 13
  • 82
  • 173
  • 1
    I dealt with one of these recently. Copy it all to your development machine, and load it into a decent IDE (e.g. Eclipse, Netbeans). Then do a project-wide search and replace. – halfer May 02 '12 at 22:20
  • 3
    i would just restore from backup - you do have back-ups don't you? –  May 02 '12 at 22:20
  • I have tons of backups, but this isnt me :) hes in the middle of a large product launch though... the site cant be down for more than a few moments. i have also fixed the title. @halfer... he has 40gb of shit on his server, tons of videos or i woulda done that already. i want to do this through SSH if i can or a php script – Tallboy May 02 '12 at 22:21
  • Search and replace should do the trick. You can do a sitewide search in something like notepad++ – Paul Dessert May 02 '12 at 22:21
  • 2
    edit one file at at time, sounds very profitable. –  May 02 '12 at 22:22
  • do you have anything useful to add or just more dumb comments? thats 2 in a row – Tallboy May 02 '12 at 22:24
  • Download the site to your machine anyway, skipping large files. Wordpress tends to put media files in one location anyway - find out what those location(s) are for you, and don't copy those things from the server. – halfer May 02 '12 at 22:26
  • 1
    @Tallboy - be nice to people. You're a sitting duck for downvotes otherwise `:(`. – halfer May 02 '12 at 22:27
  • I was referring to Dagon, who hasn't added anything useful except dumb comments. You're helping a lot mang :) – Tallboy May 02 '12 at 22:28
  • its over 40 sites in the same cpanel account.. some of them are wordpress, most of them arent. A lot others are special plugins or scripts with videos mixed it, can't selectively do anything like that – Tallboy May 02 '12 at 22:28
  • (Anyway, it is best practice to edit a site on a dev machine - after all, with any system, you should test locally before deploying). – halfer May 02 '12 at 22:28
  • 3
    it looks like this malware is on one line, is this correct? If so, `sed` is your friend. `sed -e "s///g" --in-place=_cleaned *` this will remove the given pattern from all files (`*`) and store original files with the extension `_cleaned`. But *please* make a backup first!! and read `man sed` for your particular system. – mvds May 02 '12 at 22:30
  • Someone in the middle of a large project launch should have backups, and not have 40 sites in the same cPanel account. – Brad May 02 '12 at 22:30
  • Lol I know brad, it's a giant mess. I made all the same recommendations... for now though there is the urgent task of removal at hand – Tallboy May 02 '12 at 22:32
  • --in-place doesnt seem to work – Tallboy May 02 '12 at 22:47
  • You're not rebuilding/replacing the server? That sounds... brave... – bobince May 04 '12 at 09:51
  • I am. I said a few time's he's mid-launch. I'm slowly migrating all his other stuff to other cpanel accounts fresh, new ftps, sql passwords, etc. even after i move it though i still have thousands of files to clean up, which i'm not going to be doing by hand. if theres a problem i would just restore from a backup (that i have now configured) – Tallboy May 04 '12 at 19:05

2 Answers2

12

Here is how you clean the entire project with pure php.

In no respect shall I incur any liability for any damages, including, but limited to, direct, indirect, special, or consequential damages arising out of, resulting from, or any way connected to the use of the code provided, whether or not based upon warranty, contract, tort, or otherwise; whether or not injury was sustained by persons or property or otherwise; and whether or not loss was sustained from, or arose out of, the results of, the use if this code. ;p

<?php 
//Enter it as it is and escape any single quotes
$find='<?php @error_reporting(0); if (!isset($eva1fYlbakBcVSir)) {$eva1fYlbakBcVSir =\'\';?>';

echo findString('./',$find);

function findString($path,$find){
    $return='';
    ob_start();
    if ($handle = opendir($path)) {
        while (false !== ($file = readdir($handle))) {
            if ($file != "." && $file != "..") {
                if(is_dir($path.'/'.$file)){
                    $sub=findString($path.'/'.$file,$find);
                    if(isset($sub)){
                        echo $sub.PHP_EOL;
                    }
                }else{
                    $ext=substr(strtolower($file),-3);
                    if($ext=='php'){
                        $filesource=file_get_contents($path.'/'.$file);
                        $pos = strpos($filesource, $find);
                        if ($pos === false) {
                            continue;
                        } else {
                        //The cleaning bit
                        echo "The string '".htmlentities($find)."' was found in the file '$path/$file and exists at position $pos and has been removed from the source file.<br />";
                        $clean_source = str_replace($find,'',$filesource);
                        file_put_contents($path.'/'.$file,$clean_source);
                        }
                    }else{
                        continue;
                    }
                }
            }
        }
        closedir($handle);
    }
    $return = ob_get_contents();
    ob_end_clean();
    return $return;
}
?>

Good Luck.

UPDATE (With Regex):

<?php 
error_reporting(E_ALL);
$find='<\?php @error_reporting\(0\); if \(!isset\((.*?)\?>';

echo findString('./',$find);

function findString($path,$find){
    $return='';
    ob_start();
    if ($handle = opendir($path)) {
        while (false !== ($file = readdir($handle))) {
            if ($file != "." && $file != "..") {
                if(is_dir($path.'/'.$file)){
                    $sub=findString($path.'/'.$file,$find);
                    if(isset($sub)){
                        echo $sub.PHP_EOL;
                    }
                }else{
                    $ext=substr(strtolower($file),-3);
                    if($ext=='php'){

                        $filesource=file_get_contents($path.'/'.$file);
                        //The cleaning bit
                        echo "The string '".htmlentities($find)."' was found in the file '$path/$file and has been removed from the source file.<br />";
                        $clean_source = preg_replace('#'.$find.'#','',$filesource);
                        // $clean_source = str_replace($find,'',$filesource);
                        file_put_contents($path.'/'.$file,$clean_source);
                    }else{
                        continue;
                    }
                }
            }
        }
        closedir($handle);
    }
    $return = ob_get_contents();
    ob_end_clean();
    return $return;
}
?>
Lawrence Cherone
  • 46,049
  • 7
  • 62
  • 106
1

So far this is the closest (thank you mvds)

sed -e "s/<?php @error_reporting.*?>//g" --in-place=_cleaned *

although --in-place=_cleaned is giving the error sed: illegal option -- -

Tallboy
  • 12,847
  • 13
  • 82
  • 173