感染したサイトファイルを悪意のあるコードからクリーニングする

こんにちは、Habrausers様!



しばらく前、約1か月、当社のサーバーにウイルスが出現しました。 主要なプロジェクトの1つでは、すべての* .jsファイルが影響を受けました。 状況は一般的です-悪意のあるコードがファイルの最後に追加されました。 Yandexはサイトの感染について警告を発し、タスク部門はそれをきれいにするタスクを受け取りました。 状況はすぐに解決し、プロジェクトはクリーンなリポジトリから本番環境にアップロードされ、パスワードが変更されました。



しかしすぐに、技術部門の会社のすべての部門から感染サイトに関する苦情が寄せられるようになりました。 クライアントはマネージャーに不満を言い、SEOはサイトが地面を失っているとラッパを打ちました。 真の流行が始まった。 * .jsファイルに加えて、* .phpファイルも感染し、その最後にコードが追加されました。



echo 'http://somedomain.com/style.js';
      
      





サーバーは、このような大規模な感染に初めてさらされました。 この件についての考えは異なり、汚いトリックをプレイすることに決めた不満を抱いた解雇された従業員の裏口までです。 ただし、これは確認されていません。 * .phpファイルをクレンジングするシェルスクリプトが作成されましたが、クリーンなリポジトリからアンロードすることで* .jsファイルに対処しました。 サイトのアクセスアカウントのパスワード、ftpへのアクセス-すべてが変更されました。 ftpで作業するすべての人をWinSCPに転送し、アクセスキーファイルを配布しました。



サーバーはゆっくりと「回復」し始め、サイトはYandexに戻りました。 ただし、当社のサーバーには100を超えるクライアントサイトに加えて、サードパーティのホスティングを使用しているクライアントがあります。 FTPアクセス、コマンドラインおよびシェルなし。 ほとんどすべてのサイトは、fckeditor'omまたはckeditor'ovの古いバージョンと組み合わせて、自己記述CMS(N年前に記述)を使用しています。 ckfinderファイルマネージャでは、単純なreturn trueによって許可チェックが実装されます。 しぶしぶ使用します。 また、多くの感染した* .jsファイルは、クリーンなリポジトリをアンロードしても修復できないことに注意してください。 このようなサイトではGitを使用せず、ホスティングのほとんどのバックアップは最大7日間保存されます。 また、サードパーティのホスティングにあるサイトは実際には監視されていないため、すべてのバックアップも感染していました。



ウイルスは、各ファイルに1〜5個の任意の数の独自のコピーを追加しました(もう見ていません)。それらはすべて異なる変数名、関数名を持ち、それらに固執することは不可能でした。 各ポリモーフのコードの唯一の定数部分は、次のコードセクションでした。

 =Array.prototype.slice.call(arguments).join(""),
      
      





彼はファイルのエントリを検索するために選ばれました。 JQueryライブラリ1.6.3および1.7.2がチェックされ、ソースコードで一致するものが見つかりませんでした。 したがって、シーケンスを使用できます。



手動で各サイトに多数の* .jsファイルを配置しないように、phpでスクリプトを作成することにしました。 彼は、目的の行について、示されたすべてのファイルをスキャンする必要があります。 視野を広げるために、いわば、exec()、system()コマンド、または例えばphpseclibライブラリを使用しないことが決定されました。 アルゴリズムは不名誉に簡単です。スクリプトは、検索ファイル内の指定された行を検索して、指定されたディレクトリからすべてのディレクトリをスキャンします。 変更を行う前に、スクリプトはファイルをバックアップし(何を知っているかわかりません)、目的のサブストリングが存在する行を削除します。 ウイルスが1行でファイルに書き込まれたという事実により、行ごとの作業が選択されました。



* .jsファイルにウイルスコードの例を示します: pastebin.com/J0zRduQw



私は気にしないで、それを分解しませんでした-難読化されたコードを解析するためのインターネット上の多くの例があります。 したがって、すぐにスキャナーコードに進みます。



 <? /* ---------------------------------------------------------------------------------- dScaner Class - START ---------------------------------------------------------------------------------- */ /* * *  - dScaner         *    * * :   *  : 03-04-2012 *  : 0.0.3 * */ Class dScaner { //      // $get_str -   // $separator -     function request($get_str, $separator) { if (isset($get_str) && !empty($get_str)) { //        $obj = explode($separator, $get_str); return $obj; } else { return false; } } /* * *       : * * $this->find($path, $files_allowed, $requested_string); * * $path -   ,      * $files_allowed -  ,    * $requested_string -   * */ function find($path = './', $files_allowed, $requested_string) { //      ,    $dir_disallow = array('.', '..', '.htaccess', '.git'); if(is_dir($path)) { $temp = opendir($path); while (false !== ($dir = readdir($temp))) { if ((is_dir($path . $dir)) && (!in_array($dir, $dir_disallow)) ) { //   -   $sub_dir = $path . $dir . '/'; $this->find($sub_dir, $files_allowed, $requested_string); } elseif ((is_file($path . $dir)) && (!in_array($dir, $dir_disallow)) && (strpos($dir, $files_allowed) == true) && (strpos($dir, '_BACKUP') == false) ) { //   //      $in_dir_file = $path . $dir; //     $temporary_file = file_get_contents($in_dir_file); //      $file_founded = false; //     $tf_strings = explode("\n", $temporary_file); //    foreach ($tf_strings AS $item) { $item = strval($item); //        if (strpos($item, $requested_string) !== false) { $file_founded = true; } } //      if ($file_founded) { //         print "<span style='display:block; padding:5px; border:1px solid #1f4f18; background-color:#d5f5ce; font-size:12px; line-height:16px; font-family:tahoma, sans-serif; margin-bottom:-15px;'>" . $in_dir_file . " -     .<br> </span><br>"; } } } closedir($temp); } } /* * *    : * * $this->scan($path, $files_allowed, $requested_string); * * $path -   ,      * $files_allowed -  ,    * $requested_string - ,       * */ function scan($path = './', $files_allowed, $requested_string) { //       $dir_disallow = array('.', '..', '.htaccess', '.git'); if(is_dir($path)) { $temp = opendir($path); while (false !== ($dir = readdir($temp))) { if ((is_dir($path . $dir)) && (!in_array($dir, $dir_disallow)) ) { //   -   $sub_dir = $path . $dir . '/'; $new_parent_dir = $path . $dir; $this->scan($sub_dir, $files_allowed, $requested_string, $new_parent_dir); } elseif ((is_file($path . $dir)) && (!in_array($dir, $dir_disallow)) && (strpos($dir, $files_allowed) == true) && (strpos($dir, '_BACKUP') == false) ) { //   //      $in_dir_file = $path . $dir; //     $temporary_file = file_get_contents($in_dir_file); //    $create_backup = false; //         $tf_strings = explode("\n", $temporary_file); //    $str_index = 0; //     foreach ($tf_strings AS $item) { $item = strval($item); if (strpos($item, $requested_string) !== false) { //        //   ,      $create_backup = true; //       unset($tf_strings[$str_index]); } $str_index++; } //   if ($create_backup) { //              chmod($path, 0777); //     $temp_file_backup = $in_dir_file.'_BACKUP'; //       file_put_contents($temp_file_backup, $temporary_file); //      $scanned_file = implode("\n", $tf_strings); //    if (file_put_contents($in_dir_file, $scanned_file)) { //   print "<span style='display:block; padding:5px; border:1px solid #1f4f18; background-color:#d5f5ce; font-size:12px; line-height:16px; font-family:tahoma, sans-serif; margin-bottom:-15px;'>" . $in_dir_file . " -  . (+ BACKUP) <br> </span><br>"; } else { //    print "<span style='display:block; padding:5px; border:1px solid #822121; background-color:#ea7575; font-size:12px; line-height:16px; font-family:tahoma, sans-serif; margin-bottom:-15px;'>".$in_dir_file ." -   . </span><br>"; } //          755 chmod($path, 0755); } } } closedir($temp); } } /* * *     * * $this->restore_backups($path, $files_allowed); * * $path -   ,      * $files_allowed -  ,    * */ function restore_backups($path = './', $files_allowed) { //       $dir_disallow = array('.', '..', '.htaccess', '.git'); if(is_dir($path)) { $temp = opendir($path); while (false !== ($dir = readdir($temp))) { if ((is_dir($path . $dir)) && (!in_array($dir, $dir_disallow)) ) { //   -   $sub_dir = $path . $dir . '/'; $this->restore_backups($sub_dir, $files_allowed); } elseif ((is_file($path . $dir)) && (!in_array($dir, $dir_disallow)) && (strpos($dir, $files_allowed) == true) ) { //   //      $in_dir_file = $path . $dir; if (is_file($in_dir_file.'_BACKUP')) { //  ,    $temporary_file_from_backup = file_get_contents($in_dir_file.'_BACKUP'); //    if (file_put_contents($in_dir_file, $temporary_file_from_backup)) { //   unlink($_SERVER['DOCUMENT_ROOT'].'/'.$in_dir_file.'_BACKUP'); //   print "<span style='display:block; padding:5px; border:1px solid #1f4f18; background-color:#d5f5ce; font-size:12px; line-height:16px; font-family:tahoma, sans-serif; margin-bottom:-15px;'>".$in_dir_file ." - . </span><br>"; } else { //    print "<span style='display:block; padding:5px; border:1px solid #822121; background-color:#ea7575; font-size:12px; line-height:16px; font-family:tahoma, sans-serif; margin-bottom:-15px;'>".$in_dir_file ." -  . </span><br>"; } } } } closedir($temp); } } } /* ---------------------------------------------------------------------------------- dScaner Class - END ---------------------------------------------------------------------------------- */ ?>
      
      





クラスコードはある程度詳細にコメント化されており、質問はありません。

使用例(最初のパラメーターは検索の開始ディレクトリ、2番目は検索に関係するファイルの種類、3番目は検索文字列):



スキャナーのインスタンスを作成します。



 $dron = new dScaner;
      
      





何かを上書きする前に、検索条件を満たすファイルがあるかどうかを確認する価値があります。



 $dron->find('./', '.js', '=Array.prototype.slice.call(arguments).join(""),');
      
      





ストリッピングを開始します。



 $dron->scan('./', '.js', '=Array.prototype.slice.call(arguments).join(""),');
      
      





その場合、作成したバックアップをいつでも復元できます。



 $dron->restore_backups('./', '.js');
      
      





スキャナーは多くのサイトでテストされ、必要に応じて機能します。サーバーで発生した唯一の問題はファイルの所有者です。 ファイルのowner'omはwww:wwwである必要があります。 平均して、数十個の* .jsファイルがあるサイトでは5〜10〜20秒かかりました。 そして、スクリプトが正常にテストされたホスティングサービスのリストを示します:infobox、agava、jino、mchost、hc。 とりわけ、mchostは最も低速でしたが、それ以外ではすべてが非常にスマートに機能しました。



PSこのスクリプトは、ウイルスの万能薬のふりをするものではなく、特定の感染事例と、その後の改善のために設計されています。 ただし、タスクに対処します。 誰かが役に立つことを願っています。

よろしく!



All Articles