class RubySMB::SMB2::Pipe
Represents a pipe on the Remote server that we can perform various I/O operations on.
Constants
- STATUS_CLOSING
- STATUS_CONNECTED
Public Class Methods
new(tree:, response:, name:)
click to toggle source
Calls superclass method
RubySMB::SMB2::File::new
# 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 '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
# File lib/ruby_smb/smb2/pipe.rb, line 84 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={})
click to toggle source
# File lib/ruby_smb/smb2/pipe.rb, line 91 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 # TODO: handle fragmentation when the request size > MAX_XMIT_FRAG 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, packet: ioctl_response ) 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?()
click to toggle source
@return [Boolean] True if pipe is connected, false otherwise
# File lib/ruby_smb/smb2/pipe.rb, line 72 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)
click to toggle source
Performs a peek operation on the named pipe
@param peek_size [Integer] Amount of data to peek @return [RubySMB::SMB2::Packet::IoctlResponse] @raise [RubySMB::Error::InvalidPacket] if not a valid FIoctlResponse response @raise [RubySMB::Error::UnexpectedStatusCode] If status is not STATUS_BUFFER_OVERFLOW or STATUS_SUCCESS
# File lib/ruby_smb/smb2/pipe.rb, line 34 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, 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/smb2/pipe.rb, line 58 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_state()
click to toggle source
@return [Integer] Pipe
status
# File lib/ruby_smb/smb2/pipe.rb, line 66 def peek_state packet = peek packet.buffer.unpack('V')[0] end
Private Instance Methods
dcerpc_response_from_raw_response(raw_data)
click to toggle source
# File lib/ruby_smb/smb2/pipe.rb, line 137 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