arachni 0.2.4 → 0.3
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.
- data/CHANGELOG.md +33 -0
- data/README.md +2 -4
- data/Rakefile +15 -4
- data/bin/arachni +0 -0
- data/bin/arachni_web +0 -0
- data/bin/arachni_web_autostart +0 -0
- data/bin/arachni_xmlrpc +0 -0
- data/bin/arachni_xmlrpcd +0 -0
- data/bin/arachni_xmlrpcd_monitor +0 -0
- data/lib/arachni.rb +1 -1
- data/lib/framework.rb +36 -6
- data/lib/http.rb +12 -5
- data/lib/module/auditor.rb +482 -59
- data/lib/module/base.rb +17 -0
- data/lib/module/manager.rb +26 -2
- data/lib/module/trainer.rb +1 -12
- data/lib/module/utilities.rb +12 -0
- data/lib/parser/auditable.rb +8 -3
- data/lib/parser/elements.rb +11 -0
- data/lib/parser/page.rb +3 -1
- data/lib/parser/parser.rb +130 -18
- data/lib/rpc/xml/server/dispatcher.rb +21 -0
- data/lib/spider.rb +141 -82
- data/lib/ui/cli/cli.rb +2 -3
- data/lib/ui/web/addon_manager.rb +273 -0
- data/lib/ui/web/addons/autodeploy.rb +172 -0
- data/lib/ui/web/addons/autodeploy/lib/manager.rb +291 -0
- data/lib/ui/web/addons/autodeploy/views/index.erb +124 -0
- data/lib/ui/web/addons/sample.rb +78 -0
- data/lib/ui/web/addons/sample/views/index.erb +4 -0
- data/lib/ui/web/addons/scheduler.rb +139 -0
- data/lib/ui/web/addons/scheduler/views/index.erb +131 -0
- data/lib/ui/web/addons/scheduler/views/options.erb +93 -0
- data/lib/ui/web/dispatcher_manager.rb +80 -13
- data/lib/ui/web/instance_manager.rb +87 -0
- data/lib/ui/web/scheduler.rb +166 -0
- data/lib/ui/web/server.rb +142 -202
- data/lib/ui/web/server/public/js/jquery-ui-timepicker.js +985 -0
- data/lib/ui/web/server/public/plugins/sample/style.css +0 -0
- data/lib/ui/web/server/public/style.css +42 -0
- data/lib/ui/web/server/views/addon.erb +15 -0
- data/lib/ui/web/server/views/addons.erb +46 -0
- data/lib/ui/web/server/views/dispatchers.erb +1 -1
- data/lib/ui/web/server/views/instance.erb +9 -11
- data/lib/ui/web/server/views/layout.erb +14 -1
- data/lib/ui/web/server/views/welcome.erb +7 -6
- data/lib/ui/web/utilities.rb +134 -0
- data/modules/audit/code_injection_timing.rb +6 -2
- data/modules/audit/code_injection_timing/payloads.txt +2 -2
- data/modules/audit/os_cmd_injection_timing.rb +7 -3
- data/modules/audit/os_cmd_injection_timing/payloads.txt +1 -1
- data/modules/audit/sqli_blind_rdiff.rb +18 -233
- data/modules/audit/sqli_blind_rdiff/payloads.txt +5 -0
- data/modules/audit/sqli_blind_timing.rb +9 -2
- data/path_extractors/anchors.rb +1 -1
- data/path_extractors/forms.rb +1 -1
- data/path_extractors/frames.rb +1 -1
- data/path_extractors/generic.rb +1 -1
- data/path_extractors/links.rb +1 -1
- data/path_extractors/meta_refresh.rb +1 -1
- data/path_extractors/scripts.rb +1 -1
- data/path_extractors/sitemap.rb +1 -1
- data/plugins/proxy/server.rb +3 -2
- data/plugins/waf_detector.rb +0 -3
- metadata +37 -34
- data/lib/anemone/cookie_store.rb +0 -35
- data/lib/anemone/core.rb +0 -371
- data/lib/anemone/exceptions.rb +0 -5
- data/lib/anemone/http.rb +0 -144
- data/lib/anemone/page.rb +0 -338
- data/lib/anemone/page_store.rb +0 -160
- data/lib/anemone/storage.rb +0 -34
- data/lib/anemone/storage/base.rb +0 -75
- data/lib/anemone/storage/exceptions.rb +0 -15
- data/lib/anemone/storage/mongodb.rb +0 -89
- data/lib/anemone/storage/pstore.rb +0 -50
- data/lib/anemone/storage/redis.rb +0 -90
- data/lib/anemone/storage/tokyo_cabinet.rb +0 -57
- data/lib/anemone/tentacle.rb +0 -40
data/lib/anemone/page_store.rb
DELETED
@@ -1,160 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
|
3
|
-
module Anemone
|
4
|
-
class PageStore
|
5
|
-
extend Forwardable
|
6
|
-
|
7
|
-
def_delegators :@storage, :keys, :values, :size, :each
|
8
|
-
|
9
|
-
def initialize(storage = {})
|
10
|
-
@storage = storage
|
11
|
-
end
|
12
|
-
|
13
|
-
# We typically index the hash with a URI,
|
14
|
-
# but convert it to a String for easier retrieval
|
15
|
-
def [](index)
|
16
|
-
@storage[index.to_s]
|
17
|
-
end
|
18
|
-
|
19
|
-
def []=(index, other)
|
20
|
-
@storage[index.to_s] = other
|
21
|
-
end
|
22
|
-
|
23
|
-
def delete(key)
|
24
|
-
@storage.delete key.to_s
|
25
|
-
end
|
26
|
-
|
27
|
-
def has_key?(key)
|
28
|
-
@storage.has_key? key.to_s
|
29
|
-
end
|
30
|
-
|
31
|
-
def each_value
|
32
|
-
each { |key, value| yield value }
|
33
|
-
end
|
34
|
-
|
35
|
-
def values
|
36
|
-
result = []
|
37
|
-
each { |key, value| result << value }
|
38
|
-
result
|
39
|
-
end
|
40
|
-
|
41
|
-
def touch_key(key)
|
42
|
-
self[key] = Page.new(key)
|
43
|
-
end
|
44
|
-
|
45
|
-
def touch_keys(keys)
|
46
|
-
@storage.merge! keys.inject({}) { |h, k| h[k.to_s] = Page.new(k); h }
|
47
|
-
end
|
48
|
-
|
49
|
-
# Does this PageStore contain the specified URL?
|
50
|
-
# HTTP and HTTPS versions of a URL are considered to be the same page.
|
51
|
-
def has_page?(url)
|
52
|
-
schemes = %w(http https)
|
53
|
-
if schemes.include? url.scheme
|
54
|
-
u = url.dup
|
55
|
-
return schemes.any? { |s| u.scheme = s; has_key?(u) }
|
56
|
-
end
|
57
|
-
|
58
|
-
has_key? url
|
59
|
-
end
|
60
|
-
|
61
|
-
#
|
62
|
-
# Use a breadth-first search to calculate the single-source
|
63
|
-
# shortest paths from *root* to all pages in the PageStore
|
64
|
-
#
|
65
|
-
def shortest_paths!(root)
|
66
|
-
root = URI(root) if root.is_a?(String)
|
67
|
-
raise "Root node not found" if !has_key?(root)
|
68
|
-
|
69
|
-
q = Queue.new
|
70
|
-
|
71
|
-
q.enq root
|
72
|
-
root_page = self[root]
|
73
|
-
root_page.depth = 0
|
74
|
-
root_page.visited = true
|
75
|
-
self[root] = root_page
|
76
|
-
while !q.empty?
|
77
|
-
page = self[q.deq]
|
78
|
-
page.links.each do |u|
|
79
|
-
begin
|
80
|
-
link = self[u]
|
81
|
-
next if link.nil? || !link.fetched? || link.visited
|
82
|
-
|
83
|
-
q << u unless link.redirect?
|
84
|
-
link.visited = true
|
85
|
-
link.depth = page.depth + 1
|
86
|
-
self[u] = link
|
87
|
-
|
88
|
-
if link.redirect?
|
89
|
-
u = link.redirect_to
|
90
|
-
redo
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
self
|
97
|
-
end
|
98
|
-
|
99
|
-
#
|
100
|
-
# Removes all Pages from storage where redirect? is true
|
101
|
-
#
|
102
|
-
def uniq!
|
103
|
-
each_value { |page| delete page.url if page.redirect? }
|
104
|
-
self
|
105
|
-
end
|
106
|
-
|
107
|
-
#
|
108
|
-
# If given a single URL (as a String or URI), returns an Array of Pages which link to that URL
|
109
|
-
# If given an Array of URLs, returns a Hash (URI => [Page, Page...]) of Pages linking to those URLs
|
110
|
-
#
|
111
|
-
def pages_linking_to(urls)
|
112
|
-
unless urls.is_a?(Array)
|
113
|
-
urls = [urls]
|
114
|
-
single = true
|
115
|
-
end
|
116
|
-
|
117
|
-
urls.map! do |url|
|
118
|
-
unless url.is_a?(URI)
|
119
|
-
URI(url) rescue nil
|
120
|
-
else
|
121
|
-
url
|
122
|
-
end
|
123
|
-
end
|
124
|
-
urls.compact
|
125
|
-
|
126
|
-
links = {}
|
127
|
-
urls.each { |url| links[url] = [] }
|
128
|
-
values.each do |page|
|
129
|
-
urls.each { |url| links[url] << page if page.links.include?(url) }
|
130
|
-
end
|
131
|
-
|
132
|
-
if single and !links.empty?
|
133
|
-
return links[urls.first]
|
134
|
-
else
|
135
|
-
return links
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
#
|
140
|
-
# If given a single URL (as a String or URI), returns an Array of URLs which link to that URL
|
141
|
-
# If given an Array of URLs, returns a Hash (URI => [URI, URI...]) of URLs linking to those URLs
|
142
|
-
#
|
143
|
-
def urls_linking_to(urls)
|
144
|
-
unless urls.is_a?(Array)
|
145
|
-
urls = [urls] unless urls.is_a?(Array)
|
146
|
-
single = true
|
147
|
-
end
|
148
|
-
|
149
|
-
links = pages_linking_to(urls)
|
150
|
-
links.each { |url, pages| links[url] = pages.map{|p| p.url} }
|
151
|
-
|
152
|
-
if single and !links.empty?
|
153
|
-
return links[urls.first]
|
154
|
-
else
|
155
|
-
return links
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
end
|
160
|
-
end
|
data/lib/anemone/storage.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
module Anemone
|
2
|
-
module Storage
|
3
|
-
|
4
|
-
def self.Hash(*args)
|
5
|
-
hash = Hash.new(*args)
|
6
|
-
# add close method for compatibility with Storage::Base
|
7
|
-
class << hash; def close; end; end
|
8
|
-
hash
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.PStore(*args)
|
12
|
-
require Arachni::Options.instance.dir['lib'] + 'anemone/storage/pstore'
|
13
|
-
self::PStore.new(*args)
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.TokyoCabinet(file = 'anemone.tch')
|
17
|
-
require Arachni::Options.instance.dir['lib'] + 'anemone/storage/tokyo_cabinet'
|
18
|
-
self::TokyoCabinet.new(file)
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.MongoDB(mongo_db = nil, collection_name = 'pages')
|
22
|
-
require Arachni::Options.instance.dir['lib'] + 'anemone/storage/mongodb'
|
23
|
-
mongo_db ||= Mongo::Connection.new.db('anemone')
|
24
|
-
raise "First argument must be an instance of Mongo::DB" unless mongo_db.is_a?(Mongo::DB)
|
25
|
-
self::MongoDB.new(mongo_db, collection_name)
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.Redis(opts = {})
|
29
|
-
require Arachni::Options.instance.dir['lib'] + 'anemone/storage/redis'
|
30
|
-
self::Redis.new(opts)
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
34
|
-
end
|
data/lib/anemone/storage/base.rb
DELETED
@@ -1,75 +0,0 @@
|
|
1
|
-
require Arachni::Options.instance.dir['lib'] + 'anemone/storage/exceptions'
|
2
|
-
|
3
|
-
module Anemone
|
4
|
-
module Storage
|
5
|
-
class Base
|
6
|
-
|
7
|
-
def initialize(adapter)
|
8
|
-
@adap = adapter
|
9
|
-
|
10
|
-
# verify adapter conforms to this class's methods
|
11
|
-
methods.each do |method|
|
12
|
-
if !@adap.respond_to?(method.to_sym)
|
13
|
-
raise "Storage adapter must support method #{method}"
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def [](key)
|
19
|
-
@adap[key]
|
20
|
-
rescue
|
21
|
-
puts key
|
22
|
-
raise RetrievalError, $!
|
23
|
-
end
|
24
|
-
|
25
|
-
def []=(key, value)
|
26
|
-
@adap[key] = value
|
27
|
-
rescue
|
28
|
-
raise InsertionError, $!
|
29
|
-
end
|
30
|
-
|
31
|
-
def delete(key)
|
32
|
-
@adap.delete(key)
|
33
|
-
rescue
|
34
|
-
raise DeletionError, $!
|
35
|
-
end
|
36
|
-
|
37
|
-
def each
|
38
|
-
@adap.each { |k, v| yield k, v }
|
39
|
-
rescue
|
40
|
-
raise GenericError, $!
|
41
|
-
end
|
42
|
-
|
43
|
-
def merge!(hash)
|
44
|
-
@adap.merge!(hash)
|
45
|
-
rescue
|
46
|
-
raise GenericError, $!
|
47
|
-
end
|
48
|
-
|
49
|
-
def close
|
50
|
-
@adap.close
|
51
|
-
rescue
|
52
|
-
raise CloseError, $!
|
53
|
-
end
|
54
|
-
|
55
|
-
def size
|
56
|
-
@adap.size
|
57
|
-
rescue
|
58
|
-
raise GenericError, $!
|
59
|
-
end
|
60
|
-
|
61
|
-
def keys
|
62
|
-
@adap.keys
|
63
|
-
rescue
|
64
|
-
raise GenericError, $!
|
65
|
-
end
|
66
|
-
|
67
|
-
def has_key?(key)
|
68
|
-
@adap.has_key?(key)
|
69
|
-
rescue
|
70
|
-
raise GenericError, $!
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,89 +0,0 @@
|
|
1
|
-
begin
|
2
|
-
require 'mongo'
|
3
|
-
rescue LoadError
|
4
|
-
puts "You need the mongo gem to use Anemone::Storage::MongoDB"
|
5
|
-
exit
|
6
|
-
end
|
7
|
-
|
8
|
-
module Anemone
|
9
|
-
module Storage
|
10
|
-
class MongoDB
|
11
|
-
|
12
|
-
BINARY_FIELDS = %w(body headers data)
|
13
|
-
|
14
|
-
def initialize(mongo_db, collection_name)
|
15
|
-
@db = mongo_db
|
16
|
-
@collection = @db[collection_name]
|
17
|
-
@collection.remove
|
18
|
-
@collection.create_index 'url'
|
19
|
-
end
|
20
|
-
|
21
|
-
def [](url)
|
22
|
-
if value = @collection.find_one('url' => url.to_s)
|
23
|
-
load_page(value)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def []=(url, page)
|
28
|
-
hash = page.to_hash
|
29
|
-
BINARY_FIELDS.each do |field|
|
30
|
-
hash[field] = BSON::Binary.new(hash[field]) unless hash[field].nil?
|
31
|
-
end
|
32
|
-
@collection.update(
|
33
|
-
{'url' => page.url.to_s},
|
34
|
-
hash,
|
35
|
-
:upsert => true
|
36
|
-
)
|
37
|
-
end
|
38
|
-
|
39
|
-
def delete(url)
|
40
|
-
page = self[url]
|
41
|
-
@collection.remove('url' => url.to_s)
|
42
|
-
page
|
43
|
-
end
|
44
|
-
|
45
|
-
def each
|
46
|
-
@collection.find do |cursor|
|
47
|
-
cursor.each do |doc|
|
48
|
-
page = load_page(doc)
|
49
|
-
yield page.url.to_s, page
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def merge!(hash)
|
55
|
-
hash.each { |key, value| self[key] = value }
|
56
|
-
self
|
57
|
-
end
|
58
|
-
|
59
|
-
def size
|
60
|
-
@collection.count
|
61
|
-
end
|
62
|
-
|
63
|
-
def keys
|
64
|
-
keys = []
|
65
|
-
self.each { |k, v| keys << k.to_s }
|
66
|
-
keys
|
67
|
-
end
|
68
|
-
|
69
|
-
def has_key?(url)
|
70
|
-
!!@collection.find_one('url' => url.to_s)
|
71
|
-
end
|
72
|
-
|
73
|
-
def close
|
74
|
-
@db.connection.close
|
75
|
-
end
|
76
|
-
|
77
|
-
private
|
78
|
-
|
79
|
-
def load_page(hash)
|
80
|
-
BINARY_FIELDS.each do |field|
|
81
|
-
hash[field] = hash[field].to_s
|
82
|
-
end
|
83
|
-
Page.from_hash(hash)
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
@@ -1,50 +0,0 @@
|
|
1
|
-
require 'pstore'
|
2
|
-
require 'forwardable'
|
3
|
-
|
4
|
-
module Anemone
|
5
|
-
module Storage
|
6
|
-
class PStore
|
7
|
-
extend Forwardable
|
8
|
-
|
9
|
-
def_delegators :@keys, :has_key?, :keys, :size
|
10
|
-
|
11
|
-
def initialize(file)
|
12
|
-
File.delete(file) if File.exists?(file)
|
13
|
-
@store = ::PStore.new(file)
|
14
|
-
@keys = {}
|
15
|
-
end
|
16
|
-
|
17
|
-
def [](key)
|
18
|
-
@store.transaction { |s| s[key] }
|
19
|
-
end
|
20
|
-
|
21
|
-
def []=(key,value)
|
22
|
-
@keys[key] = nil
|
23
|
-
@store.transaction { |s| s[key] = value }
|
24
|
-
end
|
25
|
-
|
26
|
-
def delete(key)
|
27
|
-
@keys.delete(key)
|
28
|
-
@store.transaction { |s| s.delete key}
|
29
|
-
end
|
30
|
-
|
31
|
-
def each
|
32
|
-
@keys.each_key do |key|
|
33
|
-
value = nil
|
34
|
-
@store.transaction { |s| value = s[key] }
|
35
|
-
yield key, value
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def merge!(hash)
|
40
|
-
@store.transaction do |s|
|
41
|
-
hash.each { |key, value| s[key] = value; @keys[key] = nil }
|
42
|
-
end
|
43
|
-
self
|
44
|
-
end
|
45
|
-
|
46
|
-
def close; end
|
47
|
-
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
@@ -1,90 +0,0 @@
|
|
1
|
-
require 'redis'
|
2
|
-
|
3
|
-
module Anemone
|
4
|
-
module Storage
|
5
|
-
class Redis
|
6
|
-
|
7
|
-
MARSHAL_FIELDS = %w(links visited fetched)
|
8
|
-
|
9
|
-
def initialize(opts = {})
|
10
|
-
@redis = ::Redis.new(opts)
|
11
|
-
@key_prefix = opts[:key_prefix] || 'anemone'
|
12
|
-
keys.each { |key| delete(key) }
|
13
|
-
end
|
14
|
-
|
15
|
-
def [](key)
|
16
|
-
rkey = "#{@key_prefix}:pages:#{key.to_s}"
|
17
|
-
rget(rkey)
|
18
|
-
end
|
19
|
-
|
20
|
-
def []=(key, value)
|
21
|
-
rkey = "#{@key_prefix}:pages:#{key.to_s}"
|
22
|
-
hash = value.to_hash
|
23
|
-
MARSHAL_FIELDS.each do |field|
|
24
|
-
hash[field] = Marshal.dump(hash[field])
|
25
|
-
end
|
26
|
-
hash.each do |field, value|
|
27
|
-
@redis.hset(rkey, field, value)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def delete(key)
|
32
|
-
rkey = "#{@key_prefix}:pages:#{key.to_s}"
|
33
|
-
page = self[key]
|
34
|
-
@redis.del(rkey)
|
35
|
-
page
|
36
|
-
end
|
37
|
-
|
38
|
-
def each
|
39
|
-
rkeys = @redis.keys("#{@key_prefix}:pages:*")
|
40
|
-
rkeys.each do |rkey|
|
41
|
-
page = rget(rkey)
|
42
|
-
yield page.url.to_s, page
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def merge!(hash)
|
47
|
-
hash.each { |key, value| self[key] = value }
|
48
|
-
self
|
49
|
-
end
|
50
|
-
|
51
|
-
def size
|
52
|
-
@redis.keys("#{@key_prefix}:pages:*").size
|
53
|
-
end
|
54
|
-
|
55
|
-
def keys
|
56
|
-
keys = []
|
57
|
-
self.each { |k, v| keys << k.to_s }
|
58
|
-
keys
|
59
|
-
end
|
60
|
-
|
61
|
-
def has_key?(key)
|
62
|
-
rkey = "#{@key_prefix}:pages:#{key.to_s}"
|
63
|
-
@redis.exists(rkey)
|
64
|
-
end
|
65
|
-
|
66
|
-
def close
|
67
|
-
@redis.quit
|
68
|
-
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
def load_value(hash)
|
73
|
-
MARSHAL_FIELDS.each do |field|
|
74
|
-
unless hash[field].nil? || hash[field] == ''
|
75
|
-
hash[field] = Marshal.load(hash[field])
|
76
|
-
end
|
77
|
-
end
|
78
|
-
Page.from_hash(hash)
|
79
|
-
end
|
80
|
-
|
81
|
-
def rget(rkey)
|
82
|
-
hash = @redis.hgetall(rkey)
|
83
|
-
if !!hash
|
84
|
-
load_value(hash)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|