ruby_smb 0.0.14 → 0.0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/examples/anonymous_auth.rb +27 -0
- data/examples/authenticate.rb +6 -1
- data/lib/ruby_smb/client.rb +16 -4
- data/lib/ruby_smb/client/authentication.rb +52 -2
- data/lib/ruby_smb/client/negotiation.rb +11 -3
- data/lib/ruby_smb/dispatcher/socket.rb +2 -3
- data/lib/ruby_smb/error.rb +4 -0
- data/lib/ruby_smb/smb1/packet.rb +2 -0
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +50 -0
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +36 -0
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/client_spec.rb +51 -1
- data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +24 -1
- data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +62 -0
- data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +52 -0
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +2 -1
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +2 -1
- metadata +9 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ff3adf882b31caf2ae7b1d97cf9ada237cc6752
|
4
|
+
data.tar.gz: 6c120054caaa7c6e61702d8d7a84b878c096a20a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a4a7dbab03e8c80b676601cf31fc52807ed5f7936e7e850f3eee05606985864bd1686c3f4580e87fc5f021b2a8863e439aac7d1c9beff8603dfe1cc6f88b947
|
7
|
+
data.tar.gz: ba6758711607388641d4286df70d2cd06f04de3e2c5617321aa4e414b7ef6185646c99fc54a9f3bfb2837368bd3169c2cf435be9a9869d2132ead4649da1c0e9
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# This script tests a full Authentication/Session Setup cycle
|
4
|
+
# including protocol negotiation and authentication.
|
5
|
+
|
6
|
+
require 'bundler/setup'
|
7
|
+
require 'ruby_smb'
|
8
|
+
|
9
|
+
|
10
|
+
def run_authentication(address, smb1, smb2, username, password)
|
11
|
+
# Create our socket and add it to the dispatcher
|
12
|
+
sock = TCPSocket.new address, 445
|
13
|
+
dispatcher = RubySMB::Dispatcher::Socket.new(sock)
|
14
|
+
|
15
|
+
client = RubySMB::Client.new(dispatcher, smb1: smb1, smb2: smb2, username: username, password: password)
|
16
|
+
protocol = client.negotiate
|
17
|
+
status = client.authenticate
|
18
|
+
puts "#{protocol} : #{status}"
|
19
|
+
end
|
20
|
+
|
21
|
+
address = ARGV[0]
|
22
|
+
username = ''
|
23
|
+
password = ''
|
24
|
+
|
25
|
+
# Negotiate with only SMB1 enabled
|
26
|
+
run_authentication(address, true, false, username, password)
|
27
|
+
|
data/examples/authenticate.rb
CHANGED
@@ -15,7 +15,12 @@ def run_authentication(address, smb1, smb2, username, password)
|
|
15
15
|
client = RubySMB::Client.new(dispatcher, smb1: smb1, smb2: smb2, username: username, password: password)
|
16
16
|
protocol = client.negotiate
|
17
17
|
status = client.authenticate
|
18
|
-
|
18
|
+
if client.peer_native_os
|
19
|
+
native_os = "(#{client.peer_native_os})"
|
20
|
+
else
|
21
|
+
native_os = ''
|
22
|
+
end
|
23
|
+
puts "#{protocol} : #{status} #{native_os}"
|
19
24
|
end
|
20
25
|
|
21
26
|
address = ARGV[0]
|
data/lib/ruby_smb/client.rb
CHANGED
@@ -33,6 +33,11 @@ module RubySMB
|
|
33
33
|
# @return [String]
|
34
34
|
attr_accessor :domain
|
35
35
|
|
36
|
+
# The address of the remote host
|
37
|
+
# @!attribute [rw] host
|
38
|
+
# @return [String]
|
39
|
+
attr_accessor :host
|
40
|
+
|
36
41
|
# The local workstation to pretend to be
|
37
42
|
# @!attribute [rw] local_workstation
|
38
43
|
# @return [String]
|
@@ -48,6 +53,12 @@ module RubySMB
|
|
48
53
|
# @return [String]
|
49
54
|
attr_accessor :password
|
50
55
|
|
56
|
+
# The Native OS of the Peer/Server.
|
57
|
+
# Currently only available with SMB1.
|
58
|
+
# @!attribute [rw] peer_native_os
|
59
|
+
# @return [String]
|
60
|
+
attr_accessor :peer_native_os
|
61
|
+
|
51
62
|
# The Sequence Counter used for SMB1 Signing.
|
52
63
|
# It tracks the number of packets both sent and received
|
53
64
|
# since the NTLM session was initialized with the Challenge.
|
@@ -101,14 +112,15 @@ module RubySMB
|
|
101
112
|
@dispatcher = dispatcher
|
102
113
|
@domain = domain
|
103
114
|
@local_workstation = local_workstation
|
104
|
-
@password = password.encode("utf-8")
|
115
|
+
@password = password.encode("utf-8") || ''.encode("utf-8")
|
105
116
|
@sequence_counter = 0
|
106
117
|
@session_id = 0x00
|
107
118
|
@session_key = ''
|
108
119
|
@signing_required = false
|
109
120
|
@smb1 = smb1
|
110
121
|
@smb2 = smb2
|
111
|
-
@username = username.encode("utf-8")
|
122
|
+
@username = username.encode("utf-8") || ''.encode("utf-8")
|
123
|
+
@host = dispatcher.tcp_socket.peeraddr[2]
|
112
124
|
|
113
125
|
@ntlm_client = Net::NTLM::Client.new(
|
114
126
|
@username,
|
@@ -167,8 +179,8 @@ module RubySMB
|
|
167
179
|
def login(username: self.username, password: self.password, domain: self.domain, local_workstation: self.local_workstation )
|
168
180
|
@domain = domain
|
169
181
|
@local_workstation = local_workstation
|
170
|
-
@password = password.encode("utf-8")
|
171
|
-
@username = username.encode("utf-8")
|
182
|
+
@password = password.encode("utf-8") || ''.encode("utf-8")
|
183
|
+
@username = username.encode("utf-8") || ''.encode("utf-8")
|
172
184
|
|
173
185
|
@ntlm_client = Net::NTLM::Client.new(
|
174
186
|
@username,
|
@@ -10,7 +10,11 @@ module RubySMB
|
|
10
10
|
# @return [WindowsError::NTStatus] the NTStatus object from the SessionSetup exchange.
|
11
11
|
def authenticate
|
12
12
|
if self.smb1
|
13
|
-
|
13
|
+
if self.username.empty? && self.password.empty?
|
14
|
+
smb1_anonymous_auth
|
15
|
+
else
|
16
|
+
smb1_authenticate
|
17
|
+
end
|
14
18
|
else
|
15
19
|
smb2_authenticate
|
16
20
|
end
|
@@ -20,6 +24,47 @@ module RubySMB
|
|
20
24
|
# SMB1 Methods
|
21
25
|
#
|
22
26
|
|
27
|
+
# Attempts an Anonymous logon to the remote server.
|
28
|
+
#
|
29
|
+
# @return [WindowsError::ErrorCode] the status code the server returned
|
30
|
+
def smb1_anonymous_auth
|
31
|
+
request = smb1_anonymous_auth_request
|
32
|
+
raw_response = send_recv(request)
|
33
|
+
response = smb1_anonymous_auth_response(raw_response)
|
34
|
+
response_code = response.status_code
|
35
|
+
|
36
|
+
if response_code.name == "STATUS_SUCCESS"
|
37
|
+
self.user_id = user_id
|
38
|
+
self.peer_native_os = response.data_block.native_os
|
39
|
+
end
|
40
|
+
|
41
|
+
response_code
|
42
|
+
end
|
43
|
+
|
44
|
+
# Creates a {SessionSetupRequest} for an anonymous
|
45
|
+
# access session.
|
46
|
+
def smb1_anonymous_auth_request
|
47
|
+
packet = RubySMB::SMB1::Packet::SessionSetupLegacyRequest.new
|
48
|
+
packet.data_block.oem_password = "\x00"
|
49
|
+
packet.parameter_block.max_buffer_size = 4356
|
50
|
+
packet.parameter_block.max_mpx_count = 50
|
51
|
+
packet.parameter_block.capabilities.extended_security = 0
|
52
|
+
packet
|
53
|
+
end
|
54
|
+
|
55
|
+
def smb1_anonymous_auth_response(raw_response)
|
56
|
+
begin
|
57
|
+
packet = RubySMB::SMB1::Packet::SessionSetupLegacyResponse.read(raw_response)
|
58
|
+
rescue
|
59
|
+
packet = RubySMB::SMB1::Packet::EmptyPacket.read(raw_response)
|
60
|
+
end
|
61
|
+
|
62
|
+
unless packet.smb_header.command == RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
|
63
|
+
raise RubySMB::Error::InvalidPacket, "Command was #{packet.smb_header.command} and not #{RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP}"
|
64
|
+
end
|
65
|
+
packet
|
66
|
+
end
|
67
|
+
|
23
68
|
# Handles the SMB1 NTLMSSP 4-way handshake for Authentication
|
24
69
|
def smb1_authenticate
|
25
70
|
response = smb1_ntlmssp_negotiate
|
@@ -29,7 +74,12 @@ module RubySMB
|
|
29
74
|
raw = smb1_ntlmssp_authenticate(challenge_message, user_id)
|
30
75
|
response = smb1_ntlmssp_final_packet(raw)
|
31
76
|
response_code = response.status_code
|
32
|
-
|
77
|
+
|
78
|
+
if response_code.name == "STATUS_SUCCESS"
|
79
|
+
self.user_id = user_id
|
80
|
+
self.peer_native_os = response.data_block.native_os
|
81
|
+
end
|
82
|
+
|
33
83
|
response_code
|
34
84
|
end
|
35
85
|
|
@@ -10,9 +10,17 @@ module RubySMB
|
|
10
10
|
#
|
11
11
|
# @return [void]
|
12
12
|
def negotiate
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
begin
|
14
|
+
raw_response = negotiate_request
|
15
|
+
response_packet = negotiate_response(raw_response)
|
16
|
+
parse_negotiate_response(response_packet)
|
17
|
+
rescue RubySMB::Error::InvalidPacket, Errno::ECONNRESET
|
18
|
+
error = "Unable to Negotiate with #{self.host}"
|
19
|
+
if smb1 && !smb2
|
20
|
+
error << ', SMB1 may be disabled'
|
21
|
+
end
|
22
|
+
raise RubySMB::Error::NegotiationFailure, error
|
23
|
+
end
|
16
24
|
end
|
17
25
|
|
18
26
|
# Creates and dispatches the first Negotiate Request Packet and
|
@@ -31,7 +31,7 @@ class RubySMB::Dispatcher::Socket < RubySMB::Dispatcher::Base
|
|
31
31
|
while bytes_written < data.size
|
32
32
|
bytes_written += @tcp_socket.write(data[bytes_written..-1])
|
33
33
|
end
|
34
|
-
rescue IOError => e
|
34
|
+
rescue IOError, Errno::ECONNABORTED, Errno::ECONNRESET => e
|
35
35
|
raise RubySMB::Error::CommunicationError, "An error occured writing to the Socket"
|
36
36
|
end
|
37
37
|
nil
|
@@ -41,7 +41,6 @@ class RubySMB::Dispatcher::Socket < RubySMB::Dispatcher::Base
|
|
41
41
|
# Throw Error::NetBiosSessionService if there's an error reading the first 4 bytes,
|
42
42
|
# which are assumed to be the NetBiosSessionService header.
|
43
43
|
# @return [String]
|
44
|
-
# @todo should return SMB2::Packet
|
45
44
|
def recv_packet
|
46
45
|
begin
|
47
46
|
IO.select([@tcp_socket])
|
@@ -56,7 +55,7 @@ class RubySMB::Dispatcher::Socket < RubySMB::Dispatcher::Base
|
|
56
55
|
data << @tcp_socket.read(length - data.length) while data.length < length
|
57
56
|
|
58
57
|
data
|
59
|
-
rescue Errno::EINVAL, TypeError => e
|
58
|
+
rescue Errno::EINVAL, Errno::ECONNABORTED, Errno::ECONNRESET, TypeError => e
|
60
59
|
raise RubySMB::Error::CommunicationError, "An error occured reading from the Socket"
|
61
60
|
end
|
62
61
|
end
|
data/lib/ruby_smb/error.rb
CHANGED
@@ -17,4 +17,8 @@ module RubySMB::Error
|
|
17
17
|
|
18
18
|
# Raised when an error occurs with the underlying socket.
|
19
19
|
class CommunicationError < StandardError; end
|
20
|
+
|
21
|
+
# Raised when Protocol Negotiation fails, possibly due to an
|
22
|
+
# unsupported protocol.
|
23
|
+
class NegotiationFailure < StandardError; end
|
20
24
|
end
|
data/lib/ruby_smb/smb1/packet.rb
CHANGED
@@ -6,7 +6,9 @@ module RubySMB
|
|
6
6
|
require 'ruby_smb/smb1/packet/negotiate_response'
|
7
7
|
require 'ruby_smb/smb1/packet/negotiate_response_extended'
|
8
8
|
require 'ruby_smb/smb1/packet/session_setup_request'
|
9
|
+
require 'ruby_smb/smb1/packet/session_setup_legacy_request'
|
9
10
|
require 'ruby_smb/smb1/packet/session_setup_response'
|
11
|
+
require 'ruby_smb/smb1/packet/session_setup_legacy_response'
|
10
12
|
require 'ruby_smb/smb1/packet/tree_connect_request'
|
11
13
|
require 'ruby_smb/smb1/packet/tree_connect_response'
|
12
14
|
require 'ruby_smb/smb1/packet/tree_disconnect_request'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module SMB1
|
3
|
+
module Packet
|
4
|
+
|
5
|
+
# A SMB1 SMB_COM_SESSION_SETUP_ANDX Request Packet, without NTLMSSP as defined in
|
6
|
+
# [2.2.4.53.1 Request](https://msdn.microsoft.com/en-us/library/ee441849.aspx)
|
7
|
+
class SessionSetupLegacyRequest < RubySMB::GenericPacket
|
8
|
+
|
9
|
+
# A SMB1 Parameter Block as defined by the {SessionSetupRequest}
|
10
|
+
class ParameterBlock < RubySMB::SMB1::ParameterBlock
|
11
|
+
and_x_block :andx_block
|
12
|
+
uint16 :max_buffer_size, label: 'Max Buffer Size'
|
13
|
+
uint16 :max_mpx_count, label: 'Max Mpx Count'
|
14
|
+
uint16 :vc_number, label: 'VC Number'
|
15
|
+
uint32 :session_key, label: 'Session Key'
|
16
|
+
uint16 :oem_password_length, label: 'OEM Password Length', value: lambda { self.parent.data_block.oem_password.length }
|
17
|
+
uint16 :unicode_password_length, label: 'Unicored Password Length', value: lambda { self.parent.data_block.unicode_password.length }
|
18
|
+
uint32 :reserved
|
19
|
+
capabilities :capabilities
|
20
|
+
end
|
21
|
+
|
22
|
+
# Represents the specific layout of the DataBlock for a {SessionSetupRequest} Packet.
|
23
|
+
# Due to vagaries of character encoding and the way we currently handle NTLM authentication
|
24
|
+
# for the security blob, you must null-terminate the {native_os} and {native_lan_man} fields
|
25
|
+
# yourself if you set them away from their defaults.
|
26
|
+
class DataBlock < RubySMB::SMB1::DataBlock
|
27
|
+
string :oem_password, label: 'OEM Password'
|
28
|
+
string :unicode_password, label: 'Unicode password'
|
29
|
+
string :padding, label: 'Padding'
|
30
|
+
string :account_name, label: 'Account Name(username)', length: 2
|
31
|
+
string :primary_domain, label: 'Primary Domain', length: 2
|
32
|
+
stringz :native_os, label: 'Native OS', initial_value: 'Windows 7 Ultimate N 7601 Service Pack 1'
|
33
|
+
stringz :native_lan_man, label: 'Native LAN Manager', initial_value: 'Windows 7 Ultimate N 6.1'
|
34
|
+
end
|
35
|
+
|
36
|
+
smb_header :smb_header
|
37
|
+
parameter_block :parameter_block
|
38
|
+
data_block :data_block
|
39
|
+
|
40
|
+
def initialize_instance
|
41
|
+
super
|
42
|
+
smb_header.command = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
|
43
|
+
parameter_block.capabilities.extended_security = 0
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module SMB1
|
3
|
+
module Packet
|
4
|
+
|
5
|
+
# A SMB1 SMB_COM_SESSION_SETUP Legacy Response Packet as defined in
|
6
|
+
# [2.2.4.53.2 Response](https://msdn.microsoft.com/en-us/library/ee442143.aspx)
|
7
|
+
class SessionSetupLegacyResponse < RubySMB::GenericPacket
|
8
|
+
|
9
|
+
# A SMB1 Parameter Block as defined by the {SessionSetupResponse}
|
10
|
+
class ParameterBlock < RubySMB::SMB1::ParameterBlock
|
11
|
+
and_x_block :andx_block
|
12
|
+
uint16 :action, label: 'Action'
|
13
|
+
end
|
14
|
+
|
15
|
+
# Represents the specific layout of the DataBlock for a {SessionSetupResponse} Packet.
|
16
|
+
class DataBlock < RubySMB::SMB1::DataBlock
|
17
|
+
string :pad, label: 'Padding', length: 0
|
18
|
+
stringz :native_os, label: 'Native OS'
|
19
|
+
stringz :native_lan_man, label: 'Native LAN Manager'
|
20
|
+
stringz :primary_domain, label: 'Primary Domain'
|
21
|
+
end
|
22
|
+
|
23
|
+
smb_header :smb_header
|
24
|
+
parameter_block :parameter_block
|
25
|
+
data_block :data_block
|
26
|
+
|
27
|
+
def initialize_instance
|
28
|
+
super
|
29
|
+
smb_header.command = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
|
30
|
+
smb_header.flags.reply = 1
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/ruby_smb/version.rb
CHANGED
@@ -2,12 +2,14 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
RSpec.describe RubySMB::Client do
|
4
4
|
|
5
|
-
let(:
|
5
|
+
let(:sock) { double("Socket", peeraddr: '192.168.1.5' ) }
|
6
|
+
let(:dispatcher) { RubySMB::Dispatcher::Socket.new(sock) }
|
6
7
|
let(:username) { 'msfadmin' }
|
7
8
|
let(:password) { 'msfadmin' }
|
8
9
|
subject(:client) { described_class.new(dispatcher, username: username, password: password) }
|
9
10
|
let(:smb1_client) { described_class.new(dispatcher, smb2:false, username: username, password: password) }
|
10
11
|
let(:smb2_client) { described_class.new(dispatcher, smb1:false, username: username, password: password) }
|
12
|
+
let(:empty_packet) { RubySMB::SMB1::Packet::EmptyPacket.new }
|
11
13
|
|
12
14
|
describe '#initialize' do
|
13
15
|
it 'should raise an ArgumentError without a valid dispatcher' do
|
@@ -319,6 +321,26 @@ RSpec.describe RubySMB::Client do
|
|
319
321
|
"MATgBKAEQARwAwAFUA\nQQA5ADAARgADAB4AVwBJAE4ALQBTAE4ASgBEAEcAMABVAEEAOQAwAEYABw" +
|
320
322
|
"AI\nADxThZ4nnNIBAAAAAA==\n"
|
321
323
|
}
|
324
|
+
|
325
|
+
describe '#authenticate' do
|
326
|
+
it 'calls #smb2_authenticate if SMB2 was selected/negotiated' do
|
327
|
+
expect(smb2_client).to receive(:smb2_authenticate)
|
328
|
+
smb2_client.authenticate
|
329
|
+
end
|
330
|
+
|
331
|
+
it 'calls #smb1_authenticate if SMB1 was selected and we have credentials' do
|
332
|
+
expect(smb1_client).to receive(:smb1_authenticate)
|
333
|
+
smb1_client.authenticate
|
334
|
+
end
|
335
|
+
|
336
|
+
it 'calls #smb1_anonymous_auth if using SMB1 and no credentials were supplied' do
|
337
|
+
smb1_client.username = ''
|
338
|
+
smb1_client.password = ''
|
339
|
+
expect(smb1_client).to receive(:smb1_anonymous_auth)
|
340
|
+
smb1_client.authenticate
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
322
344
|
context 'for SMB1' do
|
323
345
|
let(:ntlm_client) { smb1_client.ntlm_client }
|
324
346
|
let(:type1_message) { ntlm_client.init_context }
|
@@ -439,6 +461,34 @@ RSpec.describe RubySMB::Client do
|
|
439
461
|
expect(smb1_client.smb1_type2_message(response_packet)).to eq [fake_type2].pack('m')
|
440
462
|
end
|
441
463
|
end
|
464
|
+
|
465
|
+
describe 'Anonymous Auth' do
|
466
|
+
let(:anonymous_request) { RubySMB::SMB1::Packet::SessionSetupLegacyRequest.new }
|
467
|
+
let(:anonymous_response) { RubySMB::SMB1::Packet::SessionSetupLegacyResponse.new }
|
468
|
+
|
469
|
+
describe '#smb1_anonymous_auth_request' do
|
470
|
+
it 'creates a SessionSetupLegacyRequest packet with a null byte for the oem password' do
|
471
|
+
expect(smb1_client.smb1_anonymous_auth_request.data_block.oem_password).to eq "\x00"
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
describe '#smb1_anonymous_auth_response' do
|
476
|
+
it 'returns a Legacy Session SetupResponse Packet' do
|
477
|
+
expect(smb1_client.smb1_anonymous_auth_response(anonymous_response.to_binary_s)).to eq anonymous_response
|
478
|
+
end
|
479
|
+
|
480
|
+
it 'returns an empty packet if the raw data is not a valid response' do
|
481
|
+
empty_packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
|
482
|
+
expect(smb1_client.smb1_anonymous_auth_response(empty_packet.to_binary_s)).to eq empty_packet
|
483
|
+
end
|
484
|
+
|
485
|
+
it 'raises an InvalidPacket error if the command is wrong' do
|
486
|
+
anonymous_response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_NEGOTIATE
|
487
|
+
expect{ smb1_client.smb1_anonymous_auth_response(anonymous_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
end
|
442
492
|
end
|
443
493
|
|
444
494
|
context 'for SMB2' do
|
@@ -1,5 +1,13 @@
|
|
1
1
|
RSpec.describe RubySMB::Dispatcher::Socket do
|
2
|
-
|
2
|
+
class FakeSocket < StringIO
|
3
|
+
def setsockopt(*args)
|
4
|
+
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:fake_tcp_socket) {
|
9
|
+
FakeSocket.new('deadbeef')
|
10
|
+
}
|
3
11
|
|
4
12
|
subject(:smb_socket) { described_class.new(fake_tcp_socket) }
|
5
13
|
let(:negotiate_response) { RubySMB::SMB2::Packet::NegotiateResponse.new }
|
@@ -11,6 +19,11 @@ RSpec.describe RubySMB::Dispatcher::Socket do
|
|
11
19
|
allow(IO).to receive(:select).and_return(nil)
|
12
20
|
end
|
13
21
|
|
22
|
+
it 'should attempt to set KEEPALIVE on the socket' do
|
23
|
+
expect(fake_tcp_socket).to receive(:setsockopt).with(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
|
24
|
+
described_class.new(fake_tcp_socket)
|
25
|
+
end
|
26
|
+
|
14
27
|
describe '#connect' do
|
15
28
|
it 'should support setting a custom socket object' do
|
16
29
|
socket = described_class.connect('172.16.22.165', socket: fake_tcp_socket)
|
@@ -44,6 +57,11 @@ RSpec.describe RubySMB::Dispatcher::Socket do
|
|
44
57
|
smb_socket.recv_packet
|
45
58
|
end
|
46
59
|
end
|
60
|
+
|
61
|
+
it 'raises a CommunicationError if it encounters a socket error' do
|
62
|
+
expect(fake_tcp_socket).to receive(:read).and_raise(Errno::ECONNRESET)
|
63
|
+
expect{ smb_socket.recv_packet }.to raise_error(RubySMB::Error::CommunicationError)
|
64
|
+
end
|
47
65
|
end
|
48
66
|
|
49
67
|
describe '#send_packet' do
|
@@ -56,5 +74,10 @@ RSpec.describe RubySMB::Dispatcher::Socket do
|
|
56
74
|
expect(fake_tcp_socket).to receive(:write).with(response_packet).and_call_original
|
57
75
|
smb_socket.send_packet(negotiate_response)
|
58
76
|
end
|
77
|
+
|
78
|
+
it 'raises a CommunicationError if it encounters a socket error' do
|
79
|
+
expect(fake_tcp_socket).to receive(:write).and_raise(Errno::ECONNRESET)
|
80
|
+
expect{ smb_socket.send_packet(negotiate_response) }.to raise_error(RubySMB::Error::CommunicationError)
|
81
|
+
end
|
59
82
|
end
|
60
83
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe RubySMB::SMB1::Packet::SessionSetupLegacyRequest do
|
4
|
+
|
5
|
+
subject(:packet) { described_class.new }
|
6
|
+
|
7
|
+
describe '#smb_header' do
|
8
|
+
subject(:header) { packet.smb_header }
|
9
|
+
|
10
|
+
it 'is a standard SMB Header' do
|
11
|
+
expect(header).to be_a RubySMB::SMB1::SMBHeader
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should have the command set to SMB_COM_NEGOTIATE' do
|
15
|
+
expect(header.command).to eq RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should not have the response flag set' do
|
19
|
+
expect(header.flags.reply).to eq 0
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#parameter_block' do
|
24
|
+
subject(:parameter_block) { packet.parameter_block }
|
25
|
+
|
26
|
+
it 'is a standard ParameterBlock' do
|
27
|
+
expect(parameter_block).to be_a RubySMB::SMB1::ParameterBlock
|
28
|
+
end
|
29
|
+
|
30
|
+
it { is_expected.to respond_to :andx_block }
|
31
|
+
it { is_expected.to respond_to :max_buffer_size }
|
32
|
+
it { is_expected.to respond_to :max_mpx_count }
|
33
|
+
it { is_expected.to respond_to :vc_number }
|
34
|
+
it { is_expected.to respond_to :session_key }
|
35
|
+
it { is_expected.to respond_to :oem_password_length }
|
36
|
+
it { is_expected.to respond_to :unicode_password_length }
|
37
|
+
it { is_expected.to respond_to :capabilities }
|
38
|
+
|
39
|
+
it 'has an AndXBlock' do
|
40
|
+
expect(parameter_block.andx_block).to be_a RubySMB::SMB1::AndXBlock
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#data_block' do
|
45
|
+
subject(:data_block) { packet.data_block }
|
46
|
+
|
47
|
+
it 'is a standard DataBlock' do
|
48
|
+
expect(data_block).to be_a RubySMB::SMB1::DataBlock
|
49
|
+
end
|
50
|
+
|
51
|
+
it { is_expected.to respond_to :oem_password }
|
52
|
+
it { is_expected.to respond_to :unicode_password }
|
53
|
+
it { is_expected.to respond_to :padding }
|
54
|
+
it { is_expected.to respond_to :account_name }
|
55
|
+
it { is_expected.to respond_to :primary_domain }
|
56
|
+
it { is_expected.to respond_to :native_os }
|
57
|
+
it { is_expected.to respond_to :native_lan_man }
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe RubySMB::SMB1::Packet::SessionSetupLegacyResponse do
|
4
|
+
|
5
|
+
subject(:packet) { described_class.new }
|
6
|
+
|
7
|
+
describe '#smb_header' do
|
8
|
+
subject(:header) { packet.smb_header }
|
9
|
+
|
10
|
+
it 'is a standard SMB Header' do
|
11
|
+
expect(header).to be_a RubySMB::SMB1::SMBHeader
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should have the command set to SMB_COM_NEGOTIATE' do
|
15
|
+
expect(header.command).to eq RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should have the response flag set' do
|
19
|
+
expect(header.flags.reply).to eq 1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#parameter_block' do
|
24
|
+
subject(:parameter_block) { packet.parameter_block }
|
25
|
+
|
26
|
+
it 'is a standard ParameterBlock' do
|
27
|
+
expect(parameter_block).to be_a RubySMB::SMB1::ParameterBlock
|
28
|
+
end
|
29
|
+
|
30
|
+
it { is_expected.to respond_to :andx_block }
|
31
|
+
it { is_expected.to respond_to :action }
|
32
|
+
|
33
|
+
|
34
|
+
it 'has an AndXBlock' do
|
35
|
+
expect(parameter_block.andx_block).to be_a RubySMB::SMB1::AndXBlock
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#data_block' do
|
40
|
+
subject(:data_block) { packet.data_block }
|
41
|
+
|
42
|
+
it 'is a standard DataBlock' do
|
43
|
+
expect(data_block).to be_a RubySMB::SMB1::DataBlock
|
44
|
+
end
|
45
|
+
|
46
|
+
it { is_expected.to respond_to :primary_domain }
|
47
|
+
it { is_expected.to respond_to :native_os }
|
48
|
+
it { is_expected.to respond_to :native_lan_man }
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe RubySMB::SMB1::Tree do
|
4
|
-
let(:
|
4
|
+
let(:sock) { double("Socket", peeraddr: '192.168.1.5' ) }
|
5
|
+
let(:dispatcher) { RubySMB::Dispatcher::Socket.new(sock) }
|
5
6
|
|
6
7
|
let(:client) { RubySMB::Client.new(dispatcher, username: 'msfadmin', password: 'msfadmin') }
|
7
8
|
let(:tree_id) { 2049 }
|
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe RubySMB::SMB2::Tree do
|
4
|
-
let(:
|
4
|
+
let(:sock) { double("Socket", peeraddr: '192.168.1.5' ) }
|
5
|
+
let(:dispatcher) { RubySMB::Dispatcher::Socket.new(sock) }
|
5
6
|
|
6
7
|
let(:client) { RubySMB::Client.new(dispatcher, username: 'msfadmin', password: 'msfadmin') }
|
7
8
|
let(:tree_id) { 2049 }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_smb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Maloney
|
@@ -89,7 +89,7 @@ cert_chain:
|
|
89
89
|
G+Hmcg1v810agasPdoydE0RTVZgEOOMoQ07qu7JFXVWZ9ZQpHT7qJATWL/b2csFG
|
90
90
|
8mVuTXnyJOKRJA==
|
91
91
|
-----END CERTIFICATE-----
|
92
|
-
date: 2017-05-
|
92
|
+
date: 2017-05-24 00:00:00.000000000 Z
|
93
93
|
dependencies:
|
94
94
|
- !ruby/object:Gem::Dependency
|
95
95
|
name: redcarpet
|
@@ -221,6 +221,7 @@ files:
|
|
221
221
|
- LICENSE.txt
|
222
222
|
- README.md
|
223
223
|
- Rakefile
|
224
|
+
- examples/anonymous_auth.rb
|
224
225
|
- examples/authenticate.rb
|
225
226
|
- examples/negotiate.rb
|
226
227
|
- examples/tree_connect.rb
|
@@ -290,6 +291,8 @@ files:
|
|
290
291
|
- lib/ruby_smb/smb1/packet/nt_trans/request.rb
|
291
292
|
- lib/ruby_smb/smb1/packet/nt_trans/response.rb
|
292
293
|
- lib/ruby_smb/smb1/packet/nt_trans/subcommands.rb
|
294
|
+
- lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb
|
295
|
+
- lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb
|
293
296
|
- lib/ruby_smb/smb1/packet/session_setup_request.rb
|
294
297
|
- lib/ruby_smb/smb1/packet/session_setup_response.rb
|
295
298
|
- lib/ruby_smb/smb1/packet/trans2.rb
|
@@ -383,6 +386,8 @@ files:
|
|
383
386
|
- spec/lib/ruby_smb/smb1/packet/nt_trans/create_response_spec.rb
|
384
387
|
- spec/lib/ruby_smb/smb1/packet/nt_trans/request_spec.rb
|
385
388
|
- spec/lib/ruby_smb/smb1/packet/nt_trans/response_spec.rb
|
389
|
+
- spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb
|
390
|
+
- spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb
|
386
391
|
- spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb
|
387
392
|
- spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb
|
388
393
|
- spec/lib/ruby_smb/smb1/packet/trans2/open2_request_spec.rb
|
@@ -494,6 +499,8 @@ test_files:
|
|
494
499
|
- spec/lib/ruby_smb/smb1/packet/nt_trans/create_response_spec.rb
|
495
500
|
- spec/lib/ruby_smb/smb1/packet/nt_trans/request_spec.rb
|
496
501
|
- spec/lib/ruby_smb/smb1/packet/nt_trans/response_spec.rb
|
502
|
+
- spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb
|
503
|
+
- spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb
|
497
504
|
- spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb
|
498
505
|
- spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb
|
499
506
|
- spec/lib/ruby_smb/smb1/packet/trans2/open2_request_spec.rb
|
metadata.gz.sig
CHANGED
Binary file
|