ronin-support 0.3.0 → 0.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/ChangeLog.md +77 -7
  2. data/README.md +19 -3
  3. data/gemspec.yml +2 -2
  4. data/lib/ronin/extensions/regexp.rb +50 -2
  5. data/lib/ronin/extensions/string.rb +1 -0
  6. data/lib/ronin/formatting.rb +1 -0
  7. data/lib/ronin/formatting/extensions.rb +1 -0
  8. data/lib/ronin/formatting/extensions/binary/string.rb +56 -5
  9. data/lib/ronin/formatting/extensions/html/string.rb +6 -7
  10. data/lib/ronin/formatting/extensions/sql/string.rb +34 -0
  11. data/lib/ronin/formatting/extensions/text/string.rb +0 -180
  12. data/lib/ronin/fuzzing.rb +21 -0
  13. data/lib/ronin/fuzzing/extensions.rb +20 -0
  14. data/lib/ronin/fuzzing/extensions/string.rb +380 -0
  15. data/lib/ronin/fuzzing/fuzzing.rb +191 -0
  16. data/lib/ronin/network/esmtp.rb +94 -1
  17. data/lib/ronin/network/extensions/esmtp/net.rb +2 -82
  18. data/lib/ronin/network/extensions/http/net.rb +1 -736
  19. data/lib/ronin/network/extensions/imap/net.rb +1 -103
  20. data/lib/ronin/network/extensions/pop3/net.rb +1 -71
  21. data/lib/ronin/network/extensions/smtp/net.rb +2 -157
  22. data/lib/ronin/network/extensions/ssl/net.rb +1 -132
  23. data/lib/ronin/network/extensions/tcp/net.rb +2 -296
  24. data/lib/ronin/network/extensions/telnet/net.rb +1 -135
  25. data/lib/ronin/network/extensions/udp/net.rb +2 -214
  26. data/lib/ronin/network/http/http.rb +750 -5
  27. data/lib/ronin/network/imap.rb +105 -2
  28. data/lib/ronin/network/mixins.rb +1 -1
  29. data/lib/ronin/network/mixins/esmtp.rb +49 -52
  30. data/lib/ronin/network/mixins/http.rb +49 -53
  31. data/lib/ronin/network/mixins/imap.rb +47 -44
  32. data/lib/ronin/network/mixins/mixin.rb +58 -0
  33. data/lib/ronin/network/mixins/pop3.rb +44 -38
  34. data/lib/ronin/network/mixins/smtp.rb +49 -51
  35. data/lib/ronin/network/mixins/tcp.rb +56 -69
  36. data/lib/ronin/network/mixins/telnet.rb +57 -50
  37. data/lib/ronin/network/mixins/udp.rb +48 -52
  38. data/lib/ronin/network/network.rb +1 -0
  39. data/lib/ronin/network/pop3.rb +72 -2
  40. data/lib/ronin/network/smtp/email.rb +1 -0
  41. data/lib/ronin/network/smtp/smtp.rb +159 -3
  42. data/lib/ronin/network/ssl.rb +131 -2
  43. data/lib/ronin/network/tcp.rb +306 -1
  44. data/lib/ronin/network/telnet.rb +136 -2
  45. data/lib/ronin/network/udp.rb +229 -1
  46. data/lib/ronin/support.rb +2 -3
  47. data/lib/ronin/support/support.rb +38 -0
  48. data/lib/ronin/support/version.rb +1 -1
  49. data/lib/ronin/templates/erb.rb +2 -1
  50. data/lib/ronin/ui/output/helpers.rb +35 -1
  51. data/lib/ronin/ui/shell.rb +12 -2
  52. data/lib/ronin/wordlist.rb +157 -0
  53. data/spec/extensions/regexp_spec.rb +38 -0
  54. data/spec/formatting/html/string_spec.rb +1 -1
  55. data/spec/formatting/sql/string_spec.rb +23 -3
  56. data/spec/formatting/text/string_spec.rb +0 -110
  57. data/spec/fuzzing/string_spec.rb +158 -0
  58. data/spec/wordlist_spec.rb +65 -0
  59. metadata +35 -27
@@ -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
- if (Output.verbose? && !(Output.silent?))
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
  #
@@ -74,7 +74,15 @@ module Ronin
74
74
  @prompt = options.fetch(:prompt,DEFAULT_PROMPT)
75
75
 
76
76
  @commands = Set[:help, :exit]
77
- @commands += protected_methods.map { |name| name.to_sym }
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
- handler(line)
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) { "one %26 two" }
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 "SQL escaping" do
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 "SQL-hex encoding" do
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 "SQL-hex decoding" do
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|