ruby_smb 0.0.14 → 0.0.15
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.
- 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
|