📄 psa-chapter09.txt
字号:
}
}
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
.
-------
print "-- scanning tcpdlog --\n";
open(TCPDLOG,$tcpdlog) or die "Unable to read $tcpdlog:$!\n";
while(<TCPDLOG>){
next if !/connect from /; # we only care about connections
($connecto,$connectfrom) = /(.+):\s+connect from\s+(.+)/;
$connectfrom =~ s/^.+@//;
# tcpwrappers can log the entire hostname, not just the first N
# characters like some wtmp logs. As a result, we need to truncate
# the hostname at the same place as the wtmp file if we want to
# perform a hash lookup below
$connectfrom = substr($connectfrom,0,$hostlen);
print if (exists $contacts{$connectfrom} and
$connectfrom !~ /$ignore/o);
}
-------
#*
#* correlate wu-ftpd log files with wtmp file to determine which transfers
#* happened when and from what machines
#*
# for date->UNIX time (secs from Epoch) conversion
use Time::Local;
$xferlog = "/var/log/xferlog"; # location of transfer log
$wtmp = "/var/adm/wtmp"; # location of wtmp
$template = "A8 A8 A16 l"; # SunOS 4.1.4 template for wtmp
$recordsize = length(pack($template,())); # size of each wtmp entry
$hostlen = 16; # max length of the hostname in wtmp
# month name to number mapping
%month = qw{Jan 0 Feb 1 Mar 2 Apr 3 May 4 Jun 5 Jul 6
Aug 7 Sep 8 Oct 9 Nov 10 Dec 11};
&ScanXferlog; # scan the transfer log
&ScanWtmp; # scan the wtmp log
&ShowTransfers; # correlate and print transfers
sub ScanXferlog {
local($sec,$min,$hours,$mday,$mon,$year);
my($time,$rhost,$fname,$direction);
print STDERR "Scanning $xferlog...";
open(XFERLOG,$xferlog) or
die "Unable to open $xferlog:$!\n";
while (<XFERLOG>){
# use an array slice to select the fields we want
($mon,$mday,$time,$year,$rhost,$fname,$direction) =
(split)[1,2,3,4,6,8,11];
# add the direction of transfer to the file name,
# i is "transferred in"
$fname = ($direction eq 'i' ? "-> " : "<- ") . $fname;
# convert the transfer time to UNIX epoch format
($hours,$min,$sec) = split(':',$time);
$unixdate = timelocal($sec,$min,$hours,$mday,$month{$mon},$year);
# put the data into a hash of lists of lists:
push(@{$transfers{substr($rhost,0,$hostlen)}},[$unixdate,$fname]);
}
close(XFERLOG);
print STDERR "done.\n";
}
# scans the wtmp file and populates the @sessions structure
# with ftp sessions
sub ScanWtmp {
my($record,$tty,$name,$host,$time,%connections);
print STDERR "Scanning $wtmp...\n";
open(WTMP,$wtmp) or die "Unable to open $wtmp:$!\n";
while (read(WTMP,$record,$recordsize)) {
# don't even bother to unpack if record does not begin
# with ftp. NOTE: this creates a wtmp format dependency
# as a trade-off for speed
next if (substr($record,0,3) ne "ftp");
($tty,$name,$host,$time)=unpack($template,$record);
# if we find an open connection record, then
# create a hash of list of lists. The LoL will be used
# as a stack below.
if ($name and substr($name,0,1) ne "\0"){
push(@{$connections{$tty}},[$host,$time]);
}
# if we find a close connection record, we try to pair
# it with a previous open connection record we recorded
# before
else {
unless (exists $connections{$tty}){
warn "found lone logout on $tty:" .
scalar localtime($time)."\n";
next;
}
# we'll use the previous open connect and this
# close connect to record this as a single session.
# To do that we create a list of lists where each
# list is (hostname, login, logout)
push(@sessions,[@{shift @{$connections{$tty}}},$time]);
# if no more connections on the stack for that
# tty, remove from hash
delete $connections{$tty} unless (@{$connections{$tty}});
}
}
close(WTMP);
print STDERR "done.\n";
}
# iterate over the session log, pairing sessions
# with transfers
sub ShowTransfers {
local($session);
foreach $session (@sessions){
# print session times
print scalar localtime($$session[1]) . "-" .
scalar localtime($$session[2]) .
" $$session[0]\n";
# find all files transferred in this connection triad
# and print them
print &FindFiles(@{$session}),"\n";
}
}
# returns all of the files transferred for a given connect session triad
sub FindFiles{
my($rhost,$login,$logout) = @_;
my($transfer,@found);
# easy case, no transfers in this login
unless (exists $transfers{$rhost}){
return "\t(no transfers in xferlog)\n";
}
# easy case, first transfer we have on record is
# after this login
if ($transfers{$rhost}->[0]->[0] > $logout){
return "\t(no transfers in xferlog)\n";
}
# find any files transferred in this session
foreach $transfer (@{$transfers{$rhost}}){
# if transfer happened before login
next if ($$transfer[0] < $login);
# if transfer happened after logout
last if ($$transfer[0] > $logout);
# if we've already used this entry
next unless (defined $$transfer[1]);
push(@found,"\t".$$transfer[1]."\n");
undef $$transfer[1];
}
($#found > -1 ? @found : "\t(no transfers in xferlog)\n")
}
-------
#*
#* show all deliveries in a sendmail mail log
#*
use SyslogScan::DeliveryIterator;
# a list of mail syslog files
$maillogs = ["/var/log/mail/maillog"];
$iterator = new SyslogScan::DeliveryIterator(syslogList => $maillogs);
while ($delivery = $iterator -> next()){
print $delivery->{Sender}." -> ".
join(",",@{$delivery->{ReceiverList}}),"\n";
}
-------
#*
#* summarize all deliveries in a sendmail mail log
#*
use SyslogScan::DeliveryIterator;
use SyslogScan::Summary;
use SyslogScan::ByGroup;
use SyslogScan::Usage;
# the location of our maillog
$maillogs = ["/var/log/mail/maillog"];
# get an iterator for this file
$iterator = new SyslogScan::DeliveryIterator(
syslogList => $maillogs);
# feed this iterator to ::Summary, receive a summary object
$summary = new SyslogScan::Summary($iterator);
# feed this summary object to ::ByGroup and receive a
# stats-by-group object
$bygroup = new SyslogScan::ByGroup($summary);
# print the contents of this object
foreach $group (sort keys %$bygroup){
($bmesg,$bbytes)=@{$bygroup->{$group}->
{groupUsage}->getBroadcastVolume()};
($smesg,$sbytes)=@{$bygroup->{$group}->
{groupUsage}->getSendVolume()};
($rmesg,$rbytes)=@{$bygroup->{$group}->
{groupUsage}->getReceiveVolume()};
($rmesg,$rbytes)=@{$bygroup->{$group}->
{groupUsage}->getReceiveVolume()};
write;
}
format STDOUT_TOP =
Name Bmesg BByytes Smesg SBytes Rmesg Rbytes
--------------------------- ----- -------- ------ -------- ------ -------
.
format STDOUT =
@<<<<<<<<<<<<<<<<< @>>>>> @>>>>>>> @>>>>> @>>>>>>> @>>>>> @>>>>>>>
$group,$bmesg,$bbytes,$smesg,$sbytes,$rmesg,$rbytes
.
-------
#*
#* popular DB_File database with contents of wtmp
#*
use DB_File;
use FreezeThaw qw(freeze thaw);
use Sys::Hostname; # to get the current host name
use Fcntl; # for the definition of O_CREAT and O_RDWR
# find the executable for the last program
(-x "/bin/last" and $lastex = "/bin/last") or
(-x "/usr/ucb/last" and $lastex = "/usr/ucb/last");
$userdb = "userdata"; # user database file
$connectdb = "connectdata"; # connection database file
$thishost = &hostname;
open(LAST,"$lastex|") or die "Can't run the program $lastex:$!\n";
# read each line of the output from "last"
while (<LAST>){
next if /^reboot\s/ or /^shutdown\s/ or
/^ftp\s/ or /^wtmp\s/;
($user,$tty,$host,$day,$mon,$date,$time) = split;
next if $tty =~ /^:0/ or $tty =~ /^console$/;
next if (length($host) < 4);
$when = $mon." ".$date." ".$time;
# save each record in a hash of list of lists
push(@{$users{$user}},[$thishost,$host,$when]);
push(@{$connects{$host}},[$thishost,$user,$when]);
}
close(LAST);
# tie to a database file, creating it (for Read & Write) if
# it does not exist see the footnote in the text re: $DB_BTREE
tie %userdb, "DB_File",$userdb,O_CREAT|O_RDWR, 0600, $DB_BTREE
or die "Unable to open $userdb database for r/w:$!\n";
# iterate through the users and store the info in our
# database using freeze
foreach $user (keys %users){
if (exists $userdb{$user}){
($userinfo) = thaw($userdb{$user});
push(@{$userinfo},@{$users{$user}});
$userdb{$user}=freeze $userinfo;
}
else {
$userdb{$user}=freeze $users{$user};
}
}
untie %userdb;
# do the same for the connections
tie %connectdb, "DB_File",$connectdb,O_CREAT|O_RDWR, 0600, $DB_BTREE
or die "Unable to open $connectdb database for r/w:$!\n";
foreach $connect (keys %connects){
if (exists $connectdb{$connect}){
($connectinfo) = thaw($connectdb{$connect});
push(@{$connectinfo},@{$connects{$connect}});
$connectdb{$connect}=freeze($connectinfo);
}
else {
$connectdb{$connect}=freeze($connects{$connect});
}
}
untie %connectdb;
-------
#*
#* breach finder III - using a database (of the sort created by the previous
#* example
#*
use DB_File;
use FreezeThaw qw(freeze thaw);
use Fcntl;
# accept the username and hosts to ignore as command line arguments
($user,$ignore) = @ARGV;
# database files we'll be using
$userdb ="userdata";
$connectdb ="connectdata";
tie %userdb, "DB_File",$userdb,O_RDONLY,666,$DB_BTREE
or die "Unable to open $userdb database for reading:$!\n";
tie %connectdb, "DB_File",$connectdb,O_RDONLY,666,$DB_BTREE
or die "Unable to open $connectdb database for reading:$!\n";
# we can exit if we've never seen a connect from this user
unless (exists $userdb{$user}){
print "No logins from that user.\n";
untie %userdb;
untie %connectdb;
exit;
}
($userinfo) = thaw($userdb{$user});
print "-- first host contacts from $user --\n";
foreach $contact (@{$userinfo}){
next if (defined $ignore and $contact->[1] =~ /$ignore/o);
print $contact->[1] . " -> " . $contact->[0] .
" on ".$contact->[2]."\n";
$otherhosts{$contact->[1]}='';
}
print "-- other connects from source machines --\n";
foreach $host (keys %otherhosts){
next if (defined $ignore and $host =~ /$ignore/o);
next unless (exists $connectdb{$host});
($connectinfo) = thaw($connectdb{$host});
foreach $connect (@{$connectinfo}){
next if (defined $ignore and $connect->[0] =~ /$ignore/o);
$userseen{$connect->[1]}='';
}
}
foreach $user (sort keys %userseen){
next unless (exists $userdb{$user});
($userinfo) = thaw($userdb{$user});
foreach $contact (@{$userinfo}){
next if (defined $ignore and $contact->[1] =~ /$ignore/o);
write if (exists $otherhosts{$contact->[1]});
}
}
untie %userdb;
untie %connectdb;
format STDOUT =
@<<<<<<<< @<<<<<<<<<<<<<<< -> @<<<<<<<<<<<<<<< on @<<<<<<<<<<<
$user.":",$contact->[1],$contact->[0],$contact->[2]
.
-------
#*
#* populating an SQL database with wtmp records using DBI
#*
use DBI;
use Sys::Hostname;
$db = "dnb"; # the name of the database we're using
# locate the last executable
(-x "/bin/last" and $lastex = "/bin/last") or
(-x "/usr/ucb/last" and $lastex = "/usr/ucb/last");
# connect to a Sybase database using user "dnb" and a password
# provided on the command line
$dbh = DBI->connect('dbi:Sybase:','dnb',$ARGV[0]);
die "Unable to connect: $DBI::errstr\n"
unless (defined $dbh);
# change to the database we'll be using
$dbh->do("use $db") or
die "Unable to change to $db: ".$dbh->errstr."\n";
# create the lastinfo table if it doesn't exist
unless ($dbh->selectrow_array(
q{SELECT name from sysobjects WHERE name="lastinfo"})){
$dbh->do(q{create table lastinfo (username char(8),
localhost char(40),
otherhost varchar(75),
when char(18))}) or
die "Unable to create lastinfo: ".$dbh->errstr."\n";
}
$thishost = &hostname;
$sth = $dbh->prepare(
qq{INSERT INTO lastinfo(username,localhost,otherhost,when)
VALUES (?,'$thishost', ?, ?)}) or
die "Unable to prepare insert: ".$dbh->errstr."\n";
open(LAST,"$lastex|") or die "Can't run the program $lastex:$!\n";
while (<LAST>){
next if /^reboot\s/ or /^shutdown\s/ or
/^ftp\s/ or /^wtmp\s/;
($user,$tty,$host,$day,$mon,$date,$time) = split;
next if $tty =~ /^:0/ or $tty =~ /^console$/;
next if (length($host) < 4);
$when = $mon." ".$date." ".$time;
$sth->execute($user,$host,$when);
}
close(LAST);
$dbh->disconnect;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -