美文网首页
[PHP] Webshell简单的免杀思路

[PHP] Webshell简单的免杀思路

作者: w_w_wei | 来源:发表于2018-08-02 09:12 被阅读0次

    接到一个简单的需求, 客户自己的扫描工具把我们的代码,误认为是webshell。经过简单排查, 确认了误报 原因, 这个确认思路,也可以作为webshell的免杀思路。

    前提条件是,源码类型的webshell,没有经过加密的。
    而且代码被杀软识别成webshell

    思路

    1. 截取文件部分代码,然后对文件进行扫描, 如果不报毒,则表示存在特征码使得代码被识别为webshell。
    2. 截取文件部分代码,然后对截取进行扫描, 如果报毒,则表示存在特征码使得代码被识别为webshell。
    3. 在代码中随机截取一定数量行的代码,生成样本, 对大量的样本进行扫描,再对有毒样本,进行思路1方法处理。

    再对代码量相对比较小的样本,进行人工分析, 基本是可以完全找出所有的特征码关键字。

    代码片段

    之前排查问题写的一些代码片段。

    #1 截取文件部分代码,然后对文件进行扫描
    <?php
    ini_set('display_errors', 'On');
    /**
    * 特征码查找
    */
    class KeyFind {
        public $config;
        public $valider = null;
        public $filepath = '';
        public $file = null;
    
        public function __construct($config)
        {
            $default_config = array(
                'line'          => 3,
                'source_file'   => '',
                'source_dir'    => dirname(__FILE__) . '/',
                'dest_file'         => dirname(__FILE__) . '/dest/file.php',
                'log_file'      => dirname(__FILE__) . '/log.txt'
            );
            $this->config = array_merge($default_config, $config);
        }
        //记录日志
        public function log( $info ){
            if (!file_exists($this->config['log_file'])) {
                $myfile = fopen($this->config['log_file'], "w") or die("Unable to open file!");
                $txt = "log\n";
                fwrite($myfile, $txt);
                fclose($myfile);
            }
            file_put_contents($this->config['log_file'], $info, FILE_APPEND);
        }
        public function _desturct() {
            // if (!is_null($this->file)) {
            //  $this->file = null;
            // }
        }
    public function valider()
        {
            $number = 0;
            $command = "./webshellscanner -d dest";
            exec($command, $output);
            foreach( $output as $str) {
                    if ( preg_match('/发现webshell数量(.*?)(\d{1,})(.*?)$/', $str, $matches)) {
                            $number = $matches[2];
                    }
            }
            return $number;
        }
    public function fileCreate($array, $temp_str)
        {
            file_put_contents($this->config['dest_file'], implode("",$array));
        }
        /**
        * 打开文件
        */
        public function preCheck() {
            $this->filepath = $this->config['source_dir'] . $this->config['source_file'];
            if (empty($this->config['source_file'])
                || !file_exists($this->filepath)) {
                throw new Exception('File ['.$this->filepath.'] not Exists');
            }
            // if (is_null($this->valider)) {
            //  throw new Exception('Valider not Set');
            // }
            //$this->filepath = $this->config['source_dir'] . $this->config['source_file'];
            // $this->file = new SplFileObject($this->filepath, 'r');  
        }
        public function readFile() {
            $array = array();
            $file = new SplFileObject($this->filepath, 'r');  
            while (!$file->eof()) {
                $array[] = $file->current();
                $file->next();
              }
            $file = null;
            return $array;
        }
    public function search() {
            $this->preCheck();
            $problem = array();
            
            $array = $this->readFile($this->filepath);
            $line_count = count($array);
            $line_deal = $this->config['line'];
            $temp_str = '';
            for ( $i=0; $i<$line_count; $i=$i+$line_deal) {
                //置空
                $temp_str = $array[$i];
                $array[$i] = '';
                for ( $j=1; $j<$line_deal; $j++) {
                    $temp_str = $temp_str . $array[$i+$j];
                    $array[$i+$j] = '';
                }
                //$temp_str = $array[$i];
                $this->fileCreate($array, $temp_str);
                if ( $this->valider($this->config['dest_file']) ) {
                    // 存在关键字
                    $this->log( $i . ' line  exists key' . "\r\n");
                } else {
                    $this->log( $i . ' line  nonexists key' . "\r\n");
                }
    
                unlink($this->config['dest_file']);
                if (file_exists($this->config['dest_file'])) {
                    die('file not delete');
                }
    
                //恢复数据
                $temp_list = explode("\r\n", $temp_str);
                $temp_str = '';
                for ( $j=0; $j<$line_deal; $j++) {
                    if ($array[$i+$j] != '') {
                        echo 'error';
                    }
                    $array[$i+$j] = $temp_list[$j] . "\r\n";
                }
                $temp_list = array();
            }
        }
    
    }
    if ( $argc < 2 ) {
        echo "\r\n Usage : scan.php file_need_scan";
        exit();
    }
    $config = array(
        'source_file' => $argv[1]
    );
    $search = new KeyFind($config);
    $search->search();
    
    #2 截取文件部分代码,然后对截取的代码进行扫描
    <?php
    ini_set('display_errors', 'On');
    /**
    * 特征码查找
    */
    class KeyFind {
        public $config;
        public $valider = null;
        public $filepath = '';
        public $file = null;
    
        public function __construct($config)
        {
            $default_config = array(
                'line'          => 100,
                'source_file'   => '',
                'source_dir'    => dirname(__FILE__) . '/',
                'dest_file'         => dirname(__FILE__) . '/dest/file.php',
                'log_file'      => dirname(__FILE__) . '/log.txt',
    'result_dir'    => dirname(__FILE__) . '/result/'
            );
            $this->config = array_merge($default_config, $config);
        }
        //记录日志
        public function log( $info ){
            if (!file_exists($this->config['log_file'])) {
                $myfile = fopen($this->config['log_file'], "w") or die("Unable to open file!");
                $txt = "log\n";
                fwrite($myfile, $txt);
                fclose($myfile);
            }
            file_put_contents($this->config['log_file'], $info, FILE_APPEND);
        }
        public function _desturct() {
            // if (!is_null($this->file)) {
            //  $this->file = null;
            // }
        }
    public function valider()
        {
            $number = 0;
            $command = "./webshellscanner -d dest";
            exec($command, $output);
            foreach( $output as $str) {
                    if ( preg_match('/发现webshell数量(.*?)(\d{1,})(.*?)$/', $str, $matches)) {
                            $number = $matches[2];
                    }
            }
            return $number;
        }
    public function fileCreate($array, $temp_str)
        {
            if ($dest_file == '') {
                $dest_file = $this->config['dest_file'];
            }
            file_put_contents($dest_file, "<?php", FILE_APPEND);
            file_put_contents($dest_file, $temp_str, FILE_APPEND);
        }
        /**
        * 打开文件
        */
        public function preCheck() {
            $this->filepath = $this->config['source_dir'] . $this->config['source_file'];
            if (empty($this->config['source_file'])
                || !file_exists($this->filepath)) {
                throw new Exception('File ['.$this->filepath.'] not Exists');
            }
            // if (is_null($this->valider)) {
            //  throw new Exception('Valider not Set');
            // }
            //$this->filepath = $this->config['source_dir'] . $this->config['source_file'];
            // $this->file = new SplFileObject($this->filepath, 'r');  
        }
        public function readFile() {
            $array = array();
            $file = new SplFileObject($this->filepath, 'r');  
            while (!$file->eof()) {
                $array[] = $file->current();
                $file->next();
              }
            $file = null;
            return $array;
        }
    public function search() {
            $this->preCheck();
            $problem = array();
            
            $array = $this->readFile($this->filepath);
            $line_count = count($array);
            $line_deal = $this->config['line'];
            $temp_str = '';
            for ( $i=0; $i<$line_count; $i=$i+$line_deal) {
                //$temp_str = $array[$i];
                $this->fileCreate($array, $temp_str);
                if ( $this->valider($this->config['dest_file']) ) {
                    // 存在关键字
                    $this->log( $i . ' line  exists key' . "\r\n");
                    echo "发现异常代码串" . "\r\n";
                    $this->fileCreate($array, $temp_str, $this->config['result_dir'] . rand(1000,9999) . time() . '.php');
                } else {
                    $this->log( $i . ' line  nonexists key' . "\r\n");
                }
    
                unlink($this->config['dest_file']);
                if (file_exists($this->config['dest_file'])) {
                    die('file not delete');
                }
            }
        }
    
    }
    if ( $argc < 2 ) {
        echo "\r\n Usage : scan.php file_need_scan";
        exit();
    }
    $config = array(
        'source_file' => $argv[1]
    );
    $search = new KeyFind($config);
    $search->search();
    
    #3 生成随机样本
    <?php
    ini_set('display_errors', 'On');
    /**
    * 特征码查找
    */
    class KeyFind {
        public $config;
        public $valider = null;
        public $filepath = '';
        public $file = null;
    
        public function __construct($config)
        {
            $default_config = array(
                'line'          => 100,
                'source_file'   => '',
                'source_dir'    => dirname(__FILE__) . '/',
                'dest_file'         => dirname(__FILE__) . '/dest/file.php',
                'log_file'      => dirname(__FILE__) . '/log.txt',
    'result_dir'    => dirname(__FILE__) . '/result/'
            );
            $this->config = array_merge($default_config, $config);
        }
        //记录日志
        public function log( $info ){
            if (!file_exists($this->config['log_file'])) {
                $myfile = fopen($this->config['log_file'], "w") or die("Unable to open file!");
                $txt = "log\n";
                fwrite($myfile, $txt);
                fclose($myfile);
            }
            file_put_contents($this->config['log_file'], $info, FILE_APPEND);
        }
        public function _desturct() {
            // if (!is_null($this->file)) {
            //  $this->file = null;
            // }
        }
    public function valider()
        {
            $number = 0;
            $command = "./webshellscanner -d dest";
            exec($command, $output);
            foreach( $output as $str) {
                    if ( preg_match('/发现webshell数量(.*?)(\d{1,})(.*?)$/', $str, $matches)) {
                            $number = $matches[2];
                    }
            }
            return $number;
        }
    public function fileCreate($array, $temp_str)
        {
            if ($dest_file == '') {
                $dest_file = $this->config['dest_file'];
            }
            file_put_contents($dest_file, "<?php", FILE_APPEND);
            file_put_contents($dest_file, $temp_str, FILE_APPEND);
        }
        /**
        * 打开文件
        */
        public function preCheck() {
            $this->filepath = $this->config['source_dir'] . $this->config['source_file'];
            if (empty($this->config['source_file'])
                || !file_exists($this->filepath)) {
                throw new Exception('File ['.$this->filepath.'] not Exists');
            }
            // if (is_null($this->valider)) {
            //  throw new Exception('Valider not Set');
            // }
            //$this->filepath = $this->config['source_dir'] . $this->config['source_file'];
            // $this->file = new SplFileObject($this->filepath, 'r');  
        }
        public function readFile() {
            $array = array();
            $file = new SplFileObject($this->filepath, 'r');  
            while (!$file->eof()) {
                $array[] = $file->current();
                $file->next();
              }
            $file = null;
            return $array;
        }
        /**
         * 生成随机行数
         *
         * @param [type] $line_count
         * @param [type] $line_deal
         * @return void
         */
        public function getRandLine($line_count, $line_deal)
        {
            if ( $line_count <= $line_deal) {
                die('fatel error!!!!');
            }
            $line_array = array(); //保存行数
            // 生成行数
            for ( $i=0; $i<$line_deal; $i++) {
                $rand_line = rand(1,$line_count-1);             //不需要第0行 <?php
                while( in_array($rand_line, $line_array) )  {
                    $rand_line = rand(1, $line_count-1);
                }
                $line_array[]= $rand_line;
                
            }
            return $line_array;
        }
        /**
         * 文件创建
         *
         * @param [type] $array
         * @param [type] $line_array
         * @return void
         */
        public function createSample($array, $line_array)
        {
            $dir = $this->config['result_dir'];
            $lines = join(',', $line_array);
            $file = $dir . md5($lines) . '.php';
    
            file_put_contents($file, '<?php');
            file_put_contents($file, "//$lines", FILE_APPEND);
            $temp_str = '';
            foreach( $line_array as $line) {
                $temp_str .= $array[$line];
            }
            file_put_contents($file, $temp_str, FILE_APPEND);
        }
    
    public function search() {
            $this->preCheck();
            $problem = array();
            
            $array = $this->readFile($this->filepath);
            $line_count = count($array)-1;
            $line_deal = $this->config['line'];
            $temp_str = '';
            $line_array = $this->getRandLine($line_count, $line_deal); //保存行数
            $this->createSample($array, $line_array);
            for ( $i=0; $i<10000;$i++) {
                $line_array = array();
                $line_array = $this->getRandLine($line_count, $line_deal); //保存行数
                $this->createSample($array, $line_array);
            }
        }
    
    }
    if ( $argc < 2 ) {
        echo "\r\n Usage : scan.php file_need_scan";
        exit();
    }
    $config = array(
        'source_file' => $argv[1]
    );
    $search = new KeyFind($config);
    $search->search();
    

    实现了简单的脚本辅助手工确认特征码, 根据这个思路,应该是可以做到自动化的

    相关文章

      网友评论

          本文标题:[PHP] Webshell简单的免杀思路

          本文链接:https://www.haomeiwen.com/subject/aptpsftx.html