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, #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.


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

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 successfull 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


26
27
28
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 26

def echo_thread
  @echo_thread
end

#last_commObject

Returns the value of attribute last_comm


26
27
28
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 26

def last_comm
  @last_comm
end

#mutexObject

Returns the value of attribute mutex


26
27
28
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 26

def mutex
  @mutex
end

#read_buffObject

Returns the value of attribute read_buff


26
27
28
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 26

def read_buff
  @read_buff
end

#server_max_buffer_sizeObject

Returns the value of attribute server_max_buffer_size


26
27
28
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 26

def server_max_buffer_size
  @server_max_buffer_size
end

#simpleObject

Returns the value of attribute simple


26
27
28
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 26

def simple
  @simple
end

#write_queueObject

Returns the value of attribute write_queue


26
27
28
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 26

def write_queue
  @write_queue
end

#write_threadObject

Returns the value of attribute write_thread


26
27
28
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 26

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.


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

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.


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

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.


143
144
145
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 143

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.


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

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


147
148
149
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 147

def localinfo
  self.simple.socket.localinfo
end

#peerinfoObject


151
152
153
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 151

def peerinfo
  self.simple.socket.peerinfo
end

#put(data) ⇒ Object


128
129
130
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 128

def put (data)
  write(data)
end

#read(count) ⇒ Object


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

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


132
133
134
135
# File 'lib/msf/core/handler/bind_named_pipe.rb', line 132

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