class RubySMB::SMB1::Pipe

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

Constants

STATUS_CLOSED
STATUS_DISCONNECTED

Reference: msdn.microsoft.com/en-us/library/ee441883.aspx

STATUS_LISTENING
STATUS_OK

Public Class Methods

new(tree:, response:, name:) click to toggle source
Calls superclass method RubySMB::SMB1::File::new
# 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
  end
  super(tree: tree, response: response, name: name)
end

Public Instance Methods

dcerpc_request(stub_packet, options={}) click to toggle source

Send a DCERPC request with the provided stub packet.

@params stub_packet [#opnum] the stub packet to add to the DCERPC request @return [String] the raw DCERPC response stub @raise [RubySMB::Error::InvalidPacket] if the response is not valid @raise [RubySMB::Error::UnexpectedStatusCode] if the response status code is different than STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW

# File lib/ruby_smb/smb1/pipe.rb, line 91
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)
  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
    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)
      stub_data << dcerpc_response.stub.to_s
    end
    stub_data
  else
    dcerpc_response = dcerpc_response_from_raw_response(raw_data)
    dcerpc_response.stub.to_s
  end
end
is_connected?() click to toggle source

@return [Boolean] True if pipe is connected, false otherwise

# File lib/ruby_smb/smb1/pipe.rb, line 73
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) click to toggle source

Performs a peek operation on the named pipe

@param peek_size [Integer] Amount of data to peek @return [RubySMB::SMB1::Packet::Trans::PeekNmpipeResponse] @raise [RubySMB::Error::InvalidPacket] If not a valid PeekNmpipeResponse @raise [RubySMB::Error::UnexpectedStatusCode] If status is not STATUS_BUFFER_OVERFLOW or STATUS_SUCCESS

# File lib/ruby_smb/smb1/pipe.rb, line 37
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_available() click to toggle source

@return [Integer] The number of bytes available to be read from the pipe

# File lib/ruby_smb/smb1/pipe.rb, line 60
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_state() click to toggle source

@return [Integer] Pipe status

# File lib/ruby_smb/smb1/pipe.rb, line 67
def peek_state
  packet = peek
  packet.data_block.trans_parameters.pipe_state
end

Private Instance Methods

dcerpc_response_from_raw_response(raw_data) click to toggle source
# File lib/ruby_smb/smb1/pipe.rb, line 139
def dcerpc_response_from_raw_response(raw_data)
  dcerpc_response = RubySMB::Dcerpc::Response.read(raw_data)
  unless dcerpc_response.pdu_header.ptype == RubySMB::Dcerpc::PTypes::RESPONSE
    raise RubySMB::Dcerpc::Error::InvalidPacket, "Not a Response packet"
  end
  dcerpc_response
rescue IOError
  raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the DCERPC response"
end