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::MAX_RECV_FRAG, Dcerpc::MAX_XMIT_FRAG

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

#bind

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
# 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 'srvsvc'
    extend RubySMB::Dcerpc::Srvsvc
  when 'winreg'
    extend RubySMB::Dcerpc::Winreg
  end
  super(tree: tree, response: response, name: name)
end

Instance Method Details

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

Send a DCERPC request with the provided stub packet.

Returns:

  • (String)

    the raw DCERPC response stub

Raises:



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/ruby_smb/smb1/pipe.rb', line 88

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,
      received_proto: trans_nmpipe_response.smb_header.protocol,
      received_cmd:   trans_nmpipe_response.smb_header.command
    )
  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?Boolean

Returns True if pipe is connected, false otherwise.

Returns:

  • (Boolean)

    True if pipe is connected, false otherwise



70
71
72
73
74
75
76
77
78
79
80
# File 'lib/ruby_smb/smb1/pipe.rb', line 70

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:



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/ruby_smb/smb1/pipe.rb', line 33

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,
      received_proto: response.smb_header.protocol,
      received_cmd:   response.smb_header.command
    )
  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



57
58
59
60
61
# File 'lib/ruby_smb/smb1/pipe.rb', line 57

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



64
65
66
67
# File 'lib/ruby_smb/smb1/pipe.rb', line 64

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