网络安全 频道

IPS/IDS特征识别和签名或规则编写

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有一条

 

alert tcp $HOME_NET any <> $EXTERNAL_NET any (msg:"DDOS shaft synflood"; flow:stateless; flags:S,12; seq:674711609; reference:arachnids,253; reference:cve,2000-0138; classtype:attempted-dos; sid:241; rev:10;)

这里就是说flow:stateless;也就是TCP状态异常, TCP的flags域是S,12; TCP报文的SEQ域的值是674711609,当然这个是异常特征检测,是针对工具的,没上面的NCODE的开发原理强,当然NCODE写SYNFLOOD是要根据NFR引擎的特性来的,在SNORT 的RULES里没办法这样写.

I)       关于密码策略的检测

1)      POP3密码策略,FTP密码策略,SMTP密码策略

func authdata {

       $count_array = 0;

       my_info = $1;

       # Ensure proper variable types are sent.

       foreach $i inside (my_info) {

             $count_array = $count_array + 1;      

              $check[$i] = cat(typeof( my_info[$i] ));

       }

       if( $check["STATUS"] != "int"  || $check["IP_ADDR_DST"] != "ipv4host"

              || $check["IP_ADDR_SRC"] != "ipv4host" ||

              $check["SERVICE_NAME"] != "str" || $count_array != 6 ) {

              return;

       }  

       if( ( $check["PASSWORD"] != "str" &&  my_info["PASSWORD"] != -1 )

              || ( $check["USERNAME"] != "str" && my_info["USERNAME"] != -1 ) ) {

              return;

       }

       $ipsrc = my_info["IP_ADDR_SRC"];  

       $ipdst = my_info["IP_ADDR_DST"];

       $service = my_info["SERVICE_NAME"];

       $data_recorded = 0;

       #Assign string if username and password are encrypted or unavailable..

       if ( my_info["USERNAME"] != -1 ) {

              $user = my_info["USERNAME"];

       } else {

              $user = "Username encrypted or not available.";

       }

       #Check for values setting and set record password accordingly.

       if ( my_info["PASSWORD"] != -1 ){

              if(RECORD_PASSWORDS == 0){

                     $password_record = "********";

              } else $password_record = my_info["PASSWORD"];

       } else {

              $password_record = "Password encrypted or not available.";

       }

       #Check for values setting and set alert password accordingly.

       if ( my_info["PASSWORD"] != -1 ){

              if(ALERT_PASSWORDS == 0){

                     $password_alert = "********";

              } else $password_alert = my_info["PASSWORD"];

       } else {

              $password_alert = "Password encrypted or not available.";

       }

       #Failure Alert

       if( my_info["STATUS"] == 0){

              if ( BADFREQUSER[$user]) {

                     BADFREQUSER[$user] = BADFREQUSER[$user] + 1;

              } else {

                     BADFREQUSER[$user] = 1;

              }

              $key = blobbytes(my_info["IP_ADDR_SRC"], my_info["IP_ADDR_DST"]);

              if (BADFREQIP[$key]) {

                       BADFREQIP[$key] = BADFREQIP[$key] + 1;

              } else {

                     BADFREQIP[$key] = 1;

              }

       }

       #Assign success, failure, undetermined..

       if( my_info["STATUS"] == 1 ) {

              $status = "Success";

       } else {

              if( my_info["STATUS"] == 0 )

                     $status = "Failure";

              else

                     $status = "Undetermined";

       }

}

这个是NFR NCODE里的内容,这里的密码主要基于明文密码,NFR引擎做密码缓存, Password encrypted or not available,这里说明加密的密文口令不认,现在的NFR的源代码里加入telnet和SMB的协议,telnet协议是三元协商协议,解码后得到user和password,SMB也可以部分解包,认出密码长度,这样在authentication里就可以检测口令是不是弱口令

J)       关于网络蠕虫的特征分析

1)      熊猫烧香

分析熊猫烧香病毒的主要方法就是下面几点:

A)     EXE本身的签名特征:

              这个病毒是用FSG加壳工具加过壳的,我们可以考虑解一下壳

       这里用unfsg工具:

      

下面是解壳之后EXE的特征:

特征一是:IPC$破解口令的部分,他用的密码字典,我们可以检测SMB协议,看是否存在弱口令破解,但是NFR还是SNORT没有能力做SMB口令的解码,SMB口令都是被HASH的.

 

 

 

二:

这个是本地特征,在查杀病毒的时候有用,他启动的时候用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协议的特征包就没用

1
相关文章