使用 perl 计算日志文件中变量组合的数量

Count the number of variable combinations in a logfile using perl

我有这个日志文件

 New connection: 141.8.83.213:64400 (172.17.0.6:2222) [session: e696835c]
    2016-04-29 21:13:59+0000 [SSHService ssh-userauth on HoneyPotTransport,3,141.8.83.213] login attempt [user1/test123] failed
    2016-04-29 21:14:10+0000 [SSHService ssh-userauth on HoneyPotTransport,3,141.8.83.213] login attempt [user1/test1234] failed
    2016-04-29 21:14:13+0000 [SSHService ssh-userauth on HoneyPotTransport,3,141.8.83.213] login attempt [user1/test123] failed

我想输出文件这样的结果:

Port,Status,Occurrences
64400,failed,2
64400,failed,1

"Occurrences" 变量将表示已记录在文件中的登录详细信息[用户名和密码] 组合的次数。 User1 test123可以看到同一个IP记录了两次。我怎样才能做到这一点?我现在有两个 while 循环,在第一个 while 循环中调用了一个子程序,如下所示:

子程序

sub counter(){

        $result = 0;
        #open(FILE2, $cowrie) or die "Can't open '$cowrie': $!";
        while(my $otherlines = <LOG2>){

                if($otherlines =~ /login attempt/){
                        ($user, $password) = (split /[\s:\[\]\/]+/, $otherlines)[-3,-2];
                        if($_[1] =~ /$user/ && $_[2] =~ /$password/){
                                $result++;
                        }#if ip matches i think i have to do this with split

                        #print "TEST\n";
                }
        #print "Combo $_[0] and $_[1]\n";

        }
        #print "$result";
        return $result;
}

主要方法

sub cowrieExtractor(){

        open(FILE2, $cowrie) or die "Can't open '$cowrie': $!";

        open(LOG2, $path2) or die "Can't open '$path2': $!";

        $seperator = chr(42);
        #To output user and password of login attempt, set $ip variable to the contents of array at that x position of new
        #connection to match the ip of the login attempt
        print FILE2 "SourcePort"."$seperator".
        "Status"."$seperator"."Occurences"."$seperator"."Malicious"."\n";

        $ip = "";
        $port = "";
        $usr = "";
        $pass = "";
        $status = "";
        $frequency = 0;

        #Given this is a user/pass attempt honeypot logger, I will use a wide character to reduce the possibility of stopping
        #the WEKA CSV loader from functioning by using smileyface as seperators.


        while(my $lines = <LOG2>){

                if($lines =~ /New connection/){

                ($ip, $port) = (split /[\[\]\s:()]+/, $lines)[7,8];

                }
                if($lines =~ /login attempt/){#and the ip of the new connection
if($lines =~ /$ip/){
                ($usr, $pass, $status) = (split /[\s:\[\]\/]+/, $lines)[-3,-2,-1];

                        $frequency = counter($ip, $usr, $pass);

                        #print $frequency;
                        if($ip && $port && $usr && $pass && $status ne ""){
                                print FILE2 join "$seperator",($port, $status, $frequency, $end);
                                print FILE2 "\n";
                        }
                }


                }
        }


}

现在在 Occurrences 下的输出中,我得到一个 0,当我测试时,它似乎来自我在子例程中初始化变量 $result 的内容。即 0;这意味着子例程中的 if 语句无法正常工作。有帮助吗?

这是获得预期输出的基本方法。关于上下文(目的)的问题仍然存在。

use warnings;
use strict;

my $file = 'logfile.txt';
open my $fh_in, '<', $file;

# Assemble results for required output in data structure:
# %rept = { $port => { $usr => { $status => $freq } };

my %rept;
my ($ip, $port);

while (my $line = <$fh_in>) 
{
    if ($line =~ /New connection/) {
        ($ip, $port) = $line =~ /New connection:\s+([^:]+):(\d+)/;
        next;
    }   

    my ($usr, $status) =  $line =~ m/login\ attempt \s+ \[ ( [^\]]+ ) \] \s+ (\w+)/x;
    if ($usr and $status) {
        $rept{$port}{$usr}{$status}++;
    }   
    else { warn "Line with an unexpected format:\n$line" }
}

# use Data::Dumper;
# print Dumper \%rept;

print "Port,Status,Occurences\n";
foreach my $port (sort keys %rept) {
    foreach my $usr (sort keys %{$rept{$port}}) {
        foreach my $stat ( sort keys %{$rept{$port}{$usr}} ) { 
            print "$port,$stat,$rept{$port}{$usr}{$stat}\n"; 
        }   
    }   

}

将您的输入复制到文件中 logfile.txt 这会打印

Port,Status,Occurences
64400,failed,2
64400,failed,1

我拿整个user1/test123(等等)来识别用户。这可以根据需要在正则表达式中更改。 请注意,这将不允许您以非常不同的方式查询或组织数据,它主要提取所需输出所需的内容。如果需要解释,请告诉我。


上面使用的嵌套哈希的介绍性解释

首先,我强烈建议您好好阅读一些可用的资料。 Perl 的标准教程肯定是一个好的开始 参考文献,以及各种食谱 在 Perl data structures.

用于收集数据的散列有键是端口号,每个键都有 其值是一个 哈希引用 (或者更确切地说,一个 匿名哈希 )。这些中的每一个 散列具有作为用户的键,其值再次具有散列引用。 这些键是状态的可能值,因此有两个键(失败 并成功)。它们的值是频率。这种'nesting'是复合体 数据结构。还有一件很重要的事。第一时间声明 $rept{$port}{$usr}{$status}++ 可见整个层次结构已创建。所以关键 $port 不需要预先存在。重要的是,这个自动激活 即使仅查询结构的值也会发生(除非它实际存在 已经)。

第一次迭代后,哈希为

%rept = { '64400' => { 'user1/test123' => { 'failed' => 1 } } }

在第二次迭代中看到相同的端口但是一个新用户,因此新数据被添加到二级匿名散列中。创建新用户的密钥,其值为(新)匿名散列,status => count。整个哈希是:

%rept = { 
    '64400' => { 
        'user1/test123'  => { 'failed' => 1 },
        'user1/test1234' => { 'failed' => 1 },
    } 
}

在下一次迭代中看到相同的端口和一个已经存在的用户,并且 因为它发生在状态(失败)也存在。因此,计数 状态递增。

可以很容易地看到整个结构,例如, Data::Dumper 包。 上面代码中注释掉的行将产生

$VAR1 = {
    '64400' => {
        'user1/test123' => {
                                'failed' => 2
                           },
        'user1/test1234' => {
                                'failed' => 1
                            }
                }
        };

随着我们继续处理行,根据需要添加新键(端口、用户、状态),整个层次结构向下计数(第一次为 1),或者,如果遇到现有的,则其计数为递增。例如,可以如代码中所示遍历和使用生成的数据结构。另请参阅大量文档以获取更多相关信息。