星期二, 12月 07, 2010

防堵暴力 try ssh 密碼

由於有太多的小白喜歡try別人的電腦,所謂的try是指隨便猜一個帳號及密碼,一直試,總有試出來的時候。這種情況太比較大型的網站有效,因為有太多的帳號密碼啦。至於我們實驗室的話,沒幾個帳號,是不會那麼容易被try到。不過既然這是一個問題,那麼能的話還是做些防堵的工作才好。首先當然是要把 root 的帳號登入的權限關掉,因為程式第一個try的一定是root的密碼啦。關掉的方法是把/etc/ssh/sshd_config 裏的 PermitRootLogin yes
改為
PermitRootLogin no
之後重新啟動ssh。
再來是當每次有人用ssh連線時,啟動電腦來執行一個程式,這個程式會檢查/var/log/messages 的檔案,用連線進來的ip及 Faile password為關鍵字進行篩選,如果在一個小時內有錯過10次的連線的話,就叫防火牆把它的ip檔住,不再讓它連啦。
上面的文字包含二個重點,一個是如何在有使用者用ssh連線時去執行某程式;另一個是如何檢查log檔,看該使用者是否有try超過10次錯誤。
第一個問題的答案在/etc/hosts.allow 這個檔案裏有解。
在這個檔案裏加一行
sshd : ALL : spawn (/root/bin/block_ssh.pl %c %d)
第一個是服務名稱,第二個是來源,第三個則是執行的工作,其中spawn的意思是執行一個shell 命令。 %c %d 則是hosts.allow 裏的關鍵字,除了%c %d 以外還有很多可以試,請自行 man hosts.allow 來看。
%c 指的是連線進來的電腦的電腦名稱或ip。
%d 是指透過啥服務進來。 (不過前面寫了sshd,後面當然一定會是sshd,不過如果前面是ALL,那就有用啦。)
第二個問題的答案就太美妙啦,感謝vixual的無私貢獻以又酷學園裏的大大提出bug 修正,所以就有現成可以用的程式啦,是用perl寫的,其實概念不是太難。程式內容如下,把它存成 block_ssh.pl,所有者為root,存放位置隨便,當然啦,hosts.allow裏的spawn要隨之更改,這裏是放在 /root/bin/裏面,並且讓它以有可以執行的權限。
#!/usr/bin/perl

# 及時封鎖使用 "暴入法" 入侵的使用者(SSH)
# 技術支援: http://www.vixual.net/
# 原著者是 vixual (應該是吧!)
# chmod 755 block_ssh.pl
# 編輯 /etc/hosts.allow,加入
# sshd : ALL : spawn (/root/bin/block_ssh.pl %c %d)
# 這樣就完成了,如有必要,請自行修改 block_ssh.pl 裡相關的參數。
# 以上取自原作者的網站
# ==相關參數==

#記錄 ssh 連線的 LOG 檔,預設: /var/log/secure
$log_file = "/var/log/secure";

#於多久的時間內嘗試登入(秒),預設: 1 小小時
$time_range = 1 * 60 * 60;

#於 $time_range 所設定的時間內,嘗試登入失敗多少次立即封鎖 IP,預設: 10 次
$drop_count = 10;

#寄件通知,預設收件者: root@localhost
$mail = 'cmd@www.lapom.no-ip.org';

#寄件程式的位置
$sendmail = "/usr/sbin/sendmail";

#====

use Time::Local;

$ip = $ARGV[0];
$daemon = $ARGV[1];
$count = 0;
$ip =~ s/::ffff://;
%month = (
    Jan    =>    0,
    Feb    =>    1,
    Mar    =>    2,
    Apr    =>    3,
    May    =>    4,
    Jun    =>    5,
    Jul    =>    6,
    Aug    =>    7,
    Sep    =>    8,
    Oct    =>    9,
    Nov    =>    10,
    Dec    =>    11
);

$time = time();
($second,$minute,$hour,$day,$month,$year) = localtime($time);

#取得登入失敗的 logg
@list = `cat $log_file | grep "sshd.*Failed password.*$ip "`;
for(my $i = $#list; $i >= 0; $i--){
    #取得 log 的時間
    my($log_month,$log_day,$log_time) = split(/ +/,$list[$i]);
    my($log_hour,$log_minute,$log_second) = split(/:/,$log_time);
    #前一年的記錄
    if($log_month > $month){
        $log_year = $year - 1;
    }else{
        $log_year = $year;
    }
    #將時間轉為秒數
    $log_time = timelocal($log_second,$log_minute,$log_hour,$log_day,$month{$log_month},$log_year);
    if($time < $log_time + $time_range ){
        $count++;
    }else{
        last;
    }
}

if($count > $drop_count){
    #封鎖 IP
    `iptables -I INPUT -p tcp -s $ip --dport 22 -j DROP`;
    if($mail){
        #寄件通知
        $hostname = `hostname`;
        $month++;
        $year += 1900;
        chomp($hostname);
        open(MAIL, "| $sendmail -t") || die "Can't open $sendmail !\n";
        print MAIL qq|To: $mail\n|;
        print MAIL qq|Subject: [$hostname]封鎖 $ip\n|;
        print MAIL qq|Content-Transfer-Encoding: 8bit\n|;
        print MAIL qq|Content-type: text/plain\; charset=Big5\n\n|;
        print MAIL "\n時間: $year-$month-$day $hour:$minute:$second\n----\n使用者 \"$ip\" 嘗試以 SSH 登入伺服器,共失敗 $count 次,已於防火牆封鎖該 IP。\n\n";
        print MAIL @list;
        close(MAIL);
    }
}

exit;

沒有留言: