用PERL实现一个简单的NIDS perl-ids.pl 实现抓包及检测分析的主程序。 -------- 8< -------------- #!/usr/bin/perl # # Comments/suggestions to stardust at xfocus dot org # # # $Id: perl-ids.pl,v 1.16 2004/03/04 21:51:12 stardust Exp $ # # 引用所有相关的模块 use Net::PcapUtils; use NetPacket::Ethernet qw(:strip); use NetPacket::TCP; use NetPacket::IP qw(:protos); use NetPacket::SMB; use NetPacket::FTP; # 定义日志文件名 $workingdir = "./"; $attacklog = "attack.log"; $monitorlog = "monitor.log"; # 以后台进程方式运行 daemon (); sub daemon { unless (fork) { SniffLoop (); exit 0; } exit 1; } # 抓包循环 sub SniffLoop { # 进入工作目录 chdir ("$workingdir"); # 打开日志文件 open (ATTACKLOG,">> $attacklog"); open (MONITORLOG,">> $monitorlog"); # 设置文件读写为非缓冲模式 select(ATTACKLOG); $ ++; select(MONITORLOG); $ ++; select(STDOUT); $ ++; # 设置信号处理函数,因为程序运行于后台,退出时需要利用信号处理函数做些清理工作 $SIG{"INT"} = 'HandleINT'; $SIG{"TERM"} = 'HandleTERM'; # 进入抓包回调函数 Net::PcapUtils::loop(\&sniffit, SNAPLEN => 1800, Promisc => 1, FILTER => 'tcp or udp', DEV => 'eth0'); } sub sniffit { my ($args,$header,$packet) = @_; # 解码IP包 $ip = NetPacket::IP->decode(eth_strip($packet)); # TCP协议 if ($ip->{proto} == IP_PROTO_TCP) { # 解码TCP包 $tcp = NetPacket::TCP->decode($ip->{data}); # 检查来自SMB客户端的包 if (($tcp->{dest_port} == 139) ($tcp->{dest_port} == 445)) { # 如果目的端口是139或445,认为是SMB协议包,做相应的检查 SmbClientCheck ($ip->{src_ip},$tcp->{src_port},$ip->{dest_ip},$tcp->{dest_port},$tcp->{data}); } elsif ($tcp->{dest_port} == 21) { # 如果目的端口是21,认为是FTP协议,做相应的检查 FtpClientCheck ($ip->{src_ip},$tcp->{src_port},$ip->{dest_ip},$tcp->{dest_port},$tcp->{data}); } else {} # UDP协议 } elsif ($ip->{proto} == IP_PROTO_UDP) { } else {} } sub SmbClientCheck { my ($src_ip,$src_port,$dest_ip,$dst_port,$data) = @_; # 调用SMB解码模块解码 $smb = NetPacket::SMB->decode($data); # 如果解码成功 if ($smb->{valid}) { # 示例检测新近公布eeye的那个ASN.1解码错误导致的堆破坏漏洞 # BID:9633,9635 CVEID:CAN-2003-0818 NSFOCUSID:6000 # 如果SMB命令是Session Setup AndX if ($smb->{cmd} == 0x73) { # 如果设置了Extended Security Negotiation位,表示有包里有Security Blob if ($smb->{flags2} & F2_EXTSECURINEG) { # 用正则表达式匹配通常会在攻击包里出现的OID及引发错误的畸形数据串 # 由于不是从原理上检测加之ASN.1编码的灵活性,这样的检测会导致漏报 if (($smb->{bytecount} > 0) && ($smb->{bytes} =~ m/\x06\x06\x2b\x06\x01\x05\x05\x02.*[\xa1\x05\x23\x03\x03\x01\x07 \x84\xff\xff\xff]/)) { # 记入日志文件 LogAlert ($src_ip,$src_port,$dest_ip,$dst_port,"ASN.1 malform encode attack!"); } } } } } sub FtpClientCheck { my ($src_ip,$src_port,$dest_ip,$dst_port,$data) = @_; # 调用FTP解码模块解码 $ftp = NetPacket::FTP->decode($data); # 如果解码成功 if ($ftp->{valid}) { # 示例检测新近公布的Serv-U < 5.0.0.4版FTP服务器MDTM命令溢出攻击 # BID:9751 NSFOCUSID:6078 # 遍历从数据包里解码出来的FTP命令及其参数 for (my $i = 1;$i <= $ftp->{cmdcount};$i++) { my $cmd = "cmd"."$i"; my $para = "para"."$i"; # 如果FTP命令是MDTM if (uc($ftp->{$cmd}) eq "MDTM") { # 用正则表达式匹配引发溢出的参数串,这里体现了正则 # 表达式的强大,用此匹配可以从原理上检测到畸形参数串 if ($ftp->{$para} =~ m/\d{14}[+ -]\S{5,}\s+\S{1,}/) { LogAlert ($src_ip,$src_port,$dest_ip,$dst_port,"Serv-U < v5.0.0.4 MDTM command long timezone string overflow attack!"); } } } } } # 记录攻击告警 sub LogAlert { my ($src_ip,$src_port,$dest_ip,$dst_port,$message) = @_; my $nowtime = localtime; printf ATTACKLOG ("%s\t%s:%s -> %s:%s\t%s\n",$nowtime,$src_ip,$src_port,$dest_ip,$dst_port,$message); printf ("%s\t%s:%s -> %s:%s\t%s\n",$nowtime,$src_ip,$src_port,$dest_ip,$dst_port,$message); } # 记录监控信息 sub LogMonitor { my ($src_ip,$src_port,$dest_ip,$dst_port,$message) = @_; my $nowtime = localtime; printf MONITORLOG ("%s\t%s:%s -> %s:%s\t%s\n",$nowtime,$src_ip,$src_port,$dest_ip,$dst_port,$message); printf ("%s\t%s:%s -> %s:%s\t%s\n",$nowtime,$src_ip,$src_port,$dest_ip,$dst_port,$message); } # INT信号处理例程 sub HandleINT { CleanUp (); exit (0); } # TERM信号处理例程 sub HandleTERM { CleanUp (); exit (0); } # 清理,主要工作是关闭文件句柄 sub CleanUp { close (ATTACKLOG); close (MONITORLOG); } -------- 8< -------------- |