| project-voip.rb - projectvoip - VoIP honeypot similar to ssh honeypot, using as… | |
| git clone git://jay.scot/projectvoip | |
| Log | |
| Files | |
| Refs | |
| 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 … | |
| 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… | |
| 13 | |
| 14 Phorensix was scripted in bash and logged all information to a series of… | |
| 15 Project VOIP is coded in Ruby has been updated to work with the latest v… | |
| 16 asterisk and also logs all information to a MySQL database. | |
| 17 | |
| 18 Jay Scott <[email protected]> | |
| 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' ) d… | |
| 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}", "#… | |
| 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… | |
| 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_nam… | |
| 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_fil… | |
| 185 | |
| 186 # Connect to MySQL DB. | |
| 187 begin | |
| 188 con_info = Mysql.real_connect("#{MYSQL_HOST}", "#{MYSQL_USER}", "#{MYS… | |
| 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='#… | |
| 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… | |
| 201 VALUES | |
| 202 ('#{ip_address}', '#{whois_info[1]}', '#{whois_info[2]}'… | |
| 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}','#{cal… | |
| 212 | |
| 213 | |
| 214 # This section dumps the wav and cap file information to a file to be re… | |
| 215 # cron. We cant upload the wav and cap file at the same time because you… | |
| 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 c… | |
| 219 # call function to kill tshark and then update the DB with the file data… | |
| 220 # | |
| 221 # PROBLEM: The cron could run just after this information has been saved… | |
| 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 |