分享
 
 
 

用PERL实现一个简单的NIDS

王朝厨房·作者佚名  2007-01-05
窄屏简体版  字體: |||超大  

随着对网络安全需求的深入开发,基于网络的入侵检测技术已经成为一个重要且有意思的研究方向。想学习NIDS技术除了去读一些现成的资料和一些开源系统的源码,最好的办法莫过于自己去写一个NIDS程序,只有那样才能真正体会到一些NIDS的实现需求和设计妙处。

本质上说NIDS只是一种网络流量的分析工具,通过对网络流量的分析识别出一些已知或未知的攻击行为,一个最简单的NIDS完成的主要工作也就是抓包->协议解码->匹配,众所周知PERL是极其强大的脚本语言,尤其是它的字符串处理能力可以方便地实现对于网络流量中恶意特征进行匹配。当然PERL毕竟只是脚本语言,它的执行效率不允许用于真正大流量生产性环境,但PERL的简单易学及强大功能对于实现一个简单的NIDS达到学习的目的无疑是非常好的,下面我介绍一个用PERL实现的简单NIDS框架,我们将在Linux下实现它,在其他操作系统上类似。

PERL的一个强大特性就在于它海量的CPAN模块库,很多你想实现的功能都可以找到现成的模块,你所要做的只是安装上那些模块即可,关于PERL的模块及面向对象特性的管理和使用在这就不介绍了,请参看相关资料,比如O REILLY出版的《高级Perl编程》。在用PERL编写网络流量分析脚本之前,需要安装一些底层的抓包及基本的数据包解码模块,包括如下这些:

http://www.tcpdump.org/release/libpcap-0.8.1.tar.gz

底层基本的抓包库。

http://www.cpan.org/authors/id/T/TI/TIMPOTTER/Net-Pcap-0.04.tar.gz

libpcap的PERL接口。

http://www.cpan.org/authors/id/T/TI/TIMPOTTER/Net-PcapUtils-0.01.tar.gz

Net-Pcap模块的wrapper,包装Net-Pcap的函数,可以更方便地在PERL里调用抓包。

http://www.cpan.org/authors/id/T/TI/TIMPOTTER/NetPacket-0.03.tar.gz

用于基本的IP/TCP/UDP等包解码的模块,剥除各种协议头,抽取各个字段。

下面的代码演示了一个带有基本SMB和FTP协议解码模块的最简单NIDS框架,此程序实现最简单的NIDS功能,面向单包,不关心包的状态,不具备高级的商业NIDS产品诸如流重组,包状态及应用层协议的跟踪等功能。为了提高检测的准确性,与Snort直接匹配数据区不同的是,这个脚本实现了两个应用层协议:SMB、FTP的简单解码,解码完全是面向NIDS的需要,代码也没有经过仔细的测试可能存在问题,有什么建议可联系我。

perl-ids.pl

实现抓包及检测分析的主程序。

------------------------------8<----------------------------------------

#!/usr/bin/perl

#

#Comments/suggestionstostardustatxfocusdotorg

#

#

#$Id:perl-ids.pl,v1.162004/03/0421:51:12stardustExp$

#

#引用所有相关的模块

useNet::PcapUtils;

useNetPacket::Ethernetqw(:strip);

useNetPacket::TCP;

useNetPacket::IPqw(:protos);

useNetPacket::SMB;

useNetPacket::FTP;

#定义日志文件名

$workingdir="./";

$attacklog="attack.log";

$monitorlog="monitor.log";

#以后台进程方式运行

daemon();

subdaemon{

unless(fork){

SniffLoop();

exit0;

}

exit1;

}

#抓包循环

subSniffLoop{

#进入工作目录

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=> tcporudp ,DEV=> eth0 );

}

subsniffit{

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{}

}

subSmbClientCheck{

my($src_ip,$src_port,$dest_ip,$dst_port,$data)=@_;

#调用SMB解码模块解码

$smb=NetPacket::SMB->decode($data);

#如果解码成功

if($smb->{valid}){

#示例检测新近公布eeye的那个ASN.1解码错误导致的堆破坏漏洞

#BID:9633,9635CVEID:CAN-2003-0818NSFOCUSID:6000

#如果SMB命令是SessionSetupAndX

if($smb->{cmd}==0x73){

#如果设置了ExtendedSecurityNegotiation位,表示有包里有SecurityBlob

if($smb->{flags2}&F2_EXTSECURINEG){

#用正则表达式匹配通常会在攻击包里出现的OID及引发错误的畸形数据串

#由于不是从原理上检测加之ASN.1编码的灵活性,这样的检测会导致漏报

if(($smb->{bytecount}>0)&&($smb->{bytes}=~m/x06x06x2bx06x01x05x05x02.*[xa1x05x23x03x03x01x07|x84xffxffxff]/)){

#记入日志文件

LogAlert($src_ip,$src_port,$dest_ip,$dst_port,"ASN.1malformencodeattack!");

}

}

}

}

}

subFtpClientCheck{

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:9751NSFOCUSID: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.4MDTMcommandlongtimezonestringoverflowattack!");

}

}

}

}

}

#记录攻击告警

subLogAlert{

my($src_ip,$src_port,$dest_ip,$dst_port,$message)=@_;

my$nowtime=localtime;

printfATTACKLOG("%st%s:%s->%s:%st%sn",$nowtime,$src_ip,$src_port,$dest_ip,$dst_port,$message);

printf("%st%s:%s->%s:%st%sn",$nowtime,$src_ip,$src_port,$dest_ip,$dst_port,$message);

}

#记录监控信息

subLogMonitor{

my($src_ip,$src_port,$dest_ip,$dst_port,$message)=@_;

my$nowtime=localtime;

printfMONITORLOG("%st%s:%s->%s:%st%sn",$nowtime,$src_ip,$src_port,$dest_ip,$dst_port,$message);

printf("%st%s:%s->%s:%st%sn",$nowtime,$src_ip,$src_port,$dest_ip,$dst_port,$message);

}

#INT信号处理例程

subHandleINT{

CleanUp();

exit(0);

}

#TERM信号处理例程

subHandleTERM{

CleanUp();

exit(0);

}

#清理,主要工作是关闭文件句柄

subCleanUp{

close(ATTACKLOG);close(MONITORLOG);

}

------------------------------8<----------------------------------------

FTP.pm

FTP协议解码模块,抽取数据包里的FTP命令及相应的参数,此文件需要拷贝到NetPacket系列模块所在的目录,通常是在/usr/lib/perl5/site_perl/5.x.x/NetPacket/

------------------------------8<----------------------------------------

#

#NetPacket::FTP-DecodeFTPpackets

#

#Comments/suggestionstostardustatxfocusdotorg

#

#

#$Id:FTP.pm,v1.162004/03/03l1:16:20stardustExp$

#

packageNetPacket::FTP;

usestrict;

usevarsqw($VERSION@ISA@EXPORT@EXPORT_OK%EXPORT_TAGS);

useNetPacket;

my$myclass;

BEGIN{

$myclass=__PACKAGE__;

$VERSION="0.01";

}

subVersion(){"$myclassv$VERSION"}

BEGIN{

@ISA=qw(ExporterNetPacket);

#Itemstoexportintocallersnamespacebydefault

#(moveinfrequentlyusednamesto@EXPORT_OKbelow)

@EXPORT=qw(

);

#Otheritemswearepreparedtoexportifrequested

@EXPORT_OK=qw(

);

#Tags:

%EXPORT_TAGS=(

ALL=>[@EXPORT,@EXPORT_OK],

);

}

#

#Decodethepacket

#

#FTP协议文本参看RFC959,http://www.ietf.org/rfc/rfc0959.txt

#常见的FTP命令

my@ftp_cmds=qw(ABORACCTALLOAPPECDUPCWDDELEHELPLISTMKDMODENLST

NOOPPASSPASVPORTPWDQUITREINRESTRETRRMDRNFRRNTO

SITESMNTSTATSTORSTOUSTRUSYSTTYPEUSERXCUPXCWDXMKD

XPWDXRMDLPRTLPSVADATAUTHCCCCONFENCMICPBSZPROT

FEATOPTSEPRTEPSVLANGMDTMMLSDMLSTSIZEDIGTCLNTMACB

);

subdecode{

my$class=shift;

my($data)=@_;

my$self={};

my$cmdhead=0;

my$cmdtail=0;

my@parts=();

my$cmdcount=0;

my$returnindex=0;

my$data_len=length($data);

#如果数据长度过短则不处理

if($data_len>=4){

#一个包里的FTP命令个数

$self->{cmdcount}=0;

#搜索回车,之前认为是一个命令行,需要注意的是一个包里可能包含多个FTP命令

while((($returnindex=index($data,"x0a",$cmdhead))>=0)||(($returnindex<0)&&(($data_len-$cmdhead)>=4))){

#调整一个命令行串尾指针

if($returnindex<0){

$cmdtail=$data_len-1;

}else{

$cmdtail=$returnindex;

}

if((my$cmdlen=($cmdtail-$cmdhead+1))>=4){

#取出命令行串

my$cmdline=substr($data,$cmdhead,$cmdlen);

#从命令行里拆分出命令名和它的参数串

if(splitcmd($cmdline,@parts)){

$self->{cmdcount}++;

my$cmdindex="cmd"."$self->{cmdcount}";

my$paraindex="para"."$self->{cmdcount}";

#记录到要返回到主程序的对象

$self->{$cmdindex}=$parts[0];

$self->{$paraindex}=$parts[1];

}

}

#调整命令行串头指针

$cmdhead=$cmdtail+1;

}

#如果命令个数大于0,则说明解码是有效的

if($self->{cmdcount}==0){

$self->{valid}=0;

}else{

$self->{valid}=1;

}

}else{

$self->{valid}=0;

}

#返回对象

bless($self,$class);

return$self;

}

subsplitcmd{

my($cmdline,$parts)=@_;

#去除行尾的回车

chomp($cmdline);

#用正则表达式抽取出命令名字和参数,既然效率不是考虑的主要问题就“毫无顾忌”地使用正则表达式,因为方便

if($cmdline=~m/^s*([a-zA-Z]{3,4})s+(.*)/){

my$valid_cmd=0;

#检查抽出来的命令名字是否是一个已知的合法FTP命令

for(my$i=0;$i<@ftp_cmds;$i++){

if($ftp_cmds[$i]equc($1)){

$valid_cmd=1;

last;

}

}

#如果是合法的命令则返回给调用函数

if($valid_cmd){

${$parts}[0]=$1;

${$parts}[1]=$2;

return1;

}else{

return0;

}

}else{

return0;

}

}

#

#Moduleinitialisation

#

1;

#autoloadedmethodsgoaftertheENDtoken(&&pod)below

__END__

------------------------------8<----------------------------------------

SMB.pm

对SMB包头结构的简单解码模块,此文件需要拷贝到NetPacket系列模块所在的目录,通常是在/usr/lib/perl5/site_perl/5.x.x/NetPacket/

------------------------------8<----------------------------------------

#

#NetPacket::SMB-DecodeSMBpackets

#

#Comments/suggestionstostardustatxfocusdotorg

#

#

#$Id:SMB.pm,v1.162004/02/2312:25:17stardustExp$

#

packageNetPacket::SMB;

usestrict;

usevarsqw($VERSION@ISA@EXPORT@EXPORT_OK%EXPORT_TAGS);

useNetPacket;

my$myclass;

#SMBflags

useconstantF2_LONGNAMEALLW=>0x0001;

useconstantF2_EXTATTRIBUTE=>0x0002;

useconstantF2_SECURITYSIGN=>0x0004;

useconstantF2_LONGNAMEUSED=>0x0040;

useconstantF2_EXTSECURINEG=>0x0800;

useconstantF2_DONTRESOLDFS=>0x1000;

useconstantF2_EXECONLYREAD=>0x2000;

useconstantF2_ERRORCODTYPE=>0x4000;

useconstantF2_UNICODSTRING=>0x8000;

useconstantF_LOCKANDREAD=>0x01;

useconstantF_RCVBUFFPOST=>0x02;

useconstantF_CASESENSITV=>0x08;

useconstantF_CANONICPATH=>0x10;

useconstantF_OPLOCKSREQU=>0x20;

useconstantF_NOTIFYONOPN=>0x40;

useconstantF_REQUERESPON=>0x80;

BEGIN{

$myclass=__PACKAGE__;

$VERSION="0.01";

}

subVersion(){"$myclassv$VERSION"}

BEGIN{

@ISA=qw(ExporterNetPacket);

#Itemstoexportintocallersnamespacebydefault

#(moveinfrequentlyusednamesto@EXPORT_OKbelow)

@EXPORT=qw(F2_LONGNAMEALLWF2_EXTATTRIBUTEF2_SECURITYSIGN

F2_LONGNAMEUSEDF2_EXTSECURINEGF2_DONTRESOLDFS

F2_EXECONLYREADF2_ERRORCODTYPEF2_UNICODSTRING

F_LOCKANDREADF_RCVBUFFPOSTF_CASESENSITV

F_CANONICPATHF_OPLOCKSREQUF_NOTIFYONOPN

F_REQUERESPON

);

#Otheritemswearepreparedtoexportifrequested

@EXPORT_OK=qw(smb_strip

);

#Tags:

%EXPORT_TAGS=(

ALL=>[@EXPORT,@EXPORT_OK],

strip=>[qw(smb_strip)],

);

}

#

#Stripheaderfrompacketandreturnthedatacontainedinit

#

undef&smb_strip;

*smb_strip=&strip;

#剥除SMB头的函数

substrip{

my($data)=@_;

my$smb_obj=NetPacket::SMB->decode($data);

return$smb_obj->{data};

}

#

#Decodethepacket

#

subdecode{

my$class=shift;

my($data)=@_;

my$self={};

my$data_len=0;

my$temp="";

$data_len=length($data);

#如果数据区长度小于39字节(4+32+3),则认为不是一个可解码的SMB包

if($data_len<39){

$self->{valid}=0;

}else{

#取SMB的标志串

my$smb_mark=substr($data,4,4);

#是否符合标志串

if($smb_markne"xffx53x4dx42"){

$self->{valid}=0;

}else{

$self->{valid}=1;

#DecodeSMBpacket

if(defined($data)){

#用PERL的unpack函数解码32字节长的SMB头结构,头结构可

#参考http://www.cs.uml.edu/~bill/cs592/cifs.chm

#感谢小四(sczatnsfocusdotcom)对于SMB头结构中字段字节序的提醒

($self->{nbt_type},$self->{nbt_flag},$self->{nbt_len},

$self->{mark},$self->{cmd},$self->{status},

$self->{flags},$self->{flags2},$self->{ext},

$self->{ext2},$self->{ext3},$self->{tid},

$self->{pid},$self->{uid},$self->{mid},

$self->{data})=unpack("CCna4CVCvVVVvvvva*",$data);

($self->{wordcount},$temp)=unpack("Ca*",$self->{data});

if((36+1+$self->{wordcount}*2)<=($data_len-2)){

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有