Class: RubySMB::SMB1::Pipe

Inherits:
File
  • Object
show all
Includes:
Dcerpc
Defined in:
lib/ruby_smb/smb1/pipe.rb

Overview

Represents a pipe on the Remote server that we can perform various I/O operations on.

Constant Summary collapse

STATUS_DISCONNECTED =
0x0001
STATUS_LISTENING =
0x0002
STATUS_OK =
0x0003
STATUS_CLOSED =
0x0004

Constants included from Dcerpc

Dcerpc::DCE_C_AUTHZ_DCE, Dcerpc::DCE_C_AUTHZ_NAME, Dcerpc::MAX_RECV_FRAG, Dcerpc::MAX_XMIT_FRAG, Dcerpc::RPC_C_AUTHN_DEFAULT, Dcerpc::RPC_C_AUTHN_GSS_KERBEROS, Dcerpc::RPC_C_AUTHN_GSS_NEGOTIATE, Dcerpc::RPC_C_AUTHN_GSS_SCHANNEL, Dcerpc::RPC_C_AUTHN_LEVEL_CALL, Dcerpc::RPC_C_AUTHN_LEVEL_CONNECT, Dcerpc::RPC_C_AUTHN_LEVEL_DEFAULT, Dcerpc::RPC_C_AUTHN_LEVEL_NONE, Dcerpc::RPC_C_AUTHN_LEVEL_PKT, Dcerpc::RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY, Dcerpc::RPC_C_AUTHN_NETLOGON, Dcerpc::RPC_C_AUTHN_NONE, Dcerpc::RPC_C_AUTHN_WINNT

Instance Attribute Summary

Attributes inherited from File

#attributes, #fid, #last_access, #last_change, #last_write, #name, #size, #size_on_disk, #tree

Instance Method Summary collapse

Methods included from Dcerpc

#add_auth_verifier, #auth_provider_complete_handshake, #auth_provider_decrypt_and_verify, #auth_provider_encrypt_and_sign, #auth_provider_init, #force_set_auth_params, #get_auth_padding_length, #get_response_full_stub, #handle_integrity_privacy, #max_buffer_size=, #process_ntlm_type2, #recv_struct, #send_packet, #set_decrypted_packet, #set_encrypted_packet, #set_integrity_privacy, #set_signature_on_packet

Methods inherited from File

#append, #close, #delete, #delete_packet, #read, #read_packet, #rename, #rename_packet, #send_recv_read, #send_recv_write, #set_header_fields, #write, #write_packet

Constructor Details

#initialize(tree:, response:, name:) ⇒ Pipe

Returns a new instance of Pipe.

Raises:

  • (ArgumentError)


16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/ruby_smb/smb1/pipe.rb', line 16

def initialize(tree:, response:, name:)
  raise ArgumentError, 'No Name Provided' if name.nil?
  case name
  when 'netlogon', '\\netlogon'
    extend RubySMB::Dcerpc::Netlogon
  when 'srvsvc', '\\srvsvc'
    extend RubySMB::Dcerpc::Srvsvc
  when 'svcctl', '\\svcctl'
    extend RubySMB::Dcerpc::Svcctl
  when 'winreg', '\\winreg'
    extend RubySMB::Dcerpc::Winreg
  when 'samr', '\\samr'
    extend RubySMB::Dcerpc::Samr
  when 'wkssvc', '\\wkssvc'
    extend RubySMB::Dcerpc::Wkssvc
  when 'lsarpc', '\\lsarpc'
    extend RubySMB::Dcerpc::Lsarpc
  when 'netdfs', '\\netdfs'
    extend RubySMB::Dcerpc::Dfsnm
  when 'cert', '\\cert'
    extend RubySMB::Dcerpc::Icpr
  when 'efsrpc', '\\efsrpc'
    extend RubySMB::Dcerpc::Efsrpc
  end
  super(tree: tree, response: response, name: name)
end

Instance Method Details

#bind(options = {}) ⇒ Object



43
44
45
46
47
# File 'lib/ruby_smb/smb1/pipe.rb', line 43

def bind(options={})
  @size = 1024
  @ntlm_client = @tree.client.ntlm_client
  super
end

#dcerpc_request(stub_packet, options = {}) ⇒ String

Send a DCERPC request with the provided stub packet.

Returns:

  • (String)

    the raw DCERPC response stub

Raises:



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/ruby_smb/smb1/pipe.rb', line 109

def dcerpc_request(stub_packet, options={})
  options.merge!(endpoint: stub_packet.class.name.split('::').at(-2))
  dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: stub_packet.opnum }, options)
  dcerpc_request.stub.read(stub_packet.to_binary_s)
  if options[:auth_level] &&
     [RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(options[:auth_level])
    set_integrity_privacy(dcerpc_request, auth_level: options[:auth_level], auth_type: options[:auth_type])
  end

  trans_nmpipe_request = RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest.new(options)
  @tree.set_header_fields(trans_nmpipe_request)
  trans_nmpipe_request.set_fid(@fid)
  trans_nmpipe_request.data_block.trans_data.write_data = dcerpc_request.to_binary_s

  trans_nmpipe_raw_response = @tree.client.send_recv(trans_nmpipe_request)
  trans_nmpipe_response = RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse.read(trans_nmpipe_raw_response)
  unless trans_nmpipe_response.valid?
    raise RubySMB::Error::InvalidPacket.new(
      expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
      expected_cmd:   RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse::COMMAND,
      packet:         trans_nmpipe_response
    )
  end
  unless [WindowsError::NTStatus::STATUS_SUCCESS,
          WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW].include?(trans_nmpipe_response.status_code)
    raise RubySMB::Error::UnexpectedStatusCode, trans_nmpipe_response.status_code
  end

  raw_data = trans_nmpipe_response.data_block.trans_data.read_data.to_binary_s
  if trans_nmpipe_response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW
    raw_data << read(bytes: @tree.client.max_buffer_size - trans_nmpipe_response.parameter_block.data_count)
    dcerpc_response = dcerpc_response_from_raw_response(raw_data)
    unless dcerpc_response.pdu_header.pfc_flags.first_frag == 1
      raise RubySMB::Dcerpc::Error::InvalidPacket, "Not the first fragment"
    end
    if options[:auth_level] &&
       [RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(options[:auth_level])
      handle_integrity_privacy(dcerpc_response, auth_level: options[:auth_level], auth_type: options[:auth_type])
    end
    stub_data = dcerpc_response.stub.to_s

    loop do
      break if dcerpc_response.pdu_header.pfc_flags.last_frag == 1
      raw_data = read(bytes: @tree.client.max_buffer_size)
      dcerpc_response = dcerpc_response_from_raw_response(raw_data)
      if options[:auth_level] &&
         [RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(options[:auth_level])
        handle_integrity_privacy(dcerpc_response, auth_level: options[:auth_level], auth_type: options[:auth_type])
      end
      stub_data << dcerpc_response.stub.to_s
    end
    stub_data
  else
    dcerpc_response = dcerpc_response_from_raw_response(raw_data)
    if options[:auth_level] &&
       [RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(options[:auth_level])
      handle_integrity_privacy(dcerpc_response, auth_level: options[:auth_level], auth_type: options[:auth_type])
    end
    dcerpc_response.stub.to_s
  end
end

#is_connected?Boolean

Returns True if pipe is connected, false otherwise.

Returns:

  • (Boolean)

    True if pipe is connected, false otherwise



91
92
93
94
95
96
97
98
99
100
101
# File 'lib/ruby_smb/smb1/pipe.rb', line 91

def is_connected?
  begin
    state = peek_state
  rescue RubySMB::Error::UnexpectedStatusCode => e
    if e.message == 'STATUS_INVALID_HANDLE'
      return false
    end
    raise e
  end
  state == STATUS_OK
end

#peek(peek_size: 0) ⇒ RubySMB::SMB1::Packet::Trans::PeekNmpipeResponse

Performs a peek operation on the named pipe

Parameters:

  • peek_size (Integer) (defaults to: 0)

    Amount of data to peek

Returns:

Raises:



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/ruby_smb/smb1/pipe.rb', line 55

def peek(peek_size: 0)
  packet = RubySMB::SMB1::Packet::Trans::PeekNmpipeRequest.new
  packet.fid = @fid
  packet.parameter_block.max_data_count = peek_size
  packet = @tree.set_header_fields(packet)
  raw_response = @tree.client.send_recv(packet)
  response = RubySMB::SMB1::Packet::Trans::PeekNmpipeResponse.read(raw_response)
  unless response.valid?
    raise RubySMB::Error::InvalidPacket.new(
      expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
      expected_cmd:   RubySMB::SMB1::Packet::Trans::PeekNmpipeRequest::COMMAND,
      packet:         response
    )
  end

  unless response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW or response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
    raise RubySMB::Error::UnexpectedStatusCode, response.status_code
  end

  response
end

#peek_availableInteger

Returns The number of bytes available to be read from the pipe.

Returns:

  • (Integer)

    The number of bytes available to be read from the pipe



78
79
80
81
82
# File 'lib/ruby_smb/smb1/pipe.rb', line 78

def peek_available
  packet = peek
  # Only 1 of these should be non-zero
  packet.data_block.trans_parameters.read_data_available or packet.data_block.trans_parameters.message_bytes_length
end

#peek_stateInteger

Returns Pipe status.

Returns:

  • (Integer)

    Pipe status



85
86
87
88
# File 'lib/ruby_smb/smb1/pipe.rb', line 85

def peek_state
  packet = peek
  packet.data_block.trans_parameters.pipe_state
end