mirror of https://github.com/pragma-/pbot.git
Improve ban evasion detection
This commit is contained in:
parent
416115f12f
commit
d170f75924
|
@ -143,6 +143,10 @@ sub get_flood_account {
|
||||||
$self->{pbot}->logger->log("anti-flood: [get-account] $nick!$user\@$host linked to $mask\n");
|
$self->{pbot}->logger->log("anti-flood: [get-account] $nick!$user\@$host linked to $mask\n");
|
||||||
$self->{message_history}->{"$nick!$user\@$host"} = $self->{message_history}->{$mask};
|
$self->{message_history}->{"$nick!$user\@$host"} = $self->{message_history}->{$mask};
|
||||||
|
|
||||||
|
foreach my $channel (keys %{ $self->{message_history}->{$mask}->{channels} }) {
|
||||||
|
$self->{message_history}->{$mask}->{channels}->{$channel}{validated} = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if(defined $self->{message_history}->{$mask}->{nickserv_account}) {
|
if(defined $self->{message_history}->{$mask}->{nickserv_account}) {
|
||||||
$self->check_nickserv_accounts($nick, $self->{message_history}->{$mask}->{nickserv_account});
|
$self->check_nickserv_accounts($nick, $self->{message_history}->{$mask}->{nickserv_account});
|
||||||
}
|
}
|
||||||
|
@ -276,6 +280,11 @@ sub check_flood {
|
||||||
$max_messages = $self->{pbot}->{MAX_NICK_MESSAGES};
|
$max_messages = $self->{pbot}->{MAX_NICK_MESSAGES};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# check for ban evasion if channel begins with # (not private message) and hasn't yet been validated against ban evasion
|
||||||
|
if($channel =~ m/^#/ and not $self->message_history->{$account}->{channels}->{$channel}{validated}) {
|
||||||
|
$self->check_bans($account, $channel);
|
||||||
|
}
|
||||||
|
|
||||||
if($max_messages > 0 and $length >= $max_messages) {
|
if($max_messages > 0 and $length >= $max_messages) {
|
||||||
# $self->{pbot}->logger->log("More than $max_messages messages, comparing time differences ($max_time)\n") if $mode == $self->{FLOOD_JOIN};
|
# $self->{pbot}->logger->log("More than $max_messages messages, comparing time differences ($max_time)\n") if $mode == $self->{FLOOD_JOIN};
|
||||||
|
|
||||||
|
@ -401,12 +410,7 @@ sub prune_message_history {
|
||||||
}
|
}
|
||||||
|
|
||||||
# delete account for this $mask if all its channels have been deleted
|
# delete account for this $mask if all its channels have been deleted
|
||||||
my $count = 0;
|
if(scalar keys %{ $self->{message_history}->{$mask} } == 0) {
|
||||||
foreach my $channel (keys %{ $self->{message_history}->{$mask} }) {
|
|
||||||
$count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($count == 0) {
|
|
||||||
$self->{pbot}->logger->log("$mask has no more channels remaining; deleting history account.\n");
|
$self->{pbot}->logger->log("$mask has no more channels remaining; deleting history account.\n");
|
||||||
delete $self->{message_history}->{$mask};
|
delete $self->{message_history}->{$mask};
|
||||||
}
|
}
|
||||||
|
@ -432,9 +436,15 @@ sub unbanme {
|
||||||
return "/msg $nick There is no temporary ban set for $mask in channel $channel.";
|
return "/msg $nick There is no temporary ban set for $mask in channel $channel.";
|
||||||
}
|
}
|
||||||
|
|
||||||
my $baninfo = $self->{pbot}->bantracker->get_baninfo("$nick!$user\@$host");
|
my $nickserv_account;
|
||||||
|
if(exists $self->{message_history}->{"$nick!$user\@$host"}->{nickserv_account}) {
|
||||||
|
$nickserv_account = $self->{message_history}->{"$nick!$user\@$host"}->{nickserv_account};
|
||||||
|
}
|
||||||
|
|
||||||
if(defined $baninfo) {
|
my $baninfos = $self->{pbot}->bantracker->get_baninfo("$nick!$user\@$host", $channel, $nickserv_account);
|
||||||
|
|
||||||
|
if(defined $baninfos) {
|
||||||
|
foreach my $baninfo (@$baninfos) {
|
||||||
if($self->ban_whitelisted($baninfo->{channel}, $baninfo->{banmask})) {
|
if($self->ban_whitelisted($baninfo->{channel}, $baninfo->{banmask})) {
|
||||||
$self->{pbot}->logger->log("anti-flood: [unbanme] $nick!$user\@$host banned as $baninfo->{banmask} in $baninfo->{channel}, but allowed through whitelist\n");
|
$self->{pbot}->logger->log("anti-flood: [unbanme] $nick!$user\@$host banned as $baninfo->{banmask} in $baninfo->{channel}, but allowed through whitelist\n");
|
||||||
} else {
|
} else {
|
||||||
|
@ -445,6 +455,7 @@ sub unbanme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
my $account = $self->get_flood_account($nick, $user, $host);
|
my $account = $self->get_flood_account($nick, $user, $host);
|
||||||
if(defined $account and $self->message_history->{$account}->{channels}->{$channel}{offenses} > 2) {
|
if(defined $account and $self->message_history->{$account}->{channels}->{$channel}{offenses} > 2) {
|
||||||
|
@ -477,21 +488,54 @@ sub address_to_mask {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_bans {
|
sub check_bans {
|
||||||
my ($self, $bans, $mask) = @_;
|
my ($self, $mask, $channel) = @_;
|
||||||
|
my ($bans, $nickserv_account, $host);
|
||||||
|
|
||||||
$self->{pbot}->logger->log("anti-flood: [check-bans] checking for bans on ($mask)\n");
|
if(exists $self->{message_history}->{$mask}->{nickserv_account}) {
|
||||||
|
$nickserv_account = $self->{message_history}->{$mask}->{nickserv_account};
|
||||||
|
}
|
||||||
|
|
||||||
my $baninfo = $self->{pbot}->bantracker->get_baninfo($mask);
|
($host) = $mask =~ m/\@(.*)$/;
|
||||||
|
|
||||||
if(defined $baninfo) {
|
foreach my $account (keys %{ $self->{message_history} }) {
|
||||||
|
if(exists $self->{message_history}->{$account}->{channels}->{$channel}) {
|
||||||
|
my $check_ban = 0;
|
||||||
|
|
||||||
|
# check if nickserv accounts match
|
||||||
|
if(defined $nickserv_account) {
|
||||||
|
if(exists $self->{message_history}->{$account}->{nickserv_account} and $self->{message_history}->{$account}->{nickserv_account} eq $nickserv_account) {
|
||||||
|
$self->{pbot}->logger->log("anti-flood: [check-bans] nickserv account for $account matches $nickserv_account\n");
|
||||||
|
$check_ban = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# check if hosts match
|
||||||
|
my ($account_host) = $account =~ m/\@(.*)$/;
|
||||||
|
my $target_nickserv_account;
|
||||||
|
|
||||||
|
if($host eq $account_host) {
|
||||||
|
$self->{pbot}->logger->log("anti-flood: [check-bans] host for $account matches $mask\n");
|
||||||
|
|
||||||
|
if(exists $self->{message_history}->{$account}->{nickserv_account}) {
|
||||||
|
$target_nickserv_account = $self->{message_history}->{$account}->{nickserv_account};
|
||||||
|
}
|
||||||
|
$check_ban = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($check_ban) {
|
||||||
|
$self->{pbot}->logger->log("anti-flood: [check-bans] checking for bans in $channel on $account\n");
|
||||||
|
my $baninfos = $self->{pbot}->bantracker->get_baninfo($account, $channel, $target_nickserv_account);
|
||||||
|
|
||||||
|
if(defined $baninfos) {
|
||||||
|
foreach my $baninfo (@$baninfos) {
|
||||||
if($self->ban_whitelisted($baninfo->{channel}, $baninfo->{banmask})) {
|
if($self->ban_whitelisted($baninfo->{channel}, $baninfo->{banmask})) {
|
||||||
$self->{pbot}->logger->log("anti-flood: [check-bans] $mask evaded $baninfo->{banmask} in $baninfo->{channel}, but allowed through whitelist\n");
|
$self->{pbot}->logger->log("anti-flood: [check-bans] $mask evaded $baninfo->{banmask} in $baninfo->{channel}, but allowed through whitelist\n");
|
||||||
return undef;
|
next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($baninfo->{type} eq '+b' and $baninfo->{banmask} =~ m/!\*@\*$/) {
|
if($baninfo->{type} eq '+b' and $baninfo->{banmask} =~ m/!\*@\*$/) {
|
||||||
$self->{pbot}->logger->log("anti-flood: [check-bans] Disregarding generic nick ban\n");
|
$self->{pbot}->logger->log("anti-flood: [check-bans] Disregarding generic nick ban\n");
|
||||||
return undef;
|
next;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $banmask_regex = quotemeta $baninfo->{banmask};
|
my $banmask_regex = quotemeta $baninfo->{banmask};
|
||||||
|
@ -500,27 +544,45 @@ sub check_bans {
|
||||||
|
|
||||||
if($baninfo->{type} eq '+q' and $mask =~ /^$banmask_regex$/i) {
|
if($baninfo->{type} eq '+q' and $mask =~ /^$banmask_regex$/i) {
|
||||||
$self->{pbot}->logger->log("anti-flood: [check-bans] Hostmask ($mask) matches quiet banmask ($banmask_regex), disregarding\n");
|
$self->{pbot}->logger->log("anti-flood: [check-bans] Hostmask ($mask) matches quiet banmask ($banmask_regex), disregarding\n");
|
||||||
return undef;
|
next;
|
||||||
}
|
}
|
||||||
|
|
||||||
push @$bans, $baninfo;
|
if(not defined $bans) {
|
||||||
return $baninfo;
|
$bans = [];
|
||||||
}
|
}
|
||||||
return undef;
|
|
||||||
|
$self->{pbot}->logger->log("anti-flood: [check-bans] Hostmask ($mask) matches $baninfo->{type} $baninfo->{banmask}, adding ban\n");
|
||||||
|
push @$bans, $baninfo;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(defined $bans) {
|
||||||
|
$mask =~ m/[^!]+\!(.*)/;
|
||||||
|
my $banmask = "*!$1";
|
||||||
|
|
||||||
|
foreach my $baninfo (@$bans) {
|
||||||
|
$self->{pbot}->logger->log("anti-flood: [check-bans] $mask evaded $baninfo->{banmask} banned in $baninfo->{channel} by $baninfo->{owner}, banning $banmask\n");
|
||||||
|
$self->{pbot}->chanops->ban_user_timed($banmask, $baninfo->{channel}, 60 * 60 * 5);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->message_history->{$mask}->{channels}->{$channel}{validated} = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_nickserv_accounts {
|
sub check_nickserv_accounts {
|
||||||
my ($self, $nick, $account) = @_;
|
my ($self, $nick, $account) = @_;
|
||||||
|
|
||||||
my ($account_mask, @bans);
|
|
||||||
|
|
||||||
foreach my $mask (keys %{ $self->{message_history} }) {
|
foreach my $mask (keys %{ $self->{message_history} }) {
|
||||||
if(exists $self->{message_history}->{$mask}->{nickserv_account}) {
|
if(exists $self->{message_history}->{$mask}->{nickserv_account}) {
|
||||||
# has nickserv account
|
# has nickserv account
|
||||||
if(lc $self->{message_history}->{$mask}->{nickserv_account} eq lc $account) {
|
if(lc $self->{message_history}->{$mask}->{nickserv_account} eq lc $account) {
|
||||||
# pre-existing mask found using this account previously, check for bans
|
# pre-existing mask found using this account previously
|
||||||
$self->{pbot}->logger->log("anti-flood: [check-account] $nick [nickserv: $account] seen previously as $mask.\n");
|
$self->{pbot}->logger->log("anti-flood: [check-account] $nick [nickserv: $account] seen previously as $mask.\n");
|
||||||
$self->check_bans(\@bans, $mask);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -529,24 +591,9 @@ sub check_nickserv_accounts {
|
||||||
# nick matches, must belong to account
|
# nick matches, must belong to account
|
||||||
$self->{pbot}->logger->log("anti-flood: $mask: setting nickserv account to [$account]\n");
|
$self->{pbot}->logger->log("anti-flood: $mask: setting nickserv account to [$account]\n");
|
||||||
$self->message_history->{$mask}->{nickserv_account} = $account;
|
$self->message_history->{$mask}->{nickserv_account} = $account;
|
||||||
|
|
||||||
$account_mask = $mask;
|
|
||||||
$self->check_bans(\@bans, $mask);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach my $baninfo (@bans) {
|
|
||||||
$self->{pbot}->logger->log("anti-flood: [check-bans] $account_mask may have evaded $baninfo->{banmask} banned in $baninfo->{channel} by $baninfo->{owner}\n");
|
|
||||||
#$self->{pbot}->conn->privmsg($nick, "You have been banned in $baninfo->{channel} for attempting to evade a ban on $baninfo->{banmask} set by $baninfo->{owner}");
|
|
||||||
|
|
||||||
$account_mask =~ m/[^!]+\!(.*)/;
|
|
||||||
my $banmask = "*!$1";
|
|
||||||
|
|
||||||
$self->{pbot}->logger->log("anti-flood: [check-bans] Ban detected on account $account in $baninfo->{channel}, banning $banmask.\n");
|
|
||||||
|
|
||||||
$self->{pbot}->chanops->ban_user_timed($banmask, $baninfo->{channel}, 60 * 60 * 5);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub on_whoisaccount {
|
sub on_whoisaccount {
|
||||||
|
|
|
@ -61,9 +61,9 @@ sub get_banlist {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub get_baninfo {
|
sub get_baninfo {
|
||||||
my ($self, $mask) = @_;
|
my ($self, $mask, $channel, $account) = @_;
|
||||||
|
my ($bans, $ban_account);
|
||||||
|
|
||||||
foreach my $channel (keys %{ $self->{banlist} }) {
|
|
||||||
foreach my $mode (keys %{ $self->{banlist}{$channel} }) {
|
foreach my $mode (keys %{ $self->{banlist}{$channel} }) {
|
||||||
foreach my $banmask (keys %{ $self->{banlist}{$channel}{$mode} }) {
|
foreach my $banmask (keys %{ $self->{banlist}{$channel}{$mode} }) {
|
||||||
my $banmask_key = $banmask;
|
my $banmask_key = $banmask;
|
||||||
|
@ -72,7 +72,17 @@ sub get_baninfo {
|
||||||
$banmask =~ s/\\\*/.*?/g;
|
$banmask =~ s/\\\*/.*?/g;
|
||||||
$banmask =~ s/\\\?/./g;
|
$banmask =~ s/\\\?/./g;
|
||||||
|
|
||||||
if($mask =~ m/^$banmask$/i) {
|
if($banmask =~ m/^\$a:(.*)/) {
|
||||||
|
$ban_account = lc $1;
|
||||||
|
} else {
|
||||||
|
$ban_account = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if((defined $account and $account eq $ban_account) or $mask =~ m/^$banmask$/i) {
|
||||||
|
if(not defined $bans) {
|
||||||
|
$bans = [];
|
||||||
|
}
|
||||||
|
|
||||||
my $baninfo = {};
|
my $baninfo = {};
|
||||||
$baninfo->{banmask} = $banmask_key;
|
$baninfo->{banmask} = $banmask_key;
|
||||||
$baninfo->{channel} = $channel;
|
$baninfo->{channel} = $channel;
|
||||||
|
@ -81,13 +91,12 @@ sub get_baninfo {
|
||||||
$baninfo->{type} = $mode;
|
$baninfo->{type} = $mode;
|
||||||
$self->{pbot}->logger->log("get-baninfo: dump: " . Dumper($baninfo) . "\n");
|
$self->{pbot}->logger->log("get-baninfo: dump: " . Dumper($baninfo) . "\n");
|
||||||
|
|
||||||
return $baninfo;
|
push @$bans, $baninfo;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return undef;
|
return $bans;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub on_quietlist_entry {
|
sub on_quietlist_entry {
|
||||||
|
|
|
@ -13,8 +13,8 @@ use warnings;
|
||||||
# These are set automatically by the build/commit script
|
# These are set automatically by the build/commit script
|
||||||
use constant {
|
use constant {
|
||||||
BUILD_NAME => "PBot",
|
BUILD_NAME => "PBot",
|
||||||
BUILD_REVISION => 412,
|
BUILD_REVISION => 413,
|
||||||
BUILD_DATE => "2013-07-24",
|
BUILD_DATE => "2013-07-28",
|
||||||
};
|
};
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
Loading…
Reference in New Issue