wmap 2.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +141 -0
- data/LICENSE.txt +15 -0
- data/README.rdoc +98 -0
- data/TODO +13 -0
- data/bin/deprime +21 -0
- data/bin/distrust +38 -0
- data/bin/googleBot +23 -0
- data/bin/prime +21 -0
- data/bin/refresh +26 -0
- data/bin/run_tests +16 -0
- data/bin/spiderBot +26 -0
- data/bin/trust +38 -0
- data/bin/updateAll +57 -0
- data/bin/wadd +25 -0
- data/bin/wadds +26 -0
- data/bin/wcheck +28 -0
- data/bin/wdel +25 -0
- data/bin/wdump +21 -0
- data/bin/wmap +151 -0
- data/bin/wscan +32 -0
- data/data/cidrs +2 -0
- data/data/deactivated_sites +1 -0
- data/data/domains +2 -0
- data/data/hosts +1 -0
- data/data/prime_hosts +1 -0
- data/data/sites +2 -0
- data/data/sub_domains +2 -0
- data/demos/bruter.rb +27 -0
- data/demos/dns_brutes.rb +28 -0
- data/demos/filter_cidr.rb +18 -0
- data/demos/filter_crawls.rb +5 -0
- data/demos/filter_domain.rb +25 -0
- data/demos/filter_geoip.rb +26 -0
- data/demos/filter_known_services.rb +59 -0
- data/demos/filter_netinfo.rb +23 -0
- data/demos/filter_prime.rb +25 -0
- data/demos/filter_profiler.rb +3 -0
- data/demos/filter_redirection.rb +19 -0
- data/demos/filter_site.rb +40 -0
- data/demos/filter_siteip.rb +31 -0
- data/demos/filter_status.rb +17 -0
- data/demos/filter_timestamp.rb +23 -0
- data/demos/filter_url.rb +19 -0
- data/demos/new_fnd.rb +66 -0
- data/demos/nmap_parser.pl +138 -0
- data/demos/site_format.rb +18 -0
- data/demos/whois_domain.rb +78 -0
- data/dicts/GeoIP.dat +0 -0
- data/dicts/GeoIPASNum.dat +0 -0
- data/dicts/GeoLiteCity.dat +0 -0
- data/dicts/ccsld.txt +2646 -0
- data/dicts/cctld.txt +243 -0
- data/dicts/gtld.txt +25 -0
- data/dicts/hostnames-dict.big +1402 -0
- data/dicts/hostnames-dict.txt +101 -0
- data/lib/wmap/cidr_tracker.rb +327 -0
- data/lib/wmap/dns_bruter.rb +308 -0
- data/lib/wmap/domain_tracker/sub_domain.rb +142 -0
- data/lib/wmap/domain_tracker.rb +342 -0
- data/lib/wmap/geoip_tracker.rb +72 -0
- data/lib/wmap/google_search_scraper.rb +177 -0
- data/lib/wmap/host_tracker/primary_host.rb +130 -0
- data/lib/wmap/host_tracker.rb +550 -0
- data/lib/wmap/network_profiler.rb +144 -0
- data/lib/wmap/port_scanner.rb +208 -0
- data/lib/wmap/site_tracker/deactivated_site.rb +85 -0
- data/lib/wmap/site_tracker.rb +937 -0
- data/lib/wmap/url_checker.rb +314 -0
- data/lib/wmap/url_crawler.rb +381 -0
- data/lib/wmap/utils/domain_root.rb +184 -0
- data/lib/wmap/utils/logger.rb +53 -0
- data/lib/wmap/utils/url_magic.rb +343 -0
- data/lib/wmap/utils/utils.rb +333 -0
- data/lib/wmap/whois.rb +76 -0
- data/lib/wmap.rb +227 -0
- data/logs/wmap.log +17 -0
- data/ruby_whois_patches/base_cocca2.rb +149 -0
- data/ruby_whois_patches/kero.yachay.pe.rb +120 -0
- data/ruby_whois_patches/whois.PublicDomainRegistry.com.rb +124 -0
- data/ruby_whois_patches/whois.above.com.rb +61 -0
- data/ruby_whois_patches/whois.adamsnames.tc.rb +107 -0
- data/ruby_whois_patches/whois.aeda.net.ae.rb +105 -0
- data/ruby_whois_patches/whois.ai.rb +112 -0
- data/ruby_whois_patches/whois.arnes.si.rb +121 -0
- data/ruby_whois_patches/whois.ascio.com.rb +91 -0
- data/ruby_whois_patches/whois.cnnic.cn.rb +123 -0
- data/ruby_whois_patches/whois.corporatedomains.com.rb +67 -0
- data/ruby_whois_patches/whois.crsnic.net.rb +108 -0
- data/ruby_whois_patches/whois.denic.de.rb +174 -0
- data/ruby_whois_patches/whois.dk-hostmaster.dk.rb +120 -0
- data/ruby_whois_patches/whois.dns.be.rb +134 -0
- data/ruby_whois_patches/whois.dns.lu.rb +129 -0
- data/ruby_whois_patches/whois.dns.pl.rb +150 -0
- data/ruby_whois_patches/whois.dns.pt.rb +119 -0
- data/ruby_whois_patches/whois.domain.kg.rb +126 -0
- data/ruby_whois_patches/whois.domainregistry.my.rb +123 -0
- data/ruby_whois_patches/whois.domreg.lt.rb +110 -0
- data/ruby_whois_patches/whois.dot.tk.rb +140 -0
- data/ruby_whois_patches/whois.hkirc.hk.rb +121 -0
- data/ruby_whois_patches/whois.isnic.is.rb +130 -0
- data/ruby_whois_patches/whois.je.rb +119 -0
- data/ruby_whois_patches/whois.jprs.jp.rb +137 -0
- data/ruby_whois_patches/whois.kenic.or.ke.rb +140 -0
- data/ruby_whois_patches/whois.markmonitor.com.rb +118 -0
- data/ruby_whois_patches/whois.melbourneit.com.rb +58 -0
- data/ruby_whois_patches/whois.nic.as.rb +96 -0
- data/ruby_whois_patches/whois.nic.at.rb +109 -0
- data/ruby_whois_patches/whois.nic.ch.rb +141 -0
- data/ruby_whois_patches/whois.nic.cl.rb +117 -0
- data/ruby_whois_patches/whois.nic.ec.rb +157 -0
- data/ruby_whois_patches/whois.nic.im.rb +120 -0
- data/ruby_whois_patches/whois.nic.it.rb +170 -0
- data/ruby_whois_patches/whois.nic.lv.rb +116 -0
- data/ruby_whois_patches/whois.nic.ly.rb +127 -0
- data/ruby_whois_patches/whois.nic.mu.rb +27 -0
- data/ruby_whois_patches/whois.nic.mx.rb +123 -0
- data/ruby_whois_patches/whois.nic.net.sa.rb +111 -0
- data/ruby_whois_patches/whois.nic.or.kr.rb +101 -0
- data/ruby_whois_patches/whois.nic.tel.rb +129 -0
- data/ruby_whois_patches/whois.nic.tr.rb +133 -0
- data/ruby_whois_patches/whois.nic.us.rb +129 -0
- data/ruby_whois_patches/whois.nic.ve.rb +135 -0
- data/ruby_whois_patches/whois.norid.no.rb +127 -0
- data/ruby_whois_patches/whois.pandi.or.id.rb +118 -0
- data/ruby_whois_patches/whois.psi-usa.info.rb +63 -0
- data/ruby_whois_patches/whois.registro.br.rb +109 -0
- data/ruby_whois_patches/whois.registrygate.com.rb +55 -0
- data/ruby_whois_patches/whois.rrpproxy.net.rb +61 -0
- data/ruby_whois_patches/whois.sgnic.sg.rb +130 -0
- data/ruby_whois_patches/whois.srs.net.nz.rb +166 -0
- data/ruby_whois_patches/whois.tucows.com.rb +70 -0
- data/ruby_whois_patches/whois.twnic.net.tw.rb +133 -0
- data/settings/discovery_ports +24 -0
- data/settings/google_keywords.txt +9 -0
- data/settings/google_locator.txt +23 -0
- data/test/domain_tracker_test.rb +31 -0
- data/test/utils_test.rb +168 -0
- data/version.txt +13 -0
- data/wmap.gemspec +49 -0
- metadata +202 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Wmap
|
|
3
|
+
#
|
|
4
|
+
# A pure Ruby library for the Internet web application discovery and tracking.
|
|
5
|
+
#
|
|
6
|
+
# Copyright (c) 2012-2015 Yang Li <yang.li@owasp.org>
|
|
7
|
+
#++
|
|
8
|
+
|
|
9
|
+
require "resolv"
|
|
10
|
+
require "netaddr"
|
|
11
|
+
|
|
12
|
+
# Main utility module to provide the common functions across different classes
|
|
13
|
+
module Wmap
|
|
14
|
+
module Utils
|
|
15
|
+
include Wmap::Utils::DomainRoot
|
|
16
|
+
include Wmap::Utils::UrlMagic
|
|
17
|
+
include Wmap::Utils::Logger
|
|
18
|
+
extend self
|
|
19
|
+
|
|
20
|
+
# Load entries from a text file and return an array
|
|
21
|
+
def file_2_list(f,lc=true)
|
|
22
|
+
puts "Loading records from file: #{f}" if @verbose
|
|
23
|
+
begin
|
|
24
|
+
list=Array.new
|
|
25
|
+
file = File.open(f, "r")
|
|
26
|
+
file.each_line do |line|
|
|
27
|
+
line=line.chomp.strip
|
|
28
|
+
next if line.nil?
|
|
29
|
+
next if line.empty?
|
|
30
|
+
next if line =~ /^\s*#/
|
|
31
|
+
line=line.downcase if lc==true
|
|
32
|
+
list.push(line.chomp.strip)
|
|
33
|
+
end
|
|
34
|
+
file.close
|
|
35
|
+
return list
|
|
36
|
+
rescue => ee
|
|
37
|
+
puts "Exception on method #{__method__} for file #{f}: #{ee}" if @verbose
|
|
38
|
+
return nil
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Save an array into a file
|
|
43
|
+
def list_2_file (list,file)
|
|
44
|
+
puts "Save list #{list} to plain file #{file}" if @verbose
|
|
45
|
+
begin
|
|
46
|
+
f = File.open(file, "w")
|
|
47
|
+
list.map do |ent|
|
|
48
|
+
#ent.strip!
|
|
49
|
+
# Append the unix line break
|
|
50
|
+
f.write("#{ent}\n")
|
|
51
|
+
end
|
|
52
|
+
f.close
|
|
53
|
+
rescue => ee
|
|
54
|
+
puts "Exception on method #{__method__} for file #{file}: #{ee}" if @verbose
|
|
55
|
+
return nil
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Load entries from a text file and return a hash
|
|
60
|
+
def file_2_hash(f,lc=true)
|
|
61
|
+
puts "Loading records from file: #{f}" if @verbose
|
|
62
|
+
begin
|
|
63
|
+
hs=Hash.new
|
|
64
|
+
file = File.open(f, "r")
|
|
65
|
+
file.each_line do |line|
|
|
66
|
+
line=line.chomp.strip
|
|
67
|
+
next if line.nil?
|
|
68
|
+
next if line.empty?
|
|
69
|
+
line=line.downcase if lc==true
|
|
70
|
+
next if line =~ /^\s*#/
|
|
71
|
+
hs[line]=true unless hs.key?(line)
|
|
72
|
+
end
|
|
73
|
+
file.close
|
|
74
|
+
return hs
|
|
75
|
+
rescue => ee
|
|
76
|
+
puts "Exception on method #{__method__} on #{f}: #{ee}" if @verbose
|
|
77
|
+
return nil
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Query the name-server to see if the dns record is still valid
|
|
82
|
+
def valid_dns_record? (hostname)
|
|
83
|
+
puts "Validate the hostname record: #{hostname}" if @verbose
|
|
84
|
+
begin
|
|
85
|
+
ips=Resolv.getaddresses(hostname)
|
|
86
|
+
if ips.empty?
|
|
87
|
+
return false
|
|
88
|
+
else
|
|
89
|
+
puts "Found: #{hostname}" if @verbose
|
|
90
|
+
return true
|
|
91
|
+
end
|
|
92
|
+
rescue => ee
|
|
93
|
+
puts "Exception on method #{__method__} for host #{hostname}: #{ee}" if @verbose
|
|
94
|
+
return false
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
alias_method :is_record? , :valid_dns_record?
|
|
98
|
+
alias_method :a_record? , :valid_dns_record?
|
|
99
|
+
|
|
100
|
+
# Test the DNS server if zone transfer is allowed. If allowed, save the found hosts into the class variable.
|
|
101
|
+
def zone_transferable?(domain)
|
|
102
|
+
puts "Check if the domain allows free zone transfer: #{domain}"
|
|
103
|
+
begin
|
|
104
|
+
transferable=false
|
|
105
|
+
domain=domain.downcase
|
|
106
|
+
nameservers = get_nameservers(domain)
|
|
107
|
+
raise "Unable to determine the name servers for domain #{domain}" if nameservers.nil?
|
|
108
|
+
puts "Retrieved name servers: #{nameservers}" if @verbose
|
|
109
|
+
nameservers.each do |nssrv|
|
|
110
|
+
begin
|
|
111
|
+
puts "Attempt zone transfer on name server: #{nssrv}"
|
|
112
|
+
if nssrv.nil?
|
|
113
|
+
abort "Method input variable error: no name server found!" if @verbose
|
|
114
|
+
next
|
|
115
|
+
end
|
|
116
|
+
zt = Dnsruby::ZoneTransfer.new
|
|
117
|
+
zt.server=(nssrv) if nssrv!=""
|
|
118
|
+
records = zt.transfer(domain)
|
|
119
|
+
if records==nil
|
|
120
|
+
puts "#{domain} zone transfer is not allowed on name server: #{nssrv}"
|
|
121
|
+
next
|
|
122
|
+
else
|
|
123
|
+
puts "#{domain} zone transfer is allowed!"
|
|
124
|
+
transferable=true
|
|
125
|
+
end
|
|
126
|
+
rescue Exception=>ee
|
|
127
|
+
puts "Exception on method zone_transferable? for domain #{domain}: #{ee}"
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
return transferable
|
|
131
|
+
rescue Exception => ee
|
|
132
|
+
puts "Exception on method #{__method__}: #{ee}" if @verbose
|
|
133
|
+
return nil
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Test if it's a legitimate IP4 address
|
|
138
|
+
def is_ip? (ip)
|
|
139
|
+
puts "Validate the IP format is valid: #{ip}" if @verbose
|
|
140
|
+
begin
|
|
141
|
+
ip=ip.strip
|
|
142
|
+
raise "This is an URL: #{ip}" if is_url?(ip)
|
|
143
|
+
if ip =~ /\d+\.\d+\.\d+.\d+/ and ip !~ /\/\d+/
|
|
144
|
+
octs=ip.split('.')
|
|
145
|
+
return false unless octs.size==4
|
|
146
|
+
octs.map { |x| return false unless x.to_i >=0 and x.to_i <=255 }
|
|
147
|
+
else
|
|
148
|
+
return false
|
|
149
|
+
end
|
|
150
|
+
puts "Confirmed as a valid IP: #{ip}" if @verbose
|
|
151
|
+
return true
|
|
152
|
+
rescue => ee
|
|
153
|
+
puts "Exception on method is_ip? for #{ip}: #{ee}" if @verbose
|
|
154
|
+
return false
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
alias_method :is_valid_ip?, :is_ip?
|
|
158
|
+
|
|
159
|
+
# Simple test a host string format. Return true if it contains a valid internet domain sub-string. Note: Don't be confused with another method 'valid_dns_record?', which is a stricter and time-consuming test on the DNS server for a resolvable internet host.
|
|
160
|
+
def is_fqdn? (host)
|
|
161
|
+
puts "Validate the host-name format is valid: #{host}" if @verbose
|
|
162
|
+
begin
|
|
163
|
+
return false if is_ip?(host) or is_url?(host)
|
|
164
|
+
domain=get_domain_root(host)
|
|
165
|
+
if domain.nil?
|
|
166
|
+
return false
|
|
167
|
+
elsif is_domain_root?(domain)
|
|
168
|
+
return true
|
|
169
|
+
else
|
|
170
|
+
return false
|
|
171
|
+
end
|
|
172
|
+
rescue => ee
|
|
173
|
+
puts "Exception on method is_fqdn? for #{host}: #{ee}" if @verbose
|
|
174
|
+
return false
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
alias_method :is_host?, :is_fqdn?
|
|
178
|
+
|
|
179
|
+
# Simple test to determine if the entry is in valid network cidr format
|
|
180
|
+
def is_cidr?(cidr)
|
|
181
|
+
puts "Validate if the entry is valid CIDR format: #{cidr}" if @verbose
|
|
182
|
+
begin
|
|
183
|
+
cidr=cidr.strip
|
|
184
|
+
if cidr =~ /^(\d+\.\d+\.\d+.\d+)\/(\d+)$/
|
|
185
|
+
ip=$1
|
|
186
|
+
mask=$2.to_i
|
|
187
|
+
if is_ip?(ip)
|
|
188
|
+
if mask >0 and mask <=32
|
|
189
|
+
puts "confirmed as a valid CIDR entry: #{cidr}" if @verbose
|
|
190
|
+
return true
|
|
191
|
+
else
|
|
192
|
+
return false
|
|
193
|
+
end
|
|
194
|
+
else
|
|
195
|
+
return false
|
|
196
|
+
end
|
|
197
|
+
else
|
|
198
|
+
return false
|
|
199
|
+
end
|
|
200
|
+
rescue => ee
|
|
201
|
+
puts "Exception on method #{__method__}: #{ee}" if @verbose
|
|
202
|
+
return false
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Sort an array of IPs in the ascendant order
|
|
207
|
+
def sort_ips (ips)
|
|
208
|
+
begin
|
|
209
|
+
"Sort the list of IP address in the ascendant order: #{ips}" if @verbose
|
|
210
|
+
return NetAddr.sort(ips)
|
|
211
|
+
rescue => ee
|
|
212
|
+
puts "Exception on method sort_ips for IPs #{ips}: #{ee}" # if @verbose
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Perform the DNS query on a hostname over the Internet. Return the resolved IP(s) in an array
|
|
217
|
+
def host_2_ips (hostname)
|
|
218
|
+
begin
|
|
219
|
+
ips=Array.new
|
|
220
|
+
if is_ip?(hostname)
|
|
221
|
+
ips.push(hostname)
|
|
222
|
+
return ips
|
|
223
|
+
else
|
|
224
|
+
ips = Resolv.getaddresses(hostname)
|
|
225
|
+
if (ips.empty?) then
|
|
226
|
+
puts "Failed to resolve #{hostname}" if @verbose
|
|
227
|
+
return nil
|
|
228
|
+
else
|
|
229
|
+
return ips
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
rescue => ee
|
|
233
|
+
puts "Exception on method host_2_ips for host #{hostname}: #{ee}" if @verbose
|
|
234
|
+
return nil
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
alias_method :ns_lookup, :host_2_ips
|
|
238
|
+
|
|
239
|
+
# Perform DNS query on a hostname. Return the first resolved IP as a string
|
|
240
|
+
def host_2_ip (hostname)
|
|
241
|
+
puts "Perform DNS query on host: #{hostname}" if @verbose
|
|
242
|
+
begin
|
|
243
|
+
ips=Array.new
|
|
244
|
+
if is_ip?(hostname)
|
|
245
|
+
puts "No change - same IP is returned. " if @verbose
|
|
246
|
+
return hostname.strip
|
|
247
|
+
else
|
|
248
|
+
ips=Resolv.getaddresses(hostname)
|
|
249
|
+
if (ips.empty?) then
|
|
250
|
+
puts "Failed to resolve #{hostname}" if @verbose
|
|
251
|
+
return nil
|
|
252
|
+
else
|
|
253
|
+
puts "IP found: #{ips.first}" if @verbose
|
|
254
|
+
return ips.first.strip
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
rescue => ee
|
|
258
|
+
puts "Exception on method host_2_ip for host #{hostname}: #{ee}" if @verbose
|
|
259
|
+
return nil
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Retrieve a list of the authoritative name servers from the Internet whois data repository for the host / subdomain / domain
|
|
264
|
+
def get_nameservers (host)
|
|
265
|
+
puts "Retrieve a list of authoritative name server for: #{host}" if @verbose
|
|
266
|
+
begin
|
|
267
|
+
domain=get_domain_root(host)
|
|
268
|
+
w=Wmap::Whois.new
|
|
269
|
+
ns = w.query(domain).nameservers.map! { |x| x.name }
|
|
270
|
+
if ns.empty?
|
|
271
|
+
puts "No name server found for domain root: #{domain}" if @verbose
|
|
272
|
+
return nil
|
|
273
|
+
else
|
|
274
|
+
return ns
|
|
275
|
+
end
|
|
276
|
+
rescue => ee
|
|
277
|
+
puts "Exception on method get_nameservers for #{host}: #{ee}" if @verbose
|
|
278
|
+
return nil
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Retrieve the first name server from the Internet whois data repository for the host / subdomain / domain
|
|
283
|
+
def get_nameserver (host)
|
|
284
|
+
puts "Retrieve the first authoritative name server for: #{host}" if @verbose
|
|
285
|
+
begin
|
|
286
|
+
domain=get_domain_root(host)
|
|
287
|
+
w=Wmap::Whois.new
|
|
288
|
+
ns = w.query(domain).nameservers.map! { |x| x.name }
|
|
289
|
+
if ns.empty?
|
|
290
|
+
puts "No name server found for domain root: #{domain}" if @verbose
|
|
291
|
+
return nil
|
|
292
|
+
else
|
|
293
|
+
return ns.first
|
|
294
|
+
end
|
|
295
|
+
rescue => ee
|
|
296
|
+
puts "Exception on method get_nameservers for #{host}: #{ee}" if @verbose
|
|
297
|
+
return nil
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
alias_method :get_ns, :get_nameserver
|
|
301
|
+
|
|
302
|
+
# Perform reverse dns lookup for an IP. Return the found 'hostname' if found, or the original IP if not
|
|
303
|
+
def reverse_dns_lookup (ip)
|
|
304
|
+
puts "Retrieve the hostname by the reverse DNS lookup on IP: #{ip}"
|
|
305
|
+
hostname = ip
|
|
306
|
+
begin
|
|
307
|
+
hostname = Socket.gethostbyaddr(ip.split('.').collect{ |x| x.to_i}.pack("CCCC"))[0]
|
|
308
|
+
return hostname.downcase
|
|
309
|
+
rescue => ee
|
|
310
|
+
puts "Exception on method reverse_dns_lookup: #{ee}" if @verbose
|
|
311
|
+
return hostname
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
alias_method :ip_2_host, :reverse_dns_lookup
|
|
315
|
+
|
|
316
|
+
# Convert a CIDR to a list of IPs: Input is a CIDR expression such as '192.168.1.1/30', output is an array of IPs
|
|
317
|
+
def cidr_2_ips (cidr)
|
|
318
|
+
puts "Method to convert a CIDR block into a list of IPs: #{cidr}" if @verbose
|
|
319
|
+
begin
|
|
320
|
+
cidr4 = NetAddr::CIDR.create(cidr)
|
|
321
|
+
ips = cidr4.enumerate(:Limit => 0, :Bitstep => 1)
|
|
322
|
+
#ips2 = ips.slice!(1, (ips.length-2))
|
|
323
|
+
return ips
|
|
324
|
+
rescue => ee
|
|
325
|
+
puts "Exception on method #{__method__}: #{ee}" if @verbose
|
|
326
|
+
return nil
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
end
|
|
333
|
+
end
|
data/lib/wmap/whois.rb
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Wmap
|
|
3
|
+
#
|
|
4
|
+
# A pure Ruby library for Internet web application discovery and tracking.
|
|
5
|
+
#
|
|
6
|
+
# Copyright (c) 2012-2015 Yang Li <yang.li@owasp.org>
|
|
7
|
+
#++
|
|
8
|
+
require "whois"
|
|
9
|
+
|
|
10
|
+
# Wrapper class of the 'ruby-whois' library
|
|
11
|
+
class Wmap::Whois
|
|
12
|
+
include Wmap::Utils
|
|
13
|
+
|
|
14
|
+
attr_accessor :timeout, :verbose
|
|
15
|
+
|
|
16
|
+
# Set default instance variables
|
|
17
|
+
def initialize (params = {})
|
|
18
|
+
@verbose=params.fetch(:verbose, false)
|
|
19
|
+
@timeout=params.fetch(:timeout, 10)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Wrapper for the Ruby Whois client class
|
|
23
|
+
def lookup(object)
|
|
24
|
+
puts "Perform whois lookup on: #{object}" if @verbose
|
|
25
|
+
return Whois.lookup(object)
|
|
26
|
+
end
|
|
27
|
+
alias_method :query, :lookup
|
|
28
|
+
|
|
29
|
+
# Method to extract the netname information from the whois data repository query for an IP
|
|
30
|
+
def get_netname (ip)
|
|
31
|
+
puts "Perform whois lookup on an IP address. Then extract the netname from the query result for the IP: #{ip}" if @verbose
|
|
32
|
+
begin
|
|
33
|
+
ip.strip!
|
|
34
|
+
raise "Unknown IP/CIDR format: #{ip}" unless is_ip?(ip) or is_cidr?(ip)
|
|
35
|
+
content_to_parse=query(ip).to_s
|
|
36
|
+
if content_to_parse =~ /^netname:(.+)\n/i
|
|
37
|
+
return $1.strip
|
|
38
|
+
elsif content_to_parse =~ /^.+\((NET\-.+)\).+\n/i
|
|
39
|
+
return $1.strip
|
|
40
|
+
else
|
|
41
|
+
return "UNKNOWN"
|
|
42
|
+
end
|
|
43
|
+
return "UNKNOWN"
|
|
44
|
+
rescue Exception => ee
|
|
45
|
+
puts "Exception on method get_netname: #{ee}" if @verbose
|
|
46
|
+
return "UNKNOWN"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Method to extract the netname description from the whois data repository query for an IP
|
|
51
|
+
def get_net_desc (ip)
|
|
52
|
+
puts "Perform whois lookup on an IP address. Then extract the netname description from the query result for the IP: #{ip}" if @verbose
|
|
53
|
+
begin
|
|
54
|
+
ip.strip!
|
|
55
|
+
raise "Unknown IP/CIDR format: #{ip}" unless is_ip?(ip) or is_cidr?(ip)
|
|
56
|
+
desc=String.new
|
|
57
|
+
content_to_parse=query(ip).to_s
|
|
58
|
+
content_to_parse.scan(/^descr:(.+)\n/i).flatten.map do |entry|
|
|
59
|
+
desc=desc + " " + entry.strip
|
|
60
|
+
end
|
|
61
|
+
if desc.empty?
|
|
62
|
+
if content_to_parse =~ /^(.+)\((NET\-.+)\).+\n/i
|
|
63
|
+
desc=$1.strip
|
|
64
|
+
elsif content_to_parse =~ /^OrgName:(.+)\n/i
|
|
65
|
+
desc=$1.strip
|
|
66
|
+
else
|
|
67
|
+
desc="UNKNOWN"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
return desc
|
|
71
|
+
rescue Exception => ee
|
|
72
|
+
puts "Exception on method get_net_desc: #{ee}" if @verbose
|
|
73
|
+
return "UNKNOWN"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
data/lib/wmap.rb
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Wmap
|
|
3
|
+
#
|
|
4
|
+
# A pure Ruby library for the Internet web application discovery and tracking.
|
|
5
|
+
#
|
|
6
|
+
# Copyright (c) 2012-2015 Yang Li <yang.li@owasp.org>
|
|
7
|
+
#++
|
|
8
|
+
require 'wmap/utils/domain_root'
|
|
9
|
+
require 'wmap/utils/url_magic'
|
|
10
|
+
require 'wmap/utils/logger'
|
|
11
|
+
require 'wmap/utils/utils'
|
|
12
|
+
require 'wmap/cidr_tracker'
|
|
13
|
+
require 'wmap/domain_tracker'
|
|
14
|
+
require 'wmap/domain_tracker/sub_domain'
|
|
15
|
+
require 'wmap/host_tracker'
|
|
16
|
+
require 'wmap/host_tracker/primary_host'
|
|
17
|
+
require 'wmap/whois'
|
|
18
|
+
require 'wmap/url_checker'
|
|
19
|
+
require 'wmap/network_profiler'
|
|
20
|
+
require 'wmap/port_scanner'
|
|
21
|
+
require 'wmap/url_crawler'
|
|
22
|
+
require 'wmap/dns_bruter'
|
|
23
|
+
require 'wmap/site_tracker'
|
|
24
|
+
require 'wmap/site_tracker/deactivated_site'
|
|
25
|
+
require 'wmap/geoip_tracker'
|
|
26
|
+
require 'wmap/google_search_scraper'
|
|
27
|
+
|
|
28
|
+
module Wmap
|
|
29
|
+
|
|
30
|
+
NAME = "Wmap"
|
|
31
|
+
GEM = "wmap"
|
|
32
|
+
VERSION = File.dirname(__FILE__) + "/../version.txt"
|
|
33
|
+
|
|
34
|
+
class << self
|
|
35
|
+
attr_accessor :known_internet_domains
|
|
36
|
+
attr_writer :verbose
|
|
37
|
+
|
|
38
|
+
# Simple parser for the project version file
|
|
39
|
+
def read_ver
|
|
40
|
+
ver=Hash.new
|
|
41
|
+
f=File.open(VERSION,'r')
|
|
42
|
+
f.each do |line|
|
|
43
|
+
line.chomp!
|
|
44
|
+
case line
|
|
45
|
+
when /^(\s)*#/
|
|
46
|
+
next
|
|
47
|
+
when /\=/
|
|
48
|
+
entry=line.split("=").map! {|x| x.strip}
|
|
49
|
+
ver[entry[0]]=entry[1]
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
f.close
|
|
53
|
+
return ver
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Project banner in ASCII Art 'soft' format, courtesy to http://patorjk.com/software/taag/
|
|
57
|
+
def banner
|
|
58
|
+
ver=read_ver
|
|
59
|
+
art=",--. ,--. ,--. ,--. ,--.
|
|
60
|
+
| | | | ,---. | |-. | `.' | ,--,--. ,---. ,---. ,---. ,--.--.
|
|
61
|
+
| |.'.| || .-. :| .-. ' | |'.'| |' ,-. || .-. || .-. || .-. :| .--'
|
|
62
|
+
| ,'. |\ --.| `-' | | | | |\ '-' || '-' '| '-' '\ --.| |
|
|
63
|
+
'--' '--' `----' `---' `--' `--' `--`--'| |-' | |-' `----'`--'
|
|
64
|
+
`--' `--' "
|
|
65
|
+
string = "-"*80 + "\n" + art + "\n" + "Version: " + ver["version"] + "\tRelease Date: " + ver["date"] + "\nDesigned and developed by: " + ver["author"] + "\nEmail: " + ver["email"] + "\tLinkedIn: " + ver["linkedin"] + "\n" + "-"*80
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Explorer to discover and inventory web application / service automatically
|
|
69
|
+
def wmap(seed)
|
|
70
|
+
cmd="bin/wmap" + " " + seed
|
|
71
|
+
system(cmd)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Crawler to search url contents for new sites
|
|
75
|
+
def crawl(url)
|
|
76
|
+
crawler=Wmap::UrlCrawler.new
|
|
77
|
+
crawler.crawl(url)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# whois query and sort the result into structured data
|
|
81
|
+
def whois(domain)
|
|
82
|
+
whois=Wmap::Whois.new(:verbose=>false)
|
|
83
|
+
whois.query(domain)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Fast tcp port scanner on a single host or IP
|
|
87
|
+
def scan(host)
|
|
88
|
+
scanner=Wmap::PortScanner.new
|
|
89
|
+
scanner.scan(host)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Fast multi-processes tcp port scanner on a list of targets
|
|
93
|
+
def scans(target_list)
|
|
94
|
+
scanner=Wmap::PortScanner.new
|
|
95
|
+
scanner.scans(target_list)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# CIDR Tracking - check the host against the local CIDR seed file, return the CIDR tracking path if found
|
|
99
|
+
def track(host)
|
|
100
|
+
tracker=Wmap::CidrTracker.new
|
|
101
|
+
tracker.cidr_worker(host)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# GeoIP Tracking - check the host / IP against the GeoIP data repository, return the Geographic information if found
|
|
105
|
+
def geoip(host)
|
|
106
|
+
tracker=Wmap::GeoIPTracker.new
|
|
107
|
+
tracker.query(host)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# URL checker - check the status of the remote URL
|
|
111
|
+
def check(url)
|
|
112
|
+
checker=Wmap::UrlChecker.new(:verbose=>false)
|
|
113
|
+
checker.url_worker(url)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Check if the IP is within the range of the known CIDR blocks
|
|
117
|
+
def ip_trusted?(ip)
|
|
118
|
+
tracker=Wmap::CidrTracker.new
|
|
119
|
+
tracker.ip_trusted?(ip)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Domain Tracking - check with the trust domain seed file locally, to determine if it's a new internet domain
|
|
123
|
+
# NOT to confuse with the Internet 'whois' lookup
|
|
124
|
+
def domain_known?(domain)
|
|
125
|
+
tracker=Wmap::DomainTracker.new
|
|
126
|
+
tracker.domain_known?(domain)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Host Tracking - check local hosts file to see if this is a hostname known from the host seed file
|
|
130
|
+
# NOT to confuse with a regular DNS lookup over the internet
|
|
131
|
+
def host_known?(host)
|
|
132
|
+
tracker=Wmap::HostTracker.new.host_known?(host)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Sub-domain tracking - check local hosts file to see if the sub-domain is already known
|
|
136
|
+
def sub_domain_known?(host)
|
|
137
|
+
tracker=Wmap::HostTracker.new.sub_domain_known?(host)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# IP Tracking - check local hosts file to see if this is an IP known from the seed file
|
|
141
|
+
# NOT to confuse with a regular reverse DNS lookup over the internet
|
|
142
|
+
def ip_known?(ip)
|
|
143
|
+
tracker=Wmap::HostTracker.new.ip_known?(ip)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# DNS Brute Forcer
|
|
147
|
+
def dns_brute(domain)
|
|
148
|
+
bruter=Wmap::DnsBruter.new
|
|
149
|
+
bruter.query(domain)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Retrieve root domain from a host
|
|
153
|
+
def domain_root(host)
|
|
154
|
+
Wmap::Utils.get_domain_root(host)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Log the information into file
|
|
158
|
+
def wlog(msg,agent,log_file)
|
|
159
|
+
Wmap::Utils.wlog(msg,agent,log_file)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Host-name mutation for catch easily guessable hostname, i.e. "ww1.example.com" => ["ww1,example.com","ww2.example.com",...]
|
|
163
|
+
def mutation (host)
|
|
164
|
+
Wmap::DnsBruter.new.hostname_mutation(host)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Check URL/Site response code
|
|
168
|
+
def response_code(url)
|
|
169
|
+
checker=Wmap::UrlChecker.new
|
|
170
|
+
checker.response_code(url)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Search the site repository for all entries that match the pattern
|
|
174
|
+
def search(pattern)
|
|
175
|
+
searcher=Wmap::SiteTracker.new
|
|
176
|
+
searcher.search(pattern)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Dump out the unique sites into a plain file
|
|
180
|
+
def dump(file)
|
|
181
|
+
store=Wmap::SiteTracker.new
|
|
182
|
+
store.save_uniq_sites(file)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Dump out the unique sites into a XML file
|
|
186
|
+
def dump_xml(file)
|
|
187
|
+
store=Wmap::SiteTracker.new
|
|
188
|
+
store.save_uniq_sites_xml(file)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Refresh the site information in the local data repository
|
|
192
|
+
def refresh(site)
|
|
193
|
+
store=Wmap::SiteTracker.new
|
|
194
|
+
store.refresh(site)
|
|
195
|
+
store.save!
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Refresh the site information in the local data repository
|
|
199
|
+
def refresh_all
|
|
200
|
+
store=Wmap::SiteTracker.new
|
|
201
|
+
store.refresh_all
|
|
202
|
+
store.save!
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Search the Google engines and sort out sites known by Google
|
|
206
|
+
def google
|
|
207
|
+
sites=Wmap::GoogleSearchScraper.new.workers.keys
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Print a site's full information from the repository
|
|
211
|
+
def print(site)
|
|
212
|
+
searcher=Wmap::SiteTracker.new
|
|
213
|
+
searcher.print_site(site)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Print a site's full information from the repository
|
|
217
|
+
def print_all
|
|
218
|
+
searcher=Wmap::SiteTracker.new
|
|
219
|
+
searcher.print_all_sites
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
private
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
end
|
|
227
|
+
end
|
data/logs/wmap.log
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
2018-01-10 16:23:21 -0500: wmap: Execute the command: wmap /Users/ylee/www_wmap/uploads/1/seed
|
|
2
|
+
2018-01-10 16:39:40 -0500: wmap: Execute the command: wmap uploads/1/seed
|
|
3
|
+
2018-01-10 16:53:13 -0500: wmap: Execute the command: wmap /Users/ylee/www_wmap/uploads/1/seed
|
|
4
|
+
2018-01-10 17:04:02 -0500: wmap: Execute the command: wmap uploads/1/seed
|
|
5
|
+
2018-01-11 14:33:42 -0500: wmap: Execute the command: wmap uploads/1/seed
|
|
6
|
+
2018-01-11 14:43:00 -0500: wmap: Execute the command: wmap /Users/ylee/www_wmap/uploads/1/seed
|
|
7
|
+
2018-01-11 14:47:02 -0500: wmap: Execute the command: wmap uploads/1/seed
|
|
8
|
+
2018-01-11 14:52:09 -0500: wmap: Execute the command: wmap www.cnn.com
|
|
9
|
+
2018-01-11 15:25:26 -0500: wmap: Execute the command: wmap uploads/1/seed
|
|
10
|
+
2018-01-11 15:25:39 -0500: wdump: Execute the command: wdump
|
|
11
|
+
2018-01-11 15:25:46 -0500: wdump: Execute the command: wdump /tmp/out
|
|
12
|
+
2018-01-11 15:30:42 -0500: wmap: Execute the command: wmap uploads/1/seed
|
|
13
|
+
2018-01-11 15:46:13 -0500: wmap: Execute the command: wmap uploads/1/seed
|
|
14
|
+
2018-01-11 15:54:04 -0500: wmap: Execute the command: wmap uploads/1/seed
|
|
15
|
+
2018-01-11 15:56:46 -0500: wmap: Execute the command: wmap uploads/1/seed
|
|
16
|
+
2018-01-12 14:04:50 -0500: wmap: Execute the command: wmap /Users/ylee/www_wmap/uploads/1/seed
|
|
17
|
+
2018-01-16 11:25:58 -0500: wmap: Execute the command: wmap 206.156.53.0/24
|