librex 0.0.32 → 0.0.33

Sign up to get free protection for your applications and to get access to all the features.
@@ -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