arachni 0.2.2.1 → 0.2.2.2
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 +30 -0
- data/CONTRIBUTORS.md +1 -0
- data/README.md +28 -8
- data/Rakefile +1 -0
- data/bin/arachni_web_autostart +46 -0
- data/lib/anemone/page.rb +1 -0
- data/lib/arachni.rb +1 -1
- data/lib/framework.rb +8 -3
- data/lib/http.rb +9 -39
- data/lib/mixins/observable.rb +87 -0
- data/lib/module/auditor.rb +14 -0
- data/lib/module/base.rb +0 -14
- data/lib/nokogiri/xml/node.rb +42 -0
- data/lib/ui/cli/cli.rb +1 -1
- data/lib/ui/web/log.rb +21 -14
- data/lib/ui/web/report_manager.rb +100 -15
- data/lib/ui/web/server.rb +24 -33
- data/lib/ui/web/server/public/reports/demo.testfire.net:Sun Mar 20 02:48:10 2011.afr +104829 -0
- data/lib/ui/web/server/views/layout.erb +1 -1
- data/lib/ui/web/server/views/options.erb +10 -2
- data/lib/ui/web/server/views/plugins.erb +1 -1
- data/lib/ui/web/server/views/reports.erb +8 -4
- data/lib/ui/xmlrpc/xmlrpc.rb +1 -1
- data/metamodules/autothrottle.rb +2 -2
- data/metamodules/timeout_notice.rb +1 -1
- data/modules/audit/sqli_blind_rdiff.rb +1 -1
- data/modules/recon/common_files/filenames.txt +2 -0
- data/modules/recon/directory_listing.rb +1 -0
- data/modules/recon/interesting_responses.rb +3 -3
- data/path_extractors/generic.rb +5 -1
- data/plugins/autologin.rb +15 -4
- data/plugins/content_types.rb +2 -2
- data/plugins/cookie_collector.rb +9 -16
- data/plugins/profiler.rb +237 -0
- data/reports/html.rb +21 -6
- data/reports/html/default.erb +4 -2
- data/reports/plugin_formatters/html/autologin.rb +63 -0
- data/reports/plugin_formatters/html/profiler.rb +71 -0
- data/reports/plugin_formatters/html/profiler/template.erb +177 -0
- data/reports/plugin_formatters/stdout/autologin.rb +55 -0
- data/reports/plugin_formatters/stdout/profiler.rb +90 -0
- data/reports/plugin_formatters/xml/autologin.rb +68 -0
- data/reports/plugin_formatters/xml/profiler.rb +120 -0
- metadata +23 -68
data/reports/html.rb
CHANGED
@@ -111,12 +111,14 @@ class HTML < Arachni::Report::Base
|
|
111
111
|
}
|
112
112
|
}
|
113
113
|
|
114
|
+
@total_severities = 0
|
115
|
+
@total_elements = 0
|
116
|
+
@total_verifications = 0
|
114
117
|
@audit_store.issues.each_with_index {
|
115
118
|
|issue, i|
|
116
119
|
|
117
120
|
@graph_data[:severities][issue.severity] ||= 0
|
118
121
|
@graph_data[:severities][issue.severity] += 1
|
119
|
-
@total_severities ||= 0
|
120
122
|
@total_severities += 1
|
121
123
|
|
122
124
|
@graph_data[:issues][issue.name] ||= 0
|
@@ -124,19 +126,20 @@ class HTML < Arachni::Report::Base
|
|
124
126
|
|
125
127
|
@graph_data[:elements][issue.elem] ||= 0
|
126
128
|
@graph_data[:elements][issue.elem] += 1
|
127
|
-
@total_elements ||= 0
|
128
129
|
@total_elements += 1
|
129
130
|
|
130
131
|
verification = issue.verification ? 'Yes' : 'No'
|
131
132
|
@graph_data[:verification][verification] ||= 0
|
132
133
|
@graph_data[:verification][verification] += 1
|
133
|
-
@total_verifications ||= 0
|
134
134
|
@total_verifications += 1
|
135
135
|
|
136
136
|
issue.variations.each_with_index {
|
137
137
|
|variation, j|
|
138
138
|
|
139
139
|
if( variation['response'] && !variation['response'].empty? )
|
140
|
+
|
141
|
+
variation['response'] = variation['response'].force_encoding( 'utf-8' )
|
142
|
+
|
140
143
|
@audit_store.issues[i].variations[j]['escaped_response'] =
|
141
144
|
Base64.encode64( variation['response'] ).gsub( /\n/, '' )
|
142
145
|
end
|
@@ -158,17 +161,29 @@ class HTML < Arachni::Report::Base
|
|
158
161
|
|
159
162
|
@graph_data[:severities].each {
|
160
163
|
|severity, cnt|
|
161
|
-
|
164
|
+
begin
|
165
|
+
@graph_data[:severities][severity] = ((cnt/Float(@total_severities)) * 100).to_i
|
166
|
+
rescue
|
167
|
+
@graph_data[:severities][severity] = 0
|
168
|
+
end
|
162
169
|
}
|
163
170
|
|
164
171
|
@graph_data[:elements].each {
|
165
172
|
|elem, cnt|
|
166
|
-
|
173
|
+
begin
|
174
|
+
@graph_data[:elements][elem] = ((cnt/Float(@total_elements)) * 100).to_i
|
175
|
+
rescue
|
176
|
+
@graph_data[:elements][elem] = 0
|
177
|
+
end
|
167
178
|
}
|
168
179
|
|
169
180
|
@graph_data[:verification].each {
|
170
181
|
|verification, cnt|
|
171
|
-
|
182
|
+
begin
|
183
|
+
@graph_data[:verification][verification] = ((cnt/Float(@total_verifications)) * 100).to_i
|
184
|
+
rescue
|
185
|
+
@graph_data[:verification][verification] = 0
|
186
|
+
end
|
172
187
|
}
|
173
188
|
|
174
189
|
end
|
data/reports/html/default.erb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
<%# Encoding: utf-8%>
|
2
|
+
|
1
3
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
4
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
5
|
|
@@ -180,7 +182,7 @@
|
|
180
182
|
|
181
183
|
var verification;
|
182
184
|
$(document).ready(function() {
|
183
|
-
|
185
|
+
verification = new Highcharts.Chart({
|
184
186
|
chart: {
|
185
187
|
renderTo: 'chart-verification',
|
186
188
|
backgroundColor: '#ccc'
|
@@ -949,7 +951,7 @@
|
|
949
951
|
<p> </p>
|
950
952
|
|
951
953
|
<%@plugins.values.each do |plugin|%>
|
952
|
-
<p><%=plugin%></p>
|
954
|
+
<p><%=plugin.force_encoding( 'utf-8' )%></p>
|
953
955
|
<%end%>
|
954
956
|
</section>
|
955
957
|
|
@@ -0,0 +1,63 @@
|
|
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
|
+
|
13
|
+
module Reports
|
14
|
+
|
15
|
+
class HTML
|
16
|
+
module PluginFormatters
|
17
|
+
|
18
|
+
#
|
19
|
+
# HTML formatter for the results of the AutoLogin plugin
|
20
|
+
#
|
21
|
+
# @author: Tasos "Zapotek" Laskos
|
22
|
+
# <tasos.laskos@gmail.com>
|
23
|
+
# <zapotek@segfault.gr>
|
24
|
+
# @version: 0.1
|
25
|
+
#
|
26
|
+
class AutoLogin < Arachni::Plugin::Formatter
|
27
|
+
|
28
|
+
def initialize( plugin_data )
|
29
|
+
@results = plugin_data[:results]
|
30
|
+
@description = plugin_data[:description]
|
31
|
+
end
|
32
|
+
|
33
|
+
def run
|
34
|
+
return ERB.new( tpl ).result( binding )
|
35
|
+
end
|
36
|
+
|
37
|
+
def tpl
|
38
|
+
%q{
|
39
|
+
<h3>AutoLogin</h3>
|
40
|
+
<blockquote><pre><%=::Arachni::Reports::HTML.prep_description(@description)%></pre></blockquote>
|
41
|
+
|
42
|
+
<h4>Result</h4>
|
43
|
+
<blockquote><%=@results[:msg]%></blockquote>
|
44
|
+
|
45
|
+
<% if @results[:cookies].is_a?( Hash )%>
|
46
|
+
<h5>Cookies were set to:</h5>
|
47
|
+
<ul>
|
48
|
+
<% @results[:cookies].each_pair do |name, val|%>
|
49
|
+
<li><%=name%> = <%=val%></li>
|
50
|
+
<%end%>
|
51
|
+
<ul>
|
52
|
+
<%end%>
|
53
|
+
}
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,71 @@
|
|
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
|
+
require 'json'
|
12
|
+
|
13
|
+
module Arachni
|
14
|
+
|
15
|
+
module Reports
|
16
|
+
|
17
|
+
class HTML
|
18
|
+
module PluginFormatters
|
19
|
+
|
20
|
+
#
|
21
|
+
# HTML formatter for the results of the Profiler plugin
|
22
|
+
#
|
23
|
+
# @author: Tasos "Zapotek" Laskos
|
24
|
+
# <tasos.laskos@gmail.com>
|
25
|
+
# <zapotek@segfault.gr>
|
26
|
+
# @version: 0.1
|
27
|
+
#
|
28
|
+
class Profiler < Arachni::Plugin::Formatter
|
29
|
+
|
30
|
+
def initialize( plugin_data )
|
31
|
+
@results = plugin_data[:results]
|
32
|
+
@description = plugin_data[:description]
|
33
|
+
end
|
34
|
+
|
35
|
+
def run
|
36
|
+
|
37
|
+
@results['times'].each_with_index {
|
38
|
+
|itm, i|
|
39
|
+
@results['times'][i] = escape_hash( itm )
|
40
|
+
}
|
41
|
+
|
42
|
+
times = @results['times'].map{ |item| item['time'] }
|
43
|
+
total_time = 0
|
44
|
+
times.each {
|
45
|
+
|time|
|
46
|
+
total_time += time
|
47
|
+
}
|
48
|
+
|
49
|
+
avg_time = total_time / times.size
|
50
|
+
times.reject!{ |time| time < avg_time }
|
51
|
+
|
52
|
+
return ERB.new( IO.read( File.dirname( __FILE__ ) + '/profiler/template.erb' ) ).result( binding )
|
53
|
+
end
|
54
|
+
|
55
|
+
def escape_hash( hash )
|
56
|
+
hash.each_pair {
|
57
|
+
|k, v|
|
58
|
+
hash[k] = CGI.escape( v ) if v.is_a?( String )
|
59
|
+
hash[k] = escape_hash( v ) if v.is_a? Hash
|
60
|
+
}
|
61
|
+
|
62
|
+
return hash
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
<h3>Profiler</h3>
|
2
|
+
<blockquote><pre><%=::Arachni::Reports::HTML.prep_description(@description)%></pre></blockquote>
|
3
|
+
|
4
|
+
<script type="text/javascript">
|
5
|
+
|
6
|
+
var tooltip_data = <%=@results['times'].to_json%>;
|
7
|
+
var chart;
|
8
|
+
$(document).ready(function() {
|
9
|
+
chart = new Highcharts.Chart({
|
10
|
+
chart: {
|
11
|
+
renderTo: 'chart-response-times',
|
12
|
+
zoomType: 'x',
|
13
|
+
spacingRight: 20,
|
14
|
+
backgroundColor: '#ccc'
|
15
|
+
},
|
16
|
+
title: {
|
17
|
+
text: 'Response times exceeding the average (<%=times.size%> out of <%=@results['times'].size%> responses)'
|
18
|
+
},
|
19
|
+
subtitle: {
|
20
|
+
text: document.ontouchstart === undefined ?
|
21
|
+
'Click and drag in the plot area to zoom in' :
|
22
|
+
'Drag your finger over the plot to zoom in'
|
23
|
+
},
|
24
|
+
xAxis: {
|
25
|
+
title: {
|
26
|
+
text: null
|
27
|
+
}
|
28
|
+
},
|
29
|
+
yAxis: {
|
30
|
+
title: {
|
31
|
+
text: 'Seconds'
|
32
|
+
},
|
33
|
+
min: <%=times.min%>,
|
34
|
+
startOnTick: false,
|
35
|
+
showFirstLabel: false
|
36
|
+
},
|
37
|
+
tooltip: {
|
38
|
+
formatter: function() {
|
39
|
+
return 'URL: <b>' + unescape( tooltip_data[this.x]['url'] ) + '</b><br/>' +
|
40
|
+
'Response time: <b>' + this.y +'</b> seconds<br/>';
|
41
|
+
}
|
42
|
+
},
|
43
|
+
legend: {
|
44
|
+
enabled: false
|
45
|
+
},
|
46
|
+
plotOptions: {
|
47
|
+
area: {
|
48
|
+
fillColor: {
|
49
|
+
linearGradient: [0, 0, 0, 300],
|
50
|
+
stops: [
|
51
|
+
[0, 'rgba(0,0,0,0)'],
|
52
|
+
[1, 'rgba(2,0,0,0)']
|
53
|
+
]
|
54
|
+
},
|
55
|
+
lineWidth: 1,
|
56
|
+
marker: {
|
57
|
+
enabled: false,
|
58
|
+
states: {
|
59
|
+
hover: {
|
60
|
+
enabled: true,
|
61
|
+
radius: 5
|
62
|
+
}
|
63
|
+
}
|
64
|
+
},
|
65
|
+
shadow: false,
|
66
|
+
states: {
|
67
|
+
hover: {
|
68
|
+
lineWidth: 1
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}
|
72
|
+
},
|
73
|
+
|
74
|
+
series: [{
|
75
|
+
type: 'area',
|
76
|
+
pointInterval: 1,
|
77
|
+
data: <%=times.to_s%>
|
78
|
+
}]
|
79
|
+
});
|
80
|
+
|
81
|
+
|
82
|
+
});
|
83
|
+
</script>
|
84
|
+
|
85
|
+
|
86
|
+
<h4>Response time analysis</h4>
|
87
|
+
|
88
|
+
<strong>Average response time</strong>: <%=avg_time%> seconds <br/>
|
89
|
+
<strong>Max</strong>: <%=times.max%> seconds <br/>
|
90
|
+
<strong>Min</strong>: <%=@results['times'].map{ |item| item['time'] }.min%> seconds <br/>
|
91
|
+
<strong>Sample size</strong>: <%=@results['times'].size%> responses <br/>
|
92
|
+
|
93
|
+
<div id="chart-response-times" style="width: 1000px"></div>
|
94
|
+
|
95
|
+
<p> </p>
|
96
|
+
|
97
|
+
<h4>Inputs that affect output (Taint analysis)</h4>
|
98
|
+
<% @results['inputs'].each_with_index do |item, idx| %>
|
99
|
+
|
100
|
+
<h5>
|
101
|
+
<%=item['element']['type'].capitalize%>
|
102
|
+
|
103
|
+
<%if item['element']['name']%>
|
104
|
+
named '<%=item['element']['name']%>'
|
105
|
+
<%end%>
|
106
|
+
|
107
|
+
<%if item['element']['altered']%>
|
108
|
+
using the '<%=item['element']['altered']%>' input
|
109
|
+
<%end%>
|
110
|
+
|
111
|
+
<%if item['element']['owner']%>
|
112
|
+
at <a href="<%=CGI.escapeHTML( item['element']['owner'] )%>"> <%=item['element']['owner']%> </a>
|
113
|
+
<%end%>
|
114
|
+
|
115
|
+
<%if item['element']['action']%>
|
116
|
+
pointing to <a href="<%=CGI.escapeHTML( item['element']['action'] )%>"> <%=item['element']['action']%> </a>
|
117
|
+
<%end%>
|
118
|
+
|
119
|
+
<%if item['element']['method']%>
|
120
|
+
using <%=item['request']['method']%>
|
121
|
+
<%end%>
|
122
|
+
</h5>
|
123
|
+
|
124
|
+
<p>
|
125
|
+
It was submitted using the following parameters:
|
126
|
+
<ul>
|
127
|
+
<%item['element']['auditable'].each_pair do |k, v| %>
|
128
|
+
<li><%=k%> = <%=v%></li>
|
129
|
+
<%end%>
|
130
|
+
</ul>
|
131
|
+
|
132
|
+
The taint landed in the following elements at <a href="<%=CGI.escapeHTML( item['request']['url'] )%>"> <%=item['request']['url']%> </a>:
|
133
|
+
<ul>
|
134
|
+
<%item['landed'].each do |elem| %>
|
135
|
+
<li>
|
136
|
+
<%=elem['type'].capitalize%>
|
137
|
+
|
138
|
+
<%if elem['name']%>
|
139
|
+
named '<%=elem['name']%>'
|
140
|
+
<%end%>
|
141
|
+
|
142
|
+
<%if elem['altered']%>
|
143
|
+
using the '<%=elem['altered']%>' input
|
144
|
+
<%end%>
|
145
|
+
|
146
|
+
<%if elem['auditable']%>
|
147
|
+
<ul>
|
148
|
+
<%elem['auditable'].each_pair do |k, v| %>
|
149
|
+
<li><%=k%> = <%=v%></li>
|
150
|
+
<%end%>
|
151
|
+
</ul>
|
152
|
+
<%end%>
|
153
|
+
</li>
|
154
|
+
<%end%>
|
155
|
+
</ul>
|
156
|
+
|
157
|
+
<div class="hidden" id="inspection-dialog_<%=idx%>" title="Relevant content is shown in red.">
|
158
|
+
<% match = CGI.escapeHTML( item['response']['body'] )%>
|
159
|
+
<pre> <%=CGI.escapeHTML( item['response']['body'] ).gsub( item['taint'], '<strong style="color: red">' + item['taint'] + '</strong>' ) %> </pre>
|
160
|
+
</div>
|
161
|
+
|
162
|
+
<form style="display:inline" action="#">
|
163
|
+
<input onclick="javascript:inspect( '#inspection-dialog_<%=idx%>')" type="button" value="Inspect" />
|
164
|
+
</form>
|
165
|
+
|
166
|
+
<form style="display:inline" action="<%=item['request']['url']%>" target="_blank" method="<%=item['request']['method']%>">
|
167
|
+
<% item['request']['params'].each_pair do |name, value|%>
|
168
|
+
<input type="hidden" name="<%=CGI.escapeHTML(name)%>" value="<%=CGI.escapeHTML( value )%>" />
|
169
|
+
<%end%>
|
170
|
+
<input type="submit" value="Replay" />
|
171
|
+
</form>
|
172
|
+
|
173
|
+
</p>
|
174
|
+
|
175
|
+
<p> </p>
|
176
|
+
|
177
|
+
<%end%>
|
@@ -0,0 +1,55 @@
|
|
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 Reports
|
13
|
+
|
14
|
+
class Stdout
|
15
|
+
module PluginFormatters
|
16
|
+
|
17
|
+
#
|
18
|
+
# Stdout formatter for the results of the AutoLogin plugin
|
19
|
+
#
|
20
|
+
#
|
21
|
+
# @author: Tasos "Zapotek" Laskos
|
22
|
+
# <tasos.laskos@gmail.com>
|
23
|
+
# <zapotek@segfault.gr>
|
24
|
+
# @version: 0.1
|
25
|
+
#
|
26
|
+
class AutoLogin < Arachni::Plugin::Formatter
|
27
|
+
|
28
|
+
def initialize( plugin_data )
|
29
|
+
@results = plugin_data[:results]
|
30
|
+
@description = plugin_data[:description]
|
31
|
+
end
|
32
|
+
|
33
|
+
def run
|
34
|
+
print_status( 'AutoLogin' )
|
35
|
+
print_info( '~~~~~~~~~~~~~~' )
|
36
|
+
|
37
|
+
print_info( 'Description: ' + @description )
|
38
|
+
print_line
|
39
|
+
print_ok( @results[:msg] )
|
40
|
+
|
41
|
+
return if !@results[:cookies]
|
42
|
+
print_info( 'Cookies set to:' )
|
43
|
+
@results[:cookies].each_pair {
|
44
|
+
|name, val|
|
45
|
+
print_info( ' * ' + name + ' = ' + val )
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|