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
File without changes
@@ -307,6 +307,48 @@ li ol, li ul {
307
307
  text-decoration: none;
308
308
  }
309
309
 
310
+ #nav ul ul {
311
+ position:absolute;
312
+ display: none; /* Hide off-screen when not needed (this is more accessible than display:none;) */
313
+ background: none;
314
+ margin-left: -6px
315
+ }
316
+
317
+ #nav ul li:hover ul li{
318
+ list-style: none;
319
+ margin: 0;
320
+ height: 50px;
321
+ background: none;
322
+ }
323
+
324
+ #nav li:hover ul { /* Display the dropdown on hover */
325
+ display: block; /* Bring back on-screen when needed */
326
+ background: none;
327
+
328
+ -moz-border-radius-bottomleft: 10px;
329
+ -moz-border-radius-bottomright: 10px;
330
+ -webkit-border-radius-bottomright: 10px;
331
+ -webkit-border-radius-bottomleft: 10px;
332
+ }
333
+
334
+ #nav li:hover a{ /* These create persistent hover states, meaning the top-most link stays 'hovered' even when your cursor has moved down the list. */
335
+ }
336
+
337
+ #nav li:hover ul a{ /* The persistent hover state does however create a global style for links even before they're hovered. Here we undo these effects. */
338
+ text-decoration: none;
339
+ background: #202020;
340
+ -moz-border-radius-bottomleft: 10px;
341
+ -moz-border-radius-bottomright: 10px;
342
+ -webkit-border-radius-bottomright: 10px;
343
+ -webkit-border-radius-bottomleft: 10px;
344
+ height: 10px
345
+ }
346
+
347
+ #nav li:hover ul li a:hover{ /* Here we define the most explicit hover states--what happens when you hover each individual link. */
348
+ background: #333;
349
+ }
350
+
351
+
310
352
  #page-intro {
311
353
  height: 150px;
312
354
  padding: 50px 20px 0;
@@ -0,0 +1,15 @@
1
+
2
+ <div id="page-intro">
3
+ <h2><%=escape( addon['name'] )%> v<%=escape( addon['version'] )%></h2>
4
+ <p>
5
+ <%=escape( addon['description'] )%>
6
+ <br/>
7
+ <i>By <%=escape( addon['author'] )%></i>
8
+ </p>
9
+ </div>
10
+
11
+ <%= erb :flash, {:layout => false} %>
12
+
13
+
14
+ <%= erb tpl, {:layout => false}, tpl_args %>
15
+
@@ -0,0 +1,46 @@
1
+
2
+ <div id="page-intro">
3
+ <form action="/addons" method="post">
4
+
5
+ <h2>Add-ons</h2>
6
+ <p>This page allows you to enable add-ons in order to extend the functionality of the WebUI.
7
+ <br/><br/>
8
+ </p>
9
+
10
+ <% if !addons.available.empty? %>
11
+ <%= csrf_tag %>
12
+ <input type="submit" value="Save" />
13
+ <% end %>
14
+
15
+ </div>
16
+ <%= erb :flash, {:layout => false} %>
17
+
18
+ <% if !addons.available.empty? %>
19
+ <table>
20
+ <tr>
21
+ <th>Name</th>
22
+ <th>Description</th>
23
+ <th>Author</th>
24
+ <th>Version</th>
25
+ <th>Enable</th>
26
+ </tr>
27
+ <% addons.available.each do |addon| %>
28
+ <tr>
29
+
30
+ <td><%=escape( addon['name'] )%></td>
31
+ <td><%=escape( addon['description'] )%></td>
32
+ <td><%=escape( addon['author'] )%></td>
33
+ <td><%=escape( addon['version'] )%></td>
34
+
35
+ <td>
36
+ <input type="checkbox" class="audit" name="addons[<%=addon['filename']%>]"
37
+ <% if addons.enabled.include?( addon['filename'] ) %>
38
+ checked="checked" <% end %> />
39
+ </td>
40
+ </tr>
41
+ <% end %>
42
+ </table>
43
+ </form>
44
+ <% else %>
45
+ <span class="notice"> There are no available add-ons at the moment.</span>
46
+ <% end %>
@@ -21,7 +21,7 @@
21
21
  </h2>
22
22
 
23
23
  <%if !dispatcher_stats['running_jobs'].empty? %>
24
- <form action="/dispatchers/<%=sanitize_url( d_url.dup )%>/shutdown_all" method="post">
24
+ <form action="/dispatchers/<%=remove_proto( d_url.dup )%>/shutdown_all" method="post">
25
25
  <%= csrf_tag %>
26
26
  <input type="submit" value="Shutdown all" />
27
27
  </form>
@@ -1,6 +1,6 @@
1
1
 
2
2
  <div id="page-intro">
3
- <h2 id="page_header">Attached to instance @<%=sanitize_url( params['url'] )%></h2>
3
+ <h2 id="page_header">Attached to instance @<%=remove_proto( params['url'] )%></h2>
4
4
  <p id="page_description">
5
5
  This page allows you to see what's going on at the other end of the wire (i.e. get status messages directly from the remote scanner).
6
6
  <br/>
@@ -11,20 +11,20 @@
11
11
  <%if !shutdown %>
12
12
 
13
13
  <%if !paused %>
14
- <form action="/instance/<%=sanitize_url( params['url'] )%>/pause" method="post">
14
+ <form action="/instance/<%=remove_proto( params['url'] )%>/pause" method="post">
15
15
  <%= csrf_tag %>
16
16
  <input type="submit" value="Pause" />
17
17
  </form>
18
18
  <%end%>
19
19
 
20
20
  <%if paused %>
21
- <form action="/instance/<%=sanitize_url( params['url'] )%>/resume" method="post">
21
+ <form action="/instance/<%=remove_proto( params['url'] )%>/resume" method="post">
22
22
  <%= csrf_tag %>
23
23
  <input type="submit" value="Resume" />
24
24
  </form>
25
25
  <%end%>
26
26
 
27
- <form action="/instance/<%=sanitize_url( params['url'] )%>/shutdown" method="post">
27
+ <form action="/instance/<%=remove_proto( params['url'] )%>/shutdown" method="post">
28
28
  <%= csrf_tag %>
29
29
  <input type="submit" value="Shutdown" />
30
30
  </form>
@@ -39,9 +39,9 @@
39
39
  <h3>Scan statisics</h3>
40
40
  <div class="left">
41
41
  <ul>
42
- <li>Pages audited: <span id="audited">0</span></li>
43
- <li>Pages crawled: <span id="crawled">0</span></li>
42
+ <li>Pages discovered: <span id="crawled">0</span></li>
44
43
  <li>Progress: <span id="percentage">0</span>%</li>
44
+ <li>Runtime: <span id="runtime">00:00:00</span></li>
45
45
  </ul>
46
46
  </div>
47
47
  <div>
@@ -120,15 +120,14 @@
120
120
  function setStats( stats ){
121
121
  if( stats == undefined ){ return }
122
122
 
123
- document.getElementById( 'audited' ).innerHTML = stats.auditmap_size;
124
123
  document.getElementById( 'crawled' ).innerHTML = stats.sitemap_size;
125
124
  document.getElementById( 'current_page' ).innerHTML = stats.current_page;
126
125
  document.getElementById( 'average_res_time' ).innerHTML = stats.average_res_time;
127
126
  document.getElementById( 'time_out_count' ).innerHTML = stats.time_out_count;
128
127
  document.getElementById( 'max_concurrency' ).innerHTML = stats.max_concurrency;
129
128
 
130
- percentage = (stats.auditmap_size / stats.sitemap_size) * 100
131
- document.getElementById( 'percentage' ).innerHTML = parseInt( percentage );
129
+ document.getElementById( 'runtime' ).innerHTML = stats.time;
130
+ document.getElementById( 'percentage' ).innerHTML = parseInt( stats.progress );
132
131
  }
133
132
 
134
133
  function updateProgressBar(){
@@ -136,8 +135,7 @@
136
135
  $.getJSON( stats_url, function(data) {
137
136
  if( data.stats == undefined ){ return }
138
137
  setStats( data.stats );
139
- percentage = (data.stats.auditmap_size / data.stats.sitemap_size) * 100
140
- setProgressBar( percentage );
138
+ setProgressBar( parseInt( data.stats.progress ) );
141
139
  });
142
140
  }
143
141
 
@@ -9,6 +9,7 @@
9
9
 
10
10
  <script type="text/javascript" src="/js/jquery-1.4.4.min.js"></script>
11
11
  <script type="text/javascript" src="/js/jquery-ui-1.8.9.custom.min.js"></script>
12
+ <script type="text/javascript" src="/js/jquery-ui-timepicker.js"></script>
12
13
 
13
14
  <script type="text/javascript">
14
15
  function checkAll( type ) {
@@ -37,6 +38,17 @@
37
38
  <li <% if selected_tab?( 'plugins' )%>class="selected" <%end%>><a href="/plugins">Plugins</a></li>
38
39
  <li <% if selected_tab?( 'settings' )%>class="selected" <%end%>><a href="/settings">Settings</a></li>
39
40
  <li <% if selected_tab?( 'reports' )%>class="selected" <%end%>><a href="/reports">Reports [<%=report_count%>]</a></li>
41
+ <li <% if selected_tab?( 'addons' )%>class="selected" <%end%>><a href="/addons">Add-ons <%if !addons.enabled.empty? %>&darr;<%end%> [<%=addons.enabled.size%>/<%=addons.available.size%>]</a>
42
+
43
+ <% if !addons.enabled.empty?%>
44
+ <ul>
45
+ <% addons.enabled.each do |name| %>
46
+ <li><a href="/addons/<%=escape( name )%>/"><%= escape( addons.by_name( name )['title'] ) %></a></li>
47
+ <%end%>
48
+ </ul>
49
+ <%end%>
50
+
51
+ </li>
40
52
  <li <% if selected_tab?( 'dispatchers' )%>class="selected" <%end%>><a href="/dispatchers">Dispatchers</a></li>
41
53
  <li <% if selected_tab?( 'log' )%>class="selected" <%end%>><a href="/log">Log</a></li>
42
54
  </ul>
@@ -55,7 +67,7 @@
55
67
  <div class="footer-content">
56
68
 
57
69
  <div class="footer-box">
58
- <h4><a href="https://github.com/Zapotek/arachni">About Arachni</a></h4>
70
+ <h4><a href="http://arachni.segfault.gr">About Arachni</a></h4>
59
71
  <p>
60
72
  Arachni is a feature-full, modular, high-performance Ruby framework aimed towards <br/>
61
73
  helping penetration testers and administrators evaluate the security of web applications.
@@ -77,6 +89,7 @@
77
89
 
78
90
  <h4>Interesting links</h4>
79
91
  <ul>
92
+ <li><a href="http://arachni.segfault.gr">Homepage</a></li>
80
93
  <li><a href="http://trainofthought.segfault.gr/category/projects/arachni/">News straight from the developer's blog</a></li>
81
94
  <li><a href="http://twitter.com/Zap0tek">Developer's Twitter feed</a></li>
82
95
  <li><a href="https://github.com/Zapotek/arachni/tree/experimental">The bleeding edge, Arachni's experimental branch</a></li>
@@ -14,10 +14,11 @@
14
14
  <p>
15
15
  It is a way to:
16
16
  <ul>
17
- <li>make working with Arachni easier</li>
18
- <li>make report management easier</li>
19
- <li>run and manage multiple scans at the same time <em>(each scan will try its best for maximum bandwidth utilization so it'll be like lions fighting in a cage -- make sure you have sufficient resources)</em></li>
20
- <li>work with and manage multiple Dispatchers</li>
17
+ <li>make working with Arachni easier;</li>
18
+ <li>make report management easier;</li>
19
+ <li>enable you to schedule scans;</li>
20
+ <li>run and manage multiple scans at the same time <em>(each scan will try its best for maximum bandwidth utilization so it'll be like lions fighting in a cage -- make sure you have sufficient resources)</em>;</li>
21
+ <li>work with, and manage, multiple Dispatchers.</li>
21
22
  </ul>
22
23
  </p>
23
24
 
@@ -25,8 +26,8 @@
25
26
  <p>
26
27
  It isn't:
27
28
  <ul>
28
- <li>too stable</li>
29
- <li>a way to make Arachni's goodies available to multiple users <em>(you could but it wouldn't be safe)</em></li>
29
+ <li>too stable;</li>
30
+ <li>a way to make Arachni's goodies available to multiple users <em>(you could but it wouldn't be safe)</em>.</li>
30
31
  </ul>
31
32
  </p>
32
33
 
@@ -0,0 +1,134 @@
1
+ =begin
2
+ Arachni
3
+ Copyright (c) 2010-2011 Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
4
+
5
+ This is free software; you can copy and distribute and modify
6
+ this program under the term of the GPL v2.0 License
7
+ (See LICENSE file for details)
8
+
9
+ =end
10
+
11
+ module Arachni
12
+ module UI
13
+ module Web
14
+
15
+ #
16
+ # General utility methods.
17
+ #
18
+ # @author: Tasos "Zapotek" Laskos
19
+ # <tasos.laskos@gmail.com>
20
+ # <zapotek@segfault.gr>
21
+ # @version: 0.1
22
+ #
23
+ module Utilities
24
+
25
+ #
26
+ # Escapes HTML chars.
27
+ #
28
+ # @param [String] str
29
+ #
30
+ # @return [String]
31
+ #
32
+ # @see CGI.escapeHTML
33
+ #
34
+ def escape( str )
35
+ CGI.escapeHTML( str )
36
+ end
37
+
38
+ #
39
+ # Unescapes HTML chars.
40
+ #
41
+ # @param [String] str
42
+ #
43
+ # @return [String]
44
+ #
45
+ # @see CGI.unescapeHTML
46
+ #
47
+ def unescape( str )
48
+ CGI.unescapeHTML( str )
49
+ end
50
+
51
+ #
52
+ # Recursively escapes all HTML characters.
53
+ #
54
+ # @param [Hash] hash
55
+ #
56
+ # @return [Hash]
57
+ #
58
+ def escape_hash( hash )
59
+ hash.each_pair {
60
+ |k, v|
61
+ hash[k] = escape( hash[k] ) if hash[k].is_a?( String )
62
+ hash[k] = escape_hash( v ) if v.is_a? Hash
63
+ }
64
+
65
+ return hash
66
+ end
67
+
68
+ #
69
+ # Recursively unescapes all HTML characters.
70
+ #
71
+ # @param [Hash] hash
72
+ #
73
+ # @return [Hash]
74
+ #
75
+ def unescape_hash( hash )
76
+ hash.each_pair {
77
+ |k, v|
78
+ hash[k] = unescape( hash[k] ) if hash[k].is_a?( String )
79
+ hash[k] = unescape_hash( v ) if v.is_a? Hash
80
+ }
81
+
82
+ return hash
83
+ end
84
+
85
+ #
86
+ # Parses datetime strings such as 07/23/2011 15:34 into Time objects.
87
+ #
88
+ # @param [String] datetime
89
+ #
90
+ # @return [Time]
91
+ #
92
+ def parse_datetime( datetime )
93
+ date, time = datetime.split( ' ' )
94
+
95
+ month, day, year = date.split( '/' )
96
+ hour, minute = time.split( ':' )
97
+
98
+ Time.new( year, month, day, hour, minute )
99
+ end
100
+
101
+ #
102
+ # Constructs an instance URL by port using its dispatcher's url.
103
+ #
104
+ # @param [Integer] port
105
+ # @param [String] dispatcher_url URL of the dispatcher
106
+ # @param [Bool] no_scheme include scheme in the URL?
107
+ #
108
+ # @return [String]
109
+ #
110
+ def port_to_url( port, dispatcher_url, no_scheme = nil )
111
+ uri = URI( dispatcher_url )
112
+ uri.port = port.to_i
113
+ uri = uri.to_s
114
+
115
+ uri = remove_proto( uri ) if no_scheme
116
+ return uri
117
+ end
118
+
119
+ #
120
+ # Removes the protocol from URL string.
121
+ #
122
+ # @param [String] url
123
+ #
124
+ # @return [String]
125
+ #
126
+ def remove_proto( url )
127
+ url.gsub!( 'http://', '' )
128
+ escape( url.gsub( 'https://', '' ) )
129
+ end
130
+
131
+ end
132
+ end
133
+ end
134
+ end
@@ -20,7 +20,7 @@ module Modules
20
20
  # @author: Tasos "Zapotek" Laskos
21
21
  # <tasos.laskos@gmail.com>
22
22
  # <zapotek@segfault.gr>
23
- # @version: 0.2
23
+ # @version: 0.2.1
24
24
  #
25
25
  # @see http://cwe.mitre.org/data/definitions/94.html
26
26
  # @see http://php.net/manual/en/function.eval.php
@@ -63,6 +63,10 @@ class CodeInjectionTiming < Arachni::Module::Base
63
63
  audit_timeout( @@__injection_str, @__opts )
64
64
  end
65
65
 
66
+ def redundant
67
+ [ 'code_injection' ]
68
+ end
69
+
66
70
  def self.info
67
71
  {
68
72
  :name => 'Code injection (timing)',
@@ -76,7 +80,7 @@ class CodeInjectionTiming < Arachni::Module::Base
76
80
  Issue::Element::HEADER
77
81
  ],
78
82
  :author => 'Tasos "Zapotek" Laskos <tasos.laskos@gmail.com> ',
79
- :version => '0.1',
83
+ :version => '0.2.1',
80
84
  :references => {
81
85
  'PHP' => 'http://php.net/manual/en/function.eval.php',
82
86
  'Perl' => 'http://perldoc.perl.org/functions/eval.html',
@@ -1,4 +1,4 @@
1
- sleep(__TIME__);
2
- import time;time.sleep(__TIME__);
1
+ sleep(__TIME__/1000);
2
+ import time;time.sleep(__TIME__/1000);
3
3
  Thread.sleep(__TIME__);
4
4
  Thread.Sleep(__TIME__);
@@ -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/78.html
24
24
  # @see http://www.owasp.org/index.php/OS_Command_Injection
@@ -50,7 +50,7 @@ class OSCmdInjectionTiming < Arachni::Module::Base
50
50
 
51
51
  @__opts = {
52
52
  :format => [ Format::STRAIGHT ],
53
- :timeout => 4000,
53
+ :timeout => 10000,
54
54
  :timeout_divider => 1000
55
55
  }
56
56
 
@@ -60,6 +60,10 @@ class OSCmdInjectionTiming < Arachni::Module::Base
60
60
  audit_timeout( @@__injection_str, @__opts )
61
61
  end
62
62
 
63
+ def redundant
64
+ [ 'os_cmd_injection' ]
65
+ end
66
+
63
67
  def self.info
64
68
  {
65
69
  :name => 'OS command injection (timing)',
@@ -71,7 +75,7 @@ class OSCmdInjectionTiming < Arachni::Module::Base
71
75
  Issue::Element::HEADER
72
76
  ],
73
77
  :author => 'Tasos "Zapotek" Laskos <tasos.laskos@gmail.com> ',
74
- :version => '0.1',
78
+ :version => '0.2.1',
75
79
  :references => {
76
80
  'OWASP' => 'http://www.owasp.org/index.php/OS_Command_Injection'
77
81
  },