Class: OpenPipeSock

Inherits:
Rex::Proto::SMB::SimpleClient::OpenPipe show all
Defined in:
lib/msf/core/handler/bind_named_pipe.rb

Overview

Socket interface for named pipes. Because of the way named pipes work, reads and writes each require both a sock.send (read/write request) and a sock.recv (read/write response). So, pipe.read and pipe.write need to be synchronized so the responses arent mixed up.

The packet dispatcher calls select on the socket to check for packets to read. This is an issue when there are multiple writes since it will cause select to return which triggers a read, but there is nothing to read since the pipe will already have read the response. This read will then hold the mutex while the socket read waits to timeout. A peek operation on the pipe fixes this.

Constant Summary collapse

STATUS_BUFFER_OVERFLOW =
0x80000005
STATUS_PIPE_BROKEN =
0xc000014b

Instance Attribute Summary collapse

Attributes inherited from Rex::Proto::SMB::SimpleClient::OpenPipe

#mode

Attributes inherited from Rex::Proto::SMB::SimpleClient::OpenFile

#chunk_size, #client, #file_id, #mode, #name, #tree_id, #versions

Instance Method Summary collapse

Methods inherited from Rex::Proto::SMB::SimpleClient::OpenPipe

#peek, #peek_rex_smb, #peek_ruby_smb, #read_buffer, #read_ruby_smb, #write_trans

Methods inherited from Rex::Proto::SMB::SimpleClient::OpenFile

#<<, #delete, #read_rex_smb, #read_ruby_smb

Constructor Details

#initialize(*args, simple:, server_max_buffer_size:) ⇒ OpenPipeSock

Returns a new instance of OpenPipeSock.



29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 29

def initialize(*args, simple:, server_max_buffer_size:)
  super(*args)
  self.simple = simple
  self.client = simple.client
  self.mutex = Mutex.new      # synchronize read/writes
  self.last_comm = Time.now   # last successful read/write
  self.write_queue = Queue.new # messages to send
  self.write_thread = Thread.new { dispatcher }
  self.echo_thread = Thread.new { force_read }
  self.read_buff = ''
  self.server_max_buffer_size = server_max_buffer_size # max transaction size
  self.chunk_size = server_max_buffer_size - 260       # max read/write size
end

Instance Attribute Details

#echo_threadObject

Returns the value of attribute echo_thread.



24
25
26
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 24

def echo_thread
  @echo_thread
end

#last_commObject

Returns the value of attribute last_comm.



24
25
26
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 24

def last_comm
  @last_comm
end

#mutexObject

Returns the value of attribute mutex.



24
25
26
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 24

def mutex
  @mutex
end

#read_buffObject

Returns the value of attribute read_buff.



24
25
26
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 24

def read_buff
  @read_buff
end

#server_max_buffer_sizeObject

Returns the value of attribute server_max_buffer_size.



24
25
26
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 24

def server_max_buffer_size
  @server_max_buffer_size
end

#simpleObject

Returns the value of attribute simple.



24
25
26
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 24

def simple
  @simple
end

#write_queueObject

Returns the value of attribute write_queue.



24
25
26
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 24

def write_queue
  @write_queue
end

#write_threadObject

Returns the value of attribute write_thread.



24
25
26
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 24

def write_thread
  @write_thread
end

Instance Method Details

#closeObject

Intercepts the socket.close from the session manager when the session dies. Cleanly terminates the SMB session and closes the socket.



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 80

def close
  self.echo_thread.kill rescue nil
  # Give the meterpreter shutdown command a chance
  self.write_queue.close
  begin
    if self.write_thread.join(2.0)
      self.write_thread.kill
    end
  rescue
  end

  # close pipe, share, and socket
  super rescue nil
  self.simple.disconnect(self.simple.last_share) rescue nil
  self.client.socket.close
end

#dispatcherObject

Runs as a thread and synchronizes writes. Allows write operations to return immediately instead of waiting for the mutex.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 62

def dispatcher
  while not self.write_queue.closed?
    data = self.write_queue.pop
    self.mutex.synchronize do
      sent = 0
      while sent < data.length
        count = [self.chunk_size, data.length-sent].min
        buf = data[sent, count]
        Rex::Proto::SMB::SimpleClient::OpenPipe.instance_method(:write).bind(self).call(buf)
        self.last_comm = Time.now
        sent += count
      end
    end
  end
end

#fdObject

The session manager expects a socket object so we must implement fd, localinfo, and peerinfo. fd is passed to select while localinfo and peerinfo are used to report the addresses and ports of the connection.



141
142
143
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 141

def fd
  self.simple.socket.fd
end

#force_readObject

Send echo request to force select() to return in the packet dispatcher and read from the socket. This allows “channel -i” and “shell” to work.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 45

def force_read
  wait = 0.5                  # smaller is faster but generates more traffic
  while true
    elapsed = Time.now - self.last_comm
    if elapsed > wait
      self.mutex.synchronize do
        self.client.echo()
        self.last_comm = Time.now
      end
    else
      Rex::ThreadSafe.sleep(wait-elapsed)
    end
  end
end

#localinfoObject



145
146
147
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 145

def localinfo
  self.simple.socket.localinfo
end

#peerinfoObject



149
150
151
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 149

def peerinfo
  self.simple.socket.peerinfo
end

#put(data) ⇒ Object



126
127
128
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 126

def put (data)
  write(data)
end

#read(count) ⇒ Object



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
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 97

def read(count)
  data = ''
  if count > self.read_buff.length
    # need more data to satisfy request
    self.mutex.synchronize do
      avail = peek
      self.last_comm = Time.now
      if avail > 0
        left = [count-self.read_buff.length, avail].max
        while left > 0
          buff = super([left, self.chunk_size].min)
          self.last_comm = Time.now
          left -= buff.length
          self.read_buff += buff
        end
      end
    end
  end

  data = self.read_buff[0, [count, self.read_buff.length].min]
  self.read_buff = self.read_buff[data.length..-1]

  if data.length == 0
    # avoid full throttle polling
    Rex::ThreadSafe.sleep(0.2)
  end
  data
end

#write(data) ⇒ Object



130
131
132
133
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 130

def write (data)
  self.write_queue.push(data)
  data.length
end