| Reupload after previous removal. - projectvoip - VoIP honeypot similar to ssh h… | |
| Log | |
| Files | |
| Refs | |
| README | |
| --- | |
| commit 94772bd4e5c9fa0bb421346c9b8724a08706b972 | |
| Author: Jay Scott <[email protected]> | |
| Date: Wed, 5 May 2021 20:40:30 +0100 | |
| Reupload after previous removal. | |
| Diffstat: | |
| A README | 36 +++++++++++++++++++++++++++++… | |
| A crontab | 3 +++ | |
| A extensions.conf | 24 ++++++++++++++++++++++++ | |
| A project-voip.rb | 242 +++++++++++++++++++++++++++++… | |
| A sip.conf | 21 +++++++++++++++++++++ | |
| A voip.sql | 49 +++++++++++++++++++++++++++++… | |
| 6 files changed, 375 insertions(+), 0 deletions(-) | |
| --- | |
| diff --git a/README b/README | |
| @@ -0,0 +1,36 @@ | |
| + | |
| + __ __ __ ___ __ ___ __ __ | |
| +|__) |__) / \ | |__ / ` | \ / / \ | |__) | |
| +| | \ \__/ \__/ |___ \__, | \/ \__/ | | | |
| + | |
| + | |
| +Project VOIP was meant to be a VOIP honeypot, but I haven't had much | |
| +time to develop it, so I am uploading everything I have so far. Project | |
| +VOIP is based on phorensix v1.0 by J. Oquendo / sil @ infiltrated dot | |
| +net. | |
| + | |
| +Phorensix was scripted in bash and logged all information to a series of | |
| +files. Project VOIP is coded in Ruby has been updated to work with the | |
| +latest version of asterisk and also logs all information to a MySQL | |
| +database. | |
| + | |
| +* Logs the following information to a mysql database: | |
| + * IP Address information | |
| + * Peer(s) AS Number | |
| + * Netblock AS Number | |
| + * Netblock Prefix | |
| + * AS Name | |
| + * AS Country | |
| + * AS Domain name | |
| + * ISP Name | |
| +* Number called | |
| +* SIP Agent | |
| +* SIP channel used | |
| +* Traceroute of the IP Address | |
| +* Packet capture of the session (.cap file) | |
| +* Recording of the call (.wav) | |
| + | |
| +usage: | |
| + | |
| + $ apt-get install tshark rubygems mysql-client libmysqlclient-dev | |
| + $ gem install mysql | |
| diff --git a/crontab b/crontab | |
| @@ -0,0 +1,3 @@ | |
| +# Dump the .cap and .wav every 18 mins - dirty hack | |
| + | |
| +*/18 * * * * /usr/bin/ruby -w /home/jay/project-voip.rb -d 1 2>&1 | |
| diff --git a/extensions.conf b/extensions.conf | |
| @@ -0,0 +1,24 @@ | |
| +[general] | |
| + | |
| +static=yes | |
| +writeprotect=no | |
| +clearglobalvars=yes | |
| +autofallthrough=yes | |
| + | |
| +[project-voip] | |
| + | |
| +exten => _X.,1,Set(CTIME=${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)}) | |
| +exten => _X.,n,system(/usr/bin/ruby /home/jay/project-voip.rb -i ${SIPCHANINFO… | |
| +exten => _X.,n,Answer | |
| +exten => _X.,n,Wait(1) | |
| +exten => _X.,n,Playtones(ring) | |
| +exten => _X.,n,Wait(5) | |
| +exten => _X.,n,StopPlaytones | |
| +exten => _X.,n,Playback(press-pound-to-login-star-to-hangup) | |
| +exten => _X.,n,Record(/opt/project-voip/wav/${CTIME}:wav,,,qk) | |
| +exten => _X.,n,Wait(1) | |
| +exten => _X.,n,Hangup | |
| + | |
| +[default] | |
| + | |
| +include => project-voip | |
| diff --git a/project-voip.rb b/project-voip.rb | |
| @@ -0,0 +1,242 @@ | |
| +#!/usr/bin/ruby | |
| + | |
| +=begin | |
| + | |
| +Project VOIP | |
| +============= | |
| + | |
| + | |
| +Project VOIP was meant to be a VOIP honeypot but I havent had much time to | |
| +develop it so I am uploading everything I have so far here :-) | |
| + | |
| +Project VOIP is based on phorensix v.1 by J. Oquendo / sil @ infiltrated dot n… | |
| + | |
| +Phorensix was scripted in bash and logged all information to a series of files… | |
| +Project VOIP is coded in Ruby has been updated to work with the latest version… | |
| +asterisk and also logs all information to a MySQL database. | |
| + | |
| +Jay Scott <[email protected]> | |
| + | |
| + | |
| +What it does | |
| +------------ | |
| + | |
| +-> Logs the following information to a mysql database: | |
| + -> IP Address information | |
| + -> Peer(s) AS Number | |
| + -> Netblock AS Number | |
| + -> Netblock Prefix | |
| + -> AS Name | |
| + -> AS Country | |
| + -> AS Domain name | |
| + -> ISP Name | |
| + -> Number called | |
| + -> SIP Agent | |
| + -> SIP Channel used. | |
| + -> Traceroute of the IP Address | |
| + -> Packet capture of the session (.cap file) | |
| + -> Recording of the call (.wav) | |
| + | |
| +Installing | |
| +---------- | |
| + | |
| +Install Tshark and ruby gems if not installed already | |
| + | |
| + - apt-get install tshark rubygems mysql-client libmysqlclient-dev | |
| + | |
| +Install the ruby gem files for mysql | |
| + | |
| + gem install mysql | |
| + | |
| +Use the configs below as a template, changing the values as appropriate | |
| + | |
| + | |
| +Make sure and update the Mysql information!. | |
| + | |
| +=end | |
| + | |
| +require 'rubygems' | |
| +require 'optparse' | |
| +require 'mysql' | |
| + | |
| +BASE_DATA = "/opt/project-voip/data" | |
| +CRON_FILE = "/opt/project-voip/data/cron" | |
| +MYSQL_HOST = "" | |
| +MYSQL_USER = "" | |
| +MYSQL_PASS = "" | |
| +MYSQL_TABLE = "" | |
| +HONEYPOT_ID = "1" | |
| + | |
| +options = {} | |
| + | |
| +op = OptionParser.new do|opts| | |
| + | |
| + opts.banner = "Usage: ruby #{File.basename($0)} " | |
| + options[:ipaddress] = nil | |
| + options[:data] = nil | |
| + options[:useragent] = "Unknown" | |
| + options[:exten] = "Unknown" | |
| + options[:channel] = "Unknown" | |
| + | |
| + opts.on( '-i', '--ipaddress [IPADDRESS]', 'IP Address' ) do|i| | |
| + options[:ipaddress] = i | |
| + end | |
| + | |
| + opts.on( '-e', '--exten [EXTEN]', 'Phone number called ' ) do|e| | |
| + options[:exten] = e | |
| + end | |
| + | |
| + opts.on( '-c', '--channel [CHANNEL]', 'Channel passed by Asterisk' ) do|c| | |
| + options[:channel] = c | |
| + end | |
| + | |
| + opts.on( '-u', '--useragent [USERAGENT]', 'Callers user-agent' ) do|u| | |
| + options[:useragent] = u | |
| + end | |
| + | |
| + opts.on( '-w', '--wav [WAV FILE]', 'WAV file location' ) do|w| | |
| + options[:wav] = w | |
| + end | |
| + | |
| + opts.on( '-d', '--data', 'Upload Data Files to MySQL.' ) do | |
| + options[:data] = true | |
| + end | |
| + | |
| + opts.on( '-h', '--help', 'Display this screen' ) do | |
| + puts opts | |
| + exit | |
| + end | |
| + | |
| + opts.on("--version", "Show version") do | |
| + puts OptionParser::Version.join('.') | |
| + exit | |
| + end | |
| +end | |
| + | |
| +op.parse! | |
| + | |
| +# Get the options passed | |
| +ip_address = options[:ipaddress] | |
| +data_update = options[:data] | |
| +useragent = options[:useragent] | |
| +called_exten = options[:exten] | |
| +caller_channel = options[:channel] | |
| +wav_file = options[:wav] | |
| + | |
| +# No IP.. somethings wrong, exit :p | |
| +if !data_update.nil? | |
| + | |
| + File.open("#{CRON_FILE}").each_line{ |s| | |
| + arrayData = s.split(':') | |
| + file_id = arrayData[0] | |
| + file_cap = arrayData[1].strip | |
| + file_wav = arrayData[2].strip | |
| + | |
| + cf = File.new(file_cap, 'rb') | |
| + wf = File.new(file_wav, 'rb') | |
| + | |
| + cap_data = Mysql.escape_string(cf.sysread(cf.stat.size)) | |
| + wav_data = Mysql.escape_string(wf.sysread(wf.stat.size)) | |
| + | |
| + begin | |
| + con_info = Mysql.real_connect("#{MYSQL_HOST}", "#{MYSQL_USER}", "#{MYSQL… | |
| + rescue Mysql::Error => e | |
| + puts "Error code: #{e.errno}" | |
| + puts "Error message: #{e.error}" | |
| + puts "Error SQLSTATE: #{e.sqlstate}" if e.respond_to?("sqlstate") | |
| + end | |
| + | |
| + con_info.query("UPDATE caller SET capture=\"#{cap_data}\", recording=\"#{w… | |
| + | |
| + con_info.close | |
| + } | |
| + | |
| + puts "Updated" | |
| + File.delete("#{CRON_FILE}") | |
| + File.new("#{CRON_FILE}", "w") | |
| + exit(1) | |
| + | |
| +end | |
| + | |
| +# No IP.. somethings wrong, exit :p | |
| +if ip_address.nil? | |
| + exit(-1) | |
| +end | |
| + | |
| +# Set the dates we want | |
| +date = Time.new | |
| +current_date = date.strftime("%Y%m%d") | |
| +current_time = date.strftime("%H%M%S") | |
| +mysql_date = date.strftime("%H%M%S") | |
| + | |
| +cap_file = "#{BASE_DATA}/#{ip_address}-#{mysql_date}.cap" | |
| + | |
| +whois_info = Array.new | |
| +whois_data = `whois -h whois.pwhois.org #{ip_address}` | |
| +whois_info = whois_data.split("\n") | |
| + | |
| +# ip_address, origin_as, prefix, as_path, as_org_name, org_name, net_name, cac… | |
| +whois_info = whois_info.map do |x| | |
| + x.split(':')[1].strip | |
| +end | |
| + | |
| +traceroute_output = `traceroute -w 1 -m 25 -n #{ip_address} &` | |
| +packet_capture = `tshark -q -R \"ip.addr == #{ip_address}\" -w #{cap_file} -a … | |
| + | |
| +# Connect to MySQL DB. | |
| +begin | |
| + con_info = Mysql.real_connect("#{MYSQL_HOST}", "#{MYSQL_USER}", "#{MYSQL_PAS… | |
| +rescue Mysql::Error => e | |
| + puts "Error code: #{e.errno}" | |
| + puts "Error message: #{e.error}" | |
| + puts "Error SQLSTATE: #{e.sqlstate}" if e.respond_to?("sqlstate") | |
| +end | |
| + | |
| +rs_check = con_info.query("SELECT * from information WHERE ip_address='#{ip_ad… | |
| + | |
| +# If the IP is unique then create a new record for it. | |
| +if rs_check.num_rows == 0 | |
| + con_info.query("INSERT INTO information | |
| + (ip_address, asn, prefix, as_path, as_org_name, org_name, net_… | |
| + VALUES | |
| + ('#{ip_address}', '#{whois_info[1]}', '#{whois_info[2]}', '#{w… | |
| +end | |
| + | |
| +d = DateTime.now | |
| + | |
| +# Insert the call information | |
| +con_info.query("INSERT INTO caller | |
| + (uid, number, agent, channel, timestamp, honeypot) | |
| + VALUES | |
| + ('#{ip_address}','#{called_exten}','#{useragent}','#{caller_ch… | |
| + | |
| + | |
| +# This section dumps the wav and cap file information to a file to be read by | |
| +# cron. We cant upload the wav and cap file at the same time because you dont | |
| +# know if the call has finished so the wav and cap are still in progress. | |
| +# | |
| +# Asterisk cant reliably detect if a call has terminated, if it did we could | |
| +# call function to kill tshark and then update the DB with the file data then. | |
| +# | |
| +# PROBLEM: The cron could run just after this information has been saved or not | |
| +# finished processing and chop the data anyway | |
| +# | |
| +# meh.... need to ditch asterisk and just create my own sip server. | |
| + | |
| +# Get the ID of the record we just inserted. | |
| + | |
| +rs_id = con_info.query("SELECT MAX(id) AS MAXID FROM caller") | |
| + | |
| +while row = rs_id.fetch_row do | |
| + last_id = row[0] | |
| +end | |
| + | |
| +con_info.close | |
| + | |
| +if !File::file?(CRON_FILE) | |
| + cronFile = File.new("#{CRON_FILE }", "w") | |
| +end | |
| + | |
| +File.open("#{CRON_FILE}", "a") do |f| | |
| + f.puts "#{last_id}:#{cap_file}:#{wav_file}" | |
| +end | |
| diff --git a/sip.conf b/sip.conf | |
| @@ -0,0 +1,21 @@ | |
| +[general] | |
| +context=project-voip ; Default context for incoming calls | |
| +allowoverlap=no ; Disable overlap dialing support. (Default is… | |
| +udpbindaddr=0.0.0.0 ; IP address to bind UDP listen socket to (0.0… | |
| +tcpenable=yes ; Enable server for incoming TCP connections (… | |
| +tcpbindaddr=0.0.0.0 ; IP address for TCP server to bind to (0.0.0.… | |
| +srvlookup=yes ; Enable DNS SRV lookups on outbound calls | |
| +alwaysauthreject=no | |
| +allowguest=yes | |
| + | |
| + | |
| +[100] | |
| +username=100 | |
| +secret=100 | |
| +context=project-voip | |
| +type=friend | |
| +canreinvite=no | |
| +host=dynamic | |
| +qualify=1000 | |
| +mailbox=100 | |
| +allow=all | |
| diff --git a/voip.sql b/voip.sql | |
| @@ -0,0 +1,49 @@ | |
| + | |
| + | |
| +DROP TABLE IF EXISTS `caller`; | |
| +CREATE TABLE IF NOT EXISTS `caller` ( | |
| + `id` int(15) NOT NULL AUTO_INCREMENT, | |
| + `uid` varchar(15) NOT NULL, | |
| + `number` varchar(100) DEFAULT NULL, | |
| + `agent` varchar(100) DEFAULT NULL, | |
| + `channel` varchar(100) DEFAULT NULL, | |
| + `capture` longblob NOT NULL, | |
| + `recording` longblob NOT NULL, | |
| + `timestamp` datetime NOT NULL, | |
| + `dumped` int(1) NOT NULL DEFAULT '0', | |
| + `honeypot` int(11) NOT NULL, | |
| + PRIMARY KEY (`id`), | |
| + KEY `uid` (`uid`) | |
| +) ENGINE=MyISAM DEFAULT CHARSET=latin1; | |
| + | |
| +DROP TABLE IF EXISTS `information`; | |
| +CREATE TABLE IF NOT EXISTS `information` ( | |
| + `id` int(15) NOT NULL AUTO_INCREMENT, | |
| + `ip_address` varchar(15) NOT NULL, | |
| + `asn` varchar(100) DEFAULT NULL, | |
| + `as_path` varchar(100) DEFAULT NULL, | |
| + `prefix` varchar(100) DEFAULT NULL, | |
| + `as_org_name` varchar(100) DEFAULT NULL, | |
| + `org_name` varchar(100) DEFAULT NULL, | |
| + `country` varchar(100) DEFAULT NULL, | |
| + `net_name` varchar(100) DEFAULT NULL, | |
| + `lon` float(10,6) DEFAULT NULL, | |
| + `lat` float(10,6) DEFAULT NULL, | |
| + `city` varchar(100) DEFAULT NULL, | |
| + `region` varchar(100) DEFAULT NULL, | |
| + `traceroute` varchar(400) DEFAULT NULL, | |
| + `reported` int(1) NOT NULL DEFAULT '0', | |
| + PRIMARY KEY (`id`), | |
| + KEY `ip_address` (`ip_address`) | |
| +) ENGINE=MyISAM DEFAULT CHARSET=latin1; | |
| + | |
| +DROP TABLE IF EXISTS `report`; | |
| +CREATE TABLE IF NOT EXISTS `report` ( | |
| + `id` int(15) NOT NULL AUTO_INCREMENT, | |
| + `uid` varchar(15) NOT NULL, | |
| + `email` varchar(200) DEFAULT NULL, | |
| + `last_contact` datetime DEFAULT NULL, | |
| + `total_contact` int(2) DEFAULT NULL, | |
| + `reply` text, | |
| + PRIMARY KEY (`id`) | |
| +) ENGINE=MyISAM DEFAULT CHARSET=latin1; |