WordPress File Upload Checker Exploit Kodlamak! (CVE-2020-35489)

Bunjo

Uzman üye
14 Ara 2020
1,587
1,886
I Won


oqk18ir.png



lbn20fm.png
2q250vr.png



pDHFoUY.png


Merhaba ben saldırı timlerinden Bunjo, bu konuda sizlere WordPress'te bulunan bir pluginin güvenlik zafiyeti içeren versiyonuna exploit yazacağız.


Diğer Exploit Konularım:

Exploit Eğitimi #8 (Shellshock (Bash Bug))

Exploit Eğitimi #7 (WordPress)
Exploit Eğitimi #6 (FTP Fuzzer)
Exploit Eğitimi #5 (SMTP Enumeration)
Exploit Eğitimi #4 (Her Türden)
Exploit Eğitimi #3 (Gerçek Uygulama)(Apache DOS)
Exploit Eğitimi #2 (FTP DOS)
Exploit Eğitimi #1 (ANON-FTP)

CVE-2020-35489

Bu güvenlik açığı, WordPress İletişim Formu 7 eklentisinin 5.3.2'den önceki sürümlerindeki bir güvenlik açığından kaynaklanmaktadır.
Yetersiz giriş doğrulama ve temizleme nedeniyle kimliği doğrulanmamış saldırganların form alanları aracılığıyla kötü amaçlı komut
dosyaları yüklemesine olanak tanır ve potansiyel olarak etkilenen sitede uzaktan kod yürütülmesine yol açar.


Genel İşleyiş:

Kullanıcı programa URL listesi verir.

Program bu URL listesini teker teker okur.

Plugin tarafından otomatik oluşturulan readme.txt dosyasının varlığını kontrol eder.

Eğer varsa bu dosya içerisinden versiyon kontrol eder.

Versiyon zafiyet içeren versiyon ile uyuşuyorsa zafiyetli olarak kayıt eder.



Gerekli Kütüphaneler:

Ruby:
require 'httparty'
require 'optparse'
require 'eventmachine'
  • httparty: HTTP istekleri yapmak için kullanılan bir gem.
  • optparse: Komut satırı seçeneklerini ayrıştırmak için kullanılan bir standart Ruby kütüphanesi.
  • eventmachine: Olay odaklı programlama için kullanılan bir Ruby kütüphanesi.

Sınıf Tanımı: WP_Upload_Check:

Ruby:
class WP_Upload_Check

Bu sınıf, verilen URL'ler temelinde WordPress eklenti sürümlerini kontrol etmekten sorumludur.

Başlatma:


Ruby:
def initialize
  @headers = {
    'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0'
  }

  @vuln_urls = []

  @params = {
    input_file: nil,
    output_file: "output_file.txt",
  }
end
  • @headers: HTTP istekleri için User-Agent başlığını içerir.
  • @vuln_urls: Savunmasız URL'leri depolamak için bir dizi.
  • @params: Giriş ve çıkış dosya yolları için varsayılan değerlere sahip bir hash.
Ruby:
def detect_plugin_version(readme)
    start_tag = "Stable tag: "
    version_start_index = readme.index(start_tag)
    raise "Invalid readme format: 'Stable tag: ' not found." if version_start_index.nil?

    version_end_index = readme.index("\n", version_start_index)
    raise "Invalid readme format: Version end tag not found." if version_end_index.nil?

    plugin_version = readme[version_start_index + start_tag.length, version_end_index - version_start_index - start_tag.length]
    raise "Plugin version is missing." if plugin_version.empty?

    plugin_version
  end

detect_plugin_version fonksiyonu başlatılır ve readme parametresiyle çağrılır. Bu parametre, eklentinin readme dosyasının içeriğini içerir.

start_tag değişkeni, eklentinin sürüm numarasının başladığı etiketi belirtir. Bu örnekte, etiket "Stable tag: " olarak tanımlanmıştır.

version_start_index değişkeni, readme metnindeki start_tag'in ilk indeksini bulur. Eğer bulunamazsa (nil ise), "Invalid readme format: 'Stable tag: ' not found." hata mesajını fırlatır.

version_end_index değişkeni, version_start_index sonrasındaki ilk satır sonunu (newline karakterini) bulur.

Eğer bulunamazsa (nil ise), "Invalid readme format: Version end tag not found." hata mesajını fırlatır.plugin_version değişkeni, readme metnindeki sürüm numarasını içerir. Bu, version_start_index'den start_tag.length kadar ilerleyerek başlar ve version_end_index - version_start_index - start_tag.length kadar devam eder.

Eğer plugin_version boşsa (yoksa), yani sürüm numarası bulunamamışsa, "Plugin version is missing." hata mesajını fırlatır.


Fonksiyon, geçerli bir sürüm numarası bulunduğunda bu numarayı döndürür. Bu sayede, eklentinin sürümü başarıyla belirlenmiş olur.

Ruby:
def control_domain(domain)
    domain.start_with?('http') ? domain : "https://#{domain}"
  end

  def opt_parse
    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 "-o", "--output_file OUTPUT_FILE" do |output_file|
          @params[:output_file] = output_file
        end
      end.parse!
    rescue Exception => exception
      puts("Error: #{exception}".red)
    end
  end

control_domain: Bu fonksiyon, bir alan adını kontrol eder. Eğer alan adı 'http' ile başlıyorsa, aynı şekilde bırakılır; ancak başlamıyorsa, "https://" ön ekini ekleyerek düzenlenir. Yani, bu fonksiyonun amacı, sağlanan bir alan adını 'http' ile başlatmak veya 'https://' ön eki eklemektir.

opt_parse:


Bu fonksiyon, OptionParser kullanarak komut satırı seçeneklerini ayrıştırır. Şu adımları içerir:

OptionParser.new ile yeni bir OptionParser örneği oluşturulur.
-i veya --input_file seçeneği kullanıcı tarafından belirlendiyse, input_file değişkenine atanır. Eğer dosya mevcut değilse, hata mesajı yazdırılır ve program çıkış yapar (exit(1)).

-o veya --output_file seçeneği kullanıcı tarafından belirlendiyse, output_file değişkenine atanır.


parse! metodu ile komut satırı seçenekleri ayrıştırılır.

Hata durumlarına karşı bir begin ve rescue bloğu kullanılarak hata durumları ele alınır ve hata mesajı yazdırılır.

Bu fonksiyonlar, kullanıcı tarafından sağlanan alan adlarını ve komut satırı seçeneklerini düzenlemek ve kontrol etmek için kullanılır.


Ruby:
def check_site(domain, output_file)
    domain = control_domain(domain)
    plugin_url = "#{domain}/wp-content/plugins/contact-form-7/readme.txt"

    begin
      response = HTTParty.get(plugin_url, headers: @headers, timeout: 3)

      if response.code == 200
        plugin_version = detect_plugin_version(response.body)
        is_vuln = Gem::Version.new(plugin_version) < Gem::Version.new('5.3.2')

        if output_file and is_vuln
          unless @vuln_urls.include?(domain)
            @vuln_urls << domain
            puts("#{plugin_url} --> Vuln".green)
            File.open(output_file, 'a+') do |file|
              file.puts(domain)
            end
          end
        else
          puts("#{domain} --> Fail".red)
        end
      end
    rescue Net::OpenTimeout
      puts("#{domain} --> Fail".red)
    rescue StandardError => err
      puts("#{domain} --> Fail".red)
    end
  end

Bu fonksiyon, verilen bir alan adındaki WordPress eklentisinin sürümünü kontrol eder ve belirli bir sürümden eski ise savunmasız kabul eder.

Domain Kontrolü ve Plugin URL Oluşturma:

domain kontrol edilir ve gerekirse 'https://' ön eki eklenir.
WordPress eklentinin readme dosyasının URL'si oluşturulur.

HTTP İstek ve Cevap Kontrolü:

Oluşturulan URL'ye HTTP GET isteği yapılır.
Aldığı cevap kontrol edilir.

Cevap Durumu ve Sürüm Kontrolü:
Eğer HTTP cevabı başarılı ise devam edilir.

Readme dosyasından eklenti sürümü çekilir.
Eklenti sürümü belirli bir sürümden küçük mü kontrol edilir.

Vulnerability Kontrolü ve Loglama:

Bir çıkış dosyası belirlenmişse ve eklenti savunmasızsa:
Daha önce eklenmemişse, savunmasız URL yazdırılır ve çıkış dosyasına eklenir.
Eğer çıkış dosyası belirlenmemişse veya eklenti savunmasız değilse, hata mesajı yazdırılır.

Hata Durumlarına Karşı İstisna Yönetimi:

Eğer HTTP isteği belirlenen sürede yanıt almazsa veya başka bir hata olursa, hata mesajı yazdırılır.

Bu fonksiyon, verilen bir alan adının eklentisinin sürümünü kontrol eder ve sonucu çıkış dosyasına veya ekrana yazdırır.

Ruby:
def print_help
    help_text = <<-'HELP_TEXT'
USAGE: ruby upload_checker.rb [options]

OPTIONS:
  -i, --input_file FILE: Define the path to the URL file.
  -o, --output_file FILE: Define the name of the output log file.
    HELP_TEXT

    puts(help_text.magenta)
  end
  • Bu fonksiyon, komut satırında kullanılabilir seçenekleri ve kullanım talimatlarını içeren bir metin bloğunu oluşturur.
  • puts(help_text.magenta) ifadesi ile bu metni ekrana yazdırır. magenta renk kodu, metni pembe renkte gösterir.
Ruby:
def parse_lines(group)
  group.each do |line|
    check_site(line.strip, @params[:output_file])
  end
end

Bu fonksiyon, bir grup satırı alır ve her bir satırı check_site fonksiyonuna geçirir. line.strip ifadesi ile her satırdaki boşlukları temizler.

Ruby:
def main
  opt_parse

  unless @params[:input_file].nil?
    lines = File.readlines(@params[:input_file])

    lines.each_slice(4) do |group_lines|
      parse_lines(group_lines)
    end

    puts("Completed.".magenta)
    EM.stop
  else
    print_help
  end
end

main fonksiyonu, betiğin ana çalışma mantığını içerir.

opt_parse fonksiyonu çağrılarak komut satırı seçenekleri ayrıştırılır.

Eğer bir giriş dosyası belirlenmişse:

Dosyadaki satırlar okunur ve her dört satırı bir grup olarak ele alır.
Her grup satırları parse_lines fonksiyonuna geçirilir.

İşlem tamamlandığında "Completed." mesajı yazdırılır ve EventMachine event loop'u kapatılır (EM.stop).


Eğer giriş dosyası belirlenmemişse, print_help fonksiyonu çağrılarak kullanım talimatları ekrana yazdırı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

  • Bu kod bloğu, String sınıfına üç ayrı renk metodunu ekler: red, green, ve magenta.
  • Bu metodlar, metin içinde bu renklere sahip çıktı üretmek için kullanılabilir. \e[31m, \e[32m, ve \e[35m ANSI renk kodları sırasıyla kırmızı, yeşil ve pembe renkleri temsil eder.
  • \e[0m ise renkli çıktıyı sıfırlar, yani sonrasında gelecek metin normal renkte olur.
Ruby:
EM.run do
  begin
    upload_checker = WP_Upload_Check.new
    upload_checker.main
  rescue => error
    puts("Error: #{error}")
  end
end


  • EM.run do ile EventMachine olay döngüsü başlatılır.
  • begin ve rescue blokları ile olası hatalara karşı bir hata yönetimi sağlanır.
  • WP_Upload_Check sınıfından bir örnek (upload_checker) oluşturulur ve main fonksiyonu çağrılır.
  • Eğer herhangi bir hata oluşursa (örneğin, WP_Upload_Check sınıfındaki main fonksiyonunda bir hata), hata mesajı ekrana yazdırılır.

Tüm Kod:
Ruby:
require 'httparty'
require 'optparse'
require 'eventmachine'

class WP_Upload_Check
  def initialize
    @headers = {
      'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0'
    }

    @vuln_urls = []

    @params = {
      input_file: nil,
      output_file: "output_file.txt",
    }
  end

  def detect_plugin_version(readme)
    start_tag = "Stable tag: "
    version_start_index = readme.index(start_tag)
    raise "Invalid readme format: 'Stable tag: ' not found." if version_start_index.nil?

    version_end_index = readme.index("\n", version_start_index)
    raise "Invalid readme format: Version end tag not found." if version_end_index.nil?

    plugin_version = readme[version_start_index + start_tag.length, version_end_index - version_start_index - start_tag.length]
    raise "Plugin version is missing." if plugin_version.empty?

    plugin_version
  end

  def control_domain(domain)
    domain.start_with?('http') ? domain : "https://#{domain}"
  end

  def opt_parse
    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 "-o", "--output_file OUTPUT_FILE" do |output_file|
          @params[:output_file] = output_file
        end
      end.parse!
    rescue Exception => exception
      puts("Error: #{exception}".red)
    end
  end

  def check_site(domain, output_file)
    domain = control_domain(domain)
    plugin_url = "#{domain}/wp-content/plugins/contact-form-7/readme.txt"

    begin
      response = HTTParty.get(plugin_url, headers: @headers, timeout: 3)

      if response.code == 200
        plugin_version = detect_plugin_version(response.body)
        is_vuln = Gem::Version.new(plugin_version) < Gem::Version.new('5.3.2')

        if output_file and is_vuln
          unless @vuln_urls.include?(domain)
            @vuln_urls << domain
            puts("#{plugin_url} --> Vuln".green)
            File.open(output_file, 'a+') do |file|
              file.puts(domain)
            end
          end
        else
          puts("#{domain} --> Fail".red)
        end
      end
    rescue Net::OpenTimeout
      puts("#{domain} --> Fail".red)
    rescue StandardError => err
      puts("#{domain} --> Fail".red)
    end
  end

  def print_help
    help_text = <<-'HELP_TEXT'
USAGE: ruby upload_checker.rb [options]

OPTIONS:
  -i, --input_file FILE: Define the path to the URL file.
  -o, --output_file FILE: Define the name of the output log file.
    HELP_TEXT

    puts(help_text.magenta)
  end

  def parse_lines(group)
    group.each do |line|
      check_site(line.strip, @params[:output_file])
    end
  end

  def main
    opt_parse

    unless @params[:input_file].nil?
      lines = File.readlines(@params[:input_file])

      lines.each_slice(4) do |group_lines|
        parse_lines(group_lines)
      end

      puts("Completed.".magenta)
      EM.stop
    else
      print_help
    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
  begin
    upload_checker = WP_Upload_Check.new
    upload_checker.main
  rescue => error
    puts("Error: #{error}")
  end
end


2qh3j49.png


Emeğe karşılık konuyu beğenebilirsiniz. Örnek olması amacıyla açılmıştır.

Saatin biraz geç olması dolayısıyla gözden kaçan şeyler olabilir :)

İyi forumlar...

Github: GitHub - thebunjo/CVE-2020-35489


 
Son düzenleme:

Bunjo

Uzman üye
14 Ara 2020
1,587
1,886
I Won
Teşekkür ederim 😁
Eski logoyu görünce duygulandım, eline sağlık...
Teşekkür ederim
Süper, Eline Sağlık Cano.
❤️
Elinize emeğinize sağlık :)
💘🙂
Çok güzel, eline sağlık 👍
❤️
Eline sağlık Bunjom, şimdi sırada işleme girip zafiyetlerin bulunduğu sitelere up.php yükleme zamanı 👍🙂
Teşekkürler hocam 😁💘
 

teux

Katılımcı Üye
23 Ocak 2023
959
1,329


oqk18ir.png



lbn20fm.png
2q250vr.png



pDHFoUY.png


Merhaba ben saldırı timlerinden Bunjo, bu konuda sizlere WordPress'te bulunan bir pluginin güvenlik zafiyeti içeren versiyonuna exploit yazacağız.


Diğer Exploit Konularım:

Exploit Eğitimi #8 (Shellshock (Bash Bug))

Exploit Eğitimi #7 (WordPress)
Exploit Eğitimi #6 (FTP Fuzzer)
Exploit Eğitimi #5 (SMTP Enumeration)
Exploit Eğitimi #4 (Her Türden)
Exploit Eğitimi #3 (Gerçek Uygulama)(Apache DOS)
Exploit Eğitimi #2 (FTP DOS)
Exploit Eğitimi #1 (ANON-FTP)

CVE-2020-35489

Bu güvenlik açığı, WordPress İletişim Formu 7 eklentisinin 5.3.2'den önceki sürümlerindeki bir güvenlik açığından kaynaklanmaktadır.
Yetersiz giriş doğrulama ve temizleme nedeniyle kimliği doğrulanmamış saldırganların form alanları aracılığıyla kötü amaçlı komut
dosyaları yüklemesine olanak tanır ve potansiyel olarak etkilenen sitede uzaktan kod yürütülmesine yol açar.


Genel İşleyiş:

Kullanıcı programa URL listesi verir.

Program bu URL listesini teker teker okur.

Plugin tarafından otomatik oluşturulan readme.txt dosyasının varlığını kontrol eder.

Eğer varsa bu dosya içerisinden versiyon kontrol eder.

Versiyon zafiyet içeren versiyon ile uyuşuyorsa zafiyetli olarak kayıt eder.



Gerekli Kütüphaneler:

Ruby:
require 'httparty'
require 'optparse'
require 'eventmachine'
  • httparty: HTTP istekleri yapmak için kullanılan bir gem.
  • optparse: Komut satırı seçeneklerini ayrıştırmak için kullanılan bir standart Ruby kütüphanesi.
  • eventmachine: Olay odaklı programlama için kullanılan bir Ruby kütüphanesi.

Sınıf Tanımı: WP_Upload_Check:

Ruby:
class WP_Upload_Check

Bu sınıf, verilen URL'ler temelinde WordPress eklenti sürümlerini kontrol etmekten sorumludur.

Başlatma:


Ruby:
def initialize
  @headers = {
    'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0'
  }

  @vuln_urls = []

  @params = {
    input_file: nil,
    output_file: "output_file.txt",
  }
end
  • @headers: HTTP istekleri için User-Agent başlığını içerir.
  • @vuln_urls: Savunmasız URL'leri depolamak için bir dizi.
  • @params: Giriş ve çıkış dosya yolları için varsayılan değerlere sahip bir hash.
Ruby:
def detect_plugin_version(readme)
    start_tag = "Stable tag: "
    version_start_index = readme.index(start_tag)
    raise "Invalid readme format: 'Stable tag: ' not found." if version_start_index.nil?

    version_end_index = readme.index("\n", version_start_index)
    raise "Invalid readme format: Version end tag not found." if version_end_index.nil?

    plugin_version = readme[version_start_index + start_tag.length, version_end_index - version_start_index - start_tag.length]
    raise "Plugin version is missing." if plugin_version.empty?

    plugin_version
  end

detect_plugin_version fonksiyonu başlatılır ve readme parametresiyle çağrılır. Bu parametre, eklentinin readme dosyasının içeriğini içerir.

start_tag değişkeni, eklentinin sürüm numarasının başladığı etiketi belirtir. Bu örnekte, etiket "Stable tag: " olarak tanımlanmıştır.

version_start_index değişkeni, readme metnindeki start_tag'in ilk indeksini bulur. Eğer bulunamazsa (nil ise), "Invalid readme format: 'Stable tag: ' not found." hata mesajını fırlatır.

version_end_index değişkeni, version_start_index sonrasındaki ilk satır sonunu (newline karakterini) bulur.

Eğer bulunamazsa (nil ise), "Invalid readme format: Version end tag not found." hata mesajını fırlatır.plugin_version değişkeni, readme metnindeki sürüm numarasını içerir. Bu, version_start_index'den start_tag.length kadar ilerleyerek başlar ve version_end_index - version_start_index - start_tag.length kadar devam eder.

Eğer plugin_version boşsa (yoksa), yani sürüm numarası bulunamamışsa, "Plugin version is missing." hata mesajını fırlatır.


Fonksiyon, geçerli bir sürüm numarası bulunduğunda bu numarayı döndürür. Bu sayede, eklentinin sürümü başarıyla belirlenmiş olur.

Ruby:
def control_domain(domain)
    domain.start_with?('http') ? domain : "https://#{domain}"
  end

  def opt_parse
    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 "-o", "--output_file OUTPUT_FILE" do |output_file|
          @params[:output_file] = output_file
        end
      end.parse!
    rescue Exception => exception
      puts("Error: #{exception}".red)
    end
  end

control_domain: Bu fonksiyon, bir alan adını kontrol eder. Eğer alan adı 'http' ile başlıyorsa, aynı şekilde bırakılır; ancak başlamıyorsa, "https://" ön ekini ekleyerek düzenlenir. Yani, bu fonksiyonun amacı, sağlanan bir alan adını 'http' ile başlatmak veya 'https://' ön eki eklemektir.

opt_parse:


Bu fonksiyon, OptionParser kullanarak komut satırı seçeneklerini ayrıştırır. Şu adımları içerir:

OptionParser.new ile yeni bir OptionParser örneği oluşturulur.
-i veya --input_file seçeneği kullanıcı tarafından belirlendiyse, input_file değişkenine atanır. Eğer dosya mevcut değilse, hata mesajı yazdırılır ve program çıkış yapar (exit(1)).

-o veya --output_file seçeneği kullanıcı tarafından belirlendiyse, output_file değişkenine atanır.


parse! metodu ile komut satırı seçenekleri ayrıştırılır.

Hata durumlarına karşı bir begin ve rescue bloğu kullanılarak hata durumları ele alınır ve hata mesajı yazdırılır.

Bu fonksiyonlar, kullanıcı tarafından sağlanan alan adlarını ve komut satırı seçeneklerini düzenlemek ve kontrol etmek için kullanılır.


Ruby:
def check_site(domain, output_file)
    domain = control_domain(domain)
    plugin_url = "#{domain}/wp-content/plugins/contact-form-7/readme.txt"

    begin
      response = HTTParty.get(plugin_url, headers: @headers, timeout: 3)

      if response.code == 200
        plugin_version = detect_plugin_version(response.body)
        is_vuln = Gem::Version.new(plugin_version) < Gem::Version.new('5.3.2')

        if output_file and is_vuln
          unless @vuln_urls.include?(domain)
            @vuln_urls << domain
            puts("#{plugin_url} --> Vuln".green)
            File.open(output_file, 'a+') do |file|
              file.puts(domain)
            end
          end
        else
          puts("#{domain} --> Fail".red)
        end
      end
    rescue Net::OpenTimeout
      puts("#{domain} --> Fail".red)
    rescue StandardError => err
      puts("#{domain} --> Fail".red)
    end
  end

Bu fonksiyon, verilen bir alan adındaki WordPress eklentisinin sürümünü kontrol eder ve belirli bir sürümden eski ise savunmasız kabul eder.

Domain Kontrolü ve Plugin URL Oluşturma:

domain kontrol edilir ve gerekirse 'https://' ön eki eklenir.
WordPress eklentinin readme dosyasının URL'si oluşturulur.

HTTP İstek ve Cevap Kontrolü:

Oluşturulan URL'ye HTTP GET isteği yapılır.
Aldığı cevap kontrol edilir.

Cevap Durumu ve Sürüm Kontrolü:
Eğer HTTP cevabı başarılı ise devam edilir.

Readme dosyasından eklenti sürümü çekilir.
Eklenti sürümü belirli bir sürümden küçük mü kontrol edilir.

Vulnerability Kontrolü ve Loglama:

Bir çıkış dosyası belirlenmişse ve eklenti savunmasızsa:
Daha önce eklenmemişse, savunmasız URL yazdırılır ve çıkış dosyasına eklenir.
Eğer çıkış dosyası belirlenmemişse veya eklenti savunmasız değilse, hata mesajı yazdırılır.

Hata Durumlarına Karşı İstisna Yönetimi:

Eğer HTTP isteği belirlenen sürede yanıt almazsa veya başka bir hata olursa, hata mesajı yazdırılır.

Bu fonksiyon, verilen bir alan adının eklentisinin sürümünü kontrol eder ve sonucu çıkış dosyasına veya ekrana yazdırır.

Ruby:
def print_help
    help_text = <<-'HELP_TEXT'
USAGE: ruby upload_checker.rb [options]

OPTIONS:
  -i, --input_file FILE: Define the path to the URL file.
  -o, --output_file FILE: Define the name of the output log file.
    HELP_TEXT

    puts(help_text.magenta)
  end
  • Bu fonksiyon, komut satırında kullanılabilir seçenekleri ve kullanım talimatlarını içeren bir metin bloğunu oluşturur.
  • puts(help_text.magenta) ifadesi ile bu metni ekrana yazdırır. magenta renk kodu, metni pembe renkte gösterir.
Ruby:
def parse_lines(group)
  group.each do |line|
    check_site(line.strip, @params[:output_file])
  end
end

Bu fonksiyon, bir grup satırı alır ve her bir satırı check_site fonksiyonuna geçirir. line.strip ifadesi ile her satırdaki boşlukları temizler.

Ruby:
def main
  opt_parse

  unless @params[:input_file].nil?
    lines = File.readlines(@params[:input_file])

    lines.each_slice(4) do |group_lines|
      parse_lines(group_lines)
    end

    puts("Completed.".magenta)
    EM.stop
  else
    print_help
  end
end

main fonksiyonu, betiğin ana çalışma mantığını içerir.

opt_parse fonksiyonu çağrılarak komut satırı seçenekleri ayrıştırılır.

Eğer bir giriş dosyası belirlenmişse:

Dosyadaki satırlar okunur ve her dört satırı bir grup olarak ele alır.
Her grup satırları parse_lines fonksiyonuna geçirilir.

İşlem tamamlandığında "Completed." mesajı yazdırılır ve EventMachine event loop'u kapatılır (EM.stop).


Eğer giriş dosyası belirlenmemişse, print_help fonksiyonu çağrılarak kullanım talimatları ekrana yazdırı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

  • Bu kod bloğu, String sınıfına üç ayrı renk metodunu ekler: red, green, ve magenta.
  • Bu metodlar, metin içinde bu renklere sahip çıktı üretmek için kullanılabilir. \e[31m, \e[32m, ve \e[35m ANSI renk kodları sırasıyla kırmızı, yeşil ve pembe renkleri temsil eder.
  • \e[0m ise renkli çıktıyı sıfırlar, yani sonrasında gelecek metin normal renkte olur.
Ruby:
EM.run do
  begin
    upload_checker = WP_Upload_Check.new
    upload_checker.main
  rescue => error
    puts("Error: #{error}")
  end
end


  • EM.run do ile EventMachine olay döngüsü başlatılır.
  • begin ve rescue blokları ile olası hatalara karşı bir hata yönetimi sağlanır.
  • WP_Upload_Check sınıfından bir örnek (upload_checker) oluşturulur ve main fonksiyonu çağrılır.
  • Eğer herhangi bir hata oluşursa (örneğin, WP_Upload_Check sınıfındaki main fonksiyonunda bir hata), hata mesajı ekrana yazdırılır.

Tüm Kod:
Ruby:
require 'httparty'
require 'optparse'
require 'eventmachine'

class WP_Upload_Check
  def initialize
    @headers = {
      'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0'
    }

    @vuln_urls = []

    @params = {
      input_file: nil,
      output_file: "output_file.txt",
    }
  end

  def detect_plugin_version(readme)
    start_tag = "Stable tag: "
    version_start_index = readme.index(start_tag)
    raise "Invalid readme format: 'Stable tag: ' not found." if version_start_index.nil?

    version_end_index = readme.index("\n", version_start_index)
    raise "Invalid readme format: Version end tag not found." if version_end_index.nil?

    plugin_version = readme[version_start_index + start_tag.length, version_end_index - version_start_index - start_tag.length]
    raise "Plugin version is missing." if plugin_version.empty?

    plugin_version
  end

  def control_domain(domain)
    domain.start_with?('http') ? domain : "https://#{domain}"
  end

  def opt_parse
    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 "-o", "--output_file OUTPUT_FILE" do |output_file|
          @params[:output_file] = output_file
        end
      end.parse!
    rescue Exception => exception
      puts("Error: #{exception}".red)
    end
  end

  def check_site(domain, output_file)
    domain = control_domain(domain)
    plugin_url = "#{domain}/wp-content/plugins/contact-form-7/readme.txt"

    begin
      response = HTTParty.get(plugin_url, headers: @headers, timeout: 3)

      if response.code == 200
        plugin_version = detect_plugin_version(response.body)
        is_vuln = Gem::Version.new(plugin_version) < Gem::Version.new('5.3.2')

        if output_file and is_vuln
          unless @vuln_urls.include?(domain)
            @vuln_urls << domain
            puts("#{plugin_url} --> Vuln".green)
            File.open(output_file, 'a+') do |file|
              file.puts(domain)
            end
          end
        else
          puts("#{domain} --> Fail".red)
        end
      end
    rescue Net::OpenTimeout
      puts("#{domain} --> Fail".red)
    rescue StandardError => err
      puts("#{domain} --> Fail".red)
    end
  end

  def print_help
    help_text = <<-'HELP_TEXT'
USAGE: ruby upload_checker.rb [options]

OPTIONS:
  -i, --input_file FILE: Define the path to the URL file.
  -o, --output_file FILE: Define the name of the output log file.
    HELP_TEXT

    puts(help_text.magenta)
  end

  def parse_lines(group)
    group.each do |line|
      check_site(line.strip, @params[:output_file])
    end
  end

  def main
    opt_parse

    unless @params[:input_file].nil?
      lines = File.readlines(@params[:input_file])

      lines.each_slice(4) do |group_lines|
        parse_lines(group_lines)
      end

      puts("Completed.".magenta)
      EM.stop
    else
      print_help
    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
  begin
    upload_checker = WP_Upload_Check.new
    upload_checker.main
  rescue => error
    puts("Error: #{error}")
  end
end


2qh3j49.png


Emeğe karşılık konuyu beğenebilirsiniz. Örnek olması amacıyla açılmıştır.

Saatin biraz geç olması dolayısıyla gözden kaçan şeyler olabilir :)

İyi forumlar...

Github: GitHub - thebunjo/CVE-2020-35489


eline emeğine sağlık
 

aslan aslan

Basın&Medya Ekibi Asistanı
1 Şub 2023
650
245


oqk18ir.png



lbn20fm.png
2q250vr.png



pDHFoUY.png


Merhaba ben saldırı timlerinden Bunjo, bu konuda sizlere WordPress'te bulunan bir pluginin güvenlik zafiyeti içeren versiyonuna exploit yazacağız.


Diğer Exploit Konularım:

Exploit Eğitimi #8 (Shellshock (Bash Bug))

Exploit Eğitimi #7 (WordPress)
Exploit Eğitimi #6 (FTP Fuzzer)
Exploit Eğitimi #5 (SMTP Enumeration)
Exploit Eğitimi #4 (Her Türden)
Exploit Eğitimi #3 (Gerçek Uygulama)(Apache DOS)
Exploit Eğitimi #2 (FTP DOS)
Exploit Eğitimi #1 (ANON-FTP)

CVE-2020-35489

Bu güvenlik açığı, WordPress İletişim Formu 7 eklentisinin 5.3.2'den önceki sürümlerindeki bir güvenlik açığından kaynaklanmaktadır.
Yetersiz giriş doğrulama ve temizleme nedeniyle kimliği doğrulanmamış saldırganların form alanları aracılığıyla kötü amaçlı komut
dosyaları yüklemesine olanak tanır ve potansiyel olarak etkilenen sitede uzaktan kod yürütülmesine yol açar.


Genel İşleyiş:

Kullanıcı programa URL listesi verir.

Program bu URL listesini teker teker okur.

Plugin tarafından otomatik oluşturulan readme.txt dosyasının varlığını kontrol eder.

Eğer varsa bu dosya içerisinden versiyon kontrol eder.

Versiyon zafiyet içeren versiyon ile uyuşuyorsa zafiyetli olarak kayıt eder.



Gerekli Kütüphaneler:

Ruby:
require 'httparty'
require 'optparse'
require 'eventmachine'
  • httparty: HTTP istekleri yapmak için kullanılan bir gem.
  • optparse: Komut satırı seçeneklerini ayrıştırmak için kullanılan bir standart Ruby kütüphanesi.
  • eventmachine: Olay odaklı programlama için kullanılan bir Ruby kütüphanesi.

Sınıf Tanımı: WP_Upload_Check:

Ruby:
class WP_Upload_Check

Bu sınıf, verilen URL'ler temelinde WordPress eklenti sürümlerini kontrol etmekten sorumludur.

Başlatma:


Ruby:
def initialize
  @headers = {
    'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0'
  }

  @vuln_urls = []

  @params = {
    input_file: nil,
    output_file: "output_file.txt",
  }
end
  • @headers: HTTP istekleri için User-Agent başlığını içerir.
  • @vuln_urls: Savunmasız URL'leri depolamak için bir dizi.
  • @params: Giriş ve çıkış dosya yolları için varsayılan değerlere sahip bir hash.
Ruby:
def detect_plugin_version(readme)
    start_tag = "Stable tag: "
    version_start_index = readme.index(start_tag)
    raise "Invalid readme format: 'Stable tag: ' not found." if version_start_index.nil?

    version_end_index = readme.index("\n", version_start_index)
    raise "Invalid readme format: Version end tag not found." if version_end_index.nil?

    plugin_version = readme[version_start_index + start_tag.length, version_end_index - version_start_index - start_tag.length]
    raise "Plugin version is missing." if plugin_version.empty?

    plugin_version
  end

detect_plugin_version fonksiyonu başlatılır ve readme parametresiyle çağrılır. Bu parametre, eklentinin readme dosyasının içeriğini içerir.

start_tag değişkeni, eklentinin sürüm numarasının başladığı etiketi belirtir. Bu örnekte, etiket "Stable tag: " olarak tanımlanmıştır.

version_start_index değişkeni, readme metnindeki start_tag'in ilk indeksini bulur. Eğer bulunamazsa (nil ise), "Invalid readme format: 'Stable tag: ' not found." hata mesajını fırlatır.

version_end_index değişkeni, version_start_index sonrasındaki ilk satır sonunu (newline karakterini) bulur.

Eğer bulunamazsa (nil ise), "Invalid readme format: Version end tag not found." hata mesajını fırlatır.plugin_version değişkeni, readme metnindeki sürüm numarasını içerir. Bu, version_start_index'den start_tag.length kadar ilerleyerek başlar ve version_end_index - version_start_index - start_tag.length kadar devam eder.

Eğer plugin_version boşsa (yoksa), yani sürüm numarası bulunamamışsa, "Plugin version is missing." hata mesajını fırlatır.


Fonksiyon, geçerli bir sürüm numarası bulunduğunda bu numarayı döndürür. Bu sayede, eklentinin sürümü başarıyla belirlenmiş olur.

Ruby:
def control_domain(domain)
    domain.start_with?('http') ? domain : "https://#{domain}"
  end

  def opt_parse
    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 "-o", "--output_file OUTPUT_FILE" do |output_file|
          @params[:output_file] = output_file
        end
      end.parse!
    rescue Exception => exception
      puts("Error: #{exception}".red)
    end
  end

control_domain: Bu fonksiyon, bir alan adını kontrol eder. Eğer alan adı 'http' ile başlıyorsa, aynı şekilde bırakılır; ancak başlamıyorsa, "https://" ön ekini ekleyerek düzenlenir. Yani, bu fonksiyonun amacı, sağlanan bir alan adını 'http' ile başlatmak veya 'https://' ön eki eklemektir.

opt_parse:


Bu fonksiyon, OptionParser kullanarak komut satırı seçeneklerini ayrıştırır. Şu adımları içerir:

OptionParser.new ile yeni bir OptionParser örneği oluşturulur.
-i veya --input_file seçeneği kullanıcı tarafından belirlendiyse, input_file değişkenine atanır. Eğer dosya mevcut değilse, hata mesajı yazdırılır ve program çıkış yapar (exit(1)).

-o veya --output_file seçeneği kullanıcı tarafından belirlendiyse, output_file değişkenine atanır.


parse! metodu ile komut satırı seçenekleri ayrıştırılır.

Hata durumlarına karşı bir begin ve rescue bloğu kullanılarak hata durumları ele alınır ve hata mesajı yazdırılır.

Bu fonksiyonlar, kullanıcı tarafından sağlanan alan adlarını ve komut satırı seçeneklerini düzenlemek ve kontrol etmek için kullanılır.


Ruby:
def check_site(domain, output_file)
    domain = control_domain(domain)
    plugin_url = "#{domain}/wp-content/plugins/contact-form-7/readme.txt"

    begin
      response = HTTParty.get(plugin_url, headers: @headers, timeout: 3)

      if response.code == 200
        plugin_version = detect_plugin_version(response.body)
        is_vuln = Gem::Version.new(plugin_version) < Gem::Version.new('5.3.2')

        if output_file and is_vuln
          unless @vuln_urls.include?(domain)
            @vuln_urls << domain
            puts("#{plugin_url} --> Vuln".green)
            File.open(output_file, 'a+') do |file|
              file.puts(domain)
            end
          end
        else
          puts("#{domain} --> Fail".red)
        end
      end
    rescue Net::OpenTimeout
      puts("#{domain} --> Fail".red)
    rescue StandardError => err
      puts("#{domain} --> Fail".red)
    end
  end

Bu fonksiyon, verilen bir alan adındaki WordPress eklentisinin sürümünü kontrol eder ve belirli bir sürümden eski ise savunmasız kabul eder.

Domain Kontrolü ve Plugin URL Oluşturma:

domain kontrol edilir ve gerekirse 'https://' ön eki eklenir.
WordPress eklentinin readme dosyasının URL'si oluşturulur.

HTTP İstek ve Cevap Kontrolü:

Oluşturulan URL'ye HTTP GET isteği yapılır.
Aldığı cevap kontrol edilir.

Cevap Durumu ve Sürüm Kontrolü:
Eğer HTTP cevabı başarılı ise devam edilir.

Readme dosyasından eklenti sürümü çekilir.
Eklenti sürümü belirli bir sürümden küçük mü kontrol edilir.

Vulnerability Kontrolü ve Loglama:

Bir çıkış dosyası belirlenmişse ve eklenti savunmasızsa:
Daha önce eklenmemişse, savunmasız URL yazdırılır ve çıkış dosyasına eklenir.
Eğer çıkış dosyası belirlenmemişse veya eklenti savunmasız değilse, hata mesajı yazdırılır.

Hata Durumlarına Karşı İstisna Yönetimi:

Eğer HTTP isteği belirlenen sürede yanıt almazsa veya başka bir hata olursa, hata mesajı yazdırılır.

Bu fonksiyon, verilen bir alan adının eklentisinin sürümünü kontrol eder ve sonucu çıkış dosyasına veya ekrana yazdırır.

Ruby:
def print_help
    help_text = <<-'HELP_TEXT'
USAGE: ruby upload_checker.rb [options]

OPTIONS:
  -i, --input_file FILE: Define the path to the URL file.
  -o, --output_file FILE: Define the name of the output log file.
    HELP_TEXT

    puts(help_text.magenta)
  end
  • Bu fonksiyon, komut satırında kullanılabilir seçenekleri ve kullanım talimatlarını içeren bir metin bloğunu oluşturur.
  • puts(help_text.magenta) ifadesi ile bu metni ekrana yazdırır. magenta renk kodu, metni pembe renkte gösterir.
Ruby:
def parse_lines(group)
  group.each do |line|
    check_site(line.strip, @params[:output_file])
  end
end

Bu fonksiyon, bir grup satırı alır ve her bir satırı check_site fonksiyonuna geçirir. line.strip ifadesi ile her satırdaki boşlukları temizler.

Ruby:
def main
  opt_parse

  unless @params[:input_file].nil?
    lines = File.readlines(@params[:input_file])

    lines.each_slice(4) do |group_lines|
      parse_lines(group_lines)
    end

    puts("Completed.".magenta)
    EM.stop
  else
    print_help
  end
end

main fonksiyonu, betiğin ana çalışma mantığını içerir.

opt_parse fonksiyonu çağrılarak komut satırı seçenekleri ayrıştırılır.

Eğer bir giriş dosyası belirlenmişse:

Dosyadaki satırlar okunur ve her dört satırı bir grup olarak ele alır.
Her grup satırları parse_lines fonksiyonuna geçirilir.

İşlem tamamlandığında "Completed." mesajı yazdırılır ve EventMachine event loop'u kapatılır (EM.stop).


Eğer giriş dosyası belirlenmemişse, print_help fonksiyonu çağrılarak kullanım talimatları ekrana yazdırı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

  • Bu kod bloğu, String sınıfına üç ayrı renk metodunu ekler: red, green, ve magenta.
  • Bu metodlar, metin içinde bu renklere sahip çıktı üretmek için kullanılabilir. \e[31m, \e[32m, ve \e[35m ANSI renk kodları sırasıyla kırmızı, yeşil ve pembe renkleri temsil eder.
  • \e[0m ise renkli çıktıyı sıfırlar, yani sonrasında gelecek metin normal renkte olur.
Ruby:
EM.run do
  begin
    upload_checker = WP_Upload_Check.new
    upload_checker.main
  rescue => error
    puts("Error: #{error}")
  end
end


  • EM.run do ile EventMachine olay döngüsü başlatılır.
  • begin ve rescue blokları ile olası hatalara karşı bir hata yönetimi sağlanır.
  • WP_Upload_Check sınıfından bir örnek (upload_checker) oluşturulur ve main fonksiyonu çağrılır.
  • Eğer herhangi bir hata oluşursa (örneğin, WP_Upload_Check sınıfındaki main fonksiyonunda bir hata), hata mesajı ekrana yazdırılır.

Tüm Kod:
Ruby:
require 'httparty'
require 'optparse'
require 'eventmachine'

class WP_Upload_Check
  def initialize
    @headers = {
      'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0'
    }

    @vuln_urls = []

    @params = {
      input_file: nil,
      output_file: "output_file.txt",
    }
  end

  def detect_plugin_version(readme)
    start_tag = "Stable tag: "
    version_start_index = readme.index(start_tag)
    raise "Invalid readme format: 'Stable tag: ' not found." if version_start_index.nil?

    version_end_index = readme.index("\n", version_start_index)
    raise "Invalid readme format: Version end tag not found." if version_end_index.nil?

    plugin_version = readme[version_start_index + start_tag.length, version_end_index - version_start_index - start_tag.length]
    raise "Plugin version is missing." if plugin_version.empty?

    plugin_version
  end

  def control_domain(domain)
    domain.start_with?('http') ? domain : "https://#{domain}"
  end

  def opt_parse
    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 "-o", "--output_file OUTPUT_FILE" do |output_file|
          @params[:output_file] = output_file
        end
      end.parse!
    rescue Exception => exception
      puts("Error: #{exception}".red)
    end
  end

  def check_site(domain, output_file)
    domain = control_domain(domain)
    plugin_url = "#{domain}/wp-content/plugins/contact-form-7/readme.txt"

    begin
      response = HTTParty.get(plugin_url, headers: @headers, timeout: 3)

      if response.code == 200
        plugin_version = detect_plugin_version(response.body)
        is_vuln = Gem::Version.new(plugin_version) < Gem::Version.new('5.3.2')

        if output_file and is_vuln
          unless @vuln_urls.include?(domain)
            @vuln_urls << domain
            puts("#{plugin_url} --> Vuln".green)
            File.open(output_file, 'a+') do |file|
              file.puts(domain)
            end
          end
        else
          puts("#{domain} --> Fail".red)
        end
      end
    rescue Net::OpenTimeout
      puts("#{domain} --> Fail".red)
    rescue StandardError => err
      puts("#{domain} --> Fail".red)
    end
  end

  def print_help
    help_text = <<-'HELP_TEXT'
USAGE: ruby upload_checker.rb [options]

OPTIONS:
  -i, --input_file FILE: Define the path to the URL file.
  -o, --output_file FILE: Define the name of the output log file.
    HELP_TEXT

    puts(help_text.magenta)
  end

  def parse_lines(group)
    group.each do |line|
      check_site(line.strip, @params[:output_file])
    end
  end

  def main
    opt_parse

    unless @params[:input_file].nil?
      lines = File.readlines(@params[:input_file])

      lines.each_slice(4) do |group_lines|
        parse_lines(group_lines)
      end

      puts("Completed.".magenta)
      EM.stop
    else
      print_help
    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
  begin
    upload_checker = WP_Upload_Check.new
    upload_checker.main
  rescue => error
    puts("Error: #{error}")
  end
end


2qh3j49.png


Emeğe karşılık konuyu beğenebilirsiniz. Örnek olması amacıyla açılmıştır.

Saatin biraz geç olması dolayısıyla gözden kaçan şeyler olabilir :)

İyi forumlar...

Github: GitHub - thebunjo/CVE-2020-35489


Eline sağlık.
 
Üst

Turkhackteam.org internet sitesi 5651 sayılı kanun’un 2. maddesinin 1. fıkrasının m) bendi ile aynı kanunun 5. maddesi kapsamında "Yer Sağlayıcı" konumundadır. İçerikler ön onay olmaksızın tamamen kullanıcılar tarafından oluşturulmaktadır. Turkhackteam.org; Yer sağlayıcı olarak, kullanıcılar tarafından oluşturulan içeriği ya da hukuka aykırı paylaşımı kontrol etmekle ya da araştırmakla yükümlü değildir. Türkhackteam saldırı timleri Türk sitelerine hiçbir zararlı faaliyette bulunmaz. Türkhackteam üyelerinin yaptığı bireysel hack faaliyetlerinden Türkhackteam sorumlu değildir. Sitelerinize Türkhackteam ismi kullanılarak hack faaliyetinde bulunulursa, site-sunucu erişim loglarından bu faaliyeti gerçekleştiren ip adresini tespit edip diğer kanıtlarla birlikte savcılığa suç duyurusunda bulununuz.