Files
sshbruted/sshbruted.rb
T
2006-04-22 04:13:19 +00:00

127 lines
3.2 KiB
Ruby

#!/usr/bin/env ruby
# Copyright (c) 2006, Stephen Waits <steve@waits.net>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# Neither the name of the Waits Consulting, Inc. nor the names of
# its contributors may be used to endorse or promote products
# derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Script to blacklist idiots brute forcing sshd.
#
# By: Stephen Waits <steve@waits.net>
#
# 1. Build sshd with TCP Wrapper support.
#
# 2. Add a line like "ALL : /etc/hosts.blacklist : deny"
# to your /etc/hosts.allow
#
# 3. Run this script with appropriate rights
#
require 'time'
require 'thread'
LOG_COMMAND = "/usr/bin/tail -F /var/log/auth.log"
BLACKLIST = "/etc/hosts.blacklist"
MAX_FAILURES = 2
TIME_LIMIT = 30 * 60 # 30 minutes
class Blacklister
def initialize
@iplog = Hash.new([])
@iplog_lock = Mutex.new
end
def add_hit(ip,time)
@iplog_lock.synchronize do
@iplog[ip] += [time]
end
end
def list
badlist = []
@iplog_lock.synchronize do
# clean up stale entries
@iplog.reject! { |ip,times| stale_entry(times) }
# create our blacklist
@iplog.each { |ip,times| badlist << ip if fresh_count(times) >= MAX_FAILURES }
end
badlist.sort
end
private
def fresh_count(times)
fresh_times = times.select { |t| (Time.now - t) < TIME_LIMIT }
fresh_times.size
end
def stale_entry(times)
fresh_count(times) == 0
end
end
blacklister = Blacklister.new
input_thread = Thread.new do
regexp = /(.+) [^\s]+ sshd\[\d+\]: Failed ([^\s]+) for ([\w\s]+?) from ([\d\.]+) port (\d+) ssh2/
logfile = IO.popen(LOG_COMMAND)
while line = logfile.gets
match = regexp.match(line)
if match
time = Time.parse(match[1])
ip = match[4]
blacklister.add_hit(ip,time)
end
end
end
output_thread = Thread.new do
list = []
loop do
sleep 1
new_list = blacklister.list.join("\n")
if new_list != list
File.open(BLACKLIST,"w") { |f| f.puts(new_list) }
#puts "[#{Time.now}]\n#{new_list}"
list = new_list
end
end
end
input_thread.join