Talos Vulnerability Report

TALOS-2018-0690

Netgate pfSense system_advanced_misc.php powerd_normal_mode remote command injection vulnerability

December 3, 2018
CVE Number

CVE-2018-4019, CVE-2018-4020, CVE-2018-4021

Summary

Three exploitable command injection vulnerabilities exists in the way Netgate pfSense CE 2.4.4-RELEASE processes the parameters of a specific POST request. The attacker can exploit this and gain the ability to execute arbitrary commands on the system. An attacker needs to be able to send authenticated POST requests to the administration web interface in order to abuse these vulnerabilities.

Tested Versions

Netgate pfSense CE 2.4.4-RELEASE

Product URLs

https://www.pfsense.org/

CVSSv3 Score

7.2 - CVSS:3.0/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H

CWE

CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')

Details

PfSense is a popular open-source firewall and router distribution based on the FreeBSD operating system. When processing requests to /system_advanced_misc.php, the firewall does not properly sanitize the certain POST parameters.

CVE-2018-4019 powerd_normal_mode POST parameter

The firewall does not properly sanitize powerd_normal_mode POST parameter:

<?php

if ($_POST) {
unset($input_errors);
$pconfig = $_POST;

if ($_POST['powerd_enable'] == "yes") {
$config['system']['powerd_enable'] = true;
 else 
unset($config['system']['powerd_enable']);

$config['system']['powerd_normal_mode'] = $_POST['powerd_normal_mode'];

activate_powerd();

As you can see above, system_advanced_misc.php will set $config['system']['powerd_enable'] to true if $_POST['powerd_enable'] is set to yes.

It will also set the $config['system']['powerd_normal_mode'] to the value of the POST parameter powerd_normal_mode.

It will then call activate_powerd():

function activate_powerd() {
global $config, $g;

if (is_process_running("powerd")) {
 exec("/usr/bin/killall powerd");

if (isset($config['system']['powerd_enable'])) {
 $ac_mode = "hadp";
 if (!empty($config['system']['powerd_ac_mode'])) {
  $ac_mode = $config['system']['powerd_ac_mode'];


$battery_mode = "hadp";
 if (!empty($config['system']['powerd_battery_mode'])) {
 $battery_mode = $config['system']['powerd_battery_mode'];


 $normal_mode = "hadp";
 if (!empty($config['system']['powerd_normal_mode'])) {
 $normal_mode = $config['system']['powerd_normal_mode'];


mwexec("/usr/sbin/powerd -b $battery_mode -a $ac_mode -n $normal_mode");

As you can see above, activate_powerd() will set $normal_mode to the value in $config['system']['powerd_normal_mode'] if $config['system']['powerd_enable'] is set to true.

It will then concatenate $normal_mode (which came from the POST parameter powerd_normal_mode) into a system shell command and execute it with mwexec():

/* wrapper for exec()
 Executes in background or foreground.
 For background execution, returns PID of background process to allow calling code control */
function mwexec($command, $nologentry = false, $clearsigmask = false, $background = false) {
global $g;
$retval = 0;

$outputarray = array();
exec("$command 2>&1", $outputarray, $retval);

return $retval;

Thus, command injection is possible in the powerd_normal_mode parameter.

CVE-2018-4020 powerd_ac_mode POST parameter

Similarly, firewall does not properly sanitize powerd_ac_mode POST parameter:

<?php

if ($_POST) {
unset($input_errors);
$pconfig = $_POST;

 if ($_POST['powerd_enable'] == "yes") {
 $config['system']['powerd_enable'] = true;
  else {
  unset($config['system']['powerd_enable']);


$config['system']['powerd_ac_mode'] = $_POST['powerd_ac_mode'];

activate_powerd();

As you can see above, system_advanced_misc.php will set $config['system']['powerd_enable'] to true if $_POST['powerd_enable'] is set to yes. It will also set the $config['system']['powerd_ac_mode'] to the value of the POST parameter powerd_ac_mode. It will then call activate_powerd(). Activate_powerd() will set $ac_mode to the value in $config['system']['powerd_ac_mode'] if $config['system']['powerd_enable'] is set to true. It will then concatenate $ac_mode (which came from the POST parameter powerd_ac_mode) into a system shell command and execute it with mwexec(). Thus, command injection is possible in the powerd_ac_mode parameter.

CVE-2018-4021 powerd_battery_mode POST parameter

Same is true for powerd_battery_mode POST parameter:

<?php

if ($_POST) {
unset($input_errors);
$pconfig = $_POST;

if ($_POST['powerd_enable'] == "yes") {
$config['system']['powerd_enable'] = true;
else {
unset($config['system']['powerd_enable']);

$config['system']['powerd_battery_mode'] = $_POST['powerd_battery_mode'];

activate\_powerd();

As you can see above, system_advanced_misc.php will set $config['system']['powerd_enable'] to true if $_POST['powerd_enable'] is set to yes. It will also set the $config['system']['powerd_battery_mode'] to the value of the POST parameter powerd_battery_mode. It will then call activate_powerd(). activate_powerd() will set $battery_mode to the value in $config['system']['powerd_battery_mode'] if $config['system']['powerd_enable'] is set to true. It will then concatenate $battery_mode (which came from the POST parameter powerd_battery_mode) into a system shell command and execute it with mwexec(). Thus, command injection is possible in the powerd_battery_mode parameter.

Timeline

2018-10-23 - Vendor Disclosure
2018-12-03 - Public Release

Credit

Discovered by Brandon Stultz of Cisco Talos.