With the in-depth development of network security requirements, network-based intrusion detection technology has become an important and interesting research direction. The best way to learn NIDS is to write an NIDS program by yourself, in addition to reading some existing materials and the source code of some open-source systems, only in this way can we truly understand the implementation requirements and design advantages of some NIDS.
Essentially, NIDS is only a network traffic analysis tool. It identifies known or unknown attack behaviors through network traffic analysis, the most simple task of NIDS is packet capture-> protocol decoding-> matching. As we all know, PERL is an extremely powerful scripting language, in particular, its string processing capability can easily match malicious features in network traffic. Of course, PERL is only a scripting language, and its execution efficiency cannot be used in a truly large-volume productive environment, however, PERL is easy to learn and has powerful functions. It is undoubtedly very good to implement a simple NIDS for learning purposes. Next I will introduce a simple NIDS framework implemented with PERL, we will implement it in Linux, similar to other operating systems.
A powerful feature of PERL lies in its massive library of CPAN modules. You can find ready-made modules for many features you want to implement. All you need to do is install those modules, this section does not cover the management and use of PERL modules and object-oriented features. For more information, see related documents, such as advanced Perl programming published by o'reilly. Before writing a network traffic analysis script using PERL, you need to install some underlying packet capture and basic packet decoding modules, including the following:
Http://www.tcpdump.org/release/libpcap-0.8.1.tar.gz
The underlying basic packet capture library.
Http://www.cpan.org/authors/id/T/TI/TIMPOTTER/Net-Pcap-0.04.tar.gz
Libpcap PERL interface.
Http://www.cpan.org/authors/id/T/TI/TIMPOTTER/Net-PcapUtils-0.01.tar.gz
The wrapper of the Net-Pcap module is packaged with the Net-Pcap function, which makes it easier to call packet capture in PERL.
Http://www.cpan.org/authors/id/T/TI/TIMPOTTER/NetPacket-0.03.tar.gz
This module is used to decode basic IP/TCP/UDP packets. It removes various protocol headers and extracts various fields.
The following code demonstrates a simple NIDS framework with basic SMB and FTP protocol decoding modules. This program implements the simplest NIDS function, which is oriented to a single package and does not care about the package status, advanced Commercial NIDS products such as stream restructuring, packet status and application layer protocol tracking are not available. In order to improve the accuracy of the detection, what is different from Snort's direct matching of the Data zone is that this script implements two application layer protocols: simple Decoding of SMB and FTP, and decoding is completely NIDS-oriented, the Code has not been carefully tested and may be faulty.
(A) perl-ids.pl to Achieve Packet Capture and detection analysis of the main program.
Copy codeThe Code is as follows :#! /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 $
#
# Reference all related modules
Use Net: PcapUtils;
Use NetPacket: Ethernet qw (: strip );
Use NetPacket: TCP;
Use NetPacket: IP qw (: protos );
Use NetPacket: SMB;
Use NetPacket: FTP;
# Define the log file name
$ Workingdir = "./";
$ Attacklog = "attack. log ";
$ Monitorlog = "monitor. log ";
# Subsequent processes
Daemon ();
Sub daemon {
Unless (fork ){
SniffLoop ();
Exit 0;
}
Exit 1;
}
# Packet capture cycle
Sub SniffLoop {
# Enter the working directory
Chdir ("$ workingdir ");
# Open a log file
Open (ATTACKLOG, ">>$ attacklog ");
Open (MONITORLOG, ">>$ monitorlog ");
# Set the non-buffer mode for file read/write
Select (ATTACKLOG); $ ++; select (MONITORLOG); $ ++; select (STDOUT); $ ++;
# Set the signal processing function. Because the program runs on the background, you need to use the signal processing function to clear the program when exiting.
$ SIG {"INT"} = 'handleint ';
$ SIG {"TERM"} = 'handleter ';
# Go To The packet capture callback function
Net: PcapUtils: loop (& sniffit, SNAPLEN => 1800, Promisc => 1, FILTER => 'tcp or udp', DEV => 'eth0 ');
}
Sub sniffit {
My ($ args, $ header, $ packet) = @_;
# Decoding an IP package
$ Ip = NetPacket: IP-> decode (eth_strip ($ packet ));
# TCP protocol
If ($ ip-> {proto} = IP_PROTO_TCP ){
# Decoding TCP Packets
$ Tcp = NetPacket: TCP-> decode ($ ip-> {data });
# Check the packages from the SMB Client
If ($ tcp-> {dest_port} = 139) ($ tcp-> {dest_port} = 445 )){
# If the destination port is 139 or 445 and is considered as an SMB protocol package, check the port accordingly.
SmbClientCheck ($ ip-> {src_ip}, $ tcp-> {src_port}, $ ip-> {dest_ip}, $ tcp-> {dest_port }, $ tcp-> {data });
} Elsif ($ tcp-> {dest_port} = 21 ){
# If the target port is 21 and is considered as the FTP protocol, check the port accordingly.
FtpClientCheck ($ ip-> {src_ip}, $ tcp-> {src_port}, $ ip-> {dest_ip}, $ tcp-> {dest_port }, $ tcp-> {data });
} Else {}
# UDP protocol
} Elsif ($ ip-> {proto} = IP_PROTO_UDP ){
} Else {}
}
Sub SmbClientCheck {
My ($ src_ip, $ src_port, $ dest_ip, $ dst_port, $ data) = @_;
# Call the SMB decoding module for decoding
$ Smb = NetPacket: SMB-> decode ($ data );
# If decoding is successful
If ($ smb-> {valid }){
# Example: detects the heap corruption vulnerability caused by the newly published ASN.1 decoding error of eeye.
# BID: 9633,9635 CVEID: CAN-2003-0818 NSFOCUSID: 6000
# If the SMB command is Session Setup AndX
If ($ smb-> {cmd} = 0x73 ){
# If the Extended Security Negotiation bit is set, Security Blob exists in the package.
If ($ smb-> {flags2} & F2_EXTSECURINEG ){
# Use regular expressions to match the OID and malformed data string that usually appears in the attack package
# Because it is not based on principle detection and the ASN.1 encoding flexibility, such detection will cause false positives
If ($ smb-> {bytecount}> 0) & ($ smb-> {bytes} = ~ M/x06x06x2bx06x01x05x05x02. * [xa1x05x23x03x03x01x07 x84xffxffxff]/) {
# Log files
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) = @_;
# Call the FTP decoding module for decoding
$ Ftp = NetPacket: FTP-> decode ($ data );
# If decoding is successful
If ($ ftp-> {valid }){
# Example: detects the recently released MDTM command overflow attack on the FTP server of Serv-U <5.0.0.4
# BID: 9751 NSFOCUSID: 6078
# Traverse the FTP commands decoded from the data packet and Their Parameters
For (my $ I = 1; $ I <= $ ftp-> {memory count}; $ I ++ ){
My $ cmd = "cmd". "$ I ";
My $ para = "para". "$ I ";
# If the FTP command is MDTM
If (uc ($ ftp-> {$ cmd}) eq "MDTM "){
# Use a regular expression to match the parameter string that causes overflow.
# The expression is powerful. With this match, malformed parameter strings can be detected in principle.
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! ");
}
}
}
}
}
# Recording attack alarms
Sub LogAlert {
My ($ src_ip, $ src_port, $ dest_ip, $ dst_port, $ message) = @_;
My $ nowtime = localtime;
Printf ATTACKLOG ("% s: % s-> % s: % s", $ nowtime, $ src_ip, $ src_port, $ dest_ip, $ dst_port, $ message );
Printf ("% s: % s-> % s: % s", $ nowtime, $ src_ip, $ src_port, $ dest_ip, $ dst_port, $ message );
}
# Record monitoring information
Sub LogMonitor {
My ($ src_ip, $ src_port, $ dest_ip, $ dst_port, $ message) = @_;
My $ nowtime = localtime;
Printf MONITORLOG ("% s: % s-> % s: % s", $ nowtime, $ src_ip, $ src_port, $ dest_ip, $ dst_port, $ message );
Printf ("% s: % s-> % s: % s", $ nowtime, $ src_ip, $ src_port, $ dest_ip, $ dst_port, $ message );
}
# INT Signal Processing Routine
Sub HandleINT {
CleanUp ();
Exit (0 );
}
# TERM Signal Processing Routine
Sub HandleTERM {
CleanUp ();
Exit (0 );
}
# Cleaning: the main task is to close the file handle.
Sub CleanUp {
Close (ATTACKLOG); close (MONITORLOG );
}
(2) FTP. the pm FTP protocol decoding module extracts FTP commands and corresponding parameters from the data packets. This file needs to be copied to the directory where the NetPacket series modules are located, usually in/usr/lib/perl5/site_perl/5. x. x/NetPacket/
Copy codeThe Code is as follows :#
# 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 protocol text see RFC959, http://www.ietf.org/rfc/rfc0959.txt
# Common FTP commands
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 $ pipeline head = 0;
My $ shorttail = 0;
My @ parts = ();
My $ counters COUNT = 0;
My $ returnindex = 0;
My $ data_len = length ($ data );
# If the data length is too short, it will not be processed
If ($ data_len> = 4 ){
# Number of FTP commands in a package
$ Self-> {distinct count} = 0;
# Search and press Enter. It is considered to be a command line. Note that a package may contain multiple FTP commands.
While ($ returnindex = index ($ data, "x0a", $ response head)> = 0) ($ returnindex <0) & ($ data_len-$ pipeline head)> = 4 ))){
# Adjust the tail pointer of a command line string
If ($ returnindex <0 ){
$ Shorttail = $ data_len-1;
} Else {
$ Shorttail = $ returnindex;
}
If (my $ export Len = ($ your tail-$ your head + 1)> = 4 ){
# Retrieve the command line string
My $ pipeline line = substr ($ data, $ pipeline head, $ pipeline Len );
# Split the command name and its parameter string from the command line
If (splitcmd ($ cmdline, @ parts )){
$ Self-> {distinct count} ++;
My $ cmdindex = "cmd". "$ self-> {distinct count }";
My $ paraindex = "para". "$ self-> {limit count }";
# Record the object to be returned to the main program
$ Self-> {$ cmdindex} = $ parts [0];
$ Self-> {$ paraindex} = $ parts [1];
}
}
# Adjust the pointer of the command line string Header
$ Shorthead = $ shorttail + 1;
}
# If the number of commands is greater than 0, decoding is effective.
If ($ self-> {distinct count }== 0 ){
$ Self-> {valid} = 0;
} Else {
$ Self-> {valid} = 1;
}
} Else {
$ Self-> {valid} = 0;
}
# Returned object
Bless ($ self, $ class );
Return $ self;
}
Sub splitcmd {
My ($ partition line, $ parts) = @_;
# Remove the carriage return at the end of a row
Chomp ($ cmdline );
# Use a regular expression to extract the command name and parameters. Since efficiency is not a major concern, you can use the regular expression with no scruples because it is convenient.
If ($ response line = ~ M/^ s * ([a-zA-Z] {3, 4}) s + (.*)/){
My $ valid_cmd = 0;
# Check whether the extracted command name is a known legal FTP command
For (my $ I = 0; $ I <@ ftp_cmds; $ I ++ ){
If ($ ftp_cmds [$ I] eq uc ($1 )){
$ Valid_cmd = 1;
Last;
}
}
# If it is a valid command, return it to the called Function
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 __
(3) SMB. the simple decoding module of the SMB header structure by pm. This file needs to be copied to the directory where the NetPacket series modules are located, usually in/usr/lib/perl5/site_perl/5. x. x/NetPacket/
Copy codeThe Code is as follows :#
# NetPacket: SMB-Decode SMB packets
#
# Comments/suggestions to stardust at xfocus dot org
#
#
# $ Id: SMB. pm, v 1.16 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;
# Remove the SMB header Function
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 );
# If the Data Partition length is less than 39 bytes (4 + 32 + 3), it is considered not a decoded SMB package.
If ($ data_len <39 ){
$ Self-> {valid} = 0;
} Else {
# Retrieve the SMB flag string
My $ smb_mark = substr ($ data, 4, 4 );
# Conformity with flag string
If ($ smb_mark ne "xffx53x4dx42 "){
$ Self-> {valid} = 0;
} Else {
$ Self-> {valid} = 1;
# Decode SMB packet
If (defined ($ data )){
# Use the PERL unpack function to decode the 32-byte SMB header structure. The header structure can be
# Reference http://www.cs.uml.edu /~ Bill/cs592/cifs. chm
# Thanks to scz at nsfocus dot com for reminding me of the byte order of fields in the SMB header Structure
($ 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 ("ccna4cvvvvvvvva *", $ data );
($ Self-> {wordcount}, $ temp) = unpack ("Ca *", $ self-> {data });
If (36 + 1 + $ self-> {wordcount} * 2) <= ($ data_len-2 )){
# Decode wordcount and bytecount data in the SMB Structure
My $ wordbytes = $ self-> {wordcount} * 2;
($ Self-> {wordcount}, $ self-> {words}, $ self-> {bytecount}, $ self-> {bytes}) = unpack ("C ". "". "$ wordbytes ". "va *", $ self-> {data });
} Else {
($ Self-> {wordcount}, $ self-> {words}) = unpack ("Ca *", $ self-> {data });
$ Self-> {bytecount} =-1; $ self-> {bytes} = "";
}
}
}
}
# Returned object
Bless ($ self, $ class );
Return $ self;
}
#
# Module initialisation
#
1;
# Autoloaded methods go after the END token (& pod) below
_ END __