ruby_smb 1.0.4 → 2.0.2

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 (130) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.travis.yml +3 -2
  5. data/Gemfile +6 -2
  6. data/README.md +35 -47
  7. data/examples/enum_registry_key.rb +28 -0
  8. data/examples/enum_registry_values.rb +30 -0
  9. data/examples/negotiate.rb +51 -8
  10. data/examples/pipes.rb +2 -1
  11. data/examples/read_file_encryption.rb +56 -0
  12. data/examples/read_registry_key_value.rb +32 -0
  13. data/lib/ruby_smb.rb +4 -1
  14. data/lib/ruby_smb/client.rb +207 -18
  15. data/lib/ruby_smb/client/authentication.rb +27 -8
  16. data/lib/ruby_smb/client/encryption.rb +62 -0
  17. data/lib/ruby_smb/client/negotiation.rb +153 -12
  18. data/lib/ruby_smb/client/signing.rb +19 -0
  19. data/lib/ruby_smb/client/tree_connect.rb +4 -4
  20. data/lib/ruby_smb/client/utils.rb +8 -7
  21. data/lib/ruby_smb/client/winreg.rb +46 -0
  22. data/lib/ruby_smb/crypto.rb +30 -0
  23. data/lib/ruby_smb/dcerpc.rb +38 -0
  24. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  25. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  26. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  27. data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
  28. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  29. data/lib/ruby_smb/dcerpc/request.rb +28 -9
  30. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
  31. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  32. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  33. data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
  34. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  35. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  36. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  37. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  38. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  39. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  40. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  41. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  42. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  43. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  44. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  45. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  46. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
  47. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  48. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  49. data/lib/ruby_smb/dispatcher/socket.rb +4 -3
  50. data/lib/ruby_smb/error.rb +28 -1
  51. data/lib/ruby_smb/smb1/commands.rb +1 -1
  52. data/lib/ruby_smb/smb1/file.rb +6 -4
  53. data/lib/ruby_smb/smb1/packet/empty_packet.rb +4 -2
  54. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +1 -1
  55. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -2
  56. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
  57. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -2
  58. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +1 -1
  59. data/lib/ruby_smb/smb1/pipe.rb +79 -3
  60. data/lib/ruby_smb/smb1/tree.rb +12 -3
  61. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  62. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  63. data/lib/ruby_smb/smb2/file.rb +25 -43
  64. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  65. data/lib/ruby_smb/smb2/packet.rb +2 -0
  66. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  67. data/lib/ruby_smb/smb2/packet/error_packet.rb +9 -4
  68. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -14
  69. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +50 -4
  70. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  71. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +92 -6
  72. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -26
  73. data/lib/ruby_smb/smb2/pipe.rb +77 -3
  74. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  75. data/lib/ruby_smb/smb2/tree.rb +23 -17
  76. data/lib/ruby_smb/version.rb +1 -1
  77. data/ruby_smb.gemspec +5 -3
  78. data/spec/lib/ruby_smb/client_spec.rb +1441 -61
  79. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  80. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  81. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  82. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
  83. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  84. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
  85. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  86. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  87. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  88. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  89. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
  90. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  91. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  92. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  93. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  94. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  95. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
  96. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  97. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
  98. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  99. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  100. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
  101. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  102. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
  103. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  104. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +2 -2
  105. data/spec/lib/ruby_smb/error_spec.rb +59 -0
  106. data/spec/lib/ruby_smb/smb1/file_spec.rb +9 -1
  107. data/spec/lib/ruby_smb/smb1/packet/empty_packet_spec.rb +10 -0
  108. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  109. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  110. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  111. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  112. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +210 -148
  113. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  114. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  115. data/spec/lib/ruby_smb/smb2/file_spec.rb +86 -62
  116. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  117. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  118. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +29 -2
  119. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  120. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  121. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  122. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  123. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -30
  124. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +220 -149
  125. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  126. data/spec/lib/ruby_smb/smb2/tree_spec.rb +53 -8
  127. metadata +187 -81
  128. metadata.gz.sig +0 -0
  129. data/lib/ruby_smb/smb1/dcerpc.rb +0 -72
  130. data/lib/ruby_smb/smb2/dcerpc.rb +0 -75
@@ -0,0 +1,57 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Winreg
4
+
5
+ # This class represents a BaseRegQueryValue Response Packet as defined in
6
+ # [3.1.5.17 BaseRegQueryValue (Opnum 17)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rrp/8bc10aa3-2f91-44e8-aa33-b3263c49ab9d)
7
+ class QueryValueResponse < BinData::Record
8
+ attr_reader :opnum
9
+
10
+ endian :little
11
+
12
+ ndr_lp_dword :lp_type
13
+ ndr_lp_byte :lp_data
14
+ string :pad, length: -> { pad_length }
15
+ ndr_lp_dword :lpcb_data
16
+ ndr_lp_dword :lpcb_len
17
+ uint32 :error_status
18
+
19
+ def initialize_instance
20
+ super
21
+ @opnum = REG_QUERY_VALUE
22
+ end
23
+
24
+ # Determines the correct length for the padding in front of
25
+ # #lpcb_data. It should always force a 4-byte alignment.
26
+ def pad_length
27
+ offset = (lp_data.abs_offset + lp_data.to_binary_s.length) % 4
28
+ (4 - offset) % 4
29
+ end
30
+
31
+ # Returns the data portion of the registry value formatted according to its type:
32
+ # [3.1.1.5 Values](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rrp/3d64dbea-f016-4373-8cac-e43bf343837d)
33
+ def data
34
+ bytes = lp_data.bytes.to_a.pack('C*')
35
+ case lp_type
36
+ when 1,2
37
+ bytes.force_encoding('utf-16le').strip
38
+ when 3
39
+ bytes
40
+ when 4
41
+ bytes.unpack('V').first
42
+ when 5
43
+ bytes.unpack('N').first
44
+ when 7
45
+ str = bytes.force_encoding('utf-16le')
46
+ str.split("\0".encode('utf-16le'))
47
+ when 11
48
+ bytes.unpack('Q<').first
49
+ else
50
+ ""
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,40 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Winreg
4
+
5
+ # This class represents a REGSAM structure as defined in
6
+ # [2.2.3 REGSAM](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rrp/fefbc801-b141-4bb1-9dcb-bf366da3ae7e)
7
+ # [2.4.3 ACCESS_MASK](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/7a53f60e-e730-4dfe-bbe9-b21b62eb790b)
8
+ class Regsam < BinData::Record
9
+ endian :little
10
+ bit2 :reserved, label: 'Reserved Space'
11
+ bit1 :key_create_link, label: 'Key Create Link'
12
+ bit1 :key_notify, label: 'Key Notify'
13
+ bit1 :key_enumerate_sub_keys, label: 'Key Enumerate Sub Keys'
14
+ bit1 :key_create_sub_key, label: 'Key Create Sub Key'
15
+ bit1 :key_set_value, label: 'Key Set Value'
16
+ bit1 :key_query_value, label: 'Key Query Value'
17
+ # byte boundary
18
+ bit6 :reserved2, label: 'Reserved Space'
19
+ bit1 :key_wow64_32key, label: 'Key Wow64 32key'
20
+ bit1 :key_wow64_64key, label: 'Key Wow64 64key'
21
+ # byte boundary
22
+ bit3 :reserved3, label: 'Reserved Space'
23
+ bit1 :synchronize, label: 'Synchronize'
24
+ bit1 :write_owner, label: 'Write Owner'
25
+ bit1 :write_dac, label: 'Write DAC'
26
+ bit1 :read_control, label: 'Read Control'
27
+ bit1 :delete_access, label: 'Delete'
28
+ # byte boundary
29
+ bit1 :generic_read, label: 'Generic Read'
30
+ bit1 :generic_write, label: 'Generic Write'
31
+ bit1 :generic_execute, label: 'Generic Execute'
32
+ bit1 :generic_all, label: 'Generic All'
33
+ bit2 :reserved4, label: 'Reserved Space'
34
+ bit1 :maximum, label: 'Maximum Allowed'
35
+ bit1 :system_security, label: 'System Security'
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -48,7 +48,7 @@ module RubySMB
48
48
  end
49
49
 
50
50
  rescue IOError, Errno::ECONNABORTED, Errno::ECONNRESET => e
51
- raise RubySMB::Error::CommunicationError, "An error occured writing to the Socket: #{e.message}"
51
+ raise RubySMB::Error::CommunicationError, "An error occurred writing to the Socket: #{e.message}"
52
52
  end
53
53
  nil
54
54
  end
@@ -61,13 +61,14 @@ module RubySMB
61
61
  # which are assumed to be the NetBiosSessionService header.
62
62
  # @raise [RubySMB::Error::CommunicationError] if the read timeout expires or an error occurs when reading the socket
63
63
  def recv_packet(full_response: false)
64
+ raise RubySMB::Error::CommunicationError, 'Connection has already been closed' if @tcp_socket.closed?
64
65
  if IO.select([@tcp_socket], nil, nil, @read_timeout).nil?
65
66
  raise RubySMB::Error::CommunicationError, "Read timeout expired when reading from the Socket (timeout=#{@read_timeout})"
66
67
  end
67
68
 
68
69
  begin
69
70
  nbss_data = @tcp_socket.read(4)
70
- raise IOError if nbss_data.nil?
71
+ raise RubySMB::Error::CommunicationError, 'Socket read returned nil' if nbss_data.nil?
71
72
  nbss_header = RubySMB::Nbss::SessionHeader.read(nbss_data)
72
73
  rescue IOError
73
74
  raise ::RubySMB::Error::NetBiosSessionService, 'NBSS Header is missing'
@@ -84,7 +85,7 @@ module RubySMB
84
85
  end
85
86
  data
86
87
  rescue Errno::EINVAL, Errno::ECONNABORTED, Errno::ECONNRESET, TypeError, NoMethodError => e
87
- raise RubySMB::Error::CommunicationError, "An error occured reading from the Socket #{e.message}"
88
+ raise RubySMB::Error::CommunicationError, "An error occurred reading from the Socket #{e.message}"
88
89
  end
89
90
  end
90
91
  end
@@ -53,7 +53,28 @@ module RubySMB
53
53
  end
54
54
 
55
55
  # Raised when a response packet has a NTStatus code that was unexpected.
56
- class UnexpectedStatusCode < RubySMBError; end
56
+ class UnexpectedStatusCode < RubySMBError
57
+ attr_reader :status_code
58
+
59
+ def initialize(status_code)
60
+ case status_code
61
+ when WindowsError::ErrorCode
62
+ @status_code = status_code
63
+ when Integer
64
+ @status_code = WindowsError::NTStatus.find_by_retval(status_code).first
65
+ if @status_code.nil?
66
+ @status_code = WindowsError::ErrorCode.new("0x#{status_code.to_s(16)}", status_code, "Unknown 0x#{status_code.to_s(16)}")
67
+ end
68
+ else
69
+ raise ArgumentError, "Status code must be a WindowsError::ErrorCode or an Integer, got #{status_code.class}"
70
+ end
71
+ super
72
+ end
73
+
74
+ def to_s
75
+ "The server responded with an unexpected status code: #{status_code.name}"
76
+ end
77
+ end
57
78
 
58
79
  # Raised when an error occurs with the underlying socket.
59
80
  class CommunicationError < RubySMBError; end
@@ -65,5 +86,11 @@ module RubySMB
65
86
  # Raised when trying to parse raw binary into a BitField and the data
66
87
  # is invalid.
67
88
  class InvalidBitField < RubySMBError; end
89
+
90
+ # Raised when an encryption operation fails
91
+ class EncryptionError < RubySMBError; end
92
+
93
+ # Raised when an signing operation fails
94
+ class SigningError < RubySMBError; end
68
95
  end
69
96
  end
@@ -10,7 +10,7 @@ module RubySMB
10
10
  SMB_COM_TRANSACTION2_SECONDARY = 0x33
11
11
  SMB_COM_TREE_DISCONNECT = 0x71
12
12
  SMB_COM_NEGOTIATE = 0x72
13
- SMB_COM_SESSION_SETUP = 0x73
13
+ SMB_COM_SESSION_SETUP_ANDX = 0x73
14
14
  SMB_COM_LOGOFF = 0x74
15
15
  SMB_COM_TREE_CONNECT = 0x75
16
16
  SMB_COM_NT_TRANSACT = 0xA0
@@ -91,7 +91,7 @@ module RubySMB
91
91
  )
92
92
  end
93
93
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
94
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
94
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
95
95
  end
96
96
  response.status_code
97
97
  end
@@ -127,7 +127,7 @@ module RubySMB
127
127
  )
128
128
  end
129
129
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
130
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
130
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
131
131
  end
132
132
 
133
133
  if response.is_a?(RubySMB::SMB1::Packet::ReadAndxResponse)
@@ -156,6 +156,8 @@ module RubySMB
156
156
  def read_packet(read_length: 0, offset: 0)
157
157
  read_request = set_header_fields(RubySMB::SMB1::Packet::ReadAndxRequest.new)
158
158
  read_request.parameter_block.max_count_of_bytes_to_return = read_length
159
+ read_request.parameter_block.min_count_of_bytes_to_return = read_length
160
+ read_request.parameter_block.remaining = read_length
159
161
  read_request.parameter_block.offset = offset
160
162
  read_request
161
163
  end
@@ -174,7 +176,7 @@ module RubySMB
174
176
  )
175
177
  end
176
178
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
177
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
179
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
178
180
  end
179
181
 
180
182
  response.data_block.data.to_binary_s
@@ -242,7 +244,7 @@ module RubySMB
242
244
  )
243
245
  end
244
246
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
245
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
247
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
246
248
  end
247
249
  bytes_written = response.parameter_block.count_low + (response.parameter_block.count_high << 16)
248
250
  total_bytes_written += bytes_written
@@ -1,7 +1,7 @@
1
1
  module RubySMB
2
2
  module SMB1
3
3
  module Packet
4
- # This packet represent an SMB1 Response Packet when the parameter and
4
+ # This packet represent an SMB1 Error Response Packet when the parameter and
5
5
  # data blocks will be empty.
6
6
  class EmptyPacket < RubySMB::GenericPacket
7
7
  attr_accessor :original_command
@@ -12,7 +12,9 @@ module RubySMB
12
12
 
13
13
  def valid?
14
14
  return smb_header.protocol == RubySMB::SMB1::SMB_PROTOCOL_ID &&
15
- smb_header.command == @original_command
15
+ smb_header.command == @original_command &&
16
+ parameter_block.word_count == 0 &&
17
+ data_block.byte_count == 0
16
18
  end
17
19
  end
18
20
  end
@@ -4,7 +4,7 @@ module RubySMB
4
4
  # A SMB1 SMB_COM_SESSION_SETUP_ANDX Request Packet, without NTLMSSP as defined in
5
5
  # [2.2.4.53.1 Request](https://msdn.microsoft.com/en-us/library/ee441849.aspx)
6
6
  class SessionSetupLegacyRequest < RubySMB::GenericPacket
7
- COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
7
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
8
8
 
9
9
  # A SMB1 Parameter Block as defined by the {SessionSetupRequest}
10
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
@@ -1,10 +1,10 @@
1
1
  module RubySMB
2
2
  module SMB1
3
3
  module Packet
4
- # A SMB1 SMB_COM_SESSION_SETUP Legacy Response Packet as defined in
4
+ # A SMB1 SMB_COM_SESSION_SETUP_ANDX Legacy Response Packet as defined in
5
5
  # [2.2.4.53.2 Response](https://msdn.microsoft.com/en-us/library/ee442143.aspx)
6
6
  class SessionSetupLegacyResponse < RubySMB::GenericPacket
7
- COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
7
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
8
8
 
9
9
  # A SMB1 Parameter Block as defined by the {SessionSetupResponse}
10
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
@@ -4,7 +4,7 @@ module RubySMB
4
4
  # A SMB1 SMB_COM_SESSION_SETUP_ANDX Request Packet as defined in
5
5
  # [2.2.4.6.1](https://msdn.microsoft.com/en-us/library/cc246328.aspx)
6
6
  class SessionSetupRequest < RubySMB::GenericPacket
7
- COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
7
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
8
8
 
9
9
  # A SMB1 Parameter Block as defined by the {SessionSetupRequest}
10
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
@@ -1,10 +1,10 @@
1
1
  module RubySMB
2
2
  module SMB1
3
3
  module Packet
4
- # A SMB1 SMB_COM_SESSION_SETUP Response Packet as defined in
4
+ # A SMB1 SMB_COM_SESSION_SETUP_ANDX Response Packet as defined in
5
5
  # [2.2.4.6.2](https://msdn.microsoft.com/en-us/library/cc246329.aspx)
6
6
  class SessionSetupResponse < RubySMB::GenericPacket
7
- COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
7
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
8
8
 
9
9
  # A SMB1 Parameter Block as defined by the {SessionSetupResponse}
10
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
@@ -43,7 +43,7 @@ module RubySMB
43
43
 
44
44
  # Represents the specific layout of the DataBlock for a {WriteAndxRequest} Packet.
45
45
  class DataBlock < RubySMB::SMB1::DataBlock
46
- uint8 :pad, label: 'Pad'
46
+ string :pad, label: 'Pad', length: 1
47
47
  string :data, label: 'Data'
48
48
  end
49
49
 
@@ -3,9 +3,9 @@ module RubySMB
3
3
  # Represents a pipe on the Remote server that we can perform
4
4
  # various I/O operations on.
5
5
  class Pipe < File
6
- require 'ruby_smb/smb1/dcerpc'
6
+ require 'ruby_smb/dcerpc'
7
7
 
8
- include RubySMB::SMB1::Dcerpc
8
+ include RubySMB::Dcerpc
9
9
 
10
10
  # Reference: https://msdn.microsoft.com/en-us/library/ee441883.aspx
11
11
  STATUS_DISCONNECTED = 0x0001
@@ -13,6 +13,17 @@ module RubySMB
13
13
  STATUS_OK = 0x0003
14
14
  STATUS_CLOSED = 0x0004
15
15
 
16
+ def initialize(tree:, response:, name:)
17
+ raise ArgumentError, 'No Name Provided' if name.nil?
18
+ case name
19
+ when 'srvsvc'
20
+ extend RubySMB::Dcerpc::Srvsvc
21
+ when 'winreg'
22
+ extend RubySMB::Dcerpc::Winreg
23
+ end
24
+ super(tree: tree, response: response, name: name)
25
+ end
26
+
16
27
  # Performs a peek operation on the named pipe
17
28
  #
18
29
  # @param peek_size [Integer] Amount of data to peek
@@ -36,7 +47,7 @@ module RubySMB
36
47
  end
37
48
 
38
49
  unless response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW or response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
39
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
50
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
40
51
  end
41
52
 
42
53
  response
@@ -68,6 +79,71 @@ module RubySMB
68
79
  state == STATUS_OK
69
80
  end
70
81
 
82
+ # Send a DCERPC request with the provided stub packet.
83
+ #
84
+ # @params stub_packet [#opnum] the stub packet to add to the DCERPC request
85
+ # @return [String] the raw DCERPC response stub
86
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not valid
87
+ # @raise [RubySMB::Error::UnexpectedStatusCode] if the response status code is different than STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW
88
+ def dcerpc_request(stub_packet, options={})
89
+ options.merge!(endpoint: stub_packet.class.name.split('::').at(-2))
90
+ dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: stub_packet.opnum }, options)
91
+ dcerpc_request.stub.read(stub_packet.to_binary_s)
92
+ trans_nmpipe_request = RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest.new(options)
93
+ @tree.set_header_fields(trans_nmpipe_request)
94
+ trans_nmpipe_request.set_fid(@fid)
95
+ trans_nmpipe_request.data_block.trans_data.write_data = dcerpc_request.to_binary_s
96
+
97
+ trans_nmpipe_raw_response = @tree.client.send_recv(trans_nmpipe_request)
98
+ trans_nmpipe_response = RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse.read(trans_nmpipe_raw_response)
99
+ unless trans_nmpipe_response.valid?
100
+ raise RubySMB::Error::InvalidPacket.new(
101
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
102
+ expected_cmd: RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse::COMMAND,
103
+ received_proto: trans_nmpipe_response.smb_header.protocol,
104
+ received_cmd: trans_nmpipe_response.smb_header.command
105
+ )
106
+ end
107
+ unless [WindowsError::NTStatus::STATUS_SUCCESS,
108
+ WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW].include?(trans_nmpipe_response.status_code)
109
+ raise RubySMB::Error::UnexpectedStatusCode, trans_nmpipe_response.status_code
110
+ end
111
+
112
+ raw_data = trans_nmpipe_response.data_block.trans_data.read_data.to_binary_s
113
+ if trans_nmpipe_response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW
114
+ raw_data << read(bytes: @tree.client.max_buffer_size - trans_nmpipe_response.parameter_block.data_count)
115
+ dcerpc_response = dcerpc_response_from_raw_response(raw_data)
116
+ unless dcerpc_response.pdu_header.pfc_flags.first_frag == 1
117
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Not the first fragment"
118
+ end
119
+ stub_data = dcerpc_response.stub.to_s
120
+
121
+ loop do
122
+ break if dcerpc_response.pdu_header.pfc_flags.last_frag == 1
123
+ raw_data = read(bytes: @tree.client.max_buffer_size)
124
+ dcerpc_response = dcerpc_response_from_raw_response(raw_data)
125
+ stub_data << dcerpc_response.stub.to_s
126
+ end
127
+ stub_data
128
+ else
129
+ dcerpc_response = dcerpc_response_from_raw_response(raw_data)
130
+ dcerpc_response.stub.to_s
131
+ end
132
+ end
133
+
134
+
135
+ private
136
+
137
+ def dcerpc_response_from_raw_response(raw_data)
138
+ dcerpc_response = RubySMB::Dcerpc::Response.read(raw_data)
139
+ unless dcerpc_response.pdu_header.ptype == RubySMB::Dcerpc::PTypes::RESPONSE
140
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Not a Response packet"
141
+ end
142
+ dcerpc_response
143
+ rescue IOError
144
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the DCERPC response"
145
+ end
146
+
71
147
  end
72
148
  end
73
149
  end
@@ -123,6 +123,15 @@ module RubySMB
123
123
  raw_response = @client.send_recv(nt_create_andx_request)
124
124
  response = RubySMB::SMB1::Packet::NtCreateAndxResponse.read(raw_response)
125
125
  unless response.valid?
126
+ if response.is_a?(RubySMB::SMB1::Packet::EmptyPacket) &&
127
+ response.smb_header.protocol == RubySMB::SMB1::SMB_PROTOCOL_ID &&
128
+ response.smb_header.command == response.original_command
129
+ raise RubySMB::Error::InvalidPacket.new(
130
+ 'The response seems to be an SMB1 NtCreateAndxResponse but an '\
131
+ 'error occurs while parsing it. It is probably missing the '\
132
+ 'required extended information.'
133
+ )
134
+ end
126
135
  raise RubySMB::Error::InvalidPacket.new(
127
136
  expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
128
137
  expected_cmd: RubySMB::SMB1::Packet::NtCreateAndxResponse::COMMAND,
@@ -131,7 +140,7 @@ module RubySMB
131
140
  )
132
141
  end
133
142
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
134
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
143
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
135
144
  end
136
145
 
137
146
  case response.parameter_block.resource_type
@@ -191,7 +200,7 @@ module RubySMB
191
200
  )
192
201
  end
193
202
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
194
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
203
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
195
204
  end
196
205
 
197
206
  results = response.results(type, unicode: unicode)
@@ -226,7 +235,7 @@ module RubySMB
226
235
  )
227
236
  end
228
237
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
229
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
238
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
230
239
  end
231
240
 
232
241
  results += response.results(type, unicode: unicode)