ronin-support 0.3.0 → 0.4.0.rc1
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 +77 -7
- data/README.md +19 -3
- data/gemspec.yml +2 -2
- data/lib/ronin/extensions/regexp.rb +50 -2
- data/lib/ronin/extensions/string.rb +1 -0
- data/lib/ronin/formatting.rb +1 -0
- data/lib/ronin/formatting/extensions.rb +1 -0
- data/lib/ronin/formatting/extensions/binary/string.rb +56 -5
- data/lib/ronin/formatting/extensions/html/string.rb +6 -7
- data/lib/ronin/formatting/extensions/sql/string.rb +34 -0
- data/lib/ronin/formatting/extensions/text/string.rb +0 -180
- data/lib/ronin/fuzzing.rb +21 -0
- data/lib/ronin/fuzzing/extensions.rb +20 -0
- data/lib/ronin/fuzzing/extensions/string.rb +380 -0
- data/lib/ronin/fuzzing/fuzzing.rb +191 -0
- data/lib/ronin/network/esmtp.rb +94 -1
- data/lib/ronin/network/extensions/esmtp/net.rb +2 -82
- data/lib/ronin/network/extensions/http/net.rb +1 -736
- data/lib/ronin/network/extensions/imap/net.rb +1 -103
- data/lib/ronin/network/extensions/pop3/net.rb +1 -71
- data/lib/ronin/network/extensions/smtp/net.rb +2 -157
- data/lib/ronin/network/extensions/ssl/net.rb +1 -132
- data/lib/ronin/network/extensions/tcp/net.rb +2 -296
- data/lib/ronin/network/extensions/telnet/net.rb +1 -135
- data/lib/ronin/network/extensions/udp/net.rb +2 -214
- data/lib/ronin/network/http/http.rb +750 -5
- data/lib/ronin/network/imap.rb +105 -2
- data/lib/ronin/network/mixins.rb +1 -1
- data/lib/ronin/network/mixins/esmtp.rb +49 -52
- data/lib/ronin/network/mixins/http.rb +49 -53
- data/lib/ronin/network/mixins/imap.rb +47 -44
- data/lib/ronin/network/mixins/mixin.rb +58 -0
- data/lib/ronin/network/mixins/pop3.rb +44 -38
- data/lib/ronin/network/mixins/smtp.rb +49 -51
- data/lib/ronin/network/mixins/tcp.rb +56 -69
- data/lib/ronin/network/mixins/telnet.rb +57 -50
- data/lib/ronin/network/mixins/udp.rb +48 -52
- data/lib/ronin/network/network.rb +1 -0
- data/lib/ronin/network/pop3.rb +72 -2
- data/lib/ronin/network/smtp/email.rb +1 -0
- data/lib/ronin/network/smtp/smtp.rb +159 -3
- data/lib/ronin/network/ssl.rb +131 -2
- data/lib/ronin/network/tcp.rb +306 -1
- data/lib/ronin/network/telnet.rb +136 -2
- data/lib/ronin/network/udp.rb +229 -1
- data/lib/ronin/support.rb +2 -3
- data/lib/ronin/support/support.rb +38 -0
- data/lib/ronin/support/version.rb +1 -1
- data/lib/ronin/templates/erb.rb +2 -1
- data/lib/ronin/ui/output/helpers.rb +35 -1
- data/lib/ronin/ui/shell.rb +12 -2
- data/lib/ronin/wordlist.rb +157 -0
- data/spec/extensions/regexp_spec.rb +38 -0
- data/spec/formatting/html/string_spec.rb +1 -1
- data/spec/formatting/sql/string_spec.rb +23 -3
- data/spec/formatting/text/string_spec.rb +0 -110
- data/spec/fuzzing/string_spec.rb +158 -0
- data/spec/wordlist_spec.rb +65 -0
- metadata +35 -27
data/lib/ronin/templates/erb.rb
CHANGED
@@ -29,6 +29,7 @@ module Ronin
|
|
29
29
|
#
|
30
30
|
module Erb
|
31
31
|
include Template
|
32
|
+
|
32
33
|
#
|
33
34
|
# Renders the inline ERB template in the scope of the object.
|
34
35
|
#
|
@@ -50,7 +51,7 @@ module Ronin
|
|
50
51
|
# @api public
|
51
52
|
#
|
52
53
|
def erb(template)
|
53
|
-
ERB.new(template).result(binding)
|
54
|
+
ERB.new(template,nil,'-').result(binding)
|
54
55
|
end
|
55
56
|
|
56
57
|
#
|
@@ -184,7 +184,7 @@ module Ronin
|
|
184
184
|
# @api public
|
185
185
|
#
|
186
186
|
def print_warning(*message)
|
187
|
-
|
187
|
+
unless Output.silent?
|
188
188
|
Output.handler.print_warning(format_message(message))
|
189
189
|
return true
|
190
190
|
end
|
@@ -220,6 +220,40 @@ module Ronin
|
|
220
220
|
return false
|
221
221
|
end
|
222
222
|
|
223
|
+
#
|
224
|
+
# Prints an exception.
|
225
|
+
#
|
226
|
+
# @param [Exception] exception
|
227
|
+
# The exception to print.
|
228
|
+
#
|
229
|
+
# @return [Boolean]
|
230
|
+
# Specifies whether the exception was printed or not.
|
231
|
+
#
|
232
|
+
# @example
|
233
|
+
# begin
|
234
|
+
# socket.write(buffer)
|
235
|
+
# rescue => e
|
236
|
+
# print_exception(e)
|
237
|
+
# end
|
238
|
+
#
|
239
|
+
# @since 0.4.0
|
240
|
+
#
|
241
|
+
# @api public
|
242
|
+
#
|
243
|
+
def print_exception(exception)
|
244
|
+
return false if Output.silent?
|
245
|
+
|
246
|
+
print_error "#{exception.class}: #{exception.message}"
|
247
|
+
|
248
|
+
if Output.verbose?
|
249
|
+
exception.backtrace[0,5].each do |line|
|
250
|
+
print_error " #{line}"
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
return true
|
255
|
+
end
|
256
|
+
|
223
257
|
protected
|
224
258
|
|
225
259
|
#
|
data/lib/ronin/ui/shell.rb
CHANGED
@@ -74,7 +74,15 @@ module Ronin
|
|
74
74
|
@prompt = options.fetch(:prompt,DEFAULT_PROMPT)
|
75
75
|
|
76
76
|
@commands = Set[:help, :exit]
|
77
|
-
|
77
|
+
|
78
|
+
self.class.ancestors.each do |subclass|
|
79
|
+
if subclass < Shell
|
80
|
+
subclass.protected_instance_methods(false).each do |name|
|
81
|
+
@commands << name.to_sym
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
78
86
|
|
79
87
|
@input_handler = block
|
80
88
|
end
|
@@ -126,7 +134,7 @@ module Ronin
|
|
126
134
|
history_rollback += 1
|
127
135
|
|
128
136
|
begin
|
129
|
-
|
137
|
+
call(line)
|
130
138
|
rescue => e
|
131
139
|
print_error "#{e.class.name}: #{e.message}"
|
132
140
|
end
|
@@ -169,6 +177,8 @@ module Ronin
|
|
169
177
|
end
|
170
178
|
end
|
171
179
|
|
180
|
+
alias << write
|
181
|
+
|
172
182
|
protected
|
173
183
|
|
174
184
|
#
|
@@ -0,0 +1,157 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com)
|
3
|
+
#
|
4
|
+
# This file is part of Ronin Support.
|
5
|
+
#
|
6
|
+
# Ronin Support is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Ronin Support is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with Ronin Support. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'ronin/fuzzing/extensions'
|
21
|
+
|
22
|
+
module Ronin
|
23
|
+
#
|
24
|
+
# An Enumerable class for iterating over wordlist files or lists of words.
|
25
|
+
#
|
26
|
+
# @since 0.4.0
|
27
|
+
#
|
28
|
+
class Wordlist
|
29
|
+
|
30
|
+
include Enumerable
|
31
|
+
|
32
|
+
# The path to the wordlist file or a list of words
|
33
|
+
attr_accessor :list
|
34
|
+
|
35
|
+
# Mutation rules to apply to every word in the list
|
36
|
+
attr_reader :mutations
|
37
|
+
|
38
|
+
#
|
39
|
+
# Initializes the wordlist.
|
40
|
+
#
|
41
|
+
# @param [String, Enumerable] list
|
42
|
+
# The path of the wordlist or list of words.
|
43
|
+
#
|
44
|
+
# @param [Hash{Regexp,String,Symbol => Symbol,#each}] mutations
|
45
|
+
# Additional mutation rules to perform on each word in the list.
|
46
|
+
#
|
47
|
+
# @yield [wordlist]
|
48
|
+
# The given block will be passed the new wordlist.
|
49
|
+
#
|
50
|
+
# @yieldparam [Wordlist] wordlist
|
51
|
+
# The new wordlist object.
|
52
|
+
#
|
53
|
+
# @example Use a file wordlist
|
54
|
+
# wordlist = Wordlist.new('passwords.txt')
|
55
|
+
#
|
56
|
+
# @example Use a range of Strings
|
57
|
+
# wordlist = Wordlist.new('aaaa'..'zzzz')
|
58
|
+
#
|
59
|
+
# @example Specify mutation rules
|
60
|
+
# wordlist = Wordlist.new('passwords.txt', /e/ => ['E', '3'])
|
61
|
+
#
|
62
|
+
# @see String#mutate
|
63
|
+
#
|
64
|
+
# @api public
|
65
|
+
#
|
66
|
+
def initialize(list,mutations={})
|
67
|
+
@list = list
|
68
|
+
@mutations = {}
|
69
|
+
@mutations.merge!(mutations)
|
70
|
+
|
71
|
+
yield self if block_given?
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Iterates over each word in the list.
|
76
|
+
#
|
77
|
+
# @yield [word]
|
78
|
+
# The given block will be passed each word.
|
79
|
+
#
|
80
|
+
# @yieldparam [String] word
|
81
|
+
# A word from the list.
|
82
|
+
#
|
83
|
+
# @return [Enumerator]
|
84
|
+
# If no block is given, an Enumerator will be returned.
|
85
|
+
#
|
86
|
+
# @raise [TypeError]
|
87
|
+
# The list was not a path to a wordlist file, nor a list of words.
|
88
|
+
#
|
89
|
+
# @api public
|
90
|
+
#
|
91
|
+
def each_word(&block)
|
92
|
+
return enum_for(:each_word) unless block
|
93
|
+
|
94
|
+
case @list
|
95
|
+
when String
|
96
|
+
File.open(@list) do |file|
|
97
|
+
file.each_line do |line|
|
98
|
+
yield line.chomp
|
99
|
+
end
|
100
|
+
end
|
101
|
+
when Enumerable
|
102
|
+
@list.each(&block)
|
103
|
+
else
|
104
|
+
raise(TypeError,"list must be a path or Enumerable")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# Iterates over each word, and each mutation, from the list.
|
110
|
+
#
|
111
|
+
# @yield [word]
|
112
|
+
# The given block will be passed each word.
|
113
|
+
#
|
114
|
+
# @yieldparam [String] word
|
115
|
+
# A word from the list.
|
116
|
+
#
|
117
|
+
# @return [Enumerator]
|
118
|
+
# If no block is given, an Enumerator will be returned.
|
119
|
+
#
|
120
|
+
# @api public
|
121
|
+
#
|
122
|
+
def each(&block)
|
123
|
+
return enum_for(:each) unless block
|
124
|
+
|
125
|
+
each_word do |word|
|
126
|
+
yield word
|
127
|
+
|
128
|
+
unless @mutations.empty?
|
129
|
+
# perform additional mutations
|
130
|
+
word.mutate(@mutations,&block)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# Iterates over every n words.
|
137
|
+
#
|
138
|
+
# @param [Integer, Range, Array] n
|
139
|
+
# The number of words to combine.
|
140
|
+
#
|
141
|
+
# @yield [words]
|
142
|
+
# The given block will be passed every combination of `n` words.
|
143
|
+
#
|
144
|
+
# @yieldparam [String]
|
145
|
+
# The combination of `n` words.
|
146
|
+
#
|
147
|
+
# @return [Enumerator]
|
148
|
+
# If no block is given, an Enumerator will be returned.
|
149
|
+
#
|
150
|
+
# @api public
|
151
|
+
#
|
152
|
+
def each_n_words(n,&block)
|
153
|
+
String.generate([each, n],&block)
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ronin/extensions/regexp'
|
3
|
+
|
4
|
+
describe Regexp do
|
5
|
+
describe Regexp::IPv4 do
|
6
|
+
subject { Regexp::IPv4 }
|
7
|
+
|
8
|
+
it "should match valid addresses" do
|
9
|
+
ip = '127.0.0.1'
|
10
|
+
|
11
|
+
subject.match(ip)[0].should == ip
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should match the Any address" do
|
15
|
+
ip = '0.0.0.0'
|
16
|
+
|
17
|
+
subject.match(ip)[0].should == ip
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should match the broadcast address" do
|
21
|
+
ip = '255.255.255.255'
|
22
|
+
|
23
|
+
subject.match(ip)[0].should == ip
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should not match addresses with octets > 255" do
|
27
|
+
ip = '10.1.256.1'
|
28
|
+
|
29
|
+
subject.match(ip).should be_nil
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should not match addresses with more than three digits per octet" do
|
33
|
+
ip = '10.1111.1.1'
|
34
|
+
|
35
|
+
subject.match(ip).should be_nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -74,7 +74,7 @@ describe String do
|
|
74
74
|
"%u006F%u006E%u0065%u0020%u0026%u0020%u0074%u0077%u006F"
|
75
75
|
end
|
76
76
|
let(:js_hex) { "%6F%6E%65%20%26%20%74%77%6F" }
|
77
|
-
let(:js_mixed) { "
|
77
|
+
let(:js_mixed) { "%u6F%u6E%u65 %26 two" }
|
78
78
|
|
79
79
|
it "should unescape JavaScript unicode characters" do
|
80
80
|
js_unicode.js_unescape.should == subject
|
@@ -20,7 +20,7 @@ describe String do
|
|
20
20
|
@string.should respond_to(:sql_decode)
|
21
21
|
end
|
22
22
|
|
23
|
-
describe "
|
23
|
+
describe "#sql_escape" do
|
24
24
|
it "should be able to single-quote escape" do
|
25
25
|
@string_with_quotes.sql_escape(:single).should == %{'"O''Brian"'}
|
26
26
|
end
|
@@ -30,7 +30,7 @@ describe String do
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
describe "
|
33
|
+
describe "#sql_encode" do
|
34
34
|
it "should be able to be SQL-hex encoded" do
|
35
35
|
@string.sql_encode.should == @sql_encoded
|
36
36
|
end
|
@@ -40,7 +40,7 @@ describe String do
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
describe "
|
43
|
+
describe "#sql_decode" do
|
44
44
|
it "should be able to be SQL-hex decoded" do
|
45
45
|
encoded = @string.sql_encode
|
46
46
|
|
@@ -52,4 +52,24 @@ describe String do
|
|
52
52
|
"'Conan O''Brian'".sql_decode.should == "Conan O'Brian"
|
53
53
|
end
|
54
54
|
end
|
55
|
+
|
56
|
+
describe "#sql_inject" do
|
57
|
+
context "when there is a leading quote character" do
|
58
|
+
it "should remove the first and last quote character" do
|
59
|
+
"'1' OR '1'='1'".sql_inject.should == "1' OR '1'='1"
|
60
|
+
end
|
61
|
+
|
62
|
+
context "when there is no matching leading/trailing quote characters" do
|
63
|
+
it "should comment-terminate the String" do
|
64
|
+
"'1' OR 1=1".sql_inject.should == "1' OR 1=1--"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when there is no leading quote character" do
|
70
|
+
it "should not modify the String" do
|
71
|
+
"1 OR 1=1".sql_inject.should == "1 OR 1=1"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
55
75
|
end
|
@@ -4,10 +4,6 @@ require 'ronin/formatting/text'
|
|
4
4
|
describe String do
|
5
5
|
subject { "hello" }
|
6
6
|
|
7
|
-
it "should provide String.generate" do
|
8
|
-
described_class.should respond_to(:generate)
|
9
|
-
end
|
10
|
-
|
11
7
|
it "should provide String#format_chars" do
|
12
8
|
should respond_to(:format_chars)
|
13
9
|
end
|
@@ -28,112 +24,6 @@ describe String do
|
|
28
24
|
should respond_to(:insert_after)
|
29
25
|
end
|
30
26
|
|
31
|
-
describe "generate" do
|
32
|
-
subject { described_class }
|
33
|
-
|
34
|
-
it "should generate Strings from CharSets" do
|
35
|
-
strings = subject.generate(:lowercase_hexadecimal, :numeric).to_a
|
36
|
-
|
37
|
-
strings.grep(/^[0-9a-f][0-9]$/).should == strings
|
38
|
-
end
|
39
|
-
|
40
|
-
it "should generate Strings from lengths of CharSets" do
|
41
|
-
strings = subject.generate([:numeric, 2]).to_a
|
42
|
-
|
43
|
-
strings.grep(/^[0-9]{2}$/).should == strings
|
44
|
-
end
|
45
|
-
|
46
|
-
it "should generate Strings from varying lengths of CharSets" do
|
47
|
-
strings = subject.generate([:numeric, 1..2]).to_a
|
48
|
-
|
49
|
-
strings.grep(/^[0-9]{1,2}$/).should == strings
|
50
|
-
end
|
51
|
-
|
52
|
-
it "should generate Strings from custom CharSets" do
|
53
|
-
strings = subject.generate([%w[a b c], 2]).to_a
|
54
|
-
|
55
|
-
strings.grep(/^[abc]{2}$/).should == strings
|
56
|
-
end
|
57
|
-
|
58
|
-
it "should generate Strings containing known Strings" do
|
59
|
-
strings = subject.generate('foo', [%w[a b c], 2]).to_a
|
60
|
-
|
61
|
-
strings.grep(/^foo[abc]{2}$/).should == strings
|
62
|
-
end
|
63
|
-
|
64
|
-
it "should raise a TypeError for non String, Symbol, Enumerable CharSets" do
|
65
|
-
lambda {
|
66
|
-
subject.generate([Object.new, 2]).to_a
|
67
|
-
}.should raise_error(TypeError)
|
68
|
-
end
|
69
|
-
|
70
|
-
it "should raise an ArgumentError for unknown CharSets" do
|
71
|
-
lambda {
|
72
|
-
subject.generate([:foo_bar, 2]).to_a
|
73
|
-
}.should raise_error(ArgumentError)
|
74
|
-
end
|
75
|
-
|
76
|
-
it "should raise a TypeError for non Integer,Array,Range lengths" do
|
77
|
-
lambda {
|
78
|
-
subject.generate([:numeric, 'foo']).to_a
|
79
|
-
}.should raise_error(TypeError)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
describe "#fuzz" do
|
84
|
-
subject { 'GET /one/two/three' }
|
85
|
-
|
86
|
-
it "should match Regexps" do
|
87
|
-
fuzzed = subject.fuzz(/GET/ => ['get']).to_a
|
88
|
-
|
89
|
-
fuzzed.should == ['get /one/two/three']
|
90
|
-
end
|
91
|
-
|
92
|
-
it "should match Strings" do
|
93
|
-
fuzzed = subject.fuzz('GET' => ['get']).to_a
|
94
|
-
|
95
|
-
fuzzed.should == ['get /one/two/three']
|
96
|
-
end
|
97
|
-
|
98
|
-
it "should match Integers" do
|
99
|
-
fuzzed = subject.fuzz(0x20 => ["\t"]).to_a
|
100
|
-
|
101
|
-
fuzzed.should == ["GET\t/one/two/three"]
|
102
|
-
end
|
103
|
-
|
104
|
-
it "should substitute using Procs" do
|
105
|
-
fuzzed = subject.fuzz('GET' => [lambda { |s| s.downcase }]).to_a
|
106
|
-
|
107
|
-
fuzzed.should == ['get /one/two/three']
|
108
|
-
end
|
109
|
-
|
110
|
-
it "should substitute using Integers" do
|
111
|
-
fuzzed = subject.fuzz(' ' => [0x09]).to_a
|
112
|
-
|
113
|
-
fuzzed.should == ["GET\t/one/two/three"]
|
114
|
-
end
|
115
|
-
|
116
|
-
it "should incrementally replace each occurrence" do
|
117
|
-
fuzzed = subject.fuzz('/' => ["\n\r"]).to_a
|
118
|
-
|
119
|
-
fuzzed.should == [
|
120
|
-
"GET \n\rone/two/three",
|
121
|
-
"GET /one\n\rtwo/three",
|
122
|
-
"GET /one/two\n\rthree"
|
123
|
-
]
|
124
|
-
end
|
125
|
-
|
126
|
-
it "should replace each occurrence with each substitution" do
|
127
|
-
fuzzed = subject.fuzz('GET' => ["\n\rGET", "G\n\rET", "GET\n\r"]).to_a
|
128
|
-
|
129
|
-
fuzzed.should == [
|
130
|
-
"\n\rGET /one/two/three",
|
131
|
-
"G\n\rET /one/two/three",
|
132
|
-
"GET\n\r /one/two/three"
|
133
|
-
]
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
27
|
describe "#format_bytes" do
|
138
28
|
it "should format each byte in the String" do
|
139
29
|
subject.format_bytes { |b|
|