Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/msf/core/auxiliary/redis.rb
Views: 11784
# -*- coding: binary -*-1module Msf2###3#4# This module provides methods for working with redis5#6###7module Auxiliary::Redis8include Msf::Exploit::Remote::Tcp9include Auxiliary::Scanner10include Auxiliary::Report1112#13# Initializes an instance of an auxiliary module that interacts with Redis14#15def initialize(info = {})16super17register_options(18[19Opt::RPORT(6379),20OptString.new('PASSWORD', [false, 'Redis password for authentication test', 'foobared'])21]22)2324register_advanced_options(25[26OptInt.new('READ_TIMEOUT', [true, 'Seconds to wait while reading redis responses', 2])27]28)29end3031def read_timeout32datastore['READ_TIMEOUT']33end3435def report_redis(version)36report_service(37host: rhost,38port: rport,39proto: 'tcp',40name: 'redis',41info: "version #{version}"42)43end4445def redis_command(*commands)46command_string = printable_redis_response(commands.join(' '))47unless (command_response = send_redis_command(*commands))48vprint_error("No response to '#{command_string}'")49return50end51if (match = authentication_required?(command_response))52auth_response = match[:auth_response]5354fail_with(::Msf::Module::Failure::BadConfig, "#{peer} requires authentication but Password unset") unless datastore['Password']55vprint_status("Requires authentication (#{printable_redis_response(auth_response, false)})")5657if (auth_response = send_redis_command('AUTH', datastore['PASSWORD']))58unless auth_response =~ Rex::Proto::Redis::Base::Constants::OKAY59vprint_error("Authentication failure: #{printable_redis_response(auth_response)}")60return61end62vprint_status("Authenticated")63unless (command_response = send_redis_command(*commands))64vprint_error("No response to '#{command_string}'")65return66end67else68vprint_status("Authentication failed; no response")69return70end71end7273vprint_status("Redis command '#{command_string}' got '#{printable_redis_response(command_response)}'")74command_response75end7677def parse_redis_response(response)78parser = RESPParser.new(response)79parser.parse80end8182def printable_redis_response(response_data, convert_whitespace = true)83Rex::Text.ascii_safe_hex(response_data, convert_whitespace)84end8586private8788# Verifies whether the response indicates if authentication is required89# @return [RESPParser] Returns a matched response if a hit is there; otherwise nil.90def authentication_required?(response)91response.match(Rex::Proto::Redis::Base::Constants::AUTHENTICATION_REQUIRED) ||92response.match(Rex::Proto::Redis::Version6::Constants::AUTHENTICATION_REQUIRED)93end9495def redis_proto(command_parts)96return if command_parts.blank?97command = "*#{command_parts.length}\r\n"98command_parts.each do |c|99command << "$#{c.length}\r\n#{c}\r\n"100end101command102end103104def send_redis_command(*command_parts)105sock.put(redis_proto(command_parts))106command_response = sock.get(read_timeout)107return unless command_response108command_response.strip109end110111class RESPParser112113LINE_BREAK = "\r\n"114115def initialize(data)116@raw_data = data117@counter = 0118end119120def parse121@counter = 0122parse_next123end124125def data_at_counter126@raw_data[@counter..-1]127end128129def parse_resp_array130# Read array length131unless /\A\*(?<arr_len>\d+)(\r|$)/ =~ data_at_counter132raise "RESP parsing error in array"133end134135@counter += (1 + arr_len.length)136137if data_at_counter.start_with?(LINE_BREAK)138@counter += LINE_BREAK.length139end140141arr_len = arr_len.to_i142143result = []144for index in 1..arr_len do145element = parse_next146result.append(element)147end148result149end150151def parse_simple_string152str_end = data_at_counter.index(LINE_BREAK)153str_end = str_end.to_i154result = data_at_counter[1..str_end - 1]155@counter += str_end156@counter += 2 # Skip over next CLRF157result158end159160def parse_bulk_string161unless /\A\$(?<str_len>[-\d]+)(\r|$)/ =~ data_at_counter162raise "RESP parsing error in bulk string"163end164165@counter += (1 + str_len.length)166str_len = str_len.to_i167168if data_at_counter.start_with?(LINE_BREAK)169@counter += LINE_BREAK.length170end171172result = nil173if str_len != -1174result = data_at_counter[0..str_len - 1]175@counter += str_len176@counter += 2 # Skip over next CLRF177end178result179end180181182def parse_next183case data_at_counter[0]184when "*"185parse_resp_array186when "+"187parse_simple_string188when "$"189parse_bulk_string190else191raise "RESP parsing error: " + data_at_counter192end193end194end195end196end197198199