logo

当前栏目:社区首页->电脑与信息->病毒与安全 转到:在该栏目发表文章社区后台管理搜索
用PERL实现一个简单的NIDS
作者: imac 日期: 08-03-29, 02:44

文章版权归原作者所有! (www.MegaEntry.com)

随着对网络安全需求的深入开发,基于网络的入侵检测技术已经成为一个重要且有意思的研究方向。想学习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...cap-0.04.tar.gzlibpcap的PERL接口。

http://www.cpan.org/authors/id/T/TI...ils-0.01.tar.gzNet-Pcap模块的wrapper,包装Net-Pcap的函数,可以更方便地在PERL里调用抓包。

http://www.cpan.org/authors/id/T/TI...ket-0.03.tar.gz用于基本的IP/TCP/UDP等包解码的模块,剥除各种协议头,抽取各个字段。

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

perl-ids.pl实现抓包及检测分析的主程序。------------------------------ 8< ----------------------------------------#!/usr/bin/perl

MegaEntry 网络社区与信息交流平台!

## 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 ";

文章版权归原作者所有! (www.MegaEntry.com)

$monitorlog = "monitor.log ";

# 以后台进程方式运行daemon ();

sub daemon {unless (fork) {SniffLoop ();exit 0;}exit 1;}

# 抓包循环sub SniffLoop {

# 进入工作目录chdir ( "$workingdir ");# 打开日志文件open (ATTACKLOG, ">> $attacklog ");

MegaEntry 网络社区与信息交流平台!

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包

MegaEntry 网络社区与信息交流平台!

$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解码模块解码

MegaEntry 网络社区与信息交流平台!

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

# 如果解码成功if ($smb->{valid}) {# 示例检测新近公布eeye的那个ASN.1解码错误导致的堆破坏漏洞# BID:9633,9635 CVEID:CAN-2003-0818 NSFOCUSID:6000

# 如果SMB命令是Session Setup AndXif ($smb->{cmd} == 0x73) {# 如果设置了Extended Security Negotiation位,表示有包里有Security Blobif ($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.1 malform encode attack! ");}}

文章版权归原作者所有! (www.MegaEntry.com)

}}}

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

MegaEntry 网络社区与信息交流平台!

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 %s:%s -> %s:%s %s ",$nowtime,$src_ip,$src_port,$dest_ip,$dst_port,$message);printf ( "%s %s:%s -> %s:%s %s ",$nowtime,$src_ip,$src_port,$dest_ip,$dst_port,$message);

文章版权归原作者所有! (www.MegaEntry.com)

}

# 记录监控信息sub LogMonitor {my ($src_ip,$src_port,$dest_ip,$dst_port,$message) = @_;

my $nowtime = localtime;printf MONITORLOG ( "%s %s:%s -> %s:%s %s ",$nowtime,$src_ip,$src_port,$dest_ip,$dst_port,$message);printf ( "%s %s:%s -> %s:%s %s ",$nowtime,$src_ip,$src_port,$dest_ip,$dst_port,$message);}

# INT信号处理例程sub HandleINT {

CleanUp ();exit (0);}

# TERM信号处理例程sub HandleTERM {

CleanUp ();

文章版权归原作者所有! (www.MegaEntry.com)

exit (0);}

# 清理,主要工作是关闭文件句柄sub CleanUp {close (ATTACKLOG); close (MONITORLOG);}------------------------------ 8< ----------------------------------------

FTP.pmFTP协议解码模块,抽取数据包里的FTP命令及相应的参数,此文件需要拷贝到NetPacket系列模块所在的目录,通常是在/usr/lib/perl5/site_perl/5.x.x/NetPacket/------------------------------ 8< ----------------------------------------## NetPacket::FTP - Decode FTP packets## Comments/suggestions to stardust at xfocus dot org#

MegaEntry 网络社区与信息交流平台!

## $Id: FTP.pm,v 1.16 2004/03/03 l1:16:20 stardust Exp $#

package NetPacket::FTP;

use strict;use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);use NetPacket;

my $myclass;

BEGIN {$myclass = __PACKAGE__;$VERSION = "0.01 ";}sub Version () { "$myclass v$VERSION " }

BEGIN {@ISA = qw(Exporter NetPacket);

# Items to export into callers namespace by default

文章版权归原作者所有! (www.MegaEntry.com)

# (move infrequently used names to @EXPORT_OK below)

@EXPORT = qw();

# Other items we are prepared to export if requested

@EXPORT_OK = qw();

# Tags:

%EXPORT_TAGS = (ALL => [@EXPORT, @EXPORT_OK],);

}

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

文章版权归原作者所有! (www.MegaEntry.com)

# 常见的FTP命令my @ftp_cmds = qw(ABOR ACCT ALLO APPE CDUP CWD DELE HELP LIST MKD MODE NLSTNOOP PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR RNTOSITE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKDXPWD XRMD LPRT LPSV ADAT AUTH CCC CONF ENC MIC PBSZ PROTFEAT OPTS EPRT EPSV LANG MDTM MLSD MLST SIZE DIGT CLNT MACB);

sub decode {my $class = shift;my($data) = @_;my $self = {};

my $cmdhead = 0;my $cmdtail = 0;my @parts = ();my $cmdcount = 0;my $returnindex = 0;

文章版权归原作者所有! (www.MegaEntry.com)

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);

MegaEntry 网络社区与信息交流平台!

# 从命令行里拆分出命令名和它的参数串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;

MegaEntry 网络社区与信息交流平台!

} else {$self->{valid} = 1;}

} else {$self->{valid} = 0;}

# 返回对象

bless($self, $class);return $self;}

sub splitcmd {my ($cmdline,$parts) = @_;

# 去除行尾的回车chomp($cmdline);# 用正则表达式抽取出命令名字和参数,既然效率不是考虑的主要问题就“毫无顾忌”地使用正则表达式,因为方便

MegaEntry 网络社区与信息交流平台!

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] eq uc($1)) {$valid_cmd = 1;last;}}# 如果是合法的命令则返回给调用函数if ($valid_cmd) {${$parts}[0] = $1;${$parts}[1] = $2;return 1;} else {return 0;

MegaEntry 网络社区与信息交流平台!

}

} else {return 0;}}

## Module initialisation#

1;

# autoloaded methods go after the END token (&& pod) below

__END__------------------------------ 8< ----------------------------------------

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

文章版权归原作者所有! (www.MegaEntry.com)

## NetPacket::SMB - Decode SMB packets## Comments/suggestions to stardust at xfocus dot org### $Id: SMB.pm,v 1.16 2004/02/23 12:25:17 stardust Exp $#

package NetPacket::SMB;

use strict;use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);use NetPacket;

my $myclass;

# SMB flags

use constant F2_LONGNAMEALLW => 0x0001;use constant F2_EXTATTRIBUTE => 0x0002;

MegaEntry 网络社区与信息交流平台!

use constant F2_SECURITYSIGN => 0x0004;use constant F2_LONGNAMEUSED => 0x0040;use constant F2_EXTSECURINEG => 0x0800;use constant F2_DONTRESOLDFS => 0x1000;use constant F2_EXECONLYREAD => 0x2000;use constant F2_ERRORCODTYPE => 0x4000;use constant F2_UNICODSTRING => 0x8000;

use constant F_LOCKANDREAD => 0x01;use constant F_RCVBUFFPOST => 0x02;use constant F_CASESENSITV => 0x08;use constant F_CANONICPATH => 0x10;use constant F_OPLOCKSREQU => 0x20;use constant F_NOTIFYONOPN => 0x40;use constant F_REQUERESPON => 0x80;

BEGIN {$myclass = __PACKAGE__;

文章版权归原作者所有! (www.MegaEntry.com)

$VERSION = "0.01 ";}sub Version () { "$myclass v$VERSION " }

BEGIN {@ISA = qw(Exporter NetPacket);

# Items to export into callers namespace by default# (move infrequently used names to @EXPORT_OK below)

@EXPORT = qw(F2_LONGNAMEALLW F2_EXTATTRIBUTE F2_SECURITYSIGNF2_LONGNAMEUSED F2_EXTSECURINEG F2_DONTRESOLDFSF2_EXECONLYREAD F2_ERRORCODTYPE F2_UNICODSTRINGF_LOCKANDREAD F_RCVBUFFPOST F_CASESENSITVF_CANONICPATH F_OPLOCKSREQU F_NOTIFYONOPNF_REQUERESPON);

# Other items we are prepared to export if requested

@EXPORT_OK = qw(smb_strip

文章版权归原作者所有! (www.MegaEntry.com)

);

# Tags:

%EXPORT_TAGS = (ALL => [@EXPORT, @EXPORT_OK],strip => [qw(smb_strip)],);

}

## Strip header from packet and return the data contained in it#

undef &smb_strip;*smb_strip = &strip;

# 剥除SMB头的函数sub strip {my ($data) = @_;

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

文章版权归原作者所有! (www.MegaEntry.com)

return $smb_obj->{data};}

## Decode the packet#

sub decode {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 {

文章版权归原作者所有! (www.MegaEntry.com)

# 取SMB的标志串my $smb_mark = substr ($data,4,4);

# 是否符合标志串if ($smb_mark ne "xffx53x4dx42 ") {$self->{valid} = 0;} else {$self->{valid} = 1;

# Decode SMB packet

if (defined($data)) {# 用PERL的unpack函数解码32字节长的SMB头结构,头结构可# 参考 http://www.cs.uml.edu/~bill/cs592/cifs.chm# 感谢小四(scz at nsfocus dot com)对于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},

文章版权归原作者所有! (www.MegaEntry.com)

$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)) {# 解码SMB结构下的wordcount字节及bytecount字节数据my $wordbytes = $self->{wordcount} * 2;($self->{wordcount},$self->{words},$self->{bytecount},$self->{bytes}) = unpack( "C ". "a ". "$wordbytes ". "va* ",$self->{data});} else {($self->{wordcount},$self->{words}) = unpack( "Ca* ",$self->{data});$self->{bytecount} = -1; $self->{bytes} = " ";}}}}

# 返回对象

bless($self, $class);

MegaEntry 网络社区与信息交流平台!

return $self;}

## Module initialisation#

1;

# autoloaded methods go after the END token (&& pod) below

__END__------------------------------ 8< ----------------------------------------


上一篇:构建小型的入侵检测系统(RedHat9)下一篇:使用PAM进行统一身份的认证

回复
标题: 

强烈建议采用IE 6.0或以上的浏览器