随着对网络安全需求的深入开发,基于网络的入侵检测技术已经成为一个重要且有意思的研究方向。想学习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/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< ----------------------------------------
FTP.pm
FTP协议解码模块,抽取数据包里的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
#
#
# $Id: FTP.pm,v 1.16 2004/03/03 l1:16:20 stardust Exp $
#