public
Description: A DenyHosts PLUGIN_DENY plugin that looks up a blacklisted IP's contact details and sends an abuse email to all found contacts
Homepage: http://panthersoftware.com/articles/view/5
Clone URL: git://github.com/nazar/report-hack-isp.git
report-hack-isp / notify_isp.rb
100755 170 lines (130 sloc) 5.776 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#! /usr/bin/ruby
 
#Ruby DenyHosts plugin to report attacker to ISP
#
#Copyright 2008 Nazar Aziz - nazar@panthersoftware.com
 
#######################################################################################
####### PLEASE READ INSTRUCTIONS: http://github.com/nazar/report-hack-isp/wikis #######
#######################################################################################
 
require 'net/smtp'
 
#SMTP server
SMTP_SERVER = 'localhost'
SMTP_PORT = 25
 
#EMAIL message setup
EMAIL_FROM = 'ADD_YOUR_RETURN_EMAIL_HERE' ####### ADD YOUR ACTUALL EMAIL ADDRESS HERE ##########
EMAIL_SUBJECT = 'Security Alert - Your Server May Have Been Hacked!'
# Leave empty to not send a mail to a CC address
CC = ''
# Same as for the CC address, you probably only need one of these
BCC = ''
 
#LOG_FILE = SSHD's log file ###### UPDATE THIS TO YOUR ACTUAL SSHD LOG FILE LOCATION #####
LOG_FILE = '/var/log/sshd/*'
 
#misc
TIME_LOCALE = 'GMT+1'
EMAIL_LOG_FILE = '/var/log/notify_isp.log' ##### CHECK PERMISSIONS ON DESTINATION DIRECTORY.
 
#guess apps... override if required
GREP_BIN = `which grep`.strip
CAT_BIN = `which cat`.strip
WHOIS_BIN = `which whois`.strip
HOST_BIN = `which host`.strip
TOUCH_BIN = `which touch`.strip
 
#check that we have all our BINs
raise '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 == ''
raise 'Could not find touch on your system. Manually configure TOUCH_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)
  to_cc = CC.length > 0 ? "\nCC: #{CC}" : ''
  to_bcc = BCC.length > 0 ? "\nBCC: #{BCC}" : ''
 
  email_message = <<EOF
From: #{EMAIL_FROM}
To: #{to_address}#{to_cc}#{to_bcc}
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 DenyHosts' centralised database. This means that this IP address will also shortly be blacklisted by any member who queries DenyHosts' 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.uniq
end
 
 
################# MAIN ##########################
 
#extract ip/domain from passed parameter
if ARGV.length > 0
  host = ARGV[0]
else
  raise 'No ip address or host given. Exiting'
end
 
#make sure the EMAIL_LOG_FILE exists
eval("`#{TOUCH_BIN} #{EMAIL_LOG_FILE}`")
 
#extract all email contacts for given host
contacts = get_contacts_for_host(host)
 
#lookup top level domain name and extract domain contact info
#if given ip then lookup to hostname
if 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 revers DNS pointer
    host_domain = nil
  end
else
  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 > 0
end
 
#filter out duplicates one last time
contacts.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 filter
evidence = eval("`#{CAT_BIN} #{LOG_FILE} | #{GREP_BIN} #{host}`").strip
raise "No evidence found for #{host}. Aborting" unless evidence && (evidence.length > 0)
 
#workaround for DenyHosts that runs the plugin evrytime an IP is added against all blacklisted IPS
sent = eval("`#{CAT_BIN} #{EMAIL_LOG_FILE} | #{GREP_BIN} #{host}`").strip
raise "Host #{host} has already been reported. Not reporting again." if sent && sent.length > 0
 
#are we CC/BCCing ourselves?
contacts << CC if CC.length > 0
contacts << BCC if BCC.length > 0
 
#by the time we get here we have evidence against a newly reported 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