15

流量加密又怎样? 多种姿势检测“冰蝎”

 4 years ago
source link: https://www.tuicool.com/articles/AjqMZjv
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

冰蝎是当下最流行的WebShell客户端,它可以在HTTP明文协议中建立了加密隧道,以躲避安全设备的检测。对于工作于七层的IDS,可以检测HTTP完整的双向内容,这样检测冰蝎并非难事。而对于传统的工作于四层的IDS,只能检测TCP层的单个“帧”,要想检测冰蝎,似乎难于登天。本文将总结冰蝎(v2.0.1)的通讯特征,并将特征落地为正则表达式,不管是传统的还是新型的IDS/WAF,都可用它检测冰蝎。(注:本文讨论的是webshell上传之后与冰蝎的通讯特征,属于事后检测。而不是正在上传webshell时的流量特征)

冰蝎通讯原理

冰蝎使用对称加密算法。加密过程总共分三步:

第一步,密钥传递阶段。客户端向服务器请求密钥,密钥传递是完全明文的。

YFVfAna.jpg!web

图1 密钥传递

第二步,算法协商阶段。SSL握手时会把自己支持的加密算法列表明文发给对方,然而这样会暴漏更多的特征(JA3指纹),冰蝎很显然是考虑到了这一点,它很聪明的将一串payload用不同的算法加密发送给服务器,如果服务器成功解密,那么接下来的通讯便用这种算法。如果算法不对解密失败,那么响应为空,冰蝎继续更换另一种加密算法进行尝试,直到成功为止。加密算法一般为AES128和抑或。

FvUVNvM.jpg!web

图2 密钥协商阶段

第三步,正式通讯阶段。客户端使用上述密钥加密payload,POST给服务端,服务端解密后执行将结果又以加密方式作为Response Body返回给客户端。

beUzAbI.jpg!web 第二步与第三步其实用的是同一个通讯接口,因此特征类似。

静态特征

我总结了8个冰蝎的静态特征,这些特征可分为两类,一类是强特征,这类特征误报较低,可单独使用。另一类是弱特征,单独使用误报较高,但多个弱特征配合使用可降低误报,达到和强特征一样的效果。推荐弱特征与强特征搭配使用,进一步提升强特征的准确性。

弱特征1:密钥传递时URL参数

密钥传递时,URI只有一个参数,key-value型参数,只有一个参数。Key是黑客给shell设置的密码,一般为10位以下字母和数字,很少有人设置特殊字符做一句话密码的(少数情况我们不考虑)。而Value一般是2至3位随机纯数字。

另外webshell的扩展名一般为可执行脚本,因此正则为

 \.(php|jsp|asp|jspx|asa)\?(\w){1,10}=\d{2,3}HTTP/1.1 

reQFfi2.jpg!web

图4 密钥传递阶段的请求

弱特征2:加密时的URL参数

在加密通讯过程中,没有URL参数。是的,没有参数本身也是一种特征。

 \.(php|jsp|asp|jspx|asa) HTTP/1.1 

强特征3:Accept字段(可绕过)

Accept是HTTP协议常用的字段,但冰蝎默认Accept字段的值却很特殊,我也没有想明白为什么要设置这么一个奇怪的值。这个特征存在于冰蝎的任何一个通讯阶段。

 Accept: text/html,image/gif, image/jpeg, *; q=.2, */*; q=.2 

冰蝎支持自定义HTTP Header,因此该特征可以被绕过。

强特征4:UserAgent字段(可绕过)

冰蝎内置了十余种UserAgent,每次连接shell会随机选择一个进行使用。

以下UserAgent列表是从冰蝎的jar包中提取的,可见大多是比较早的浏览器,现在很少有人使用。而且有些国产浏览器甚至精确到了小版本,众所周知,很多国产浏览器是默认自动更新,正常用户很少用过早的版本,因此可以作为强特征使用。

列表中有少量标红的UserAgent,目前用户量较大,不可作为强特征。

如果发现历史流量中同一个源IP访问某个URL时,命中了以下列表中多个UserAgent,那基本确认就是冰蝎了。

 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1(KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1 
 Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0)Gecko/20100101 Firefox/6.0 
 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.50(KHTML, like Gecko) Version/5.1 Safari/534.50 ” BOpera/9.80 (Windows NT6.1; U; zh-cn) Presto/2.9.168 Version/11.5 
 Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64;x64; Trident/5.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; Tablet PC 2.0; .NET4.0E) 
 Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64;Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729;Media Center PC 6.0; .NET4.0C; InfoPath.3) 
 Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1;Trident/4.0; GTB7.0) 
 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1) , 7 
 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) 
 Mozilla/5.0 (Windows; U; Windows NT 6.1; )AppleWebKit/534.12 (KHTML, like Gecko) Maxthon/3.0 Safari/534.12 
 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64;Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729;Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E) 
 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64;Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729;Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; SE 2.X MetaSr 1.0) 
 Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US)AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.33 Safari/534.3 SE 2.XMetaSr 
 Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64;Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729;Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E) 
 Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML,like Gecko) Chrome/13.0.782.41 Safari/535.1 QQBrowser/6.9.11079.20 
 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64;Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729;Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E) QQBrowser/6.9.11079 
 Mozilla/5.0 (compatible; MSIE 9.0; WindowsNT 6.1; WOW64; Trident/5.0) 

同样,UserAgent可由黑客自定义,因此该特征可能会被绕过。

强特征5:传递的密钥

加密所用密钥是长度为16的随机字符串,小写字母+数字组成。密钥传递阶段,密钥存在于Response Body中,如图。

imIvyie.jpg!web

图5 密钥传递阶段的响应

因此密钥特征如下:

 \r\n\r\n[a-z0-9]{16}$ 

如果不想使用正则,可使用字符串特征,误报会更多。

 Content-Length: 16 

弱特征6:加密数据上行

在加密通讯时,php/jsp shell会提交base64编码后的数据。用如下正则便可以很好的匹配。

\r\n\r\n[a-zA-Z\d\+\/]{ 20 ,}

注意这条正则最后的数字20,意思是指定的字符出现至少20个才会匹配。而要匹配这段数据长度至少有几K,在TCP层,如此长的数据不会一次性发送,而是拆分成若干“帧”,逐个发送。工作于4层的IDS只能检测“帧”,而“帧”的长度是不确定的(帧的长度是根据网络情况动态调整的),因此这里保守地只写了20。如果你的IDS工作于七层,可以拼接HTTP完整请求的话,这个值可适当增加,以减少误报。

弱特征7:加密数据下行

该特征同样存在于加密通讯时,在ResponseBody中的数据是加密后的二进制数据。

BBvMNjf.jpg!web

图6 正式通讯阶段的响应

如何用正则去匹配二进制数据呢?这里我使用的方法未必是最好的,仅供大家参考。二进制数据中必然有大量不可见的字符,当然也有不少可见字符,它们出现的位置是随机的。如果第一个位置是可见字符,那么之后的6个字符之内有很大概率出现不可见字符。匹配不可见字符使用零宽负行断言的方式,即不认识的字符即为不可见字符。为了增加准确性,本条策略里扩展了“不可见字符”的定义,这里认为的不可见字符,除了“无法显示的字符”之外,还加入了HTML/JSONP中不常见的字符

 \r\n\r\n[\w]{0,6}[^\w\s><=\-'"\/\.:;\,\!\(\)\{\}]+ 

如果用户加载图片/视频等多媒体二进制文件的话也是会引发误报,因此我们再写一条策略,规定MIME类型为html。

 Content-Type: text/html 

这两条规则是“且”的关系。意思是如果发现text/html类型的文档是二进制的,那么它就是可疑的。

需要注意的是,并不是所有正则引擎都支持“断言”模型。在使用本策略之前,请确认你使用的正则引擎支持断言,并进行严格测试。

弱特征8:长连接(可绕过)

冰蝎通讯默认使用长连接,避免了频繁的握手造成的资源开销。因此默认情况下,请求头和响应头里都会带有:

 Connection: Keep-Alive 

这个特征存在于冰蝎的任何一个通讯阶段。

动态检测

在大型互联网企业或者IDC中,出口流量动辄十几个G。即便某条规则的误报率只有百万分之一,由于分母巨大,出现的告警量也是相当可观的。为了进一步提高准确性,在“静态特征”生成的告警之上做“动态行为检测”。这一节不再是正则表达式能做到的,而是要靠开发人员编写脚本来实现逻辑上的检测。

按照上述“静态特征”编写两类规则:1.密钥传递规则。2.加密通讯规则。

如果同一个url或者源IP在数秒内内同时命中了这两种规则,那么可以非常肯定的确认是冰蝎了。注意,此处是同一个URL或者源IP,而不是同一个TCP会话,密钥传递和加密通讯未必是同一个TCP会话(源端口会变化)。

依照上面的方法虽然能够确定是冰蝎,但是不知道黑客在服务器上干了什么。因此要想进一步的调查取证,必须解密冰蝎。首先依据“密钥传递规则”匹配出的告警,提取密钥。再依据“加密通讯规则“提取出的加密后的内容进行解密。但是这里对IDS有一个较高要求,就是要提取全量的HTTP请求和响应,大概几K到几百K,中间不允许有截断,因为加密算法的”雪崩效应“,密文即便缺少一个字节也无法解密。

下面是解密PHP Shell通讯的一个简单Demo。

# -*- coding: utf-8-*-
import base64
from Crypto.Cipherimport AES
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

密钥

key ='4d21a8526f30c7e2'

冰蝎的加密请求

http_request ="""POST http://xxx:8080/fff2.php HTTP/1.1

Content-Type:application/x-www-form-urlencoded

Cookie:PHPSESSID=p88ghqvul97j646tl7dgs9hcb7; path=/

User-Agent:Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2;.NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC6.0; InfoPath.3; .NET4.0C; .NET4.0E)

Cache-Control:no-cache

Pragma: no-cache

Host: xxx:8080

Accept: text/html,image/gif, image/jpeg, *; q=.2, */*; q=.2

Connection:keep-alive

Content-Length:1112

60Yp… ….xYA=="""

defadd_to_16(value):

# print(k, type(k))

while len(value) % 16 != 0:

value += '\0'

return value

def aes_decode(k,data):

AES解密函数

aes = AES.new(add_to_16(k), AES.MODE_ECB)
    data_encode = data.encode()
    data_64 = base64.b64decode(data_encode)
    data_pass =aes.decrypt(data_64).decode(encoding='UTF-8', errors='ignore')
    return data_pass
defget_http_info(raw):

从HTTP请求中,提取POST Body

    temp_arr = raw.split("\n")
    length = 0
    for one in temp_arr:
        if "Content-Length: " in one:
            length = int(one[16:])
    body = temp_arr[-1]
    if length == len(body):
        return length, body
    else:
        return -1, body
body_length, body =get_http_info(http_request)

提取POST Body

if body_length >0:
    text_64 = base64.b64decode(body)

把post body进行base64解码,获取原始的二进制密文。

    text_str = text_64
    text_list = []

先尝试进行抑或解密

    for i in range(len(text_str)):
        text_list.append(chr(ord(text_str[i]) ^ord(key[i + 1 & 15])))
    clear_text = ''.join(text_list)
    if "assert|" in clear_text:

assert是php payload中的固定明文关键字,带此关键字说明解密成功

        print(clear_text)
    else:

亦或解密失败,再尝试AES解密

        clear_text = aes_decode(key, body)
        if "assert|" in clear_text:
            print(clear_text)
        else:

两种算法解密失败的话,则最终宣告解密失败

密钥错误 或者密文提取不全,都会导致无法解密

            print("无法解密")
else:
    print("无法提取Post Body")

以上总结的检测规则和方法,部分已经投入使用,并且表现良好。本文的不足之处,还望各位老师斧正。

*本文作者:山东星维九州安全技术有限公司,转载请注明来自FreeBuf.COM


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK