Automatically Report all SSH Brute Force Attacks to ISPs <-- ???? :(
Automatically Report all SSH Brute Force Attacks to ISPs <-- ???? :(
Update 4th July: This plugin has now been in operation for
about 12 hours and has sent a few notification emails overnight. So far
I have had four replies from system admins confirming that these
machines have been compromised. These machines have since been removed
from the Internet.
SSH Brute Force Attacks on the Increase
The last few months and particularly in the last few days (beginning of July) saw a marked increase in global SSH brute force attacks.
What is SSH?
SSH is a common and secure method to remotely connect to Linux/Unix servers, be this for managing or uploading content (via SCP/SFTP for example).
Generally, system administrators configure a SSHD server to only allow a limited access by either disallowing certain users to logon (i.e. root) or by restricting from where remote connections can be made.
The Problem.
Sadly, there exists many Linux/Unix boxes with poorly configured SSHD servers. These typically have username/password combinations of test/test, web/web, john/john and so on.
It is these servers that are targeted by botnet operators. Once such a server is found, it is often compromised and itself becomes a zombie in the botnet. These servers, in turn, then go on and attempt to compromise other servers on the internet.
The Real Problem
It is fairly easy to hack proof your server. Brute force attacks can also be thwarted by running DenyHosts on your server. DenyHosts monitors all SSHD hack attempts and blacklists the attacker. DenyHosts can also be optionally configured to share your blacklist with other DenyHosts users. This sharing of blacklists can greatly reduce SSHD attempts.
The real problem still remains. SSHD attacks are now on the increase and it is no longer sufficient to blacklist offenders.
Ideally, any SSHD hack attempt should be blacklisted, logged and most importantly; the ISP of the attacker must be notified in order to disconnect the attacking machine from the internet.
The Solution - report-hack-isp Plugin
We have developed a DenyHosts plugin that will email an attacker’s ISP as soon as the attack is detected. DenyHosts supports plugins to perform extra commands when an attacker is blacklisted. This email also contains an excerpt of the SSHD logfile that provides details of the attempted attack.
DenyHosts is the first step in this pro-active approach. Download, install and enable DenyHosts to synchronise your blacklist with the DenyHosts central server.
Armed with this, an ISP has sufficient evidence to take appropriate action.
Requirements
This is applicable to Linux/Unix/BSD servers only. The plugin requires the following applications:
- DenyHosts
- report-hack-isp plugin
- Ruby 1.8.x
- grep
- cat
- whois
- host
Download
Report-hack-isp can be downloaded from http://github.com/nazar/report-hack-isp/tree/masterInstructions
I've posted a simple guide on how to configure and setup this plugin here.
Metalog Users
Please note that several system loggers compress output by default. This means that if a log entry is repeated, the logger caches and outputs "Last output repeated" x times to the log file.
Please note that such compression should be turned off otherwise the DenyHosts log scanner will miss several entries.
If using MetaLog, add showrepeats = 1 to your metalog.conf file.
report-hack-isp / notify_isp.rb an source
----metags start here------
#! /usr/bin/ruby #Ruby DenyHosts plugin to report attacker to ISP##Copyright 2008 Nazar Aziz - nazar@panthersoftware.com require 'net/smtp' #SMTP serverSMTP_SERVER = 'localhost'SMTP_PORT = 25 #EMAIL message setupEMAIL_FROM = 'ADD_YOUR_RETURN_EMAIL_HERE'EMAIL_SUBJECT = 'Security Alert - Your Server May Have Been Hacked!' #guess apps... override if requiredGREP_BIN = `which grep`.stripCAT_BIN = `which cat`.stripWHOIS_BIN = `which whois`.stripHOST_BIN = `which host`.strip #LOG_FILE = SSHD's log fileLOG_FILE = '/var/log/sshd/*' #miscTIME_LOCALE = 'GMT+1'EMAIL_LOG_FILE = '/var/log/notify_isp.log' #check that we have all our BINsraise 'Could not find grep on your system. Manually configure GREP_BIN' if GREP_BIN == ''raise 'Could not find cat on your system. Manually configure CAT_BIN' if CAT_BIN == ''raise 'Could not find whois on your system. Manually configure WHOIS_BIN' if WHOIS_BIN == ''raise 'Could not find host on your system. Manually configure HOST_BIN' if HOST_BIN == '' ################# UTILS ########################def time2str( tm ) # [ruby-list:7928] gmt = Time.at(tm.to_i) gmt.gmtime offset = tm.to_i - Time.local(*gmt.to_a[0,6].reverse).to_i sprintf '%s, %s %s %d %02d:%02d:%02d %+.2d%.2d', tm.strftime('%a'), tm.mday, tm.strftime('%B'), tm.year, tm.hour, tm.min, tm.sec, *(offset / 60).divmod(60)end def get_email_message(to_address, offender, evidence) email_message = <<EOFFrom: #{EMAIL_FROM}To: #{to_address}Subject: #{EMAIL_SUBJECT}Date: #{time2str(Time.now)} To whom it may concern. We have detected a hack attempt originating from your network from ip: #{offender} This suggests that the above server has been compromised and is a participant in a botnet. This means that this server has been hacked and now, in turn, is attempting to hack other servers on the Internet. This IP address has now been blacklisted to protect our service from further brute force attacks. Furthermore, this IP address has been uploaded to a centralised database. This means that this IP address will also shortly be blacklisted by any member who queries this central database. An excerpt from our logfiles. All times shown are in #{TIME_LOCALE}: #{evidence} Regards.EOF end def get_contacts_for_host(lookup_host) result = [] lookup = eval("`#{WHOIS_BIN} #{lookup_host} | #{GREP_BIN} @`") #return any line that contains an @ symbol lookup.each_line do |line| email = line[/([-a-z0-9]+[\w\.\-\+]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})/i] result << email unless email == nil end #if contacts includes an abuse@ address then only send it to those. tmp = result.select { |email| email[/abuse@/] } result = tmp if tmp.length > 0 result.uniq! if result.length > 1 return result.uniqend ################# MAIN ########################## #extract ip/domain from passed parameterif ARGV.length > 0 host = ARGV[0]else raise 'No ip address or host given. Exiting'end #extract all email contacts for given hostcontacts = get_contacts_for_host(host) #lookup top level domain name and extract domain contact info#if given ip then lookup to hostnameif host[/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/] host_domain = eval("`#{HOST_BIN} #{host}`").strip unless host_domain =~ /not found:/ host_domain = host_domain[/.+\.(\w+\.\w+)/,1] else #no good... no back DNS pointer host_domain = nil endelse host_domain = host[/.+\.(\w+\.\w+)/,1]end if host_domain domain_contacts = get_contacts_for_host(host_domain) contacts << domain_contacts if domain_contacts.length > 0end #filter out duplicates one last timecontacts.uniq! if contacts.length > 1 raise "No email addresses were returned" unless contacts.length > 0 #extract evidence from ssh log file using the reported host as a filterevidence = eval("`#{CAT_BIN} #{LOG_FILE} | #{GREP_BIN} #{host}`") Net::SMTP.start(SMTP_SERVER, SMTP_PORT) do |smtp| begin #send email to each returned address contacts.flatten.each do |email| smtp.send_message get_email_message(email, host, evidence), EMAIL_FROM, email #log ip address and email my_file = File.new(EMAIL_LOG_FILE, 'a+') my_file.puts "Report generated for #{host} and sent to #{email} on #{Time.now.to_s}" end ensure smtp.finish end end
---end here---
By John Leyden edited by nad


