project-voip.rb - projectvoip - VoIP honeypot similar to ssh honeypot, using asterisk as the backend.
(HTM) git clone git://jay.scot/projectvoip
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
---
project-voip.rb (6749B)
---
1 #!/usr/bin/ruby
2
3 =begin
4
5 Project VOIP
6 =============
7
8
9 Project VOIP was meant to be a VOIP honeypot but I havent had much time to
10 develop it so I am uploading everything I have so far here :-)
11
12 Project VOIP is based on phorensix v.1 by J. Oquendo / sil @ infiltrated dot net.
13
14 Phorensix was scripted in bash and logged all information to a series of files.
15 Project VOIP is coded in Ruby has been updated to work with the latest version of
16 asterisk and also logs all information to a MySQL database.
17
18 Jay Scott <jay@jayscott.co.uk>
19
20
21 What it does
22 ------------
23
24 -> Logs the following information to a mysql database:
25 -> IP Address information
26 -> Peer(s) AS Number
27 -> Netblock AS Number
28 -> Netblock Prefix
29 -> AS Name
30 -> AS Country
31 -> AS Domain name
32 -> ISP Name
33 -> Number called
34 -> SIP Agent
35 -> SIP Channel used.
36 -> Traceroute of the IP Address
37 -> Packet capture of the session (.cap file)
38 -> Recording of the call (.wav)
39
40 Installing
41 ----------
42
43 Install Tshark and ruby gems if not installed already
44
45 - apt-get install tshark rubygems mysql-client libmysqlclient-dev
46
47 Install the ruby gem files for mysql
48
49 gem install mysql
50
51 Use the configs below as a template, changing the values as appropriate
52
53
54 Make sure and update the Mysql information!.
55
56 =end
57
58 require 'rubygems'
59 require 'optparse'
60 require 'mysql'
61
62 BASE_DATA = "/opt/project-voip/data"
63 CRON_FILE = "/opt/project-voip/data/cron"
64 MYSQL_HOST = ""
65 MYSQL_USER = ""
66 MYSQL_PASS = ""
67 MYSQL_TABLE = ""
68 HONEYPOT_ID = "1"
69
70 options = {}
71
72 op = OptionParser.new do|opts|
73
74 opts.banner = "Usage: ruby #{File.basename($0)} "
75 options[:ipaddress] = nil
76 options[:data] = nil
77 options[:useragent] = "Unknown"
78 options[:exten] = "Unknown"
79 options[:channel] = "Unknown"
80
81 opts.on( '-i', '--ipaddress [IPADDRESS]', 'IP Address' ) do|i|
82 options[:ipaddress] = i
83 end
84
85 opts.on( '-e', '--exten [EXTEN]', 'Phone number called ' ) do|e|
86 options[:exten] = e
87 end
88
89 opts.on( '-c', '--channel [CHANNEL]', 'Channel passed by Asterisk' ) do|c|
90 options[:channel] = c
91 end
92
93 opts.on( '-u', '--useragent [USERAGENT]', 'Callers user-agent' ) do|u|
94 options[:useragent] = u
95 end
96
97 opts.on( '-w', '--wav [WAV FILE]', 'WAV file location' ) do|w|
98 options[:wav] = w
99 end
100
101 opts.on( '-d', '--data', 'Upload Data Files to MySQL.' ) do
102 options[:data] = true
103 end
104
105 opts.on( '-h', '--help', 'Display this screen' ) do
106 puts opts
107 exit
108 end
109
110 opts.on("--version", "Show version") do
111 puts OptionParser::Version.join('.')
112 exit
113 end
114 end
115
116 op.parse!
117
118 # Get the options passed
119 ip_address = options[:ipaddress]
120 data_update = options[:data]
121 useragent = options[:useragent]
122 called_exten = options[:exten]
123 caller_channel = options[:channel]
124 wav_file = options[:wav]
125
126 # No IP.. somethings wrong, exit :p
127 if !data_update.nil?
128
129 File.open("#{CRON_FILE}").each_line{ |s|
130 arrayData = s.split(':')
131 file_id = arrayData[0]
132 file_cap = arrayData[1].strip
133 file_wav = arrayData[2].strip
134
135 cf = File.new(file_cap, 'rb')
136 wf = File.new(file_wav, 'rb')
137
138 cap_data = Mysql.escape_string(cf.sysread(cf.stat.size))
139 wav_data = Mysql.escape_string(wf.sysread(wf.stat.size))
140
141 begin
142 con_info = Mysql.real_connect("#{MYSQL_HOST}", "#{MYSQL_USER}", "#{MYSQL_PASS}", "#{MYSQL_TABLE}")
143 rescue Mysql::Error => e
144 puts "Error code: #{e.errno}"
145 puts "Error message: #{e.error}"
146 puts "Error SQLSTATE: #{e.sqlstate}" if e.respond_to?("sqlstate")
147 end
148
149 con_info.query("UPDATE caller SET capture=\"#{cap_data}\", recording=\"#{wav_data}\" WHERE id='#{file_id}'")
150
151 con_info.close
152 }
153
154 puts "Updated"
155 File.delete("#{CRON_FILE}")
156 File.new("#{CRON_FILE}", "w")
157 exit(1)
158
159 end
160
161 # No IP.. somethings wrong, exit :p
162 if ip_address.nil?
163 exit(-1)
164 end
165
166 # Set the dates we want
167 date = Time.new
168 current_date = date.strftime("%Y%m%d")
169 current_time = date.strftime("%H%M%S")
170 mysql_date = date.strftime("%H%M%S")
171
172 cap_file = "#{BASE_DATA}/#{ip_address}-#{mysql_date}.cap"
173
174 whois_info = Array.new
175 whois_data = `whois -h whois.pwhois.org #{ip_address}`
176 whois_info = whois_data.split("\n")
177
178 # ip_address, origin_as, prefix, as_path, as_org_name, org_name, net_name, cache_date, lat, long, city, region, country
179 whois_info = whois_info.map do |x|
180 x.split(':')[1].strip
181 end
182
183 traceroute_output = `traceroute -w 1 -m 25 -n #{ip_address} &`
184 packet_capture = `tshark -q -R \"ip.addr == #{ip_address}\" -w #{cap_file} -a duration:20 &`
185
186 # Connect to MySQL DB.
187 begin
188 con_info = Mysql.real_connect("#{MYSQL_HOST}", "#{MYSQL_USER}", "#{MYSQL_PASS}", "#{MYSQL_TABLE}")
189 rescue Mysql::Error => e
190 puts "Error code: #{e.errno}"
191 puts "Error message: #{e.error}"
192 puts "Error SQLSTATE: #{e.sqlstate}" if e.respond_to?("sqlstate")
193 end
194
195 rs_check = con_info.query("SELECT * from information WHERE ip_address='#{ip_address}'")
196
197 # If the IP is unique then create a new record for it.
198 if rs_check.num_rows == 0
199 con_info.query("INSERT INTO information
200 (ip_address, asn, prefix, as_path, as_org_name, org_name, net_name, lat, lon, city, region, country, traceroute)
201 VALUES
202 ('#{ip_address}', '#{whois_info[1]}', '#{whois_info[2]}', '#{whois_info[3]}', '#{whois_info[4]}', '#{whois_info[5]}', '#{whois_info[6]}', '#{whois_info[8]}', '#{whois_info[9]}', '#{whois_info[10]}', '#{whois_info[11]}', '#{whois_info[12]}', '#{traceroute_output}')")
203 end
204
205 d = DateTime.now
206
207 # Insert the call information
208 con_info.query("INSERT INTO caller
209 (uid, number, agent, channel, timestamp, honeypot)
210 VALUES
211 ('#{ip_address}','#{called_exten}','#{useragent}','#{caller_channel}', '#{d.to_s}', '#{HONEYPOT_ID}')")
212
213
214 # This section dumps the wav and cap file information to a file to be read by
215 # cron. We cant upload the wav and cap file at the same time because you dont
216 # know if the call has finished so the wav and cap are still in progress.
217 #
218 # Asterisk cant reliably detect if a call has terminated, if it did we could
219 # call function to kill tshark and then update the DB with the file data then.
220 #
221 # PROBLEM: The cron could run just after this information has been saved or not
222 # finished processing and chop the data anyway
223 #
224 # meh.... need to ditch asterisk and just create my own sip server.
225
226 # Get the ID of the record we just inserted.
227
228 rs_id = con_info.query("SELECT MAX(id) AS MAXID FROM caller")
229
230 while row = rs_id.fetch_row do
231 last_id = row[0]
232 end
233
234 con_info.close
235
236 if !File::file?(CRON_FILE)
237 cronFile = File.new("#{CRON_FILE }", "w")
238 end
239
240 File.open("#{CRON_FILE}", "a") do |f|
241 f.puts "#{last_id}:#{cap_file}:#{wav_file}"
242 end