Automatic matching improvements, additional of a maltego transform for finding like numbers - warvox - VoIP based wardialing tool, forked from rapid7/warvox.
DIR Log
DIR Files
DIR Refs
DIR README
---
DIR commit 3a47479fdb9dc463945b69e36fe5a1a71a3f4bc8
DIR parent 2af809ed64362553e2fd7b309fd8da3490351bb6
HTML Author: HD Moore <hd_moore@rapid7.com>
Date: Sat, 14 Feb 2009 22:04:43 +0000
Automatic matching improvements, additional of a maltego transform for finding like numbers
Diffstat:
M bin/automatch.rb | 41 ++++++++++++++++++++-----------
A bin/link_maltego.rb | 159 +++++++++++++++++++++++++++++++
A docs/maltego/local_transform.png | 0
A docs/maltego/sample_output.png | 0
M lib/warvox/db.rb | 66 +++++++++++++++++++------------
5 files changed, 226 insertions(+), 40 deletions(-)
---
DIR diff --git a/bin/automatch.rb b/bin/automatch.rb
@@ -16,13 +16,14 @@ require 'warvox'
#
def usage
- $stderr.puts "#{$0} [warvox.db] <db-threshold>"
+ $stderr.puts "#{$0} [warvox.db] <db-threshold> <fuzz>"
exit
end
threads = 2
inp = ARGV.shift || usage
thresh = (ARGV.shift() || 800).to_i
+fuzz = (ARGV.shift() || 100).to_i
wdb = WarVOX::DB.new(inp, thresh)
# Scrub the carriers out of the pool first
@@ -33,45 +34,57 @@ end
groups =
{
- "carriers" => car.keys
+ "carriers" => car.keys,
+ "unique" => []
}
oset = wdb.keys.sort
iset = oset.dup
-
while(not oset.empty?)
+ s = Time.now
k = oset.shift
- found = []
- best = nil
+ found = {}
next if not iset.include?(k)
iset.each do |n|
next if k == n
begin
- res = wdb.find_sig(k,n)
+ res = wdb.find_sig(k,n,{ :fuzz => fuzz })
rescue ::WarVOX::DB::Error
end
next if not res
next if res[:len] < 5
- found << res
+ if(not found[n] or found[n][:len] < res[:len])
+ found[n] = res
+ end
+ end
+
+ if(found.empty?)
+ next
end
- next if found.empty?
-
- groups[k] = [ ]
- found.each do |f|
- groups[k] << [ f[:num2], f[:len] ]
+ groups[k] = [ [k, 0] ]
+ found.keys.sort.each do |n|
+ groups[k] << [n, found[n][:len]]
end
- $stdout.puts "#{k} " + groups[k].map{|x| "#{x[0]}-#{x[1]}" }.join(" ")
+ $stdout.puts groups[k].map{|x| "#{x[0]}-#{x[1]}" }.join(" ")
$stdout.flush
groups[k].unshift(k)
-end
+ # Remove matches from the search listing
+ iset.delete(k)
+ found.keys.each do |k|
+ iset.delete(k)
+ end
+end
+iset.each do |k|
+ puts "#{k}-0"
+end
DIR diff --git a/bin/link_maltego.rb b/bin/link_maltego.rb
@@ -0,0 +1,159 @@
+#!/usr/bin/env ruby
+###################
+
+#
+# Load the library path
+#
+base = __FILE__
+while File.symlink?(base)
+ base = File.expand_path(File.readlink(base), File.dirname(base))
+end
+$:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib'))
+require 'warvox'
+require 'rexml/document'
+
+#
+# Script
+#
+
+#
+# http://ctas.paterva.com/view/Specification
+#
+
+def xml_results_empty
+ root = REXML::Element.new('MaltegoMessage')
+ xml2 = root.add_element('MaltegoTransformResponseMessage')
+ xml2.add_element('Entities')
+ root
+end
+
+def xml_results_matches(res)
+ root = REXML::Element.new('MaltegoMessage')
+ xml2 = root.add_element('MaltegoTransformResponseMessage')
+ xml3 = xml2.add_element('Entities')
+
+ res.each_key do |k|
+ num_area = k[0,3]
+ num_city = k[3,3]
+ num_last = k[6,4]
+
+ num = num_area + " " + num_city + " " + num_last
+
+ val = REXML::Element.new('Value')
+ val.add_text(num)
+
+ adf = REXML::Element.new('AdditionalFields')
+
+ adf_area = REXML::Element.new('Field')
+ adf_area.add_attribute('Name', 'areacode')
+ adf_area.add_text( REXML::Text.new( num_area.to_s ) )
+ adf << adf_area
+
+ adf_city = REXML::Element.new('Field')
+ adf_city.add_attribute('Name', 'citycode')
+ adf_city.add_text( REXML::Text.new( num_city.to_s ) )
+ adf << adf_city
+
+ adf_last = REXML::Element.new('Field')
+ adf_last.add_attribute('Name', 'lastnumbers')
+ adf_last.add_text( REXML::Text.new( num_last.to_s ) )
+ adf << adf_last
+
+ adf_info = REXML::Element.new('Field')
+ adf_info.add_attribute('Name', 'additional')
+ adf_info.add_text( REXML::Text.new( "Sig: " + res[k][:sig].map{|x| "#{x[0]},#{x[1]},#{x[2]}"}.join(" ") ) )
+ adf << adf_info
+
+
+ wgt = REXML::Element.new('Weight')
+ wgt.add_text( REXML::Text.new( [res[k][:len] * 10, 100].min.to_s ) )
+
+ ent = REXML::Element.new('Entity')
+ ent.add_attribute('Type', 'PhoneNumber')
+
+ ent << val
+ ent << wgt
+ ent << adf
+
+ xml3 << ent
+ end
+ root
+end
+
+
+# Only report each percentage once
+@progress_done = {}
+
+def report_progress(pct)
+ return if @progress_done[pct]
+ $stderr.puts "%#{pct}"
+ $stderr.flush
+ @progress_done[pct] = true
+end
+
+def usage
+ $stderr.puts "#{$0} [target] [params]"
+ exit
+end
+
+#
+# Parse input
+#
+
+params = {}
+target = ARGV.shift || usage()
+(ARGV.shift || usage()).split('#').each do |param|
+ k,v = param.split('=', 2)
+ params[k] = v
+end
+
+# XXX: Problematic right now
+# target_number = params['areacode'] + params['citycode'] + params['lastnumbers']
+
+target_number = target.scan(/\d+/).join
+if(target_number.length != 10)
+ $stderr.puts "D: Only 10 digit US numbers are currently supported"
+ $stdout.puts xml_results_empty().to_s
+ exit
+end
+
+
+#
+# Search database
+#
+
+carriers = {}
+
+data_root = File.join(File.dirname(base), '..', 'data')
+wdb = WarVOX::DB.new(nil)
+
+Dir.new(data_root).entries.grep(/\.db$/).each do |db|
+ $stderr.puts "D: Loading #{db}..."
+ wdb.import(File.join(data_root, db))
+end
+
+# No matching number
+if(not wdb[target_number])
+ $stderr.puts "D: Target #{target_number} (#{target}) is not in the WarVOX database"
+ $stdout.puts xml_results_empty().to_s
+ exit
+end
+
+found = {}
+cnt = 0
+wdb.each_key do |n|
+ cnt += 1
+
+ report_progress(((cnt / wdb.keys.length.to_f) * 100).to_i.to_s)
+
+ next if target_number == n
+ begin
+ res = wdb.find_sig(target_number, n, { :fuzz => 100 })
+ rescue ::WarVOX::DB::Error
+ end
+ next if not res
+ next if res[:len] < 5
+ found[n] = res
+end
+
+$stdout.puts xml_results_matches(found).to_s
DIR diff --git a/docs/maltego/local_transform.png b/docs/maltego/local_transform.png
Binary files differ.
DIR diff --git a/docs/maltego/sample_output.png b/docs/maltego/sample_output.png
Binary files differ.
DIR diff --git a/lib/warvox/db.rb b/lib/warvox/db.rb
@@ -12,7 +12,10 @@ class DB < ::Hash
self.path = path
self.threshold = threshold
self.version = VERSION
-
+ import(path) if path
+ end
+
+ def import(path)
File.open(path, "r") do |fd|
fd.each_line do |line|
line.strip!
@@ -32,47 +35,36 @@ class DB < ::Hash
self[name] << [s, l.to_i, a.to_i]
end
end
- end
+ end
end
#
# Utility methods
#
- # Find the largest pattern shared between two samples
- def find_sig(num1, num2, opts={})
-
- fuzz = opts[:fuzz] || 100
- info1 = self[num1]
- info2 = self[num2]
-
- # Make sure both samples exist in the database
- if ( not (info1 and info2 and not (info1.empty? or info2.empty?) ) )
- raise Error, "The database must contain both numbers"
- end
-
- # Remove the silence prefix from both samples
- info1.shift if info1[0][0] == "L"
- info2.shift if info2[0][0] == "L"
+ # Find a signature within a sample
+ def find_match(pat, sam, opts={})
+
+ fuzz = opts[:fuzz] || 100
+ min_sig = opts[:min_sig] || 2
- min_sig = 2
idx = 0
fnd = nil
mat = nil
r = 0
- while(idx < info1.length-min_sig)
- sig = info1[idx,info1.length]
+ while(idx < pat.length-min_sig)
+ sig = pat[idx,pat.length]
idx2 = 0
- while (idx2 < info2.length)
+ while (idx2 < sam.length)
c = 0
0.upto(sig.length-1) do |si|
- break if not info2[idx2+si]
+ break if not sam[idx2+si]
break if not (
- sig[si][0] == info2[idx2+si][0] and
- info2[idx2 + si][1] > sig[si][1]-fuzz and
- info2[idx2 + si][1] < sig[si][1]+fuzz
+ sig[si][0] == sam[idx2+si][0] and
+ sam[idx2 + si][1] > sig[si][1]-fuzz and
+ sam[idx2 + si][1] < sig[si][1]+fuzz
)
c += 1
end
@@ -80,13 +72,35 @@ class DB < ::Hash
if (c > r)
r = c
fnd = sig[0, r]
- mat = info2[idx2, r]
+ mat = sam[idx2, r]
end
idx2 += 1
end
idx += 1
end
+ # Return the results
+ [fnd, mat, r]
+ end
+
+ # Find the largest pattern shared between two samples
+ def find_sig(num1, num2, opts={})
+
+ fuzz = opts[:fuzz] || 100
+ info1 = self[num1]
+ info2 = self[num2]
+
+ # Make sure both samples exist in the database
+ if ( not (info1 and info2 and not (info1.empty? or info2.empty?) ) )
+ raise Error, "The database must contain both numbers"
+ end
+
+ # Remove the silence prefix from both samples
+ info1.shift if info1[0][0] == "L"
+ info2.shift if info2[0][0] == "L"
+
+ fnd,mat,r = find_match(info1, info2, { :fuzz => fuzz })
+
return nil if not fnd
sig = []