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.
Files changed (79) hide show
  1. data/CHANGELOG.md +33 -0
  2. data/README.md +2 -4
  3. data/Rakefile +15 -4
  4. data/bin/arachni +0 -0
  5. data/bin/arachni_web +0 -0
  6. data/bin/arachni_web_autostart +0 -0
  7. data/bin/arachni_xmlrpc +0 -0
  8. data/bin/arachni_xmlrpcd +0 -0
  9. data/bin/arachni_xmlrpcd_monitor +0 -0
  10. data/lib/arachni.rb +1 -1
  11. data/lib/framework.rb +36 -6
  12. data/lib/http.rb +12 -5
  13. data/lib/module/auditor.rb +482 -59
  14. data/lib/module/base.rb +17 -0
  15. data/lib/module/manager.rb +26 -2
  16. data/lib/module/trainer.rb +1 -12
  17. data/lib/module/utilities.rb +12 -0
  18. data/lib/parser/auditable.rb +8 -3
  19. data/lib/parser/elements.rb +11 -0
  20. data/lib/parser/page.rb +3 -1
  21. data/lib/parser/parser.rb +130 -18
  22. data/lib/rpc/xml/server/dispatcher.rb +21 -0
  23. data/lib/spider.rb +141 -82
  24. data/lib/ui/cli/cli.rb +2 -3
  25. data/lib/ui/web/addon_manager.rb +273 -0
  26. data/lib/ui/web/addons/autodeploy.rb +172 -0
  27. data/lib/ui/web/addons/autodeploy/lib/manager.rb +291 -0
  28. data/lib/ui/web/addons/autodeploy/views/index.erb +124 -0
  29. data/lib/ui/web/addons/sample.rb +78 -0
  30. data/lib/ui/web/addons/sample/views/index.erb +4 -0
  31. data/lib/ui/web/addons/scheduler.rb +139 -0
  32. data/lib/ui/web/addons/scheduler/views/index.erb +131 -0
  33. data/lib/ui/web/addons/scheduler/views/options.erb +93 -0
  34. data/lib/ui/web/dispatcher_manager.rb +80 -13
  35. data/lib/ui/web/instance_manager.rb +87 -0
  36. data/lib/ui/web/scheduler.rb +166 -0
  37. data/lib/ui/web/server.rb +142 -202
  38. data/lib/ui/web/server/public/js/jquery-ui-timepicker.js +985 -0
  39. data/lib/ui/web/server/public/plugins/sample/style.css +0 -0
  40. data/lib/ui/web/server/public/style.css +42 -0
  41. data/lib/ui/web/server/views/addon.erb +15 -0
  42. data/lib/ui/web/server/views/addons.erb +46 -0
  43. data/lib/ui/web/server/views/dispatchers.erb +1 -1
  44. data/lib/ui/web/server/views/instance.erb +9 -11
  45. data/lib/ui/web/server/views/layout.erb +14 -1
  46. data/lib/ui/web/server/views/welcome.erb +7 -6
  47. data/lib/ui/web/utilities.rb +134 -0
  48. data/modules/audit/code_injection_timing.rb +6 -2
  49. data/modules/audit/code_injection_timing/payloads.txt +2 -2
  50. data/modules/audit/os_cmd_injection_timing.rb +7 -3
  51. data/modules/audit/os_cmd_injection_timing/payloads.txt +1 -1
  52. data/modules/audit/sqli_blind_rdiff.rb +18 -233
  53. data/modules/audit/sqli_blind_rdiff/payloads.txt +5 -0
  54. data/modules/audit/sqli_blind_timing.rb +9 -2
  55. data/path_extractors/anchors.rb +1 -1
  56. data/path_extractors/forms.rb +1 -1
  57. data/path_extractors/frames.rb +1 -1
  58. data/path_extractors/generic.rb +1 -1
  59. data/path_extractors/links.rb +1 -1
  60. data/path_extractors/meta_refresh.rb +1 -1
  61. data/path_extractors/scripts.rb +1 -1
  62. data/path_extractors/sitemap.rb +1 -1
  63. data/plugins/proxy/server.rb +3 -2
  64. data/plugins/waf_detector.rb +0 -3
  65. metadata +37 -34
  66. data/lib/anemone/cookie_store.rb +0 -35
  67. data/lib/anemone/core.rb +0 -371
  68. data/lib/anemone/exceptions.rb +0 -5
  69. data/lib/anemone/http.rb +0 -144
  70. data/lib/anemone/page.rb +0 -338
  71. data/lib/anemone/page_store.rb +0 -160
  72. data/lib/anemone/storage.rb +0 -34
  73. data/lib/anemone/storage/base.rb +0 -75
  74. data/lib/anemone/storage/exceptions.rb +0 -15
  75. data/lib/anemone/storage/mongodb.rb +0 -89
  76. data/lib/anemone/storage/pstore.rb +0 -50
  77. data/lib/anemone/storage/redis.rb +0 -90
  78. data/lib/anemone/storage/tokyo_cabinet.rb +0 -57
  79. data/lib/anemone/tentacle.rb +0 -40
@@ -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
@@ -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
@@ -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,15 +0,0 @@
1
- module Anemone
2
- module Storage
3
-
4
- class GenericError < Error; end;
5
-
6
- class ConnectionError < Error; end
7
-
8
- class RetrievalError < Error; end
9
-
10
- class InsertionError < Error; end
11
-
12
- class CloseError < Error; end
13
-
14
- end
15
- 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