Class: RubySMB::SMB2::Pipe

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

Overview

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

Constant Summary collapse

STATUS_CONNECTED =
0x00000003
STATUS_CLOSING =
0x00000004

Constants included from Dcerpc

Dcerpc::MAX_RECV_FRAG, Dcerpc::MAX_XMIT_FRAG

Constants inherited from File

File::MAX_PACKET_SIZE

Instance Attribute Summary

Attributes inherited from File

#attributes, #guid, #last_access, #last_change, #last_write, #name, #size, #size_on_disk, #tree, #tree_connect_encrypt_data

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)


13
14
15
16
17
18
19
20
21
22
# File 'lib/ruby_smb/smb2/pipe.rb', line 13

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 = {}) ⇒ Object



81
82
83
84
85
86
# File 'lib/ruby_smb/smb2/pipe.rb', line 81

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)
  ioctl_send_recv(dcerpc_request, options)
end

#ioctl_send_recv(action, options = {}) ⇒ Object



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
# File 'lib/ruby_smb/smb2/pipe.rb', line 88

def ioctl_send_recv(action, options={})
  request = set_header_fields(RubySMB::SMB2::Packet::IoctlRequest.new(options))
  request.ctl_code = 0x0011C017
  request.flags.is_fsctl = 0x00000001
  request.buffer = action.to_binary_s

  ioctl_raw_response = @tree.client.send_recv(request)
  ioctl_response = RubySMB::SMB2::Packet::IoctlResponse.read(ioctl_raw_response)
  unless ioctl_response.valid?
    raise RubySMB::Error::InvalidPacket.new(
      expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
      expected_cmd:   RubySMB::SMB2::Packet::IoctlRequest::COMMAND,
      received_proto: ioctl_response.smb2_header.protocol,
      received_cmd:   ioctl_response.smb2_header.command
    )
  end
  unless [WindowsError::NTStatus::STATUS_SUCCESS,
          WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW].include?(ioctl_response.status_code)
    raise RubySMB::Error::UnexpectedStatusCode, ioctl_response.status_code
  end

  raw_data = ioctl_response.output_data
  if ioctl_response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW
    raw_data << read(bytes: @tree.client.max_buffer_size - ioctl_response.output_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



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

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

#peek(peek_size: 0) ⇒ RubySMB::SMB2::Packet::IoctlResponse

Performs a peek operation on the named pipe

Parameters:

  • peek_size (Integer) (defaults to: 0)

    Amount of data to peek

Returns:

Raises:



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

def peek(peek_size: 0)
  packet = RubySMB::SMB2::Packet::IoctlRequest.new
  packet.ctl_code = RubySMB::Fscc::ControlCodes::FSCTL_PIPE_PEEK
  packet.flags.is_fsctl = true
  # read at least 16 bytes for state, avail, msg_count, first_msg_len
  packet.max_output_response = 16 + peek_size
  packet = set_header_fields(packet)
  raw_response = @tree.client.send_recv(packet)
  response = RubySMB::SMB2::Packet::IoctlResponse.read(raw_response)
  unless response.valid?
    raise RubySMB::Error::InvalidPacket.new(
      expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
      expected_cmd:   RubySMB::SMB2::Packet::IoctlResponse::COMMAND,
      received_proto: response.smb2_header.protocol,
      received_cmd:   response.smb2_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



55
56
57
58
59
60
# File 'lib/ruby_smb/smb2/pipe.rb', line 55

def peek_available
  packet = peek
  state, avail, msg_count, first_msg_len = packet.buffer.unpack('VVVV')
  # Only 1 of these should be non-zero
  avail or first_msg_len
end

#peek_stateInteger

Returns Pipe status.

Returns:

  • (Integer)

    Pipe status



63
64
65
66
# File 'lib/ruby_smb/smb2/pipe.rb', line 63

def peek_state
  packet = peek
  packet.buffer.unpack('V')[0]
end