librex 0.0.32 → 0.0.33

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.
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env rubyj
2
+
3
+ $:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..','..','..','..','..', 'lib'))
4
+
5
+ require 'rex/post/meterpreter/extensions/stdapi/railgun/railgun'
6
+ require 'rex/post/meterpreter/extensions/stdapi/railgun/mock_magic'
7
+ require 'test/unit'
8
+ require 'benchmark'
9
+
10
+ module Rex
11
+ module Post
12
+ module Meterpreter
13
+ module Extensions
14
+ module Stdapi
15
+ module Railgun
16
+ class Railgun::UnitTest < Test::Unit::TestCase
17
+
18
+ include MockMagic
19
+
20
+ def test_add_dll
21
+ railgun = Railgun.new(make_mock_client())
22
+
23
+ target_dll_name = 'discordia'
24
+ target_windows_name = 'C:\look\behind\you'
25
+
26
+ railgun.add_dll(target_dll_name, target_windows_name)
27
+
28
+ actual_dll = railgun.get_dll(target_dll_name);
29
+
30
+ assert_not_nil(actual_dll,
31
+ "add_dll should make a DLL accessible via get_dll")
32
+
33
+ assert_equal(actual_dll.dll_path, target_windows_name,
34
+ "add_dll should set a dll path when specified")
35
+
36
+ #
37
+ # wrapper = railgun.send(target_dll_name.to_sym)
38
+ #
39
+ # assert_same(wrapper._dll, actual_dll,
40
+ # "railgun instance responds with dll wrapper as expected")
41
+ end
42
+
43
+ def test_add_function
44
+ mock_function_descriptions.each do |func|
45
+ railgun = Railgun.new(make_mock_client(func[:platform]))
46
+
47
+ dll_name = func[:dll_name]
48
+ function_name = func[:name]
49
+
50
+ railgun.add_dll(dll_name)
51
+ railgun.add_function(dll_name, function_name, func[:return_type], func[:params])
52
+
53
+ assert(railgun.get_dll(dll_name).functions.has_key?(function_name),
54
+ "add_function should add a function to the DLL specified")
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -60,11 +60,17 @@ module PacketDispatcher
60
60
 
61
61
  bytes = 0
62
62
  raw = packet.to_r
63
+ err = nil
63
64
 
64
65
  if (raw)
66
+
65
67
  begin
66
68
  bytes = self.sock.write(raw)
67
69
  rescue ::Exception => e
70
+ err = e
71
+ end
72
+
73
+ if bytes.to_i == 0
68
74
  # Mark the session itself as dead
69
75
  self.alive = false
70
76
 
@@ -72,7 +78,7 @@ module PacketDispatcher
72
78
  @finish = true
73
79
 
74
80
  # Reraise the error to the top-level caller
75
- raise e
81
+ raise err if err
76
82
  end
77
83
  end
78
84
 
@@ -113,7 +119,7 @@ module PacketDispatcher
113
119
  waiter = add_response_waiter(packet)
114
120
 
115
121
  # Transmit the packet
116
- if (send_packet(packet) <= 0)
122
+ if (send_packet(packet).to_i <= 0)
117
123
  # Remove the waiter if we failed to send the packet.
118
124
  remove_response_waiter(waiter)
119
125
  return nil
@@ -38,7 +38,7 @@ class Handler::Proc < Handler
38
38
  rescue Errno::EPIPE
39
39
  elog("Proc::on_request: Client closed connection prematurely", LogSource)
40
40
  rescue
41
- elog("Proc::on_request: #{$!}\n\n#{$@.join("\n")}", LogSource)
41
+ elog("Proc::on_request: #{$!.class}: #{$!}\n\n#{$@.join("\n")}", LogSource)
42
42
  if self.server and self.server.context
43
43
  exploit = self.server.context['MsfExploit']
44
44
  if exploit
@@ -99,15 +99,17 @@ class Server
99
99
  # Initializes an HTTP server as listening on the provided port and
100
100
  # hostname.
101
101
  #
102
- def initialize(port = 80, listen_host = '0.0.0.0', ssl = false, context = {}, comm = nil)
102
+ def initialize(port = 80, listen_host = '0.0.0.0', ssl = false, context = {}, comm = nil, ssl_cert = nil)
103
103
  self.listen_host = listen_host
104
104
  self.listen_port = port
105
+ self.ssl = ssl
105
106
  self.context = context
107
+ self.comm = comm
108
+ self.ssl_cert = ssl_cert
109
+
106
110
  self.listener = nil
107
111
  self.resources = {}
108
112
  self.server_name = DefaultServer
109
- self.ssl = ssl
110
- self.comm = comm
111
113
  end
112
114
 
113
115
  #
@@ -134,6 +136,7 @@ class Server
134
136
  'LocalPort' => self.listen_port,
135
137
  'Context' => self.context,
136
138
  'SSL' => self.ssl,
139
+ 'SSLCert' => self.ssl_cert,
137
140
  'Comm' => self.comm
138
141
  )
139
142
 
@@ -219,7 +222,7 @@ class Server
219
222
  # Adds Server headers and stuff.
220
223
  #
221
224
  def add_response_headers(resp)
222
- resp['Server'] = self.server_name
225
+ resp['Server'] = self.server_name if not resp['Server']
223
226
  end
224
227
 
225
228
  #
@@ -256,7 +259,7 @@ class Server
256
259
  cli.send_response(resp)
257
260
  end
258
261
 
259
- attr_accessor :listen_port, :listen_host, :server_name, :context, :ssl, :comm
262
+ attr_accessor :listen_port, :listen_host, :server_name, :context, :ssl, :comm, :ssl_cert
260
263
  attr_accessor :listener, :resources
261
264
 
262
265
  protected
@@ -275,10 +278,7 @@ protected
275
278
  #
276
279
  def on_client_data(cli)
277
280
  begin
278
- #
279
- # XXX: Handle ParseCode::Partial
280
- #
281
- data = cli.get
281
+ data = cli.read(65535)
282
282
 
283
283
  raise ::EOFError if not data
284
284
  raise ::EOFError if data.empty?
@@ -286,8 +286,13 @@ protected
286
286
  case cli.request.parse(data)
287
287
  when Packet::ParseCode::Completed
288
288
  dispatch_request(cli, cli.request)
289
-
290
289
  cli.reset_cli
290
+
291
+ when Packet::ParseCode::Partial
292
+ # Return and wait for the on_client_data handler to be called again
293
+ # The Request object tracks the state of the request for us
294
+ return
295
+
291
296
  when Packet::ParseCode::Error
292
297
  close_client(cli)
293
298
  end
@@ -63,6 +63,10 @@ class Rex::Socket::Parameters
63
63
  #
64
64
  # Specify SSL2, SSL3, or TLS1 (SSL3 is default)
65
65
  #
66
+ # SSLCert
67
+ #
68
+ # A file containing an SSL certificate (for server sockets)
69
+ #
66
70
  # Proxies
67
71
  #
68
72
  # List of proxies to use.
@@ -139,6 +143,14 @@ class Rex::Socket::Parameters
139
143
  self.ssl_version = hash['SSLVersion']
140
144
  end
141
145
 
146
+ if (hash['SSLCert'] and ::File.file?(hash['SSLCert']))
147
+ begin
148
+ self.ssl_cert = ::File.read(hash['SSLCert'])
149
+ rescue ::Exception => e
150
+ elog("Failed to read cert: #{e.class}: #{e}", LogSource)
151
+ end
152
+ end
153
+
142
154
  if hash['Proxies']
143
155
  self.proxies = hash['Proxies'].split('-').map{|a| a.strip}.map{|a| a.split(':').map{|b| b.strip}}
144
156
  end
@@ -325,6 +337,10 @@ class Rex::Socket::Parameters
325
337
  #
326
338
  attr_accessor :ssl_version
327
339
  #
340
+ # The SSL certificate, in pem format, stored as a string. See +SslTcpServer#make_ssl+
341
+ #
342
+ attr_accessor :ssl_cert
343
+ #
328
344
  # Whether we should use IPv6
329
345
  #
330
346
  attr_accessor :v6
@@ -47,7 +47,7 @@ module Rex::Socket::SslTcpServer
47
47
 
48
48
  def initsock(params = nil)
49
49
  raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
50
- self.sslctx = makessl()
50
+ self.sslctx = makessl(params.ssl_cert)
51
51
  super
52
52
  end
53
53
 
@@ -99,43 +99,52 @@ module Rex::Socket::SslTcpServer
99
99
  end
100
100
 
101
101
 
102
- def makessl
103
- key = OpenSSL::PKey::RSA.new(1024){ }
104
-
105
- cert = OpenSSL::X509::Certificate.new
106
- cert.version = 2
107
- cert.serial = rand(0xFFFFFFFF)
108
- # name = OpenSSL::X509::Name.new([["C","JP"],["O","TEST"],["CN","localhost"]])
109
- subject = OpenSSL::X509::Name.new([
110
- ["C","US"],
111
- ['ST', Rex::Text.rand_state()],
112
- ["L", Rex::Text.rand_text_alpha(rand(20) + 10)],
113
- ["O", Rex::Text.rand_text_alpha(rand(20) + 10)],
114
- ["CN", Rex::Text.rand_hostname],
115
- ])
116
- issuer = OpenSSL::X509::Name.new([
117
- ["C","US"],
118
- ['ST', Rex::Text.rand_state()],
119
- ["L", Rex::Text.rand_text_alpha(rand(20) + 10)],
120
- ["O", Rex::Text.rand_text_alpha(rand(20) + 10)],
121
- ["CN", Rex::Text.rand_hostname],
122
- ])
123
-
124
- cert.subject = subject
125
- cert.issuer = issuer
126
- cert.not_before = Time.now - (3600 * 365)
127
- cert.not_after = Time.now + (3600 * 365)
128
- cert.public_key = key.public_key
129
- ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
130
- cert.extensions = [
131
- ef.create_extension("basicConstraints","CA:FALSE"),
132
- ef.create_extension("subjectKeyIdentifier","hash"),
133
- ef.create_extension("extendedKeyUsage","serverAuth"),
134
- ef.create_extension("keyUsage","keyEncipherment,dataEncipherment,digitalSignature")
135
- ]
136
- ef.issuer_certificate = cert
137
- cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
138
- cert.sign(key, OpenSSL::Digest::SHA1.new)
102
+ #
103
+ # Create a new ssl context. If +ssl_cert+ is not given, generates a new
104
+ # key and a leaf certificate with random values.
105
+ #
106
+ def makessl(ssl_cert=nil)
107
+
108
+ if ssl_cert
109
+ cert = OpenSSL::X509::Certificate.new(ssl_cert)
110
+ key = OpenSSL::PKey::RSA.new(ssl_cert)
111
+ else
112
+ key = OpenSSL::PKey::RSA.new(1024){ }
113
+ cert = OpenSSL::X509::Certificate.new
114
+ cert.version = 2
115
+ cert.serial = rand(0xFFFFFFFF)
116
+ # name = OpenSSL::X509::Name.new([["C","JP"],["O","TEST"],["CN","localhost"]])
117
+ subject = OpenSSL::X509::Name.new([
118
+ ["C","US"],
119
+ ['ST', Rex::Text.rand_state()],
120
+ ["L", Rex::Text.rand_text_alpha(rand(20) + 10)],
121
+ ["O", Rex::Text.rand_text_alpha(rand(20) + 10)],
122
+ ["CN", Rex::Text.rand_hostname],
123
+ ])
124
+ issuer = OpenSSL::X509::Name.new([
125
+ ["C","US"],
126
+ ['ST', Rex::Text.rand_state()],
127
+ ["L", Rex::Text.rand_text_alpha(rand(20) + 10)],
128
+ ["O", Rex::Text.rand_text_alpha(rand(20) + 10)],
129
+ ["CN", Rex::Text.rand_hostname],
130
+ ])
131
+
132
+ cert.subject = subject
133
+ cert.issuer = issuer
134
+ cert.not_before = Time.now - (3600 * 365)
135
+ cert.not_after = Time.now + (3600 * 365)
136
+ cert.public_key = key.public_key
137
+ ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
138
+ cert.extensions = [
139
+ ef.create_extension("basicConstraints","CA:FALSE"),
140
+ ef.create_extension("subjectKeyIdentifier","hash"),
141
+ ef.create_extension("extendedKeyUsage","serverAuth"),
142
+ ef.create_extension("keyUsage","keyEncipherment,dataEncipherment,digitalSignature")
143
+ ]
144
+ ef.issuer_certificate = cert
145
+ cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
146
+ cert.sign(key, OpenSSL::Digest::SHA1.new)
147
+ end
139
148
 
140
149
  ctx = OpenSSL::SSL::SSLContext.new()
141
150
  ctx.key = key
@@ -35,6 +35,8 @@ module DispatcherShell
35
35
  #
36
36
  # Returns nil for an empty set of commands.
37
37
  #
38
+ # This method should be overridden
39
+ #
38
40
  def commands
39
41
  end
40
42
 
@@ -84,7 +86,9 @@ module DispatcherShell
84
86
  # Displays the help banner. With no arguments, this is just a list of
85
87
  # all commands grouped by dispatcher. Otherwise, tries to use a method
86
88
  # named cmd_#{+cmd+}_help for the first dispatcher that has a command
87
- # named +cmd+.
89
+ # named +cmd+. If no such method exists, uses +cmd+ as a regex to
90
+ # compare against each enstacked dispatcher's name and dumps commands
91
+ # of any that match.
88
92
  #
89
93
  def cmd_help(cmd=nil, *ignored)
90
94
  if cmd
@@ -103,6 +107,16 @@ module DispatcherShell
103
107
  break
104
108
  end
105
109
  end
110
+
111
+ unless cmd_found
112
+ # We didn't find a cmd, try it as a dispatcher name
113
+ shell.dispatcher_stack.each do |dispatcher|
114
+ if dispatcher.name =~ /#{cmd}/i
115
+ print_line(dispatcher.help_to_s)
116
+ cmd_found = help_found = true
117
+ end
118
+ end
119
+ end
106
120
  print_error("No help for #{cmd}, try -h") if cmd_found and not help_found
107
121
  print_error("No such command") if not cmd_found
108
122
  else
@@ -127,6 +141,37 @@ module DispatcherShell
127
141
 
128
142
  alias cmd_? cmd_help
129
143
 
144
+ #
145
+ # Return a pretty, user-readable table of commands provided by this
146
+ # dispatcher.
147
+ #
148
+ def help_to_s(opts={})
149
+ # If this dispatcher has no commands, we can't do anything useful.
150
+ return "" if commands.nil? or commands.length == 0
151
+
152
+ # Display the commands
153
+ tbl = Table.new(
154
+ 'Header' => "#{self.name} Commands",
155
+ 'Indent' => opts['Indent'] || 4,
156
+ 'Columns' =>
157
+ [
158
+ 'Command',
159
+ 'Description'
160
+ ],
161
+ 'ColProps' =>
162
+ {
163
+ 'Command' =>
164
+ {
165
+ 'MaxWidth' => 12
166
+ }
167
+ })
168
+
169
+ commands.sort.each { |c|
170
+ tbl << c
171
+ }
172
+
173
+ return "\n" + tbl.to_s + "\n"
174
+ end
130
175
 
131
176
  #
132
177
  # No tab completion items by default
@@ -391,45 +436,20 @@ module DispatcherShell
391
436
  #
392
437
  # Return a readable version of a help banner for all of the enstacked
393
438
  # dispatchers.
439
+ #
440
+ # See +CommandDispatcher#help_to_s+
394
441
  #
395
442
  def help_to_s(opts = {})
396
443
  str = ''
397
444
 
398
445
  dispatcher_stack.reverse.each { |dispatcher|
399
- # No commands? Suckage.
400
- next if ((dispatcher.respond_to?('commands') == false) or
401
- (dispatcher.commands == nil) or
402
- (dispatcher.commands.length == 0))
403
-
404
- # Display the commands
405
- tbl = Table.new(
406
- 'Header' => "#{dispatcher.name} Commands",
407
- 'Indent' => opts['Indent'] || 4,
408
- 'Columns' =>
409
- [
410
- 'Command',
411
- 'Description'
412
- ],
413
- 'ColProps' =>
414
- {
415
- 'Command' =>
416
- {
417
- 'MaxWidth' => 12
418
- }
419
- })
420
-
421
- dispatcher.commands.sort.each { |c|
422
- tbl << c
423
- }
424
-
425
- str << "\n" + tbl.to_s + "\n"
446
+ str << dispatcher.help_to_s
426
447
  }
427
448
 
428
449
  return str
429
450
  end
430
451
 
431
452
 
432
-
433
453
  #
434
454
  # Returns nil for an empty set of blocked commands.
435
455
  #
@@ -89,5 +89,8 @@ require 'rex/zip/entry'
89
89
  # the archive class
90
90
  require 'rex/zip/archive'
91
91
 
92
+ # a child of Archive, implements Java ARchives for creating java applications
93
+ require 'rex/zip/jar'
94
+
92
95
  end
93
96
  end
@@ -1,5 +1,5 @@
1
1
  ##
2
- # $Id: archive.rb 11175 2010-11-30 07:10:57Z egypt $
2
+ # $Id: archive.rb 12718 2011-05-25 16:45:20Z egypt $
3
3
  ##
4
4
 
5
5
  module Rex
@@ -9,14 +9,20 @@ module Zip
9
9
  # This represents an entire archive.
10
10
  #
11
11
  class Archive
12
+
13
+ # An array of the Entry objects stored in this Archive.
12
14
  attr_reader :entries
13
15
 
16
+
14
17
  def initialize(compmeth=CM_DEFLATE)
15
18
  @compmeth = compmeth
16
19
  @entries = []
17
20
  end
18
21
 
19
22
 
23
+ #
24
+ # Create a new Entry and add it to the archive.
25
+ #
20
26
  def add_file(fname, fdata=nil, xtra=nil, comment=nil)
21
27
  if (not fdata)
22
28
  begin
@@ -45,6 +51,9 @@ class Archive
45
51
  end
46
52
 
47
53
 
54
+ #
55
+ # Write the compressed file to +fname+.
56
+ #
48
57
  def save_to(fname)
49
58
  f = File.open(fname, 'wb')
50
59
  f.write(pack)
@@ -52,6 +61,9 @@ class Archive
52
61
  end
53
62
 
54
63
 
64
+ #
65
+ # Compress this archive and return the resulting zip file as a String.
66
+ #
55
67
  def pack
56
68
  ret = ''
57
69
 
@@ -92,93 +104,5 @@ class Archive
92
104
 
93
105
  end
94
106
 
95
- class Jar < Archive
96
- attr_accessor :manifest
97
-
98
- def build_manifest(opts={})
99
- main_class = opts[:main_class] || nil
100
- existing_manifest = nil
101
-
102
- @manifest = "Manifest-Version: 1.0\r\n"
103
- @manifest << "Main-Class: #{main_class}\r\n" if main_class
104
- @manifest << "\r\n"
105
- @entries.each { |e|
106
- existing_manifest = e if e.name == "META-INF/MANIFEST.MF"
107
- next unless e.name =~ /\.class$/
108
- @manifest << "Name: #{e.name}\r\n"
109
- #@manifest << "SHA1-Digest: #{Digest::SHA1.base64digest(e.data)}\r\n"
110
- @manifest << "\r\n"
111
- }
112
- if existing_manifest
113
- existing_manifest.data = @manifest
114
- else
115
- add_file("META-INF/", '')
116
- add_file("META-INF/MANIFEST.MF", @manifest)
117
- end
118
- end
119
-
120
- def to_s
121
- pack
122
- end
123
-
124
- def length
125
- pack.length
126
- end
127
-
128
- #
129
- # Add multiple files from an array
130
- #
131
- # +files+ should be structured like so:
132
- # [
133
- # [ "path", "to", "file1" ],
134
- # [ "path", "to", "file2" ]
135
- # ]
136
- # and +path+ should be the location on the file system to find the files to
137
- # add. +base_dir+ will be prepended to the path inside the jar.
138
- #
139
- # Example:
140
- # <code>
141
- # war = Rex::Zip::Jar.new
142
- # war.add_file("WEB-INF/", '')
143
- # war.add_file("WEB-INF/", "web.xml", web_xml)
144
- # war.add_file("WEB-INF/classes/", '')
145
- # files = [
146
- # [ "servlet", "examples", "HelloWorld.class" ],
147
- # [ "Foo.class" ],
148
- # [ "servlet", "Bar.class" ],
149
- # ]
150
- # war.add_files(files, "./class_files/", "WEB-INF/classes/")
151
- # </code>
152
- #
153
- # The above code would create a jar with the following structure from files
154
- # found in ./class_files/ :
155
- #
156
- # +- WEB-INF/
157
- # +- web.xml
158
- # +- classes/
159
- # +- Foo.class
160
- # +- servlet/
161
- # +- Bar.class
162
- # +- examples/
163
- # +- HelloWorld.class
164
- #
165
- def add_files(files, path, base_dir="")
166
- files.each do |file|
167
- # Add all of the subdirectories if they don't already exist
168
- 1.upto(file.length - 1) do |idx|
169
- full = base_dir + file[0,idx].join("/") + "/"
170
- if !(entries.map{|e|e.name}.include?(full))
171
- add_file(full, '')
172
- end
173
- end
174
- # Now add the actual file, grabbing data from the filesystem
175
- fd = File.open(File.join( path, file ), "rb")
176
- data = fd.read(fd.stat.size)
177
- fd.close
178
- add_file(base_dir + file.join("/"), data)
179
- end
180
- end
181
- end
182
-
183
107
  end
184
108
  end