ruby_smb 2.0.4 → 2.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.github/workflows/verify.yml +56 -0
  5. data/README.md +0 -1
  6. data/examples/delete_file.rb +0 -0
  7. data/examples/net_share_enum_all.rb +0 -0
  8. data/examples/pipes.rb +0 -0
  9. data/examples/rename_file.rb +0 -0
  10. data/lib/ruby_smb.rb +3 -2
  11. data/lib/ruby_smb/client.rb +25 -21
  12. data/lib/ruby_smb/client/negotiation.rb +10 -12
  13. data/lib/ruby_smb/compression.rb +7 -0
  14. data/lib/ruby_smb/compression/lznt1.rb +164 -0
  15. data/lib/ruby_smb/dcerpc.rb +3 -1
  16. data/lib/ruby_smb/dcerpc/ndr.rb +97 -0
  17. data/lib/ruby_smb/dcerpc/netlogon.rb +101 -0
  18. data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request.rb +37 -0
  19. data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response.rb +26 -0
  20. data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request.rb +37 -0
  21. data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response.rb +23 -0
  22. data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request.rb +32 -0
  23. data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response.rb +24 -0
  24. data/lib/ruby_smb/dcerpc/request.rb +6 -0
  25. data/lib/ruby_smb/dispatcher/socket.rb +1 -1
  26. data/lib/ruby_smb/smb1/file.rb +1 -1
  27. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +0 -1
  28. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +0 -1
  29. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +1 -2
  30. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +1 -13
  31. data/lib/ruby_smb/smb1/pipe.rb +4 -2
  32. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +4 -0
  33. data/lib/ruby_smb/smb2/pipe.rb +6 -4
  34. data/lib/ruby_smb/version.rb +1 -1
  35. data/spec/lib/ruby_smb/client_spec.rb +17 -7
  36. data/spec/lib/ruby_smb/compression/lznt1_spec.rb +32 -0
  37. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request_spec.rb +69 -0
  38. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response_spec.rb +53 -0
  39. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request_spec.rb +69 -0
  40. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response_spec.rb +37 -0
  41. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request_spec.rb +45 -0
  42. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response_spec.rb +37 -0
  43. data/spec/lib/ruby_smb/smb1/file_spec.rb +1 -1
  44. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +0 -1
  45. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +0 -1
  46. data/spec/lib/ruby_smb/smb1/packet/trans2/open2_response_spec.rb +0 -5
  47. data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_response_spec.rb +0 -6
  48. data/spec/spec_helper.rb +1 -1
  49. metadata +42 -19
  50. metadata.gz.sig +0 -0
  51. data/.travis.yml +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43f9f98936e870ad767348eb90c66f17eaf5e56c62b7792e13502c860bf8c4f1
4
- data.tar.gz: b57d0bdee72f2fde7ee700771f85cd0f442f3ca5be70b4564b1598df05474860
3
+ metadata.gz: 6c56b48b8782a1622a0d9586244a82a4527868e20519a5ab7339a4b679909288
4
+ data.tar.gz: b5aef9f1775e56661b9248567f5a2d254a7b64e67b7252cc742a66b392ab45a4
5
5
  SHA512:
6
- metadata.gz: '08c1bea7f46add92b4bf6746ec3794a7845a0d0dccb8a8d338d7b3fd07133b670b5509bfd1a71fa10dcc11059bcd01b38a0da664c9fe7b86aa8d58738d0215e8'
7
- data.tar.gz: 69efcb218b522cd233a593ccd281786732f76c717349f44c776a1a7f9790a697b46ccc63b60ab01b31b58ab38487c2d0c3ff3db7c99f55dd7fda469c1ecc5d90
6
+ metadata.gz: 1cf475d6429f701a37557668a1baba47ff241109c99c5a133e3f0c89dbdfc6eda8658952485b8e71eed8a6815541a2d729a093b96230907e2c58f9a63170851d
7
+ data.tar.gz: d5189ce906e257765049e08a6c60d360c5c6bbc389c40d51a3d18c882e68375578cadf74b5b1b892a2b8bee72d3b2ffdc3384259ce0d4213f6d98d076cfcd21e
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -0,0 +1,56 @@
1
+ name: Verify
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - '*'
7
+ pull_request:
8
+ branches:
9
+ - '*'
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-16.04
14
+ timeout-minutes: 40
15
+
16
+ strategy:
17
+ fail-fast: true
18
+ matrix:
19
+ ruby:
20
+ - 2.5
21
+ - 2.6
22
+ - 2.7
23
+ test_cmd:
24
+ - bundle exec rspec
25
+
26
+ env:
27
+ RAILS_ENV: test
28
+
29
+ name: Ruby ${{ matrix.ruby }} - ${{ matrix.test_cmd }}
30
+ steps:
31
+ - name: Checkout code
32
+ uses: actions/checkout@v2
33
+
34
+ - uses: actions/setup-ruby@v1
35
+ with:
36
+ ruby-version: ${{ matrix.ruby }}
37
+
38
+ - name: Setup bundler
39
+ run: |
40
+ gem install bundler
41
+ - uses: actions/cache@v2
42
+ with:
43
+ path: vendor/bundle
44
+ key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
45
+ restore-keys: |
46
+ ${{ runner.os }}-gems-
47
+ - name: Bundle install
48
+ run: |
49
+ bundle config path vendor/bundle
50
+ bundle install --jobs 4 --retry 3
51
+ - name: ${{ matrix.test_cmd }}
52
+ run: |
53
+ echo "${CMD}"
54
+ bash -c "${CMD}"
55
+ env:
56
+ CMD: ${{ matrix.test_cmd }}
data/README.md CHANGED
@@ -1,6 +1,5 @@
1
1
  # RubySMB
2
2
 
3
- [![Build Status](https://travis-ci.org/rapid7/ruby_smb.svg?branch=master)](https://travis-ci.org/rapid7/ruby_smb)
4
3
  [![Code Climate](https://codeclimate.com/github/rapid7/ruby_smb.png)](https://codeclimate.com/github/rapid7/ruby_smb)
5
4
  [![Coverage Status](https://coveralls.io/repos/github/rapid7/ruby_smb/badge.svg?branch=master)](https://coveralls.io/github/rapid7/ruby_smb?branch=master)
6
5
 
File without changes
File without changes
data/examples/pipes.rb CHANGED
File without changes
File without changes
data/lib/ruby_smb.rb CHANGED
@@ -8,8 +8,8 @@ require 'windows_error'
8
8
  require 'windows_error/nt_status'
9
9
  # A packet parsing and manipulation library for the SMB1 and SMB2 protocols
10
10
  #
11
- # [[MS-SMB] Server Mesage Block (SMB) Protocol Version 1](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
12
- # [[MS-SMB2] Server Mesage Block (SMB) Protocol Versions 2 and 3](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
11
+ # [[MS-SMB] Server Message Block (SMB) Protocol Version 1](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
12
+ # [[MS-SMB2] Server Message Block (SMB) Protocol Versions 2 and 3](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
13
13
  module RubySMB
14
14
  require 'ruby_smb/error'
15
15
  require 'ruby_smb/dispositions'
@@ -26,4 +26,5 @@ module RubySMB
26
26
  require 'ruby_smb/smb1'
27
27
  require 'ruby_smb/client'
28
28
  require 'ruby_smb/crypto'
29
+ require 'ruby_smb/compression'
29
30
  end
@@ -277,7 +277,8 @@ module RubySMB
277
277
  # @param smb1 [Boolean] whether or not to enable SMB1 support
278
278
  # @param smb2 [Boolean] whether or not to enable SMB2 support
279
279
  # @param smb3 [Boolean] whether or not to enable SMB3 support
280
- def initialize(dispatcher, smb1: true, smb2: true, smb3: true, username:, password:, domain: '.', local_workstation: 'WORKSTATION', always_encrypt: true)
280
+ def initialize(dispatcher, smb1: true, smb2: true, smb3: true, username:, password:, domain: '.',
281
+ local_workstation: 'WORKSTATION', always_encrypt: true, ntlm_flags: default_flags)
281
282
  raise ArgumentError, 'No Dispatcher provided' unless dispatcher.is_a? RubySMB::Dispatcher::Base
282
283
  if smb1 == false && smb2 == false && smb3 == false
283
284
  raise ArgumentError, 'You must enable at least one Protocol'
@@ -296,7 +297,7 @@ module RubySMB
296
297
  @smb3 = smb3
297
298
  @username = username.encode('utf-8') || ''.encode('utf-8')
298
299
  @max_buffer_size = MAX_BUFFER_SIZE
299
- # These sizes will be modifed during negotiation
300
+ # These sizes will be modified during negotiation
300
301
  @server_max_buffer_size = SERVER_MAX_BUFFER_SIZE
301
302
  @server_max_read_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
302
303
  @server_max_write_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
@@ -306,18 +307,12 @@ module RubySMB
306
307
  # SMB 3.x options
307
308
  @session_encrypt_data = always_encrypt
308
309
 
309
- negotiate_version_flag = 0x02000000
310
- flags = Net::NTLM::Client::DEFAULT_FLAGS |
311
- Net::NTLM::FLAGS[:TARGET_INFO] |
312
- negotiate_version_flag ^
313
- Net::NTLM::FLAGS[:OEM]
314
-
315
310
  @ntlm_client = Net::NTLM::Client.new(
316
311
  @username,
317
312
  @password,
318
313
  workstation: @local_workstation,
319
314
  domain: @domain,
320
- flags: flags
315
+ flags: ntlm_flags
321
316
  )
322
317
 
323
318
  @tree_connects = []
@@ -368,31 +363,28 @@ module RubySMB
368
363
 
369
364
  # Performs protocol negotiation and session setup. It defaults to using
370
365
  # the credentials supplied during initialization, but can take a new set of credentials if needed.
371
- def login(username: self.username, password: self.password, domain: self.domain, local_workstation: self.local_workstation)
366
+ def login(username: self.username, password: self.password,
367
+ domain: self.domain, local_workstation: self.local_workstation,
368
+ ntlm_flags: default_flags)
372
369
  negotiate
373
- session_setup(username, password, domain, true,
374
- local_workstation: local_workstation)
370
+ session_setup(username, password, domain,
371
+ local_workstation: local_workstation,
372
+ ntlm_flags: ntlm_flags)
375
373
  end
376
374
 
377
375
  def session_setup(user, pass, domain, do_recv=true,
378
- local_workstation: self.local_workstation)
376
+ local_workstation: self.local_workstation, ntlm_flags: default_flags)
379
377
  @domain = domain
380
378
  @local_workstation = local_workstation
381
379
  @password = pass.encode('utf-8') || ''.encode('utf-8')
382
380
  @username = user.encode('utf-8') || ''.encode('utf-8')
383
381
 
384
- negotiate_version_flag = 0x02000000
385
- flags = Net::NTLM::Client::DEFAULT_FLAGS |
386
- Net::NTLM::FLAGS[:TARGET_INFO] |
387
- negotiate_version_flag ^
388
- Net::NTLM::FLAGS[:OEM]
389
-
390
382
  @ntlm_client = Net::NTLM::Client.new(
391
383
  @username,
392
384
  @password,
393
385
  workstation: @local_workstation,
394
386
  domain: @domain,
395
- flags: flags
387
+ flags: ntlm_flags
396
388
  )
397
389
 
398
390
  authenticate
@@ -431,7 +423,7 @@ module RubySMB
431
423
  end
432
424
 
433
425
  # Sends a packet and receives the raw response through the Dispatcher.
434
- # It will also sign the packet if neccessary.
426
+ # It will also sign the packet if necessary.
435
427
  #
436
428
  # @param packet [RubySMB::GenericPacket] the request to be sent
437
429
  # @param encrypt [Boolean] true if encryption has to be enabled for this transaction
@@ -654,5 +646,17 @@ module RubySMB
654
646
  @preauth_integrity_hash_value + data.to_binary_s
655
647
  )
656
648
  end
649
+
650
+ private
651
+
652
+ def default_flags
653
+ negotiate_version_flag = 0x02000000
654
+ flags = Net::NTLM::Client::DEFAULT_FLAGS |
655
+ Net::NTLM::FLAGS[:TARGET_INFO] |
656
+ negotiate_version_flag ^
657
+ Net::NTLM::FLAGS[:OEM]
658
+
659
+ flags
660
+ end
657
661
  end
658
662
  end
@@ -57,15 +57,20 @@ module RubySMB
57
57
 
58
58
  # Takes the raw response data from the server and tries
59
59
  # parse it into a valid Response packet object.
60
- # This method currently assumes that all SMB1 will use Extended Security.
61
60
  #
62
61
  # @param raw_data [String] the raw binary response from the server
63
62
  # @return [RubySMB::SMB1::Packet::NegotiateResponseExtended] when the response is an SMB1 Extended Security Negotiate Response Packet
63
+ # @return [RubySMB::SMB1::Packet::NegotiateResponse] when the response is an SMB1 Negotiate Response Packet
64
64
  # @return [RubySMB::SMB2::Packet::NegotiateResponse] when the response is an SMB2 Negotiate Response Packet
65
65
  def negotiate_response(raw_data)
66
66
  response = nil
67
67
  if smb1
68
68
  packet = RubySMB::SMB1::Packet::NegotiateResponseExtended.read raw_data
69
+
70
+ unless packet.valid?
71
+ packet = RubySMB::SMB1::Packet::NegotiateResponse.read raw_data
72
+ end
73
+
69
74
  response = packet if packet.valid?
70
75
  end
71
76
  if (smb2 || smb3) && response.nil?
@@ -74,17 +79,10 @@ module RubySMB
74
79
  end
75
80
  if response.nil?
76
81
  if packet.packet_smb_version == 'SMB1'
77
- extended_security = if packet.is_a? RubySMB::SMB1::Packet::NegotiateResponseExtended
78
- packet.parameter_block.capabilities.extended_security
79
- else
80
- "n/a"
81
- end
82
82
  raise RubySMB::Error::InvalidPacket.new(
83
83
  expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
84
- expected_cmd: RubySMB::SMB1::Packet::NegotiateResponseExtended::COMMAND,
85
- expected_custom: "extended_security=1",
86
- packet: packet,
87
- received_custom: "extended_security=#{extended_security}"
84
+ expected_cmd: RubySMB::SMB1::Packet::NegotiateResponse::COMMAND,
85
+ packet: packet
88
86
  )
89
87
  elsif packet.packet_smb_version == 'SMB2'
90
88
  raise RubySMB::Error::InvalidPacket.new(
@@ -275,8 +273,8 @@ module RubySMB
275
273
  context_type: RubySMB::SMB2::NegotiateContext::SMB2_COMPRESSION_CAPABILITIES
276
274
  )
277
275
  # Adding all possible compression algorithm even if we don't support
278
- # them yet. This will force the server to disclose the support
279
- # algorithms in the repsonse.
276
+ # them yet. This will force the server to disclose the supported
277
+ # algorithms in the response.
280
278
  nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::LZNT1
281
279
  nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::LZ77
282
280
  nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::LZ77_Huffman
@@ -0,0 +1,7 @@
1
+ module RubySMB
2
+ module Compression
3
+
4
+ require 'ruby_smb/compression/lznt1'
5
+
6
+ end
7
+ end
@@ -0,0 +1,164 @@
1
+ module RubySMB
2
+ module Compression
3
+ module LZNT1
4
+ def self.compress(buf, chunk_size: 0x1000)
5
+ out = ''
6
+ until buf.empty?
7
+ chunk = buf[0...chunk_size]
8
+ compressed = compress_chunk(chunk)
9
+ # chunk is compressed
10
+ if compressed.length < chunk.length
11
+ out << [0xb000 | (compressed.length - 1)].pack('v')
12
+ out << compressed
13
+ else
14
+ out << [0x3000 | (chunk.length - 1)].pack('v')
15
+ out << chunk
16
+ end
17
+ buf = buf[chunk_size..-1]
18
+ break if buf.nil?
19
+ end
20
+
21
+ out
22
+ end
23
+
24
+ def self.compress_chunk(chunk)
25
+ blob = chunk
26
+ out = ''
27
+ pow2 = 0x10
28
+ l_mask3 = 0x1002
29
+ o_shift = 12
30
+ until blob.empty?
31
+ bits = 0
32
+ tmp = ''
33
+ i = -1
34
+ loop do
35
+ i += 1
36
+ bits >>= 1
37
+ while pow2 < (chunk.length - blob.length)
38
+ pow2 <<= 1
39
+ l_mask3 = (l_mask3 >> 1) + 1
40
+ o_shift -= 1
41
+ end
42
+
43
+ max_len = [blob.length, l_mask3].min
44
+ offset, length = find(chunk[0...(chunk.length - blob.length)], blob, max_len)
45
+
46
+ # try to find more compressed pattern
47
+ _offset2, length2 = find(chunk[0...chunk.length - blob.length + 1], blob[1..-1], max_len)
48
+
49
+ length = 0 if length < length2
50
+
51
+ if length > 0
52
+ symbol = ((offset - 1) << o_shift) | (length - 3)
53
+ tmp << [symbol].pack('v')
54
+ # set the highest bit
55
+ bits |= 0x80
56
+ blob = blob[length..-1]
57
+ else
58
+ tmp += blob[0]
59
+ blob = blob[1..-1]
60
+ end
61
+
62
+ break if blob.empty? || i == 7
63
+ end
64
+
65
+ out << [bits >> (7 - i)].pack('C')
66
+ out << tmp
67
+ end
68
+
69
+ out
70
+ end
71
+
72
+ def self.decompress(buf, length_check: true)
73
+ out = ''
74
+ until buf.empty?
75
+ header = buf.unpack1('v')
76
+ length = (header & 0xfff) + 1
77
+ raise EncodingError, 'invalid chunk length' if length_check && length > (buf.length - 2)
78
+
79
+ chunk = buf[2...length + 2]
80
+ out << if header & 0x8000 == 0
81
+ chunk
82
+ else
83
+ decompress_chunk(chunk)
84
+ end
85
+ buf = buf[length + 2..-1]
86
+ end
87
+
88
+ out
89
+ end
90
+
91
+ def self.decompress_chunk(chunk)
92
+ out = ''
93
+ until chunk.empty?
94
+ flags = chunk[0].unpack1('C')
95
+ chunk = chunk[1..-1]
96
+ 8.times do |i|
97
+ if (flags >> i & 1) == 0
98
+ out << chunk[0]
99
+ chunk = chunk[1..-1]
100
+ else
101
+ flag = chunk.unpack1('v')
102
+ pos = out.length - 1
103
+ l_mask = 0xfff
104
+ o_shift = 12
105
+ while pos >= 0x10
106
+ l_mask >>= 1
107
+ o_shift -= 1
108
+ pos >>= 1
109
+ end
110
+
111
+ length = (flag & l_mask) + 3
112
+ offset = (flag >> o_shift) + 1
113
+
114
+ if length >= offset
115
+ out_offset = out[-offset..-1]
116
+ tmp = out_offset * (0xfff / out_offset.length + 1)
117
+ out << tmp[0...length]
118
+ else
119
+ out << out[-offset..-offset + length - 1]
120
+ end
121
+ chunk = chunk[2..-1]
122
+ end
123
+ break if chunk.empty?
124
+ end
125
+ end
126
+
127
+ out
128
+ end
129
+
130
+ class << self
131
+ private
132
+
133
+ def find(src, target, max_len)
134
+ result_offset = 0
135
+ result_length = 0
136
+ 1.upto(max_len - 1) do |i|
137
+ offset = src.rindex(target[0...i])
138
+ next if offset.nil?
139
+
140
+ tmp_offset = src.length - offset
141
+ tmp_length = i
142
+ if tmp_offset == tmp_length
143
+ src_offset = src[offset..-1]
144
+ tmp = src_offset * (0xfff / src_offset.length + 1)
145
+ i.upto(max_len) do |j|
146
+ offset = tmp.rindex(target[0...j])
147
+ break if offset.nil?
148
+
149
+ tmp_length = j
150
+ end
151
+ end
152
+
153
+ if tmp_length > result_length
154
+ result_offset = tmp_offset
155
+ result_length = tmp_length
156
+ end
157
+ end
158
+
159
+ result_length < 3 ? [0, 0] : [result_offset, result_length]
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -13,14 +13,16 @@ module RubySMB
13
13
  require 'ruby_smb/dcerpc/rpc_security_attributes'
14
14
  require 'ruby_smb/dcerpc/pdu_header'
15
15
  require 'ruby_smb/dcerpc/srvsvc'
16
- require 'ruby_smb/dcerpc/winreg'
17
16
  require 'ruby_smb/dcerpc/svcctl'
17
+ require 'ruby_smb/dcerpc/winreg'
18
+ require 'ruby_smb/dcerpc/netlogon'
18
19
  require 'ruby_smb/dcerpc/request'
19
20
  require 'ruby_smb/dcerpc/response'
20
21
  require 'ruby_smb/dcerpc/bind'
21
22
  require 'ruby_smb/dcerpc/bind_ack'
22
23
 
23
24
 
25
+
24
26
  # Bind to the remote server interface endpoint.
25
27
  #
26
28
  # @param options [Hash] the options to pass to the Bind request packet. At least, :endpoint must but provided with an existing Dcerpc class