arachni 0.2.4 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
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