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,3 +1,3 @@
1
1
  ping -n __TIME__ localhost
2
2
  ping -c __TIME__ localhost
3
- /usr/sbin/ping -s localhost 1000 10
3
+ /usr/sbin/ping -s localhost 1000 __TIME__
@@ -35,209 +35,32 @@ class BlindrDiffSQLInjection < Arachni::Module::Base
35
35
  super( page )
36
36
  end
37
37
 
38
- def prepare( )
38
+ def prepare
39
+ @@__bools ||= []
39
40
 
40
- # possible quote characters used in SQL statements
41
- @__quotes = [
42
- '\'',
43
- '"',
44
- ''
45
- ]
41
+ if @@__bools.empty?
42
+ read_file( 'payloads.txt' ) {
43
+ |str|
46
44
 
47
- # this will cause a silent error if there's a blind SQL injection
48
- @__bad_chars =[
49
- '\'"`',
50
- # we need 2 requests thus we change the second one a little bit to
51
- # fool the Auditor's redundancy filter
52
- '\'"``'
53
- ]
54
-
55
- # %q% will be replaced by a character in @__quotes
56
- @__injection = '%q% and %q%1'
57
-
58
- @__opts = {
59
- :format => [ Format::APPEND ],
60
- # we need to do our own redundancy checks
61
- :redundant => true
62
- }
63
-
64
- # used for redundancy checks
65
- @@__audited ||= Set.new
66
-
67
- # this is the structure of the responses
68
- @responses = {
69
- :orig => '',
70
-
71
- :good => {
72
-
73
- },
74
- :bad => {
75
- }
76
- }
77
-
78
- end
79
-
80
- def run( )
81
-
82
- return if( __audited? )
83
-
84
- if( !@page.query_vars || @page.query_vars.empty? )
85
- print_status( 'Nothing to audit on current page, skipping...' )
86
- return
87
- end
88
-
89
- # get the link object that fits the URL of the page
90
- # this will be the one to audit
91
- @page.links.each {
92
- |link|
93
- @__candidate = link if link.action == @page.url
94
- }
95
-
96
- return if !@__candidate
97
-
98
- # let's get a fresh rendering of the page to assist us with
99
- # irrelevant dynamic content elimination (banners, ads, etc...)
100
- @http.get( @page.url, :params => @page.query_vars ).on_complete {
101
- |res|
102
-
103
- # eliminate dynamic content that's context-irrelevant
104
- # ie. changing with every refresh
105
- @responses[:orig] = @page.html.rdiff( res.body )
106
- }
107
-
108
- # force the webapp to return an error page
109
- prep_bad_responses( )
110
-
111
- # start injecting 'good' SQL queries
112
- prep_good_responses( )
113
-
114
- @http.after_run {
115
- # analyze the HTML code of the responses in order to determine
116
- # which injections were succesfull
117
- analyze( )
118
- }
119
- end
120
-
121
- # Audits page with 'bad' SQL characters and gathers error pages
122
- def prep_bad_responses( )
123
-
124
- @__bad_chars.each {
125
- |str|
126
-
127
- # get injection variations that will hopefully cause an internal/silent
128
- # SQL error
129
- variations = @__candidate.injection_sets( str, @__opts )
130
-
131
- @responses[:bad_total] = variations.size
132
-
133
- variations.each {
134
- |link|
135
-
136
- # the altered link variable
137
- altered = link.altered
138
-
139
- print_status( @__candidate.get_status_str( altered ) )
140
-
141
- # register us as the auditor
142
- link.auditor( self )
143
- # submit the link and get the response
144
- link.submit( @__opts ).on_complete {
145
- |res|
146
-
147
- @responses[:bad][altered] ||= res.body.clone
148
-
149
- # remove context-irrelevant dynamic content like banners and such
150
- # from the error page
151
- @responses[:bad][altered] = @responses[:bad][altered].rdiff( res.body.clone )
45
+ [ '\'', '"', '' ].each {
46
+ |quote|
47
+ @@__bools << str.gsub( '%q%', quote )
152
48
  }
153
49
  }
154
- }
155
- end
156
-
157
- # Injects SQL code that doesn't affect the flow of execution nor presentation
158
- def prep_good_responses( )
159
-
160
- @__quotes.each {
161
- |quote|
162
-
163
- # prepare the statement with combinations of quote characters
164
- str = @__injection.gsub( '%q%', quote )
165
-
166
- variations = @__candidate.injection_sets( str, @__opts )
167
-
168
- @responses[:good_total] = variations.size
169
-
170
- variations.each {
171
- |link|
172
-
173
- # the altered link variable
174
- altered = link.altered
175
-
176
- # register us as the auditor
177
- link.auditor( self )
178
-
179
- # submit the link and get the response
180
- link.submit( @__opts ).on_complete {
181
- |res|
182
-
183
- @responses[:good][altered] ||= []
184
-
185
- # save the response for later analysis
186
- @responses[:good][altered] << {
187
- 'str' => str,
188
- 'res' => res
189
- }
190
- }
191
-
192
- }
193
- }
194
-
195
- end
196
-
197
- # Goes through the responses induced by {#prep_good_responses} and {#__check} their code
198
- def analyze( )
199
- @responses[:good].keys.each {
200
- |key|
201
- @responses[:good][key].each {
202
- |res|
203
- __check( res['str'], res['res'], key )
204
- }
205
- }
206
- end
207
-
208
- #
209
- # Compares HTML responses in order to identify successful blind sql injections
210
- #
211
- # @param [String] str the string that unveiled the vulnerability
212
- # @param [Typhoeus::Response]
213
- # @param [String] var the vulnerable variable
214
- #
215
- def __check( str, res, var )
216
-
217
- # if one of the injections gives the same results as the
218
- # original page then a blind SQL injection exists
219
- check = res.body.rdiff( @page.html )
220
-
221
- if( check == @responses[:orig] && @responses[:bad][var] != check &&
222
- !@http.custom_404?( res ) && res.code == 200 )
223
- __log_results( var, res, str )
224
50
  end
225
-
226
51
  end
227
52
 
228
- def clean_up
229
- @@__audited << __audit_id( )
230
- end
53
+ def run( )
231
54
 
232
- def __audit_id
233
- "#{URI( normalize_url( @page.url ) ).path}::#{@page.query_vars.keys}"
234
- end
55
+ opts = {}
56
+ # fault injection seeds
57
+ opts[:faults] = [ '\'"`' ]
58
+ # boolean injection seeds
59
+ opts[:bools] = @@__bools
235
60
 
236
- def __audited?
237
- @@__audited.include?( __audit_id( ) )
61
+ audit_rdiff( opts )
238
62
  end
239
63
 
240
-
241
64
  def self.info
242
65
  {
243
66
  :name => 'Blind (rDiff) SQL Injection',
@@ -247,7 +70,9 @@ class BlindrDiffSQLInjection < Arachni::Module::Base
247
70
  (Note: This module may get confused by certain types of XSS vulnerabilities.
248
71
  If this module returns a positive result you should investigate nonetheless.)},
249
72
  :elements => [
250
- Issue::Element::LINK
73
+ Issue::Element::LINK,
74
+ Issue::Element::FORM,
75
+ Issue::Element::COOKIE
251
76
  ],
252
77
  :author => 'Tasos "Zapotek" Laskos <tasos.laskos@gmail.com> ',
253
78
  :version => '0.3',
@@ -276,46 +101,6 @@ class BlindrDiffSQLInjection < Arachni::Module::Base
276
101
  }
277
102
  end
278
103
 
279
- private
280
-
281
- def __log_results( var, res, str )
282
-
283
- url = res.effective_url
284
-
285
- # since we bypassed the auditor completely we need to create
286
- # our own opts hash and pass it to the Vulnerability class.
287
- #
288
- # this is only required for Metasploitable vulnerabilities
289
- opts = {
290
- :injected_orig => URI( @page.url ).query,
291
- :combo => @__candidate.auditable
292
- }
293
-
294
- issue = Issue.new( {
295
- :var => var,
296
- :url => url,
297
- :method => res.request.method.to_s,
298
- :opts => opts,
299
- :injected => str,
300
- :id => str,
301
- :regexp => 'n/a',
302
- :regexp_match => 'n/a',
303
- :elem => Issue::Element::LINK,
304
- :response => res.body,
305
- :verification => true,
306
- :headers => {
307
- :request => res.request.headers,
308
- :response => res.headers,
309
- }
310
- }.merge( self.class.info )
311
- )
312
-
313
- print_ok( "In #{Issue::Element::LINK} var '#{var}' ( #{url} )" )
314
-
315
- # register our results with the system
316
- register_results( [ issue ] )
317
- end
318
-
319
104
  end
320
105
  end
321
106
  end
@@ -0,0 +1,5 @@
1
+ %q% and %q%1
2
+ %q%) and %q%1
3
+ %q%)) and %q%1
4
+ %q%))) and %q%1
5
+ %q%)))) and %q%1
@@ -18,7 +18,7 @@ module Modules
18
18
  # @author: Tasos "Zapotek" Laskos
19
19
  # <tasos.laskos@gmail.com>
20
20
  # <zapotek@segfault.gr>
21
- # @version: 0.2
21
+ # @version: 0.2.1
22
22
  #
23
23
  # @see http://cwe.mitre.org/data/definitions/89.html
24
24
  # @see http://capec.mitre.org/data/definitions/7.html
@@ -55,6 +55,13 @@ class BlindTimingSQLInjection < Arachni::Module::Base
55
55
  audit_timeout( @@__injection_str, @__opts )
56
56
  end
57
57
 
58
+ def redundant
59
+ # We add ourselves to the list too.
60
+ # We don't want more than one timing-attack variation per issue,
61
+ # it's too expensive.
62
+ [ 'sqli', 'sqli_blind_rdiff', 'sqli_blind_timing' ]
63
+ end
64
+
58
65
  def self.info
59
66
  {
60
67
  :name => 'Blind (timing) SQL injection',
@@ -68,7 +75,7 @@ class BlindTimingSQLInjection < Arachni::Module::Base
68
75
  Issue::Element::HEADER
69
76
  ],
70
77
  :author => 'Tasos "Zapotek" Laskos <tasos.laskos@gmail.com> ',
71
- :version => '0.1.1',
78
+ :version => '0.2.1',
72
79
  :references => {
73
80
  'OWASP' => 'http://www.owasp.org/index.php/Blind_SQL_Injection',
74
81
  'MITRE - CAPEC' => 'http://capec.mitre.org/data/definitions/7.html'
@@ -8,7 +8,7 @@
8
8
 
9
9
  =end
10
10
 
11
- module Anemone::Extractors
11
+ module Arachni::Parser::Extractors
12
12
 
13
13
  #
14
14
  # Extracts paths from anchor elements.
@@ -8,7 +8,7 @@
8
8
 
9
9
  =end
10
10
 
11
- module Anemone::Extractors
11
+ module Arachni::Parser::Extractors
12
12
 
13
13
  #
14
14
  # Extracts paths from "form" HTML elements.
@@ -8,7 +8,7 @@
8
8
 
9
9
  =end
10
10
 
11
- module Anemone::Extractors
11
+ module Arachni::Parser::Extractors
12
12
 
13
13
  #
14
14
  # Extracts paths from frames.
@@ -8,7 +8,7 @@
8
8
 
9
9
  =end
10
10
 
11
- module Anemone::Extractors
11
+ module Arachni::Parser::Extractors
12
12
 
13
13
  #
14
14
  # Extract URLs from arbitrary text.
@@ -8,7 +8,7 @@
8
8
 
9
9
  =end
10
10
 
11
- module Anemone::Extractors
11
+ module Arachni::Parser::Extractors
12
12
 
13
13
  #
14
14
  # Extracts paths from "link" HTML elements.
@@ -8,7 +8,7 @@
8
8
 
9
9
  =end
10
10
 
11
- module Anemone::Extractors
11
+ module Arachni::Parser::Extractors
12
12
 
13
13
  #
14
14
  # Extracts meta refresh URLs.
@@ -8,7 +8,7 @@
8
8
 
9
9
  =end
10
10
 
11
- module Anemone::Extractors
11
+ module Arachni::Parser::Extractors
12
12
 
13
13
  #
14
14
  # Extracts paths from "script" HTML elements.<br/>
@@ -8,7 +8,7 @@
8
8
 
9
9
  =end
10
10
 
11
- module Anemone::Extractors
11
+ module Arachni::Parser::Extractors
12
12
 
13
13
  #
14
14
  # @author: Tasos "Zapotek" Laskos
@@ -25,7 +25,7 @@ class Proxy
25
25
  # @author: Tasos "Zapotek" Laskos
26
26
  # <tasos.laskos@gmail.com>
27
27
  # <zapotek@segfault.gr>
28
- # @version: 0.1
28
+ # @version: 0.1.1
29
29
  #
30
30
  class Server < WEBrick::HTTPProxyServer
31
31
 
@@ -57,7 +57,8 @@ class Proxy
57
57
  res.header['content-type'] = 'text/plain'
58
58
  res.header.delete( 'content-encoding' )
59
59
 
60
- res.body << reasons.map{ |msg| " * #{msg}" }.join( "\n" )
60
+ res.body << reasons.pop + "\n"
61
+ res.body << reasons.map{ |msg| " * #{msg}" }.join( "\n" )
61
62
  end
62
63
  end
63
64
  end
@@ -126,7 +126,6 @@ class WAFDetector < Arachni::Plugin::Base
126
126
 
127
127
  def queue_original
128
128
  @precision.times {
129
- # grab the page containing the login form
130
129
  @framework.http.get( @url.to_s ).on_complete {
131
130
  |res|
132
131
  @responses[:original] ||= res.body
@@ -137,7 +136,6 @@ class WAFDetector < Arachni::Plugin::Base
137
136
 
138
137
  def queue_vanilla( )
139
138
  @precision.times {
140
- # grab the page containing the login form
141
139
  @framework.http.get( @url.to_s, :params => @safe ).on_complete {
142
140
  |res|
143
141
  @responses[:vanilla] ||= res.body
@@ -148,7 +146,6 @@ class WAFDetector < Arachni::Plugin::Base
148
146
 
149
147
  def queue_spicy( )
150
148
  @precision.times {
151
- # grab the page containing the login form
152
149
  @framework.http.get( @url.to_s, :params => @unsafe ).on_complete {
153
150
  |res|
154
151
  @responses[:spicy] ||= res.body