📄 psa-chapter09.txt
字号:
Example code from Perl for System Administration by David N. Blank-Edelman
O'Reilly and Associates, 1st Edition, ISBN 1-56592-609-9
Chapter Nine
============
#*
#* two ways to scan a log file for the word "error"
#*
open(LOG,"logfile") or die "Unable to open logfile:$!\n";
while(<LOG>){
print if /\berror\b/i;
}
close(LOG);
### OR ###
# from the command line
$ perl -ne 'print if /\berror\b/i' logfile
-------
#*
#* read wtmp on SunOS and dump entries found there
#*
# this is the template we're going to feed to unpack()
$template = "A8 A8 A16 l";
# this uses pack() to help us determine the size (in bytes)
# of a single record
$recordsize = length(pack($template,()));
# open the file
open(WTMP,"/var/adm/wtmp") or die "Unable to open wtmp:$!\n";
# read it in one record at a time
while (read(WTMP,$record,$recordsize)) {
# unpack it, using our template
($tty,$name,$host,$time)=unpack($template,$record);
# handle the records with a null character specially
# (see below)
if ($name and substr($name,0,1) ne "\0"){
print "$tty:$name:$host:",scalar localtime($time),"\n";
}
else {
print "$tty:(logout):(logout):",scalar localtime($time),"\n";
}
}
# close the file
close(WTMP);
-------
#*
#* show all the unique user names found in a wtmp file using "last"
#*
# location of the last command binary
$lastexec = "/usr/ucb/last";
open(LAST,"$lastexec|") or die "Unable to run $lastexec:$!\n";
while(<LAST>){
$user = (split)[0];
print "$user","\n" unless exists $seen{$user};
$seen{$user}='';
}
close(LAST) or die "Unable to properly close pipe:$!\n";
-------
#*
#* dump the entries in the NT/2000 system event log
#*
use Win32::EventLog;
# each event has a type, this is a translation of the common types
%type = (1 => "ERROR",
2 => "WARNING",
4 => "INFORMATION",
8 => "AUDIT_SUCCESS",
16 => "AUDIT_FAILURE");
# if this is set, we also retrieve the full text of every
# message on each Read()
$Win32::EventLog::GetMessageText = 1;
# open the System event log
$log = new Win32::EventLog("System") or die "Unable to open system log:$^E\n";
# read through it one record at a time, starting with the first entry
while (
$log->Read((EVENTLOG_SEQUENTIAL_READ|EVENTLOG_FORWARDS_READ),
1,$entry)){
print scalar localtime($entry->{TimeGenerated})." ";
print $entry->{Computer}."[".($entry->{EventID} &
0xffff)."] ";
print $entry->{Source}.":".$type{$entry->{EventType}};
print $entry->{Message};
}
-------
#*
#* log rotation using Logfile::Rotate
#*
use Logfile::Rotate;
$logfile = new Logfile::Rotate(
File => "/var/adm/log/syslog",
Count => 5,
Gzip => "/usr/local/bin/gzip",
Signal =>
sub {
open PID, "/etc/syslog.pid" or
die "Unable to open pid file:$!\n";
chomp($pid = <PID>);
close PID;
# should check validity first
kill 'HUP', $pid;
}
);
$logfile->rotate();
undef $logfile;
-------
#*
#* bigbuffy - handling log files with circular buffering
#*
$buffsize = 200; # default circular buffer size (in lines)
use Getopt::Long;
# parse the options
GetOptions("buffsize=i" => \$buffsize,
"dumpfile=s" => \$dumpfile);
# set up the signal handler and initialize a counter
&setup;
# and away we go! (with just a simple
# read line-store line loop)
while (<>){
# insert line into data structure
# note, we do this first, even if we've caught a signal.
# Better to dump an extra line than lose a line of data if
# something goes wrong in the dumping process
$buffer[$whatline] = $_;
# where should the next line go?
($what_line %= $buff_size)++;
# if we receive a signal, dump the current buffer
if ($dumpnow) {
&dodump();
}
}
sub setup {
die "USAGE: $0 [--buffsize=<lines>] --dumpfile=<filename>"
unless (length($dumpfile));
$SIG{'USR1'} = \&dumpnow; # set a signal handler for dump
$whatline = 1; # start line in circular buffer
}
# simple signal handler that just sets an exception flag,
# see perlipc(1)
sub dumpnow {
$dumpnow = 1;
}
# dump the circular buffer out to a file, appending to file if
# it exists
sub dodump{
my($line); # counter for line dump
my($exists); # flag, does the output file exist already?
my(@firststat,@secondstat); # to hold output of lstats
$dumpnow = 0; # reset the flag and signal handler
$SIG{'USR1'} = \&dumpnow;
if (-e $dumpfile and (! -f $dumpfile or -l $dumpfile)) {
warn "ALERT: dumpfile exists and is not a plain file,skipping dump.\n";
return undef;
}
# we have to take special precautions when we're doing an
# append. The next set of "if" statements perform a set of
# security checks while opening the file for append
if (-e $dumpfile) {
$exists = 1;
unless(@firststat = lstat $dumpfile){
warn "Unable to lstat $dumpfile,skipping dump.\n";
return undef;
}
if ($firststat[3] != 1) {
warn "$dumpfile is a hard link, skipping dump.\n";
return undef;
}
}
unless (open(DUMPFILE,">>$dumpfile")){
warn "Unable to open $dumpfile for append,skipping dump.\n";
return undef;
}
if ($exists) {
unless (@secondstat = lstat DUMPFILE){
warn "Unable to lstat opened $dumpfile,skipping dump.\n";
return undef;
}
if ($firststat[0] != $secondstat[0] or # check dev num
$firststat[1] != $secondstat[1] or # check inode
$firststat[7] != $secondstat[7]) # check sizes
{
warn "SECURITY PROBLEM: lstats don't match,skipping dump.\n";
return undef;
}
}
$line = $whatline;
print DUMPFILE "-".scalar(localtime).("-"x50)."\n";
do {
# in case buffer was not full
last unless (defined $buffer[$line]);
print DUMPFILE $buffer[$line];
$line = ($line == $buffsize) ? 1 : $line+1;
} while ($line != $whatline);
close(DUMPFILE);
# zorch the active buffer to avoid leftovers
# in future dumps
$whatline = 1;
$buffer = ();
return 1;
}
-------
#*
#* count the total number of reboots on a Solaris 2.6 machine
#*
# template for Solaris 2.6 wtmpx, see the pack() doc
# for more information
$template = "A32 A4 A32 l s s2 x2 l2 l x20 s A257 x";
# determine the size of a record
$recordsize = length(pack($template,()));
# open the file
open(WTMP,"/var/adm/wtmpx") or die "Unable to open wtmpx:$!\n";
# read through it one record at a time
while (read(WTMP,$record,$recordsize)) {
($ut_user,$ut_id,$ut_line,$ut_pid,$ut_type,$ut_e_termination,
$ut_e_exit,$tv_sec,$tv_usec,$ut_session,$ut_syslen,$ut_host)=
unpack($template,$record);
if ($ut_line eq "system boot"){
print "rebooted ".scalar localtime($tv_sec)."\n";
$reboots++;
}
}
close(WTMP);
print "Total reboots: $reboots\n";
-------
#*
#* summarize the events in the NT/2000 System event log
#*
use Win32::EventLog;
my %event=('Length',NULL,
'RecordNumber',NULL,
'TimeGenerated',NULL,
'TimeWritten',NULL,
'EventID',NULL,
'EventType',NULL,
'Category',NULL,
'ClosingRecordNumber',NULL,
'Source',NULL,
'Computer',NULL,
'Strings',NULL,
'Data',NULL,);
# partial list of event types, i.e. Type 1 is "Error",
# 2 is "Warning", etc.
@types = ("","Error","Warning","","Information");
Win32::EventLog::Open($EventLog,'System','') or
die "Could not open System log:$^E\n";
$EventLog->Win32::EventLog::GetNumber($numevents);
$EventLog->Win32::EventLog::GetOldest($oldestevent);
$EventLog->Win32::EventLog::Read((EVENTLOG_SEEK_READ |
EVENTLOG_FORWARDS_READ),
$numevents + $oldestevent, $event);
# loop through all of the events, recording the number of
# Source and EventTypes
for ($i=0;$i<$numevents;$i++) {
$EventLog->Read((EVENTLOG_SEQUENTIAL_READ |
EVENTLOG_FORWARDS_READ),
0, $event);
$source{$event->{Source}}++;
$types{$event->{EventType}}++;
}
# now print out the totals
print "-->Event Log Source Totals:\n";
for (sort keys %source) {
print "$_: $source{$_}\n";
}
print "-"x30,"\n";
print "-->Event Log Type Totals:\n";
for (sort keys %types) {
print "$types[$_]: $types{$_}\n";
}
print "-"x30,"\n";
print "Total number of events: $numevents\n";
-------
#*
#* summarize events in an NT/2000 System event log using an external program
#*
eldump = 'c:\bin\eldump'; # path to ElDump
# output data field separated by ~ and without full message
# text (faster)
$dumpflags = '-l system -c ~ -M';
open(ELDUMP,"$eldump $dumpflags|") or die "Unable to run $eldump:$!\n";
print STDERR "Reading system log.";
while(<ELDUMP>){ ($date,$time,$source,$type,$category,$event,$user,$computer) =
split('~');
$$type{$source}++;
print STDERR ".";
}
print STDERR "done.\n";
close(ELDUMP);
# for each type of event, print out the sources and number of
# events per source
foreach $type (qw(Error Warning Information
AuditSuccess AuditFailure)){
print "-" x 65,"\n";
print uc($type)."s by source:\n";
for (sort keys %$type){
print "$_ ($$type{$_})\n";
}
}
print "-" x 65,"\n";
-------
#*
#* breach finder for SunOS 4.1.x - show all contacts from a particular
#* host involved in a security breach
#*
$template = "A8 A8 A16 l"; # for SunOS 4.1.x
$recordsize = length(pack($template,()));
($user,$ignore) = @ARGV;
print "-- scanning for first host contacts from $user --\n";
open(WTMP,"/var/adm/wtmp") or die "Unable to open wtmp:$!\n";
while (read(WTMP,$record,$recordsize)) {
($tty,$name,$host,$time)=unpack($template,$record);
if ($user eq $name){
next if (defined $ignore and $host =~ /$ignore/o);
if (length($host) > 2 and !exists $contacts{$host}){
$connect = localtime($time);
$contacts{$host}=$time;
write;
}
}
}
print "-- scanning for other contacts from those hosts --\n";
die "Unable to seek to beginning of wtmp:$!\n"
unless (seek(WTMP,0,0));
while (read(WTMP,$record,$recordsize)) {
($tty,$name,$host,$time)=unpack($template,$record);
# if it is not a logout, and we're looking for this host,
# and this is a connection from a user *other* than the
# compromised account, then record
if (substr($name,1,1) ne "\0" andexists $contacts{$host} and
$name ne $user){
$connect = localtime($time);
write;
}
}
close(WTMP);
# here's the output format, may need to be adjusted based on template
format STDOUT =
@<<<<<<<< @<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<
$name,$host,$connect
.
-------
#*
#* show the top transfers from a WU-FTPD xferlog file
#*
$xferlog = "/var/adm/log/xferlog";
open(XFERLOG,$xferlog) or die "Unable to open $xferlog:$!\n";
while (<XFERLOG>){
$files{(split)[8]}++;
}
close(XFERLOG);
for (sort {$files{$b} <=> $files{$a}||$a cmp $b} keys %files){
print "$_:$files{$_}\n";
}
-------
#*
#* breach finder II (adding tcp wrapper log file scanning)
#*
$template = "A8 A8 A16 l"; # for SunOS 4.1.x
$recordsize = length(pack($template,()));
($user,$ignore) = @ARGV;
# tcpd log file location
$tcpdlog = "/var/log/tcpd/tcpdlog";
$hostlen = 16; # max length of hostname in wtmp file
print "-- scanning for first host contacts from $user --\n";
open(WTMP,"/var/adm/wtmp") or die "Unable to open wtmp:$!\n";
while (read(WTMP,$record,$recordsize)) {
($tty,$name,$host,$time)=unpack($template,$record);
if ($user eq $name){
next if (defined $ignore and $host =~ /$ignore/o);
if (length($host) > 2 and !exists $contacts{$host}){
$connect = localtime($time);
$contacts{$host}=$time;
write;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -