ruby_smb 2.0.7 → 2.0.8
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 +1 -1
- data/.github/workflows/verify.yml +56 -0
- data/README.md +0 -1
- data/lib/ruby_smb.rb +3 -2
- data/lib/ruby_smb/client.rb +2 -2
- data/lib/ruby_smb/client/negotiation.rb +2 -2
- data/lib/ruby_smb/compression.rb +7 -0
- data/lib/ruby_smb/compression/lznt1.rb +164 -0
- data/lib/ruby_smb/dispatcher/socket.rb +1 -1
- data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +4 -0
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/compression/lznt1_spec.rb +32 -0
- data/spec/spec_helper.rb +1 -1
- metadata +7 -3
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3cfb7914736c49c84c366382e133ea0c2e9a5ecb6f0a3badceb498652bbaa76
|
4
|
+
data.tar.gz: 6f47a0ad156545a259d0496f9e1c59d50b6327889600b6acda0e93ec5c835963
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 90c181090093eeb71d4ef508a2b1e7b75ccb31b7e40cdee973397f7554af1085e8b39a518f43e9e4e3eba5fd12d0677d1f059ed614eab8dfb5b0db74a956236a
|
7
|
+
data.tar.gz: 443e97e78383d44deb155c0d6e3ca8bc3923c49b2561fa41670e09d7916cdfaa7223143a29b24697699e7470cdcaf6f71a2e0e4167d8c19a7b80b2fc56b2a555
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
�Ss�.�倮�����;�ͦ��c7-ȡ��.�84����R�Q�.]��^�_��w� =r��ܖhu9r(�9��0�D@�˫"c��z�N{-��F~U�3����@/��nD~,O���B��^tƯ�F>�Ż��4��tתp@ҫw�,B������j9��
|
2
2
|
��J�t��&kk��94��*��GTL9p�C
|
@@ -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
|
|
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
|
12
|
-
# [[MS-SMB2] Server
|
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
|
data/lib/ruby_smb/client.rb
CHANGED
@@ -296,7 +296,7 @@ module RubySMB
|
|
296
296
|
@smb3 = smb3
|
297
297
|
@username = username.encode('utf-8') || ''.encode('utf-8')
|
298
298
|
@max_buffer_size = MAX_BUFFER_SIZE
|
299
|
-
# These sizes will be
|
299
|
+
# These sizes will be modified during negotiation
|
300
300
|
@server_max_buffer_size = SERVER_MAX_BUFFER_SIZE
|
301
301
|
@server_max_read_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
|
302
302
|
@server_max_write_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
|
@@ -431,7 +431,7 @@ module RubySMB
|
|
431
431
|
end
|
432
432
|
|
433
433
|
# Sends a packet and receives the raw response through the Dispatcher.
|
434
|
-
# It will also sign the packet if
|
434
|
+
# It will also sign the packet if necessary.
|
435
435
|
#
|
436
436
|
# @param packet [RubySMB::GenericPacket] the request to be sent
|
437
437
|
# @param encrypt [Boolean] true if encryption has to be enabled for this transaction
|
@@ -275,8 +275,8 @@ module RubySMB
|
|
275
275
|
context_type: RubySMB::SMB2::NegotiateContext::SMB2_COMPRESSION_CAPABILITIES
|
276
276
|
)
|
277
277
|
# Adding all possible compression algorithm even if we don't support
|
278
|
-
# them yet. This will force the server to disclose the
|
279
|
-
# algorithms in the
|
278
|
+
# them yet. This will force the server to disclose the supported
|
279
|
+
# algorithms in the response.
|
280
280
|
nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::LZNT1
|
281
281
|
nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::LZ77
|
282
282
|
nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::LZ77_Huffman
|
@@ -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
|
@@ -55,7 +55,7 @@ module RubySMB
|
|
55
55
|
|
56
56
|
# Read a packet off the wire and parse it into a string
|
57
57
|
#
|
58
|
-
# @param full_response [Boolean] whether to include the NetBios Session Service header in the
|
58
|
+
# @param full_response [Boolean] whether to include the NetBios Session Service header in the response
|
59
59
|
# @return [String] the raw response (including the NetBios Session Service header if full_response is true)
|
60
60
|
# @raise [RubySMB::Error::NetBiosSessionService] if there's an error reading the first 4 bytes,
|
61
61
|
# which are assumed to be the NetBiosSessionService header.
|
@@ -3,6 +3,9 @@ module RubySMB
|
|
3
3
|
module Packet
|
4
4
|
# An SMB2 COMPRESSION_TRANSFORM_HEADER Packet as defined in
|
5
5
|
# [2.2.42 SMB2 COMPRESSION_TRANSFORM_HEADER](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/1d435f21-9a21-4f4c-828e-624a176cf2a0)
|
6
|
+
# NOTE: On 2021-04-06 the official documentation split the definition of COMPRESSION_TRANSFORM_HEADER into the following two variants:
|
7
|
+
# * SMB2_COMPRESSION_TRANSFORM_HEADER_CHAINED
|
8
|
+
# * SMB2_COMPRESSION_TRANSFORM_HEADER_UNCHAINED
|
6
9
|
class CompressionTransformHeader < BinData::Record
|
7
10
|
endian :little
|
8
11
|
|
@@ -11,6 +14,7 @@ module RubySMB
|
|
11
14
|
uint16 :compression_algorithm, label: 'Compression Algorithm'
|
12
15
|
uint16 :flags, label: 'Flags'
|
13
16
|
uint32 :offset, label: 'Offset / Length'
|
17
|
+
array :compressed_data, label: 'Compressed Data', type: :uint8, read_until: :eof
|
14
18
|
end
|
15
19
|
|
16
20
|
# An SMB2 SMB2_COMPRESSION_TRANSFORM_HEADER_PAYLOAD Packet as defined in
|
data/lib/ruby_smb/version.rb
CHANGED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe RubySMB::Compression::LZNT1 do
|
4
|
+
describe '.compress' do
|
5
|
+
it 'generates an empty blob when provided an empty blob' do
|
6
|
+
expected = "".b
|
7
|
+
expect(described_class.compress('')).to eq(expected)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'generates a compressed blob when provided a string with non-reoccurring characters' do
|
11
|
+
expect(described_class.compress('RubySMB')).to eq("\x060RubySMB".b)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'generates a compressed blob when provided a string of reoccurring characters' do
|
15
|
+
expect(described_class.compress("\x01" * 0x200)).to eq("\x03\xB0\x02\x01\xFC\x01".b)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '.decompress' do
|
20
|
+
it 'generates a decompressed blob for a string with non-reoccurring characters' do
|
21
|
+
expect(described_class.decompress("\x060RubySMB".b)).to eq('RubySMB')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'generates a decompressed blob for a string of reoccurring characters' do
|
25
|
+
expect(described_class.decompress("\x03\xB0\x02\x01\xFC\x01".b)).to eq("\x01" * 0x200)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'raises an EncodingError when the length is invalid' do
|
29
|
+
expect { described_class.decompress("\x010".b) }.to raise_error(EncodingError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -7,7 +7,7 @@ end
|
|
7
7
|
require 'coveralls'
|
8
8
|
require 'ruby_smb'
|
9
9
|
|
10
|
-
if ENV['
|
10
|
+
if ENV['CI'] == 'true'
|
11
11
|
# don't generate local report as it is inaccessible on travis-ci, which is
|
12
12
|
# why coveralls is being used.
|
13
13
|
SimpleCov.formatter = Coveralls::SimpleCov::Formatter
|
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: 2.0.
|
4
|
+
version: 2.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Metasploit Hackers
|
@@ -97,7 +97,7 @@ cert_chain:
|
|
97
97
|
EknWpNgVhohbot1lfVAMmIhdtOVaRVcQQixWPwprDj/ydB8ryDMDosIMcw+fkoXU
|
98
98
|
9GJsSaSRRYQ9UUkVL27b64okU8D48m8=
|
99
99
|
-----END CERTIFICATE-----
|
100
|
-
date:
|
100
|
+
date: 2021-04-09 00:00:00.000000000 Z
|
101
101
|
dependencies:
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: redcarpet
|
@@ -246,10 +246,10 @@ executables: []
|
|
246
246
|
extensions: []
|
247
247
|
extra_rdoc_files: []
|
248
248
|
files:
|
249
|
+
- ".github/workflows/verify.yml"
|
249
250
|
- ".gitignore"
|
250
251
|
- ".rspec"
|
251
252
|
- ".simplecov"
|
252
|
-
- ".travis.yml"
|
253
253
|
- ".yardopts"
|
254
254
|
- CONTRIBUTING.md
|
255
255
|
- Gemfile
|
@@ -284,6 +284,8 @@ files:
|
|
284
284
|
- lib/ruby_smb/client/tree_connect.rb
|
285
285
|
- lib/ruby_smb/client/utils.rb
|
286
286
|
- lib/ruby_smb/client/winreg.rb
|
287
|
+
- lib/ruby_smb/compression.rb
|
288
|
+
- lib/ruby_smb/compression/lznt1.rb
|
287
289
|
- lib/ruby_smb/crypto.rb
|
288
290
|
- lib/ruby_smb/dcerpc.rb
|
289
291
|
- lib/ruby_smb/dcerpc/bind.rb
|
@@ -526,6 +528,7 @@ files:
|
|
526
528
|
- lib/ruby_smb/version.rb
|
527
529
|
- ruby_smb.gemspec
|
528
530
|
- spec/lib/ruby_smb/client_spec.rb
|
531
|
+
- spec/lib/ruby_smb/compression/lznt1_spec.rb
|
529
532
|
- spec/lib/ruby_smb/crypto_spec.rb
|
530
533
|
- spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb
|
531
534
|
- spec/lib/ruby_smb/dcerpc/bind_spec.rb
|
@@ -760,6 +763,7 @@ specification_version: 4
|
|
760
763
|
summary: A pure Ruby implementation of the SMB Protocol Family
|
761
764
|
test_files:
|
762
765
|
- spec/lib/ruby_smb/client_spec.rb
|
766
|
+
- spec/lib/ruby_smb/compression/lznt1_spec.rb
|
763
767
|
- spec/lib/ruby_smb/crypto_spec.rb
|
764
768
|
- spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb
|
765
769
|
- spec/lib/ruby_smb/dcerpc/bind_spec.rb
|
metadata.gz.sig
CHANGED
Binary file
|