ruby_smb 3.3.18 → 3.3.19
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
- data/lib/ruby_smb/client/authentication.rb +53 -0
- data/lib/ruby_smb/client/negotiation.rb +10 -2
- data/lib/ruby_smb/client/tree_connect.rb +8 -1
- data/lib/ruby_smb/client.rb +16 -5
- data/lib/ruby_smb/rap/net_share_enum.rb +166 -0
- data/lib/ruby_smb/rap.rb +10 -0
- data/lib/ruby_smb/smb1/commands.rb +1 -0
- data/lib/ruby_smb/smb1/packet/negotiate_response.rb +11 -0
- data/lib/ruby_smb/smb1/packet/open_andx_request.rb +39 -0
- data/lib/ruby_smb/smb1/packet/open_andx_response.rb +40 -0
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +2 -2
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +11 -0
- data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +53 -13
- data/lib/ruby_smb/smb1/packet/trans2/find_information_level/find_info_standard.rb +39 -0
- data/lib/ruby_smb/smb1/packet/trans2/find_information_level.rb +1 -0
- data/lib/ruby_smb/smb1/packet/trans2/win9x_framing.rb +68 -0
- data/lib/ruby_smb/smb1/packet/trans2.rb +1 -0
- data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +1 -1
- data/lib/ruby_smb/smb1/packet/tree_connect_response.rb +10 -1
- data/lib/ruby_smb/smb1/packet.rb +2 -0
- data/lib/ruby_smb/smb1/pipe.rb +2 -0
- data/lib/ruby_smb/smb1/tree.rb +113 -9
- data/lib/ruby_smb/version.rb +1 -1
- data/lib/ruby_smb.rb +1 -0
- data/spec/lib/ruby_smb/client_spec.rb +2 -1
- data/spec/lib/ruby_smb/rap/net_share_enum_spec.rb +185 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/win9x_framing_spec.rb +113 -0
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +188 -2
- metadata +12 -2
|
@@ -457,6 +457,129 @@ RSpec.describe RubySMB::SMB1::Tree do
|
|
|
457
457
|
end
|
|
458
458
|
end
|
|
459
459
|
end
|
|
460
|
+
|
|
461
|
+
context 'with SMB_INFO_STANDARD (LANMAN 2.0 / Win9x)' do
|
|
462
|
+
let(:info_standard) { RubySMB::SMB1::Packet::Trans2::FindInformationLevel::FindInfoStandard }
|
|
463
|
+
|
|
464
|
+
def build_info_standard_entry(name:, size: 0, attrs: 0x20, pad_after: false)
|
|
465
|
+
entry = info_standard.new
|
|
466
|
+
entry.data_size = size
|
|
467
|
+
entry.allocation_size = size
|
|
468
|
+
entry.file_attributes = attrs
|
|
469
|
+
entry.file_name = name
|
|
470
|
+
entry.file_name_length = name.bytesize
|
|
471
|
+
pad_after ? entry.to_binary_s + "\x00" : entry.to_binary_s
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
def build_find_first2_raw(blob, status: 0)
|
|
475
|
+
packet = RubySMB::SMB1::Packet::Trans2::FindFirst2Response.new
|
|
476
|
+
packet.smb_header.nt_status = status
|
|
477
|
+
packet.data_block.trans2_parameters.eos = 1
|
|
478
|
+
packet.data_block.trans2_data.buffer = blob
|
|
479
|
+
packet.to_binary_s
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
before :each do
|
|
483
|
+
# Undo the default FindFirst2Response stubs from the outer #list block
|
|
484
|
+
allow(RubySMB::SMB1::Packet::Trans2::FindFirst2Request).to receive(:new).and_call_original
|
|
485
|
+
allow(RubySMB::SMB1::Packet::Trans2::FindFirst2Response).to receive(:read).and_call_original
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
it 'parses sequential SMB_INFO_STANDARD entries separated by a null pad' do
|
|
489
|
+
# Win9x servers insert a trailing null byte between entries.
|
|
490
|
+
blob = build_info_standard_entry(name: 'foo.txt', size: 100, pad_after: true) +
|
|
491
|
+
build_info_standard_entry(name: 'barbaz', size: 200)
|
|
492
|
+
allow(client).to receive(:send_recv).and_return(build_find_first2_raw(blob))
|
|
493
|
+
|
|
494
|
+
results = tree.list(type: info_standard)
|
|
495
|
+
|
|
496
|
+
expect(results.length).to eq 2
|
|
497
|
+
expect(results[0].file_name).to eq 'foo.txt'
|
|
498
|
+
expect(results[0].data_size).to eq 100
|
|
499
|
+
expect(results[1].file_name).to eq 'barbaz'
|
|
500
|
+
expect(results[1].data_size).to eq 200
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
it 'parses a single SMB_INFO_STANDARD entry without trailing padding' do
|
|
504
|
+
blob = build_info_standard_entry(name: 'only.txt', size: 42)
|
|
505
|
+
allow(client).to receive(:send_recv).and_return(build_find_first2_raw(blob))
|
|
506
|
+
|
|
507
|
+
results = tree.list(type: info_standard)
|
|
508
|
+
|
|
509
|
+
expect(results.length).to eq 1
|
|
510
|
+
expect(results[0].file_name).to eq 'only.txt'
|
|
511
|
+
expect(results[0].data_size).to eq 42
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
it 'stops when an entry has a zero file_name_length' do
|
|
515
|
+
entry = build_info_standard_entry(name: 'first.txt', pad_after: true)
|
|
516
|
+
zero = "\x00" * 23
|
|
517
|
+
blob = entry + zero
|
|
518
|
+
allow(client).to receive(:send_recv).and_return(build_find_first2_raw(blob))
|
|
519
|
+
|
|
520
|
+
results = tree.list(type: info_standard)
|
|
521
|
+
|
|
522
|
+
expect(results.map(&:file_name)).to eq(['first.txt'])
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
it 'raises UnexpectedStatusCode when the SMB status is not success' do
|
|
526
|
+
allow(client).to receive(:send_recv).and_return(
|
|
527
|
+
build_find_first2_raw('', status: WindowsError::NTStatus::STATUS_ACCESS_DENIED.value)
|
|
528
|
+
)
|
|
529
|
+
expect { tree.list(type: info_standard) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
it 'turns unicode off and raises search_count to 255 for SMB_INFO_STANDARD' do
|
|
533
|
+
allow(client).to receive(:send_recv) do |packet|
|
|
534
|
+
expect(packet.smb_header.flags2.unicode).to eq 0
|
|
535
|
+
expect(packet.data_block.trans2_parameters.search_count).to eq 255
|
|
536
|
+
build_find_first2_raw('')
|
|
537
|
+
end
|
|
538
|
+
tree.list(type: info_standard)
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
it 'returns an empty array when the data blob is empty' do
|
|
542
|
+
allow(client).to receive(:send_recv).and_return(build_find_first2_raw(''))
|
|
543
|
+
expect(tree.list(type: info_standard)).to eq([])
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
context 'against a Win9x-era server that omits the trans2 4-byte alignment pad' do
|
|
547
|
+
# Wire layout: word_count=10 (no setup section), parameter_offset=55
|
|
548
|
+
# points right after byte_count (no pad1), data_offset=66 points after
|
|
549
|
+
# trans2_parameters + 1-byte pad2. BinData's Trans2::DataBlock inserts
|
|
550
|
+
# its usual pad1 on read, so trans2_data.buffer arrives (data_count -
|
|
551
|
+
# pad1_length) bytes short and #results would otherwise see 0 entries.
|
|
552
|
+
# The Tree#list workaround detects the mismatch and re-slices the
|
|
553
|
+
# buffer from the server-reported data_offset.
|
|
554
|
+
def build_win9x_find_first2_raw
|
|
555
|
+
data_count = 87
|
|
556
|
+
parameter_offset = 55
|
|
557
|
+
data_offset = 66
|
|
558
|
+
smb_header = "\xffSMB\x32".b + "\x00".b * 4 + "\x98".b + "\x03\x60".b + ("\x00".b * 20)
|
|
559
|
+
param_block = [10, data_count, 0, 10, parameter_offset, 0,
|
|
560
|
+
data_count, data_offset, 0, 0].pack('v*')
|
|
561
|
+
trans2_params = [0x0300, 3, 1, 0, 74].pack('v*')
|
|
562
|
+
entry1 = "\x98\x5c\x38\x70\x98\x5c\x00\x00\x98\x5c\x39\x70" \
|
|
563
|
+
"\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x01".b + '.'
|
|
564
|
+
entry2 = "\x98\x5c\x38\x70\x98\x5c\x00\x00\x98\x5c\x39\x70" \
|
|
565
|
+
"\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x02".b + '..'
|
|
566
|
+
entry3 = "\x98\x5c\x40\x70\x98\x5c\x00\x00\x98\x5c\x4c\x70" \
|
|
567
|
+
"\x16\x00\x00\x00\x16\x00\x00\x00\x20\x00\x0c".b + 'FLAG.TXT.txt'
|
|
568
|
+
trans2_data = entry1 + "\x00".b + entry2 + "\x00".b + entry3 + "\x00".b
|
|
569
|
+
raise "data_count mismatch" unless trans2_data.bytesize == data_count
|
|
570
|
+
byte_count_value = 10 + 1 + data_count # 0 pad1 + params + 1 pad2 + data
|
|
571
|
+
smb_header + [10].pack('C') + param_block +
|
|
572
|
+
[byte_count_value].pack('v') + trans2_params + "\x00".b + trans2_data
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
it 'parses the entries that BinData would otherwise drop due to missing pad1' do
|
|
576
|
+
allow(client).to receive(:send_recv).and_return(build_win9x_find_first2_raw)
|
|
577
|
+
results = tree.list(type: info_standard)
|
|
578
|
+
expect(results.map { |r| r.file_name.to_s }).to eq(['.', '..', 'FLAG.TXT.txt'])
|
|
579
|
+
expect(results.last.data_size).to eq 22
|
|
580
|
+
end
|
|
581
|
+
end
|
|
582
|
+
end
|
|
460
583
|
end
|
|
461
584
|
|
|
462
585
|
describe '#set_header_fields' do
|
|
@@ -492,8 +615,71 @@ RSpec.describe RubySMB::SMB1::Tree do
|
|
|
492
615
|
expect(modified_request.parameter_block.max_parameter_count).to eq 10
|
|
493
616
|
end
|
|
494
617
|
|
|
495
|
-
it 'sets #max_data_count to 16,384' do
|
|
496
|
-
expect(modified_request.parameter_block.max_data_count).to eq
|
|
618
|
+
it 'sets #max_data_count to the minimum of 16,384 and server_max_buffer_size' do
|
|
619
|
+
expect(modified_request.parameter_block.max_data_count).to eq(
|
|
620
|
+
[16_384, client.server_max_buffer_size].min
|
|
621
|
+
)
|
|
622
|
+
end
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
describe '#open_file (SMB_COM_OPEN_ANDX fallback)' do
|
|
626
|
+
# Win9x and other LAN-Manager-era servers don't advertise the NT SMBs
|
|
627
|
+
# capability, so #_open dispatches to #_open_andx instead of NT_CREATE_ANDX.
|
|
628
|
+
let(:open_andx_response) do
|
|
629
|
+
packet = RubySMB::SMB1::Packet::OpenAndxResponse.new
|
|
630
|
+
packet.smb_header.nt_status = 0
|
|
631
|
+
packet.parameter_block.fid = 0x4242
|
|
632
|
+
packet.parameter_block.file_data_size = 1234
|
|
633
|
+
packet.parameter_block.resource_type = RubySMB::SMB1::ResourceType::DISK
|
|
634
|
+
packet
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
before :example do
|
|
638
|
+
client.server_supports_nt_smbs = false
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
it 'builds the OPEN_ANDX request without raising NoMethodError on bit-field assignment' do
|
|
642
|
+
allow(client).to receive(:send_recv).and_return(open_andx_response.to_binary_s)
|
|
643
|
+
expect { tree.open_file(filename: 'HELLO.TXT') }.not_to raise_error
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
it 'serializes search_attributes / file_attributes as SMB_FILE_ATTRIBUTES bit-fields' do
|
|
647
|
+
sent = nil
|
|
648
|
+
allow(client).to receive(:send_recv) do |req|
|
|
649
|
+
sent = req
|
|
650
|
+
open_andx_response.to_binary_s
|
|
651
|
+
end
|
|
652
|
+
tree.open_file(filename: 'HELLO.TXT')
|
|
653
|
+
|
|
654
|
+
# 0x0016 = directory | system | hidden in the SMB_FILE_ATTRIBUTES search half.
|
|
655
|
+
expect(sent.parameter_block.search_attributes.directory).to eq 1
|
|
656
|
+
expect(sent.parameter_block.search_attributes.system).to eq 1
|
|
657
|
+
expect(sent.parameter_block.search_attributes.hidden).to eq 1
|
|
658
|
+
expect(sent.parameter_block.search_attributes.to_binary_s).to eq([0x0016].pack('v'))
|
|
659
|
+
|
|
660
|
+
# Read-only open: file_attributes mask is zeroed.
|
|
661
|
+
expect(sent.parameter_block.file_attributes.to_binary_s).to eq([0x0000].pack('v'))
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
it 'sets the SMB_FILE_ATTRIBUTE_ARCHIVE bit when opened for write' do
|
|
665
|
+
allow(client).to receive(:send_recv).and_return(open_andx_response.to_binary_s)
|
|
666
|
+
sent = nil
|
|
667
|
+
allow(client).to receive(:send_recv) do |req|
|
|
668
|
+
sent = req
|
|
669
|
+
open_andx_response.to_binary_s
|
|
670
|
+
end
|
|
671
|
+
tree.open_file(filename: 'HELLO.TXT', write: true)
|
|
672
|
+
|
|
673
|
+
expect(sent.parameter_block.file_attributes.to_binary_s).to eq([0x0020].pack('v'))
|
|
674
|
+
expect(sent.parameter_block.file_attributes.archive).to eq 1
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
it 'returns a File handle whose FID and size come from the OPEN_ANDX response' do
|
|
678
|
+
allow(client).to receive(:send_recv).and_return(open_andx_response.to_binary_s)
|
|
679
|
+
file = tree.open_file(filename: 'HELLO.TXT')
|
|
680
|
+
expect(file).to be_a(RubySMB::SMB1::File)
|
|
681
|
+
expect(file.fid).to eq 0x4242
|
|
682
|
+
expect(file.size).to eq 1234
|
|
497
683
|
end
|
|
498
684
|
end
|
|
499
685
|
|
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: 3.3.
|
|
4
|
+
version: 3.3.19
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Metasploit Hackers
|
|
@@ -13,7 +13,7 @@ authors:
|
|
|
13
13
|
autorequire:
|
|
14
14
|
bindir: bin
|
|
15
15
|
cert_chain: []
|
|
16
|
-
date: 2026-04-
|
|
16
|
+
date: 2026-04-27 00:00:00.000000000 Z
|
|
17
17
|
dependencies:
|
|
18
18
|
- !ruby/object:Gem::Dependency
|
|
19
19
|
name: redcarpet
|
|
@@ -465,6 +465,8 @@ files:
|
|
|
465
465
|
- lib/ruby_smb/ntlm.rb
|
|
466
466
|
- lib/ruby_smb/ntlm/client.rb
|
|
467
467
|
- lib/ruby_smb/peer_info.rb
|
|
468
|
+
- lib/ruby_smb/rap.rb
|
|
469
|
+
- lib/ruby_smb/rap/net_share_enum.rb
|
|
468
470
|
- lib/ruby_smb/server.rb
|
|
469
471
|
- lib/ruby_smb/server/cli.rb
|
|
470
472
|
- lib/ruby_smb/server/server_client.rb
|
|
@@ -536,6 +538,8 @@ files:
|
|
|
536
538
|
- lib/ruby_smb/smb1/packet/nt_trans/request.rb
|
|
537
539
|
- lib/ruby_smb/smb1/packet/nt_trans/response.rb
|
|
538
540
|
- lib/ruby_smb/smb1/packet/nt_trans/subcommands.rb
|
|
541
|
+
- lib/ruby_smb/smb1/packet/open_andx_request.rb
|
|
542
|
+
- lib/ruby_smb/smb1/packet/open_andx_response.rb
|
|
539
543
|
- lib/ruby_smb/smb1/packet/read_andx_request.rb
|
|
540
544
|
- lib/ruby_smb/smb1/packet/read_andx_response.rb
|
|
541
545
|
- lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb
|
|
@@ -558,6 +562,7 @@ files:
|
|
|
558
562
|
- lib/ruby_smb/smb1/packet/trans2/find_information_level.rb
|
|
559
563
|
- lib/ruby_smb/smb1/packet/trans2/find_information_level/find_file_both_directory_info.rb
|
|
560
564
|
- lib/ruby_smb/smb1/packet/trans2/find_information_level/find_file_full_directory_info.rb
|
|
565
|
+
- lib/ruby_smb/smb1/packet/trans2/find_information_level/find_info_standard.rb
|
|
561
566
|
- lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb
|
|
562
567
|
- lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb
|
|
563
568
|
- lib/ruby_smb/smb1/packet/trans2/open2_request.rb
|
|
@@ -579,6 +584,7 @@ files:
|
|
|
579
584
|
- lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb
|
|
580
585
|
- lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb
|
|
581
586
|
- lib/ruby_smb/smb1/packet/trans2/subcommands.rb
|
|
587
|
+
- lib/ruby_smb/smb1/packet/trans2/win9x_framing.rb
|
|
582
588
|
- lib/ruby_smb/smb1/packet/tree_connect_request.rb
|
|
583
589
|
- lib/ruby_smb/smb1/packet/tree_connect_response.rb
|
|
584
590
|
- lib/ruby_smb/smb1/packet/tree_disconnect_request.rb
|
|
@@ -836,6 +842,7 @@ files:
|
|
|
836
842
|
- spec/lib/ruby_smb/nbss/session_request_spec.rb
|
|
837
843
|
- spec/lib/ruby_smb/ntlm/client/session_spec.rb
|
|
838
844
|
- spec/lib/ruby_smb/ntlm/client_spec.rb
|
|
845
|
+
- spec/lib/ruby_smb/rap/net_share_enum_spec.rb
|
|
839
846
|
- spec/lib/ruby_smb/server/server_client_spec.rb
|
|
840
847
|
- spec/lib/ruby_smb/server/session_spec.rb
|
|
841
848
|
- spec/lib/ruby_smb/server/share/provider/disk_spec.rb
|
|
@@ -915,6 +922,7 @@ files:
|
|
|
915
922
|
- spec/lib/ruby_smb/smb1/packet/trans2/response_spec.rb
|
|
916
923
|
- spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_request_spec.rb
|
|
917
924
|
- spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_response_spec.rb
|
|
925
|
+
- spec/lib/ruby_smb/smb1/packet/trans2/win9x_framing_spec.rb
|
|
918
926
|
- spec/lib/ruby_smb/smb1/packet/tree_connect_request_spec.rb
|
|
919
927
|
- spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb
|
|
920
928
|
- spec/lib/ruby_smb/smb1/packet/tree_disconnect_request_spec.rb
|
|
@@ -1187,6 +1195,7 @@ test_files:
|
|
|
1187
1195
|
- spec/lib/ruby_smb/nbss/session_request_spec.rb
|
|
1188
1196
|
- spec/lib/ruby_smb/ntlm/client/session_spec.rb
|
|
1189
1197
|
- spec/lib/ruby_smb/ntlm/client_spec.rb
|
|
1198
|
+
- spec/lib/ruby_smb/rap/net_share_enum_spec.rb
|
|
1190
1199
|
- spec/lib/ruby_smb/server/server_client_spec.rb
|
|
1191
1200
|
- spec/lib/ruby_smb/server/session_spec.rb
|
|
1192
1201
|
- spec/lib/ruby_smb/server/share/provider/disk_spec.rb
|
|
@@ -1266,6 +1275,7 @@ test_files:
|
|
|
1266
1275
|
- spec/lib/ruby_smb/smb1/packet/trans2/response_spec.rb
|
|
1267
1276
|
- spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_request_spec.rb
|
|
1268
1277
|
- spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_response_spec.rb
|
|
1278
|
+
- spec/lib/ruby_smb/smb1/packet/trans2/win9x_framing_spec.rb
|
|
1269
1279
|
- spec/lib/ruby_smb/smb1/packet/tree_connect_request_spec.rb
|
|
1270
1280
|
- spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb
|
|
1271
1281
|
- spec/lib/ruby_smb/smb1/packet/tree_disconnect_request_spec.rb
|