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.
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|