设为首页 收藏本站
查看: 1132|回复: 0

[经验分享] 用PERL实现一个简单的NIDS

[复制链接]
累计签到:12 天
连续签到:1 天
发表于 2015-12-26 16:19:22 | 显示全部楼层 |阅读模式
  随着对 用PERL实现一个简单的NIDS
随着对网络安全需求的深入开发,基于网络的入侵检测技术已经成为一个重要且有意思的研究方向。想学习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
实现抓包及检测分析的主程序。
  #!/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/x06x06x2bx06x01x05x05x02.*[xa1x05x23x03x03x01x07 x84xffxffxff]/)) {
# 记入日志文件
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 %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);
}
  # 记录监控信息
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 ();
exit (0);
}
  # 清理,主要工作是关闭文件句柄
sub CleanUp {
close (ATTACKLOG); close (MONITORLOG);
}
  (二)
  FTP.pm
FTP协议解码模块,抽取数据包里的FTP命令及相应的参数,此文件需要拷贝到NetPacket系列模块所在的目录,通常是在/usr/lib/perl5/site_perl/5.x.x/NetPacket/
#
# 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 $
#
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
# (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
  # 常见的FTP命令
my @ftp_cmds = qw(ABOR ACCT ALLO APPE CDUP CWD DELE HELP LIST MKD MODE NLST
NOOP PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR RNTO
SITE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD
XPWD XRMD LPRT LPSV ADAT AUTH CCC CONF ENC MIC PBSZ PROT
FEAT 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;
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;
}
  
sub splitcmd {
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] eq uc($1)) {
$valid_cmd = 1;
last;
}
}
# 如果是合法的命令则返回给调用函数
if ($valid_cmd) {
${$parts}[0] = $1;
${$parts}[1] = $2;
return 1;
} else {
return 0;
}
  } else {
return 0;
}
}
  #
# Module initialisation
#
  1;
  # autoloaded methods go after the END token (&& pod) below
  __END__
  (三)
  SMB.pm
对SMB包头结构的简单解码模块,此文件需要拷贝到NetPacket系列模块所在的目录,通常是在/usr/lib/perl5/site_perl/5.x.x/NetPacket/
#
# 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;
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__;
$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_SECURITYSIGN
F2_LONGNAMEUSED F2_EXTSECURINEG F2_DONTRESOLDFS
F2_EXECONLYREAD F2_ERRORCODTYPE F2_UNICODSTRING
F_LOCKANDREAD F_RCVBUFFPOST F_CASESENSITV
F_CANONICPATH F_OPLOCKSREQU F_NOTIFYONOPN
F_REQUERESPON
);
  # Other items we are prepared to export if requested
  @EXPORT_OK = qw(smb_strip
);
  # 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);
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 {
# 取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},
$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);
return $self;
}
  
#
# Module initialisation
#
  1;
  # autoloaded methods go after the END token (&& pod) below
  __END__
  (完)

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-156697-1-1.html 上篇帖子: perl去除字符串前后的空格 下篇帖子: perl的CPAN模块安装
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表