Class: Msf::Modules::External::Bridge

Inherits:
Object
  • Object
show all
Defined in:
lib/msf/core/modules/external/bridge.rb,
lib/msf/core/modules/external/bridge.rb

Direct Known Subclasses

GoBridge, PyBridge, RbBridge

Constant Summary collapse

LOADERS =
[
  Msf::Modules::External::PyBridge,
  Msf::Modules::External::RbBridge,
  Msf::Modules::External::GoBridge,
  Msf::Modules::External::Bridge
]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(module_path, framework: nil) ⇒ Bridge

Returns a new instance of Bridge.



44
45
46
47
48
49
50
51
52
# File 'lib/msf/core/modules/external/bridge.rb', line 44

def initialize(module_path, framework: nil)
  self.env = {}
  self.running = false
  self.path = module_path
  self.cmd = [[self.path, self.path]]
  self.messages = Queue.new
  self.buf = ''
  self.framework = framework
end

Instance Attribute Details

#bufObject (protected)

Returns the value of attribute buf.



57
58
59
# File 'lib/msf/core/modules/external/bridge.rb', line 57

def buf
  @buf
end

#cmdObject (protected)

Returns the value of attribute cmd.



57
58
59
# File 'lib/msf/core/modules/external/bridge.rb', line 57

def cmd
  @cmd
end

#envObject (protected)

Returns the value of attribute env.



57
58
59
# File 'lib/msf/core/modules/external/bridge.rb', line 57

def env
  @env
end

#exit_statusObject

Returns the value of attribute exit_status.



9
10
11
# File 'lib/msf/core/modules/external/bridge.rb', line 9

def exit_status
  @exit_status
end

#frameworkObject (protected)

Returns the value of attribute framework.



57
58
59
# File 'lib/msf/core/modules/external/bridge.rb', line 57

def framework
  @framework
end

#iosObject (protected)

Returns the value of attribute ios.



57
58
59
# File 'lib/msf/core/modules/external/bridge.rb', line 57

def ios
  @ios
end

#messagesObject

Returns the value of attribute messages.



9
10
11
# File 'lib/msf/core/modules/external/bridge.rb', line 9

def messages
  @messages
end

#pathObject

Returns the value of attribute path.



9
10
11
# File 'lib/msf/core/modules/external/bridge.rb', line 9

def path
  @path
end

#read_threadObject (protected)

Returns the value of attribute read_thread.



57
58
59
# File 'lib/msf/core/modules/external/bridge.rb', line 57

def read_thread
  @read_thread
end

#runningObject

Returns the value of attribute running.



9
10
11
# File 'lib/msf/core/modules/external/bridge.rb', line 9

def running
  @running
end

#wait_threadObject (protected)

Returns the value of attribute wait_thread.



57
58
59
# File 'lib/msf/core/modules/external/bridge.rb', line 57

def wait_thread
  @wait_thread
end

Class Method Details

.applies?(module_name) ⇒ Boolean

Returns:

  • (Boolean)


11
12
13
# File 'lib/msf/core/modules/external/bridge.rb', line 11

def self.applies?(module_name)
  File::executable? module_name
end

.open(module_path, framework: nil) ⇒ Object



255
256
257
258
259
260
261
# File 'lib/msf/core/modules/external/bridge.rb', line 255

def self.open(module_path, framework: nil)
  LOADERS.each do |klass|
    return klass.new module_path, framework: framework if klass.applies? module_path
  end

  nil
end

Instance Method Details

#cleanupObject (protected)



156
157
158
159
160
161
# File 'lib/msf/core/modules/external/bridge.rb', line 156

def cleanup
  self.running = false
  self.messages.close
  harvest_process
  self.ios.each {|fd| fd.close rescue nil} # Yeah, yeah. I know.
end

#closeObject



33
34
35
36
37
38
# File 'lib/msf/core/modules/external/bridge.rb', line 33

def close
  self.running = false
  self.read_thread.join

  self
end

#exec(req) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/msf/core/modules/external/bridge.rb', line 15

def exec(req)
  unless self.running
    self.running = true
    send(req)
    self.read_thread = threadme do
      begin
        while self.running && m = next_message
          self.messages.push m
        end
      ensure
        cleanup
      end
    end

    self
  end
end

#handle_exception(e) ⇒ Object (protected)



79
80
81
# File 'lib/msf/core/modules/external/bridge.rb', line 79

def handle_exception(e)
  e
end

#harvest_processObject (protected)



145
146
147
148
149
150
151
152
153
154
# File 'lib/msf/core/modules/external/bridge.rb', line 145

def harvest_process
  if self.wait_thread.join(10)
    self.exit_status = self.wait_thread.value
  elsif Process.kill('TERM', self.wait_thread.pid) && self.wait_thread.join(10)
    self.exit_status = self.wait_thread.value
  else
    Process.kill('KILL', self.wait_thread.pid)
    self.exit_status = self.wait_thread.value
  end
end

#next_message(timeout = 600) ⇒ Object (protected)



87
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
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/msf/core/modules/external/bridge.rb', line 87

def next_message(timeout=600)
  _, out, err = self.ios
  message = ''

  # Multiple messages can come over the wire all at once, and since yajl
  # doesn't play nice with windows, we have to emulate a state machine to
  # read just enough off the wire to get one request at a time. Since
  # Windows cannot do a nonblocking read on a pipe, we are forced to do a
  # whole lot of `select` syscalls and keep a buffer ourselves :(
  begin
    loop do
      # This is so we don't end up calling JSON.parse on every char and
      # catch an exception. Windows can't do nonblock on pipes, so we
      # still have to do the select if we are not at the end of object
      # and don't have any buffer left
      parts = self.buf.split '}', 2
      if parts.length == 2 # [part, rest]
        message << parts[0] << '}'
        self.buf = parts[1]
        break
      elsif parts.length == 1 # [part]
        message << parts[0]
        self.buf = ''
      end

      # We would call Rex::Threadsafe directly, but that would require Rex for standalone use
      res = select([out, err], nil, nil, timeout)
      if res == nil
        # This is what we would have gotten without Rex and what `readpartial` can also raise
        raise EOFError.new
      else
        fds = res[0]
        # Preferentially drain and log stderr, EOF counts as activity, but
        # stdout might have some buffered data left, so carry on
        if fds.include?(err) && !err.eof?
          errbuf = err.readpartial(4096)
          if self.framework
            elog "Unexpected output running #{self.path}:\n#{errbuf}"
          else
            $stderr.puts errbuf
          end
        end
        if fds.include? out
          self.buf << out.readpartial(4096)
        end
      end
    end

    Message.from_module(JSON.parse(message))
  rescue JSON::ParserError
    # Probably an incomplete response, but no way to really tell. Keep trying
    # until EOF
    retry
  rescue EOFError => e
    self.running = false
  end
end

#send(message) ⇒ Object (protected)

XXX TODO non-blocking writes, check write lengths



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/msf/core/modules/external/bridge.rb', line 61

def send(message)
  input, output, err, status = ::Open3.popen3(self.env, *self.cmd)
  self.ios = [input, output, err]
  self.wait_thread = status
  # We would call Rex::Threadsafe directly, but that would require rex for standalone use
  case select(nil, [input], nil, 0.1)
  when nil
    raise "Cannot run module #{self.path}"
  when [[], [input], []]
    m = message.to_json
    write_message(input, m)
  else
    raise "Error running module #{self.path}"
  end
rescue => e
  raise handle_exception(e)
end

#success?Boolean

Returns:

  • (Boolean)


40
41
42
# File 'lib/msf/core/modules/external/bridge.rb', line 40

def success?
  self.exit_status && self.exit_status.success?
end

#threadme(&block) ⇒ Object (protected)



163
164
165
166
167
168
169
170
# File 'lib/msf/core/modules/external/bridge.rb', line 163

def threadme(&block)
  if self.framework
    # Leak as few connections as possible
    self.framework.threads.spawn("External Module #{self.path}", false, &block)
  else
    ::Thread.new &block
  end
end

#write_message(fd, json) ⇒ Object (protected)



83
84
85
# File 'lib/msf/core/modules/external/bridge.rb', line 83

def write_message(fd, json)
  fd.write(json)
end