1. 规则或签名要素
协议,源IP,目的IP,源端口,目的端口,数据报方向,IP报文的TTL域, ,IP报文的TOS域, ,IP报文的分片ID域, ,IP报文的option域, ,IP报文的fragbits域(分片比特位),IP 负载的长度,TCP的flags域, ,TCP报文的SEQ域, TCP报文的ACK域,ICMP报文的itype类型域,ICMP报文的icode代码域, ICMP报文的icmp_id回应消息ID域,ICMP报文的icmp_seq回应消息序列号域,PALOAD负载
如果使用SNORT_INLINE版本,可以用的要素是resp,就是入侵响应,可选方式:rst_snd,rst_rcv,rst_all,icmp_net,icmp_host,icmp_port,icmp_all
检测签名和规则编写的时候还要主要协议特征和PAYLOAD的特征位置, 比如offset: 0; depth: 1;这个就是说从PAYLOAD的起始位置开始,长度是1的PAYLOAD作为特征.
数据报方向在NFR的NCODE里面可以这样写:
Filter sample1 tcp(dport:80,client) 相当与从你定义的要保护的子网出去的流量.
Filter sample1 tcp(dport:80,server) 相当与其他网络到达你定义的要保护的子网进来的流量.
在SNORT的规则里flow: to_server 到你要定义的server的流量, flow:from_client是从客户端来的流量, flow还可以指定检测连接的状态的,比如stateless, established等
在NCODE里有*.cfg文件定义的几个数据大家要认识一下:
backend_id=NID-792-002
num_columns=6
column_1_attr=IP_ADDR_SRC #源地址
column_1_type=p_src_ip
column_1_label=Source Address
column_2_attr=PORT_SRC #源端口
column_2_type=p_src_port
column_2_label=Source Port
column_3_attr=IP_ADDR_DST #目的地址
column_3_type=p_dst_ip
column_3_label=Destination Address
column_4_attr=PORT_DST #目的端口
column_4_type=p_dst_port
column_4_label=Destination Port
column_5_attr=REASON
column_5_type=p_string
column_5_label=Reason
column_6_attr=PAYLOAD #数据包的DATA部分
column_6_type=p_string
column_6_label=Additional Data
gui=list
title=PPLive ACITVE
origin=NFR
disposition=enable
version=7
如果你是检测UDP协议特征,你的签名要素要这样写:
"IP_ADDR_SRC", ip.src, #源地址
"PORT_SRC", udp.sport, #UDP协议的源端口
"IP_ADDR_DST", ip.dst, #目的地址
"PORT_DST", udp.dport #UDP协议的目的端口
"IP_PROTO_NUM", 17, #UDP协议号
如果你检测的是TCP协议特征,你的签名要素要这样写:
"IP_ADDR_SRC", tcp.connsrc, #TCP协议的源地址
"IP_ADDR_DST", tcp.conndst, #TCP协议的目的地址
"PORT_SRC", tcp.connsport, #TCP协议的源端口
"PORT_DST", tcp.conndport #TCP协议的目的端口
"IP_PROTO_NUM", 6,#TCP协议号
2. SNORT和NFR签名的一般写法
A) SNORT规则写法
alert tcp $HOME_NET any -> $EXTERNAL_NET any (msg:"BLEEDING-EDGE P2P Direct Connect Traffic (client-server)"; flow:from_client,established; content:"$MyINFO"; offset:0; depth:7; classtype:policy-violation; reference:url,en.wikipedia.org/wiki/Direct_connect_file-sharing_application; sid:2002814; rev:1;)
注释:检测TCP协议,从要保护的本地网络到外部网络的连接,是任意端口到任意端口,即any到any. 方向flow:from_client,匹配payload里的第0位开始,长度是7,是否存在"$MyINFO"
alert tcp any any -> any 4660:4799 (msg: "BLEEDING-EDGE P2P ed2k file request answer"; flow: to_server,established; content:"|e3|"; offset: 0; depth: 1; content:"|00000059|"; offset: 2; depth: 4; reference:url,www.giac.org/practical/GCIH/Ian_Gosling_GCIH.pdf; classtype: policy-violation; sid: 2000333; rev:5; )
注释: 检测TCP协议,从任何网络到任何网络的连接,从任意端口到4660:4799(端口在4660到4799之间),方向是flow: to_server,payload里第一次匹配第0个字节是0xe3,第2次匹配从第2个字节开始,长度是4个字节的"|00000059|"
B) NFR签名写法
检测QQ UDP登陆的签名:qq.nfr
qq_schema = library_schema:new(1, ["time","ip","int", "ip", "int",
"str", "str"], scope()); # schema里记录的参数要和qq.cfg的对应上
RCDR = recorder ("bin/list %c","qq_schema");
#下面是常量定义
QQ_REQUEST[22]=1;#0x16
QQ_REQUEST[23]=1;#0x17
QQ_REQUEST[1]=1;#0x01
QQ_REQUEST[5]=1;#0x05
QQ_REQUEST[6]=1;#0x06
QQ_REQUEST[48]=1;#0x30
QQ_REQUEST[10]=1;#0x0a
QQ_string[22]="qq send info";
QQ_string[23]="qq client recieve info";
QQ_string[1]="qq log out";
QQ_string[5]="qq search user ";
QQ_string[6]="qq get user info ";
QQ_string[48]="qq group operation instruction ";
QQ_string[10]="qq delete good friend ";
#这个是主要的检测函数,针对UDP协议的,目的端口QQ_PORTS是在qq.values里面有的
filter qq udp( dport: QQ_PORTS ) {
if ( substr(udp.blob,0,1) == "\x02"){ #这里跟SNORT的一样,这个用函数
#substr做PAYLOAD的匹配,这里写的是从第0个字节开始,长度为1的包,也就#是第一个字节是0x02
#detect qq login
if ( substr(udp.blob,4,1) == "\x22"){ #这里写的是从第4个字节开始,长度为
#1的包,也就是第一个字节是0x22
$temp1=ulong(udp.blob,7); #这里去出QQ号
alert( source_me, qq_login_alert, #这里是报警的内容,前两个参数在
#qq.acf文件里定义了
ip.src, ip.dst, #在UDP协议检测里必须写上的原和目的的IP
cat("QQ ",$temp1 ," :log in server"), #自定义参数,QQ号
"--AlertDetails", "ALERT_ID", "911-003-001", #报警的ID,最好不要
#和其他签名重复
"ALERT_SEVERITY", "low", #报警的严重层度
"ALERT_IMPACT", "information gathering", #漏洞类型
"ALERT_EVENT_TYPE","logging", #事件类型
"ALERT_ASSESSMENT", "unknown", #事件考评,基本都是unknown
"ALERT_CONFIDENCE", 90, #签名报警的可信度
"IP_ADDR_SRC", ip.src,
"PORT_SRC", udp.sport,
"IP_ADDR_DST", ip.dst,
"PORT_DST", udp.dport,
"$temp1",$temp1,
"$temp2",$temp2);
record packet.sec, ip.src, udp.sport, ip.dst, #做数据报记录的功能,
#如果attack里的inhitit.values开启了record这个签名的功能
#,这里就要录制数据报
udp.sport, "qq client connect to server" ,"N/A" to RCDR;
}
#detect qq request info
$temp1=byte(udp.blob,4);
if(QQ_REQUEST[$temp1]==1){
alert(source_me, qq_request_info,
ip.src, ip.dst,QQ_string[$temp1],
"--AlertDetails",
"ALERT_ID","911-003-002",
"IP_ADDR_SRC", ip.src,
"PORT_SRC", udp.sport,
"IP_ADDR_DST", ip.dst,
"PORT_DST", udp.dport);
record packet.sec, ip.src, udp.sport, ip.dst,
udp.sport, "qq_request_info" ,QQ_string[$temp1] to RCDR;
}
}
}
qq.cfg
backend_id=NID-911-003
num_columns=6
column_1_attr=IP_ADDR_SRC
column_1_type=p_src_ip
column_1_label=Source Address
column_2_attr=PORT_SRC
column_2_type=p_src_port
column_2_label=Source Port
column_3_attr=IP_ADDR_DST
column_3_type=p_dst_ip
column_3_label=Destination Address
column_4_attr=PORT_DST
column_4_type=p_dst_port
column_4_label=Destination Port
column_5_attr=REASON
column_5_type=p_string
column_5_label=Reason
column_6_attr=PAYLOAD
column_6_type=p_string
column_6_label=Additional Data
gui=list
title=QQ ACITVE
origin=NFR
disposition=enable
version=7
3. 分类说明如何识别网络软件或攻击特征
(下列特征和演示例子不是检测的全部内容,这里就是举的例子,用来说明问题的,是真的检测代码的一个小部分)
A) P2P类软件
1) EMULE检测
它包括UDP的特征和TCP的特征,这里就举一个UDP抓包的例子
为了消除误报,可以将检测端口放到1024以上.加上过滤条件
下面的图是EMULE的UDP的包
if(substr(udp.blob,0,1) == "\xe4") #第一个字节是0xe4
{
if((substr(udp.blob,1,1) == "\x20") ||
(substr(udp.blob,1,1) == "\x21") || #第2个字节是0x21
(substr(udp.blob,1,1) == "\x00") ||
(substr(udp.blob,1,1) == "\x10") ||
(substr(udp.blob,1,1) == "\x18") ||
(substr(udp.blob,1,1) == "\x52") ||
(substr(udp.blob,1,1) == "\x58") ||
(substr(udp.blob,1,1) == "\x59") ||
(substr(udp.blob,1,1) == "\x28") ||
(substr(udp.blob,1,1) == "\x50") ||
(substr(udp.blob,1,1) == "\x40") )
{
alert(emule_source,emule_datatransfer_alert, ip.src,udp.sport, ip.dst,udp.dport,
"--AlertDetails", "ALERT_ID", "096-022-001", "ALERT_CONFIDENCE", 90,
"ALERT_SEVERITY", "low",
"ALERT_IMPACT", "unknown",
"ALERT_EVENT_TYPE", "probe",
"ALERT_ASSESSMENT", "unknown",
"IP_PROTO_NUM", 17,
"IP_ADDR_SRC", ip.src,
"PORT_SRC", udp.sport,
"IP_ADDR_DST", ip.dst,
"PORT_DST", udp.dport,
"ed2k","ED2K PROTOCOL DATA TRANSFER 2");
}
}
下面举个TCP的例子
if(substr(tcp.blob,0,1) == "\xc5") #0xc5 第0位
{
if((substr(tcp.blob,5,1) == "\x01") ||
(substr(tcp.blob,5,1) == "\x02") ||
(substr(tcp.blob,5,1) == "\x04") ||
(substr(tcp.blob,5,1) == "\x60") ||
(substr(tcp.blob,5,1) == "\x61") ||
(substr(tcp.blob,5,1) == "\x81") ||
(substr(tcp.blob,5,1) == "\x82") ||
(substr(tcp.blob,5,1) == "\x87") ||
(substr(tcp.blob,5,1) == "\x85") ||
(substr(tcp.blob,5,1) == "\x86") ||
(substr(tcp.blob,5,1) == "\x90") ||
(substr(tcp.blob,5,1) == "\x91") ||
(substr(tcp.blob,5,1) == "\x93")) #0xc5 第5位
{
alert(emule_source,emule_extensions_alert, ip.src,udp.sport, ip.dst,udp.dport,
"--AlertDetails", "ALERT_ID", "096-022-001", "ALERT_CONFIDENCE", 90,
"ALERT_SEVERITY", "low",
"ALERT_IMPACT", "unknown",
"ALERT_EVENT_TYPE", "probe",
"ALERT_ASSESSMENT", "unknown",
"IP_PROTO_NUM", 6,
"IP_ADDR_SRC", ip.src,
"PORT_SRC", udp.sport,
"IP_ADDR_DST", ip.dst,
"PORT_DST", udp.dport,
"ed2k","ED2K Extensions(Emule) PROTOCOL DATA TRANSFER");
}
}
总结一下:关于特征,如果不是协议特征攻击方面的,就不要找PAYLOAD以外的其他元素,在PAYLOAD里找不一定都是PAYLOAD的头一个字节开始,而且特征都不是一定连续的,可能跳多少个字节才有特征,这个最好把关键的包打印下来,在纸面上比较比较,暂时没有太好的工具来计算特征,所谓特征包,就是能重复出现的包,不论是一台机器上,也不论是一个网段里.
2) BT检测
BT的UDP特征检测
if ((substr(udp.blob,0,4) == "d1:a") || (substr(udp.blob,0,4) == "d1:r")) #这个特
#征比较明显,就是找到字符串是d1:ad2:id20: 或者是 d1:rd2:id20
{
if (substr(udp.blob,4,8) == "d2:id20:")
{
alert( source_bitudp, bit_udp_datatrans_alert,
ip.src, ip.dst,
"--AlertDetails", "ALERT_ID", "982-002-002",
"ALERT_SEVERITY", "low",
"ALERT_IMPACT", "information gathering",
"ALERT_EVENT_TYPE","logging",
"ALERT_ASSESSMENT", "unknown",
"IP_PROTO_NUM", 17,
"IP_ADDR_SRC", ip.src,
"PORT_SRC", udp.sport,
"IP_ADDR_DST", ip.dst,
"PORT_DST", udp.dport,
"BT METHOD","_9");
}
}
BT的TCP特征
if (substr($blob,1,19) == "BitTorrent protocol") { #从第1个字节开
#始算特征, "BitTorrent protocol" 是BT软件开始应用层握手的信息
$confidence = 80;
# let’s ID this sucker
$ind = substr($blob, (1 + $hdr_len + 8 + 20 + 1), 2);
debug:trace(" $ind = ",$ind, " $offset = ",(1 + $hdr_len + 8 + 20 + 1));
$client = BT_CLIENT_DB [$ind];
if ($client == NULL) { # XXX wait, there’s more
$client = "unknown";
if (BT_CLIENT_DB[substr($ind,0,1)]) {
$client = BT_CLIENT_DB[substr($ind,0,1)];
}
总结一下:BT软件是先TCP三次握手,然后应用层握手,接下来有TCP 的控制部分还有UDP的控制部分,接下来是搞TCP的DATA部分的传输,我们抓包特征有好几次机会:
1. 应用层握手从第1个字节开始算特征, "BitTorrent protocol" 是BT软件开始应用层握手的信息.
2. TCP CONTROL部分
3. UDP CONTROL部分
4. TCP DATA部分,这部分往往是加密传输,不太容易做的
这里再声明一次TCP的三次握手不用抓,因为这个不是协议异常攻击,是完全的应用层软件的东西
还有就是有时候要检测PAYLOAD的长度,一般的时候不需要,为了消除误报,可以把PAYLOAD长度作为一个条件加进去,前提是PAYLOAD长度固定或者在一定范围内.
B) IMS即时通讯工具
1) QQ检测
QQ分UDP登陆和HTTP隧道登陆方式
先看看UDP
filter qq udp( dport: QQ_PORTS ) {
if ( substr(udp.blob,0,1) == "\x02"){ #这里跟SNORT的一样,这个用函数
#substr做PAYLOAD的匹配,这里写的是从第0个字节开始,长度为1的包,也就#是第一个字节是0x02
#detect qq login
if ( substr(udp.blob,4,1) == "\x22"){ #这里写的是从第4个字节开始,长度为
#1的包,也就是第一个字节是0x22
$temp1=ulong(udp.blob,7); #这里去出QQ号
alert( source_me, qq_login_alert, #这里是报警的内容,前两个参数在
#qq.acf文件里定义了
ip.src, ip.dst, #在UDP协议检测里必须写上的原和目的的IP
cat("QQ ",$temp1 ," :log in server"), #自定义参数,QQ号
"--AlertDetails", "ALERT_ID", "911-003-001", #报警的ID,最好不要
#和其他签名重复
"ALERT_SEVERITY", "low", #报警的严重层度
"ALERT_IMPACT", "information gathering", #漏洞类型
"ALERT_EVENT_TYPE","logging", #事件类型
"ALERT_ASSESSMENT", "unknown", #事件考评,基本都是unknown
"ALERT_CONFIDENCE", 90, #签名报警的可信度
"IP_ADDR_SRC", ip.src,
"PORT_SRC", udp.sport,
"IP_ADDR_DST", ip.dst,
"PORT_DST", udp.dport,
"$temp1",$temp1,
"$temp2",$temp2);
record packet.sec, ip.src, udp.sport, ip.dst, #做数据报记录的功能,
#如果attack里的inhitit.values开启了record这个签名的功能
#,这里就要录制数据报
udp.sport, "qq client connect to server" ,"N/A" to RCDR;
}
再看看QQ的HTTP隧道特征
其他的特征包我就不在这里贴了,这里为了消除误报,当有下面的情况发生是可能也被检测成QQ HTTP登陆:
上面这个是QQ浏览www.qq.com网站的情况,我们这里要把HOST字段www.qq.com过滤掉,Referer 是http://www.qq.com过滤掉.
我们这里还要检测Cookie的特征,才能检测QQ HTTP登陆的情况
if(index($host, "www.qq.com") < 0)
{
if($Referer != NULL)
{
if(index($Referer, ".qq.com") > 0) return;
}
if ((index($url, "/getsyspanel?") >= 0)&&(index($url, "qqver") >= 0)&&(index($url, "qq=") >= 0)){
alert(ims_qqhttp_source, qq_http_alert,tcp.connsrc,
tcp.conndst,
"--AlertDetails", "ALERT_ID", "911-011-001",
"ALERT_CONFIDENCE", 90,
"ALERT_SEVERITY", "info",
"ALERT_EVENT_TYPE", "attack",
"IP_ADDR_SRC", tcp.connsrc, "IP_ADDR_DST", tcp.conndst,
"PORT_SRC", tcp.connsport, "PORT_DST", tcp.conndport,"QQURI",$url);
}
if ((index($url, "/getlogo?QQ=") >= 0)||(index($url, "superbag.xml") >= 0)){
alert(ims_qqhttp_source, qq_http_alert,tcp.connsrc,
tcp.conndst,
"--AlertDetails", "ALERT_ID", "911-011-001",
"ALERT_CONFIDENCE", 90,
"ALERT_SEVERITY", "info",
"ALERT_EVENT_TYPE", "attack",
"IP_ADDR_SRC", tcp.connsrc, "IP_ADDR_DST", tcp.conndst,
"PORT_SRC", tcp.connsport, "PORT_DST", tcp.conndport,"QQURI",$url);
}
if (((index($cookie, "QQ_IPAddress=") >= 0)&&((index($cookie, "pvid=") >= 0)||(index($cookie, "flv=") >= 0))) && (index($host, ".qq.com") >= 0)){
alert(ims_qqhttp_source, qq_http_alert,tcp.connsrc,
tcp.conndst,
"--AlertDetails", "ALERT_ID", "911-011-001",
"ALERT_CONFIDENCE", 90,
"ALERT_SEVERITY", "info",
"ALERT_EVENT_TYPE", "attack",
"IP_ADDR_SRC", tcp.connsrc, "IP_ADDR_DST", tcp.conndst,
"PORT_SRC", tcp.connsport, "PORT_DST", tcp.conndport,
"QQURI",$url,"Host",$host,"Cookie",$cookie);
}
}
2) MSN检测
这里举个MSN传文件的例子
#detect msn file transfer
if(index($buffer,"\x41\x70\x70\x49\x44\x3a\x20\x32\x0d\x0a")>=0){
$to=elem(split($buffer, "\r\n"), 5);
$from=elem(split($buffer, "\r\n"), 6);
$temp=elem(split($buffer, "\r\n"), 16);
$temp1=substr($temp,(strlen("context:")));
$temp1=substr($temp1,0,100);
$filename=debase64($temp1);
alert(ims_source, msn_file_alert, $from, $to, tcp.connsrc,
tcp.conndst,
"--AlertDetails", "ALERT_ID", "911-010-002",
"ALERT_CONFIDENCE", 90,
"ALERT_SEVERITY", "info",
"ALERT_IMPACT", "information",
"ALERT_EVENT_TYPE", "logging",
"filename",$filename,
"IP_ADDR_SRC", tcp.connsrc, "IP_ADDR_DST", tcp.conndst,
"PORT_SRC", tcp.connsport, "PORT_DST", tcp.conndport);
}
总结一下:IMS软件的特征基本在应用层协议找,不用在传输层找,应用层协议有HTTP的,有HTTP隧道的,有UDP的,特征基本在PAYLOAD这里找,还有就是将来要做IMS软件的功能的禁止或者是检测,最好作好干净的机器,卸装其他有网络应用的软件,以防止干扰,如果是做IMS的某个功能点,最好是在功能点启动的那一刻启动抓包软件进行抓包,这样少干扰的包.
C) 网络视频
1) QQLIVE检测
这里检测UDP协议:
if((udp.sport > 1024) && (udp.dport > 1024))
{
if (substr(udp.blob,0,1) == "\xfe")
{
if (( substr(udp.blob,2,2) == "\x00\x00") || ( substr(udp.blob,2,2) == "\x04\x04"))
{
alert( source_qqliveudpdata, qqlive_udp_datatrans_alert,
ip.src, ip.dst,
"--AlertDetails", "ALERT_ID", "988-002-002",
"ALERT_SEVERITY", "info",
"ALERT_IMPACT", "information gathering",
"ALERT_EVENT_TYPE","logging",
"ALERT_ASSESSMENT", "unknown",
"IP_PROTO_NUM", 17,
"IP_ADDR_SRC", ip.src,
"PORT_SRC", udp.sport,
"IP_ADDR_DST", ip.dst,
"PORT_DST", udp.dport,
"QQLIVE METHOD","_1");
}
}
if ((substr(udp.blob,0,2) == "\x91\x00") || (substr(udp.blob,0,2) == "\x92\x00"))
{
if(substr(udp.blob,3,11) == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
{
alert( source_qqliveudpdata, qqlive_udp_datatrans_alert,
ip.src, ip.dst,
"--AlertDetails", "ALERT_ID", "988-002-002",
"ALERT_SEVERITY", "info",
"ALERT_IMPACT", "information gathering",
"ALERT_EVENT_TYPE","logging",
"ALERT_ASSESSMENT", "unknown",
"IP_PROTO_NUM", 17,
"IP_ADDR_SRC", ip.src,
"PORT_SRC", udp.sport,
"IP_ADDR_DST", ip.dst,
"PORT_DST", udp.dport,
"QQLIVE METHOD","_2");
}
}
}
这些软件都属于P2P软件类,他们的数据报大概有几种类型:
1) HTTP 访问类,主要是软件界面的交互要的,一般不专门做特征的查找,因为随着P2P软件的升级,这部分改动比较大,这样签名还要修改,不太值得做.
2) TCP CONTROL的部分,主要走TCP软件控制协议,尽量找特征
3) TCP DATA的部分,主要走TCP数据部分,有时候走的是加密数据,一般不用抓特征,但是有时候也能有特征,他的特征容易误报
4) UDP CONTROL的部分,主要走UDP软件控制协议,尽量找特征
5) UDP DATA的部分,主要走UDP数据部分, 有时候走的是加密数据,一般不用抓特征,但是有时候也能有特征,他的特征容易误报
6) 端口可以作为检测条件,端口可以给定在一定范围内,这样可以减少误报
D) 基于HTTP协议的软件或相关漏洞的攻击特征
1) REDCODE检测
SNORT RULES 2.3里检测redcode 2的办法是:
alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:"WEB-IIS CodeRed v2 root.exe access"; flow:to_server,established; uricontent:"/root.exe"; nocase; reference:url,www.cert.org/advisories/CA-2001-19.html; classtype:web-application-attack; sid:1256; rev:8;)
检测URI里是否包含/root.exe
在NFR里检测还是比较合理的:
KEY_IDA = 14;
CODERED_VARIANTS["/default.ida?NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN%u9090%u6858%ucbd3%u7801%u9090%u6858%ucbd3%u7801%u9090%u6858%ucbd3%u7801%u9090%u9090%u8190%u00c3%u0003%u8b00%u531b%u53ff%u0078%u0000%u00=a"] = "CodeRed";
CODERED_VARIANTS["/default.ida?XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%u9090%u6858%ucbd3%u7801%u9090%u6858%ucbd3%u7801%u9090%u6858%ucbd3%u7801%u9090%u9090%u8190%u00c3%u0003%u8b00%u531b%u53ff%u0078%u0000%u00=a"] = "CodeRed II";
if (KEY_IDA inside $matched &&
strlen($uri) > IISISAPI_INDEXING_BUFSIZE) {
if (CODERED_VARIANTS[$uri]) {
alert(www_iis_source, codered_alert,
tcp.connsrc, tcp.conndst,
substr(CODERED_VARIANTS[$uri], 0, 1024),
"--AlertDetails", "ALERT_ID", "27-64",
"ALERT_CONFIDENCE", _:confidence(90),
"ALERT_SEVERITY", "high",
"ALERT_IMPACT", "code execution",
"ALERT_EVENT_TYPE", "attack",
"ALERT_ASSESSMENT", "unknown",
"CONTEXT", attack:context(codered_alert),
"IP_PROTO_NUM", 6,
"IP_ADDR_SRC", tcp.connsrc,
"PORT_SRC", tcp.connsport,
"IP_ADDR_DST", tcp.conndst,
"PORT_DST", tcp.conndport,
"CMD_NAME", $ci[_:CLIENT_METHOD],
"HTTP_URL", $uri);
record _:CURRENT_TIME(), tcp.connsrc, tcp.connsport,
tcp.conndst, tcp.conndport, CODERED_VARIANTS[$uri],
$uri to MYRECORDER;
misc_attacks:rec(_:CURRENT_TIME(), scope(),
CODERED_VARIANTS[$uri], tcp.connsrc, tcp.conndst);
return (1);
} else {
alert(www_iis_source, , iisindexing_alert,
tcp.connsrc, tcp.conndst, strlen($uri),
"--AlertDetails", "ALERT_ID", "27-65",
"ALERT_CONFIDENCE", _:confidence(90),
"ALERT_SEVERITY", "high",
"ALERT_IMPACT", "code execution",
"ALERT_EVENT_TYPE", "attack",
"ALERT_ASSESSMENT", "unknown",
"CONTEXT", attack:context(iisindexing_alert),
"IP_PROTO_NUM", 6,
"IP_ADDR_SRC", tcp.connsrc,
"PORT_SRC", tcp.connsport,
"IP_ADDR_DST", tcp.conndst,
"PORT_DST", tcp.conndport,
"CMD_NAME", $ci[_:CLIENT_METHOD],
"HTTP_URL", $uri);
record _:CURRENT_TIME(), tcp.connsrc, tcp.connsport,
tcp.conndst, tcp.conndport,
"IIS Indexing Service Buffer Overflow Attempt", $uri
to MYRECORDER;
misc_attacks:rec(_:CURRENT_TIME(), scope(),
"IIS Indexing Service Buffer Overflow Attempt",
tcp.connsrc, tcp.conndst);
return (1);
}
}
NCODE里讲检测REDCODE是检测他溢出传播的过程,我认为做的还是比较合理,他检测如果URI里的数据长度超过上限,大家可以认为是IIS Indexing Service Buffer Overflow Attempt,如果下面一个条件(CODERED_VARIANTS在URI里存在)符合就是redcode传播
总结一下:检测这个URL的病毒,最好不要检测病毒体最后的名称,因为变种病毒很多,最好检测病毒的发作原理,SNORT有他自己的缺点,在这方面不是一两个规则能描述清楚的
E) EMAIL相关攻击的签名
1) SMTP帐号破解
SNORT RULES 2.4中有一条规则
alert tcp $EXTERNAL_NET any -> $SMTP_SERVERS 25 (msg:"SMTP expn root"; flow:to_server,established; content:"expn"; nocase; content:"root"; nocase; pcre:"/^expn\s+root/smi"; reference:arachnids,31; reference:cve,1999-0531; reference:nessus,10249; classtype:attempted-recon; sid:660; rev:10;)
SMTP 协议获取用户名的命令字是expn,这里就是在到目的端口25的TCP的数据报的PAYLOAD里检查是否有expn 和 root,并且还用正则表达式匹配: "/^expn\s+root/smi"
NFR NCODE里检查还是比较合理的:
filter reg_call timeout (sec: 1, once) {
foreach $i inside (SMTP_PROBE_COMMANDS) {
$il = split(toupper($i),":");
$cmd = elem($il);
$status = elem($il, 1);
if ($status == "FAIL") {
if ($cmd == "EXPN") {
smtp2:reg_callback(do_failed_expn, "REPLY-CODE",
"EXPN");
} else if ($cmd == "RCPT") {
smtp2:reg_callback(do_failed_rcpt, "REPLY-CODE",
"RCPT");
} else if ($cmd == "VRFY") {
smtp2:reg_callback(do_failed_vrfy, "REPLY-CODE",
"VRFY");
}
} else if ($status == "ALL") {
if ($cmd == "EXPN") {
smtp2:reg_callback(do_expn, "COMMAND",
"EXPN");
} else if ($cmd == "RCPT") {
smtp2:reg_callback(do_rcpt, "COMMAND",
"RCPT");
} else if ($cmd == "VRFY") {
smtp2:reg_callback(do_vrfy, "COMMAND",
"VRFY");
}
}
}
}
func parse_arg {
$cmdlist = split($1);
if (($ll = listlen($cmdlist)) > 2)
return(substr($1, index($1, elem($cmdlist, 1))));
else if ($ll == 1)
return("EMPTY ARGUMENT");
else
return(elem($cmdlist, 1));
}
#CVE-1999-0531
func do_rcpt {
declare $RcptCount inside tcp.connsym;
$RcptCount = $RcptCount ? $RcptCount + 1 : 1;
declare $DidAlert inside tcp.connsym;
if ($RcptCount >= SMTP_PROBE_RCPT_THRESHOLD &&
!$DidAlert["RCPT_COUNT"]) {
$DidAlert["RCPT_COUNT"] = 1;
notify(cat($RcptCount, RCPT_STATUS, " RCPT commands"),
"do_rcpt");
}
}
func do_failed_rcpt {
if (!prefix(tcp.blob, "4") && !prefix(tcp.blob, "5")) {
return;
}
declare $RcptCount inside tcp.connsym;
$RcptCount = $RcptCount ? $RcptCount + 1 : 1;
declare $DidAlert inside tcp.connsym;
if ($RcptCount >= SMTP_PROBE_RCPT_THRESHOLD &&
!$DidAlert["RCPT_COUNT"]) {
$DidAlert["RCPT_COUNT"] = 1;
notify(cat($RcptCount, " failed RCPT commands"));
}
}
func do_expn {
$arg = parse_arg(tcp.blob);
notify(cat("Style: EXPN command. Argument: \"", $arg, "\"."));
}
func do_failed_expn {
if (!prefix(tcp.blob, "4") && !prefix(tcp.blob, "5")) {
return;
}
$arg = parse_arg($1);
notify(cat("Style: Failed EXPN command. Argument: \"", $arg, "\"."));
}
func do_vrfy {
$arg = parse_arg(tcp.blob);
notify(cat("Style: VRFY command. Argument: \"", $arg, "\"."));
}
func do_failed_vrfy {
if (!prefix(tcp.blob, "4") && !prefix(tcp.blob, "5")) {
return;
}
$arg = parse_arg($1);
notify(cat("Style: Failed VRFY command. Argument: \"", $arg, "\"."));
}
func notify {
alert (smtpuserprobes_source, probe_alert, $1,
tcp.connsrc, tcp.conndst, tcp.connhash,
"--AlertDetails", "ALERT_ID", "9-42",
"ALERT_CONFIDENCE", 70,
"ALERT_SEVERITY", "low", "ALERT_IMPACT",
"information gathering",
"ALERT_EVENT_TYPE", "information gathering",
"ALERT_ASSESSMENT", "unknown",
"REASON", $1, "SESSION_ID", tcp.connhash, "IP_PROTO_NUM", 6,
"IP_ADDR_SRC", tcp.connsrc, "IP_ADDR_DST", tcp.conndst,
"PORT_SRC", tcp.connsport, "PORT_DST", tcp.conndport,
"CONTEXT", attack:context(probe_alert));
record packet.sec, tcp.connsrc, tcp.connsport, tcp.conndst,
tcp.conndport, tcp.connhash, $1 to userprobes_recorder;
misc_attacks:rec(system.time, scope(),
cat("SMTP user probing technique: ",$1),
tcp.connsrc, tcp.conndst);
}
他的做法是先做SMTP 的DECODE,然后用timeout的内置函数判断EXPN获取用户名的次数,如果次数大于上限,就报警.
2) SENDMAIL 溢出
SNORT RULES 2.4中有一条规则
alert tcp $EXTERNAL_NET any -> $SMTP_SERVERS 25 (msg:"SMTP sendmail 8.6.9 exploit"; flow:to_server,established; content:"|0A|C|3A|daemon|0A|R"; reference:arachnids,139; reference:bugtraq,2311; reference:cve,1999-0204; classtype:attempted-user; sid:670; rev:7;)
这里主要检测到SMTP SERVER 25端口的流量的PAYLOAD里有没有:"|0A|C|3A|daemon|0A|R",我认为这样的检测方法不是很准,这种检测办法是针对某一类工具来的,真正的检测办法,是要DECODE协议,检测PAYLOAD长度是否符合溢出的长度,然后检测某些溢出针对的系统的返回地址作为特征,还有就是溢出包本身特点
总结一下:检测SMTP主要的威胁主要是破解和溢出,SNORT签名存在固有的弱点,我们在做分析的时候要多考虑以下几个要素:
a) 破解本身要基于时间统计来做检测,不能单独的只检测一个expn root或其他
b) 溢出的检测要基于其原理来分析,不能只做某个溢出工具的检测
F) SMB微软网络共享协议的特征和识别
1) SMB登陆检测
my_schema = library_schema:new(1, ["time", "ip", "int", "ip", "int", "str"],
scope());
my_recorder = recorder ("bin/list %c", "my_schema");
_:reg_callback(ssax_client, "SMB", 0x73, "CLIENT");
_:reg_callback(ssax_server, "SMB", 0x73, "SERVER");
func reg_callback {
# Register callbacks for guest accounts. Called functions will be called
# with no arguments.
# Pass in: Function to call.
CALL_GUEST_ACCOUNT = listadd(CALL_GUEST_ACCOUNT, $1);
}
func ssax_client {
declare $Username inside tcp.connsym;
if (!$2)
$wco = 36;
else
$wco = $2;
$SMBBlob = $1;
$word_count = byte($SMBBlob, $wco);
# post NTLM 0.12, without extended security bit set word count == 13
if ($word_count == 13) {
$ansi_pw_len = le_ushort($SMBBlob, $wco + 15);
$unicode_pw_len = le_ushort($SMBBlob, $wco + 17);
$tmp_offset = $wco + 30 + $ansi_pw_len + $unicode_pw_len;
if (($tmp = substr($SMBBlob, $tmp_offset, (_:absindex($SMBBlob,
"\x00\x00", $tmp_offset) + 1) - $tmp_offset))
== "\x00" ) {
$Username = "NULL_UID";
} else {
$Username = _:shrink($tmp, "\x00");
}
}
# pre NTLM 0.12 word count should be 10.
else if ($word_count == 10) {
# ANSI password should be the first item in the ByteCount
# buffer.
$pw_len = le_ushort($SMBBlob, $wco + 15);
if ($pw_len == 0) {
#NULL PASSWORD?
}
$tmp = substr($SMBBlob, $wco + 23 + $pw_len);
if (prefix($tmp, "\x00")) {
$Username = "NULL_UID";
} else {
$Username = _:shrink($tmp, "\x00");
}
}
##else if ($word_count == 12) { ##NTLMSSP Challenge Response }
}
func ssax_server {
$SMBBlob = $1;
# A NULL long() int the NT Status field indicates success.
$errors = le_long($SMBBlob, 9);
if ($errors == -1073741802) {
#NTLMSSP Chalenge Response
#NT STATUS MSG MORE PROCCESSING REQUIRED
return;
}
declare $Username inside tcp.connsym;
if ($Username == "NULL_UID" || $Username == "GUEST") {
# foreach guest account callback inside those things
foreach $c inside (CALL_GUEST_ACCOUNT) {
($c)();
}
}
if (SMB_LOGIN_NOTIFY_AUTHENTICATION) {
if ($errors == 0) {
#SUCCESSFUL LOGIN
$authblob["STATUS"] = 1;
} else {
#FAILED LOGIN
$authblob["STATUS"] = 0;
}
$authblob["IP_ADDR_SRC"] = tcp.connsrc;
$authblob["IP_ADDR_DST"] = tcp.conndst;
$authblob["PASSWORD"] = -1;
$authblob["USERNAME"] = $Username;
$authblob["SERVICE_NAME"] = "SMB";
authentication_authentication:authdata($authblob);
}
}
这里申明一点微软的SMB协议格式是未公开的,现在根本没有好的解码办法,这里写的用户名的检测是根据SMB包里的UNICODE编码转化成ASCII的字符的,本身SMB的用户名是可读的,但是密码是没办法的,密码是被HASH的
密码登陆成功与否的状态也是可读的,就是用SNORT来写规则也是可以作到的
总结一下:目前SMB协议的解码都是难题,这里的SMB登陆检测
也是用没办法的办法做的,其他一些比如SSH,HTTPS等的协议也是有一些没办法的办法来做一些事情的,也是可以考虑导进一些默认证书的办法,可以做一定层次的DECODE的
G) 基于统计的签名的特征和开发
1) WEBCC检测
这里说一下NFR的引擎的内置函数timeout:
filter checkem timeout (sec: INTERVAL, repeat)
这个内置函数在SENSOR上是作为一个定时器的作用,没有它,我们没办法做基于时间特征的统计.我们这里就不详细解释webcc的写法了,下面还会提到.
NFR的引擎还有内置函数except,exception,也就是运行NCODE或处理协议栈异常的时候调用的,在下面的SYNFLOOD的检测代码里用到的.
总结一下:我们一般使用到与时间有关系的统计签名的时候请大家考虑一下timeout函数,遇到代码异常可以考虑except
H) 基于协议异常的特征的开发和编写
1) SYNFLOOD
filter synflood except (tcpsynacktimeout) {
if (tcp.connsyn) {
if (!SynTimeoutCnt[tcp.connsrc]) {
SynTimeoutCnt[tcp.connsrc] = 1;
} else {
SynTimeoutCnt[tcp.connsrc] = SynTimeoutCnt[tcp.connsrc] + 1;
}
# grab packets if we’re gonna alert
if (!Context[tcp.connsrc] && SynTimeoutCnt[tcp.connsrc] > THRESHOLD) {
Context[tcp.connsrc] = attack:context(synflood_alert);
if (AGGRESSIVE_PREVENTION)
announce_synflood();
}
}
debug:trace("synflood: ",tcp.connsrc, Context[tcp.connsrc], SynTimeoutCnt[tcp.connsrc]);
}
上面说的意思是统计半开放连接的个数是否超过上限
filter synflood_is_working except (tcpsyntimeout) {
debug:trace("tcp syn timeout", tcp.connsrc, Context[tcp.connsrc], FloodTimer[Context[tcp.connsrc]]);
if (Context[tcp.connsrc] && !FloodTimer[Context[tcp.connsrc]]) {
announce_synflood();
FloodTimer[Context[tcp.connsrc]] = system.time;
StartTime[system.time] = listadd(StartTime[system.time], tcp.connsrc);
}
}
跟踪每个半开放连接的超时时间
在SNORTRULES 2.3里检测SYN有一条
二:
这个是本地特征,在查杀病毒的时候有用,他启动的时候用autorun.inf,关联他的程序运行的
用工具SMBFileSniffer1.0.0.1分析的监控记录的一部分如下:
2007-2-6 16:41:56 10.0.3.169 ERR Request CREATE FILE 10.0.3.77 \InternetScanner7\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 WRITE FILE 10.0.3.77 \InternetScanner7\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 CLOSE FILE 8 字节 10.0.3.77 \InternetScanner7\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 READ FILE 10.0.3.77 \InternetScanner7\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 CLOSE FILE 0 字节 10.0.3.77 \InternetScanner7\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 ERR Request CREATE FILE 10.0.3.77 \InternetScanner7\Retail\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 WRITE FILE 10.0.3.77 \InternetScanner7\Retail\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 CLOSE FILE 8 字节 10.0.3.77 \InternetScanner7\Retail\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 READ FILE 10.0.3.77 \InternetScanner7\Retail\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 CLOSE FILE 0 字节 10.0.3.77 \InternetScanner7\Retail\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 ERR Request CREATE FILE 10.0.3.77 \InternetScanner7\Retail\Windows_X86_NT4-XP\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 WRITE FILE 10.0.3.77 \InternetScanner7\Retail\Windows_X86_NT4-XP\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 CLOSE FILE 8 字节 10.0.3.77 \InternetScanner7\Retail\Windows_X86_NT4-XP\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 READ FILE 10.0.3.77 \InternetScanner7\Retail\Windows_X86_NT4-XP\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 CLOSE FILE 0 字节 10.0.3.77 \InternetScanner7\Retail\Windows_X86_NT4-XP\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 ERR Request CREATE FILE 10.0.3.77 \InternetScanner7\Retail\Windows_X86_NT4-XP\Docs\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 WRITE FILE 10.0.3.77 \InternetScanner7\Retail\Windows_X86_NT4-XP\Docs\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 CLOSE FILE 8 字节 10.0.3.77 \InternetScanner7\Retail\Windows_X86_NT4-XP\Docs\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 READ FILE 10.0.3.77 \InternetScanner7\Retail\Windows_X86_NT4-XP\Docs\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 CLOSE FILE 0 字节 10.0.3.77 \InternetScanner7\Retail\Windows_X86_NT4-XP\Docs\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 ERR Request CREATE FILE 10.0.3.77 \iris\Desktop_.ini
2007-2-6 16:41:56 10.0.3.169 WRITE FILE
上面看到的是病毒通过SMB协议写入病毒代码的部分
三
用SNIFFER看看他运行起来的数据报的情况:
a)用这个是用IPC$协议的攻击
b)
这个是用cc攻击的方式DDOS几个比较著名的网站,大家注意这里有特征,就是User-Agent是QQ
c)
我们继续抓HTTP流量的包,发现病毒访问网站www.koshadu.com,并且下载文件PANDAVIRUS_BADFILES = [ "mh.exe", "rx.exe", "wow.exe", "my.exe","dh.exe","wm.exe","cq.exe", "cs.exe", "wl.exe","zt.exe" ];
这样我们就可以又有一个特征来检测病毒了,检测HTTP的URL就可以了.我们可以该木马网站的访问,这些下载的文件名是会变,尽量不要作为特征.
d)最后我们要用全端口检测的办法检测病毒体的EXE的特征码,我建议使用病毒软件厂商提供的sig exe的工具找准特征码,或者自己做SIG工具,做全包过滤
总结一下:检测病毒不是太容易的事情,要分析EXE的SIG,而且要找准它的网络特征不是太容易的事情,要平时多使用工具,多积累点病毒的经验.
K) 有关DDOS工具的特征分析
1) TFN
在SNORT 2.3 RULES里有:
alert icmp $EXTERNAL_NET any -> $HOME_NET any (msg:"DDOS TFN Probe"; icmp_id:678; itype:8; content:"1234"; reference:arachnids,443; classtype:attempted-recon; sid:221; rev:4;)
检测ICMP协议, ICMP报文的icmp_id回应消息ID域是678, ICMP报文的itype类型域为8,ICMP 内容包有"1234",可以认为是TFN的ICMP的流量
alert icmp $EXTERNAL_NET any -> $HOME_NET any (msg:"DDOS tfn2k icmp possible communication"; icmp_id:0; itype:0; content:"AAAAAAAAAA"; reference:arachnids,425; classtype:attempted-dos; sid:222; rev:2;)
检测ICMP协议, ICMP报文的icmp_id回应消息ID域是0, ICMP报文的itype类型域为0,ICMP 内容包有"AAAAAAAAAA",可以认为是TFN2K的ICMP的流量
alert udp $EXTERNAL_NET any -> $HOME_NET 31335 (msg:"DDOS Trin00 Daemon to Master PONG message detected"; content:"PONG"; reference:arachnids,187; classtype:attempted-recon; sid:223; rev:3;)
检测UDP协议,到要保护的网段的UDP/31335的特定端口,在数据报的PAYLOAD里发现有"PONG",就可以认为是Daemon to Master的PONG的流量
alert icmp $EXTERNAL_NET any -> $HOME_NET any (msg:"DDOS TFN client command BE"; icmp_id:456; icmp_seq:0; itype:0; reference:arachnids,184; classtype:attempted-dos; sid:228; rev:3;)
检测ICMP协议, ICMP报文的icmp_id回应消息ID域是456, ICMP报文的icmp_seq回应消息序列号域为0, ICMP报文的itype类型域为0,可以认为是TFN的客户端命令
下面是NFR的检测代码,和SNORT里差不多,我就不做太多解释了
filter client_to_daemon icmp (ipv4, type: 0, code: 0)
{
# Sequence number
if (ushort(ip.blob, 6) == 0) {
$id = ushort(ip.blob, 4);
if (TFN_COMMANDS[$id]) {
alert(tfn_source, tfn_command, ip.src, ip.dst, "Network",
"client to daemon",
"--AlertDetails", "ALERT_ID", "23-9",
"ALERT_CONFIDENCE", 60,
"ALERT_SEVERITY", "high",
"ALERT_EVENT_TYPE", "unknown",
"ALERT_IMPACT", "unknown",
"ALERT_ASSESSMENT", "unknown",
"IP_ADDR_SRC", ip.src, "IP_ADDR_DST", ip.dst,
"IP_PROTO_NUM", ip.proto,
"CONTEXT", attack:context(tfn_command));
record packet.sec, ip.src, -1, ip.dst, -1, ip.proto,
cat("client to daemon command ", $id) to tfn_rec;
misc_attacks:rec(packet.sec, scope(), "tfn client to daemon",
ip.src, ip.dst);
}
}
}
func tfn2k {
if (strlen(udp.blob) == 27 && index(udp.blob, "AAAAA") > -1) {
alert(tfn_source, tfn_command, ip.src, ip.dst, "2000",
"client to daemon",
"--AlertDetails", "ALERT_ID", "23-9",
"ALERT_CONFIDENCE", 60,
"ALERT_SEVERI, TY", "high",
"ALERT_EVENT_TYPE", "unknown",
"ALERT_IMPACT", "unknown",
"ALERT_ASSESSMENT", "unknown",
"IP_ADDR_SRC", ip.src, "PORT_SRC", udp.sport,
"IP_ADDR_DST", ip.dst, "PORT_DST", udp.dport,
"IP_PROTO_NUM", ip.proto,
"CONTEXT", attack:context(tfn_command));
record packet.sec, ip.src, udp.sport, ip.dst, udp.dport, ip.proto,
"tfn2k" to tfn_rec;
misc_attacks:rec(packet.sec, scope(), "tfn2k",
ip.src, ip.dst);
}
}
总结一下:关于内网中上DDOS的傀儡主机的检测,检测其和客户端,也就是控制端的通讯是主要前提,另外还是要尽量检测他的攻击特征,因为他的攻击特征比较混合,是若干种攻击同时进行,比较难分辨,DDOS到检测攻击层面上还是和检测DOS攻击一样,近年来国内开发的分布式DOS攻击工具层出不穷,好多属于私有的,和木马功能集合的,这些分布式攻击攻击比较难收集.检测DDOS我认为要集合特征检测,加流量异常来检测
L) 有关SQL注射的问题
1) SQL INJECTOR
我这里采用
SQL_INJECTOR_REGEX1 = regcomp("/(\%27)|(\’)|(\-\-)|(\%23)|(#)/ix");
SQL_INJECTOR_REGEX2 = regcomp("/((\=)|(=))[^\n]*((\%27)|(\’)|(\-\-)|(\%3B)|(;))/i");
SQL_INJECTOR_REGEX3 = regcomp("/\w*((\%27)|(\’))((\%6F)|o|(\%4F))((\%72)|r|(\%52))/ix");
SQL_INJECTOR_REGEX4 = regcomp("/exec(\s|\+)+(s|x)p\w+/ix");
SQL_INJECTOR_REGEX5 = regcomp("/((\%3C)|<)((\%2F)|\/)*[a-z0-9\%]+((\%3E)|>)/ix");
SQL_INJECTOR_REGEX6 = regcomp("/((\%3C)|<)((\%69)|i|(\%49))((\%6D)|m|(\%4D))((\%67)|g|(\%47))[^\n]+((\%3E)|>)/I");
SQL_INJECTOR_REGEX7 = regcomp("/((\%3C)|<)[^\n]+((\%3E)|>)/I");
SQL_INJECTOR_REGEX8 = regcomp("/((\%27)|(\’))union/ix");
SQL_INJECTOR_REGEX9 = regcomp("/((\%27)|(\’))and/ix");
SQL_INJECTOR_REGEX10 = regcomp("/((\%27)|(\’))insert/ix");
SQL_INJECTOR_REGEX11 = regcomp("/((\%27)|(\’))select/ix");
SQL_INJECTOR_REGEX12 = regcomp("/((\%27)|(\’))delete/ix");
SQL_INJECTOR_REGEX13 = regcomp("/((\%27)|(\’))update/ix");
SQL_INJECTOR_REGEX14 = regcomp("/((\%27)|(\’))truncate/ix");
SQL_INJECTOR_REGEX15 = regcomp("/((\%27)|(\’))declare/ix");
SQL_INJECTOR_REGEX16 = regcomp("/((\%2F)|(\;))union/ix");
SQL_INJECTOR_REGEX17 = regcomp("/((\%2F)|(\;))and/ix");
SQL_INJECTOR_REGEX18 = regcomp("/((\%2F)|(\;))insert/ix");
SQL_INJECTOR_REGEX19 = regcomp("/((\%2F)|(\;))select/ix");
SQL_INJECTOR_REGEX20 = regcomp("/((\%2F)|(\;))delete/ix");
SQL_INJECTOR_REGEX21 = regcomp("/((\%2F)|(\;))update/ix");
SQL_INJECTOR_REGEX22 = regcomp("/((\%2F)|(\;))truncate/ix");
SQL_INJECTOR_REGEX23 = regcomp("/((\%2F)|(\;))declare/ix");
SQL_INJECTOR_REGEX24 = regcomp("/((\%2F)|(\;))exec/ix");
SQL_INJECTOR_REGEX25 = regcomp("/((\%20)|(\ ))union/ix");
SQL_INJECTOR_REGEX26 = regcomp("/((\%20)|(\ ))and/ix");
SQL_INJECTOR_REGEX27 = regcomp("/((\%20)|(\ ))insert/ix");
SQL_INJECTOR_REGEX28 = regcomp("/((\%20)|(\ ))select/ix");
SQL_INJECTOR_REGEX29 = regcomp("/((\%20)|(\ ))delete/ix");
SQL_INJECTOR_REGEX30 = regcomp("/((\%20)|(\ ))update/ix");
SQL_INJECTOR_REGEX31 = regcomp("/((\%20)|(\ ))truncate/ix");
SQL_INJECTOR_REGEX32 = regcomp("/((\%20)|(\ ))declare/ix");
SQL_INJECTOR_REGEX33 = regcomp("/((\%20)|(\ ))exec/ix");
SQL_INJECTOR_REGEX34 = regcomp("/((\%2F)|(\/))**((\%2F)|(\/))union/ix");
SQL_INJECTOR_REGEX35 = regcomp("/((\%2F)|(\/))**((\%2F)|(\/))and/ix");
SQL_INJECTOR_REGEX36 = regcomp("/((\%2F)|(\/))**((\%2F)|(\/))insert/ix");
SQL_INJECTOR_REGEX37 = regcomp("/((\%2F)|(\/))**((\%2F)|(\/))select/ix");
SQL_INJECTOR_REGEX38 = regcomp("/((\%2F)|(\/))**((\%2F)|(\/))delete/ix");
SQL_INJECTOR_REGEX39 = regcomp("/((\%2F)|(\/))**((\%2F)|(\/))update/ix");
SQL_INJECTOR_REGEX40 = regcomp("/((\%2F)|(\/))**((\%2F)|(\/))truncate/ix");
SQL_INJECTOR_REGEX41 = regcomp("/((\%2F)|(\/))**((\%2F)|(\/))declare/ix");
SQL_INJECTOR_REGEX42 = regcomp("/((\%2F)|(\/))**((\%2F)|(\/))exec/ix");
42个正则表达试过滤SQL INJECTOR的用法,但是我声明一点,这里不能过滤SQL INJECTOR的变形语法,也不能过滤SQL INJECTOR的POST的注射方法,因为这里不检测POST DATA部分.
目前技术圈子的局面就是防止SQL INJECTOR没有太好的办法,在主机上做防注射是主流.
这里就不一一解释这么多的regex的含义了,在SNORT里完全可以使用这些regex.
总结一下:这42个表达式不是功能较多的,最好还是针对某些特殊的注射点,再加上特定的URL作为条件过滤,这样就不容易产生误报,还有就是URL的编码格式的问题,这些42条并没有考虑到,如果你要用这些表达式,可以做针对性的测试.
M) 有关CC攻击的问题
1) WEBCC
CC攻击,本身是比较难检测的,它有两种可能:
a) 好多台主机,每台主机就是在做傀儡主机时,每次发少量的攻击包,GET某个网站的某个网页,一台这种情况就不构成CC攻击,但是大量的主机就可以造成CC攻击
b) 少量主机,每台傀儡主机短时间内发大量的CC攻击包攻击某个网站
目前我们写的防CC攻击的签名是针对第二种情况的.我们定义一个被保护对象,在每个定时器所规定的时间内,有到WEB端口的源主机GET数据报的数量超过我们定义的上限时就告警,软件里还设定如果一次认为源地址是CC攻击的源,下次来就不用判断,直接阻断,知道系统的缓冲满了,自动释放,下次再让这个源地址访问,进入下一次的CC攻击的判断.
总结一下:CC攻击是应用层的攻击,是TCP完全建立握手的情况下进行的,没办法检测协议异常,只能做HTTP层的统计,这个检测方法属于统计特征检测.
对付上面提到的a)攻击方式,没有太好的办法.
4. 规则和签名里的注意点
A) 有关签名调试
1) 做NCODE编码开发难度并不大,比SNORT规则稍微复杂一点,第一要认清函数的用法,常见函数要会用.第二NFR有好多原理性的例子不能随便贴,开发签名初期要知道写哪类签名需要哪些好的模版.第三几个NFR文件acf.cfg.values,nfr要彻底认清楚格式,不能疏漏.第四签名推上去检测不出来的原因有几个:a)签名的语法还是有问题,比如原来应该写成sample1_alert,结果写成sample1___alert,多下划线等 b)设备接法不对,根本抓不到发包机来的包 c)签名函数用错,类型之间不能转化,但是可以通过SENSOR的语法检查,这个比较难找,要靠经验 d)检测特征根本就不准确,比如软件第一次运行有某些包,运行第二次某些包软件就不发了,你把这个作为特征,就没法检测,还有就是软件的版本问题,有些版本有某些特征,有些就没有,找特征最好避开软件的版本,找比较通用的特征
2) SNORT签名,我认为如何消除误报,如何基于原理写RULES是比较重要的,在一两条规则中对某些特殊的攻击写特征还是一件不太容易的事情
B) 规则和签名误报的问题
1) 对某些签名或RULES我们可以采用端口范围作为过滤条件,PAYLOAD的长度范围也作为判断条件,比如P2P
2) 一些HTTP的访问的特征比较容易误报,因为现在的网络的出口上基本都是走的HTTP流量,异常情况比较多,这个没太好办法,就是多做测试
3) 一些溢出的检测可以把PAYLOAD的长度作为条件,某些特殊攻击的溢出地址作为判断,还有一些特殊协议,比如RPC,SMB协议的溢出,最好加上协议特征,这样才好消除误报
C) 关于IDS检测和IPS阻断的问题
1) IDS检测出来和IPS能阻断是两码事,能检测,不一定能阻断,如果是检测出主要数据报,比如应用握手的数据报,可能就阻断效果比较好,如果就检测出某些CONTROL包,可能还有一些DATA包能过的,就是没有阻断效果
2) 随着软件的版本不同,即便有些软件能检测到,但是不能阻断,这个说明软件里的这个特征已经不是主要的CONTROL或DATA包了,这样可以考虑修改更新签名
3) 在NFR的SENSOR上有动态黑名单的东西,如果阻断有困难,可以使用它来切断源,但是对UDP协议的特征包就没用