xmlrpc.php: skylė, kuri šiuo metu atvira 80% WordPress svetainių.
Sekmadienis, 2 valanda nakties. WordPress parduotuvės serveris pradeda strigti. PHP-FPM pasiekia darbuotojų limitą, CPU ties 100%, svetainė neatsako. Atidarai access.log — ir matai štai tai:
185.220.101.42 - - [26/May/2026:02:13:44 +0300] "POST /xmlrpc.php HTTP/1.1" 200 674
185.220.101.43 - - [26/May/2026:02:13:44 +0300] "POST /xmlrpc.php HTTP/1.1" 200 674
185.220.101.44 - - [26/May/2026:02:13:44 +0300] "POST /xmlrpc.php HTTP/1.1" 200 674
185.220.101.45 - - [26/May/2026:02:13:44 +0300] "POST /xmlrpc.php HTTP/1.1" 200 674
Tūkstančiai eilučių per sekundę. Skirtingi IP adresai, vienas tikslas. Tai ne DDoS ataka prieš tinklo pralaidumą — tai ataka prieš xmlrpc.php, o serveris sąžiningai apdoroja kiekvieną užklausą, švaistydamas CPU procesoriaus galią PHP vykdymui.
Jei administruoji WordPress svetaines ir dar neuždarei xmlrpc.php — skaityk toliau.
KAS YRA xmlrpc.php IR KODĖL JIS APSKRITAI EGZISTUOJA
XML-RPC — tai nuotolinio procedūrų kvietimo protokolas per HTTP, atsiradęs dar 1998 metais. WordPress jį pridėjo tam, kad trečiųjų šalių programos galėtų valdyti svetainę be naršyklės: skelbti įrašus iš Windows Live Writer, sinchronizuoti turinį per mobiliąsias programėles, integruotis su išoriniais servisais.
Užklausa per xmlrpc.php atrodo taip:
POST /xmlrpc.php HTTP/1.1
Host: example.com
Content-Type: text/xml
<?xml version="1.0"?>
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value>admin</value></param>
<param><value>password123</value></param>
</params>
</methodCall>
WordPress atsako — ir teisingų prisijungimo duomenų atveju — suteikia pilną prieigą prie svetainės.
Patikrinti, ar xmlrpc.php atviras tavo svetainėje, paprasta:
curl -s https://example.com/xmlrpc.php
Jei atsakyme matai:
XML-RPC server accepts POST requests only.
— sveikiname, xmlrpc.php aktyvus ir pasiekiamas iš išorės. Būtent šis atsakymas yra tai, ką botai naudoja kaip žvalgybą.
TRYS BŪDAI SULAUŽYTI SVETAINĘ PER xmlrpc.php
1. Brutalios jėgos ataka su amplifikacija per system.multicall
Klasikinę brutalios jėgos ataką prieš wp-login.php lengva blokuoti — viena užklausa, vienas bandymas. Su xmlrpc.php viskas įdomiau: metodas system.multicall leidžia supakuoti kelis kvietimus į vieną HTTP užklausą.
Iki WordPress 4.4 viena užklausa galėjo turėti tūkstančius slaptažodžio spėjimo bandymų. Gruodžio 2015 m. pataisymas privertė WordPress nutraukti paketą po pirmojo nesėkmingo autentifikavimo — tačiau ataka nedingo. Botai prisitaikė: dabar jie siunčia šimtus nuoseklių užklausų iš skirtingų botnet IP adresų, apeinant IP pagrindu veikiantį greičio limitavimą.
Paketo turinys atrodo taip:
<methodCall>
<methodName>system.multicall</methodName>
<params>
<param><value><array><data>
<value><struct>
<member>
<name>methodName</name>
<value>wp.getUsersBlogs</value>
</member>
<member>
<name>params</name>
<value><array><data>
<value>admin</value>
<value>password1</value>
</data></array></value>
</member>
</struct></value>
<!-- dar 499 bandymų -->
</data></array></value></param>
</params>
</methodCall>
Rezultatas: kiekviena užklausa yra ne vienas bandymas, o dešimtys. PHP darbuotojas užimtas visą tą laiką. Net ir su WordPress pataisymu — serverio apkrova kelis kartus didesnė nei nuo atakos prieš wp-login.php.
2. DDoS amplifikacija per pingback
Tai ataka prieš kitas svetaines — naudojant tavo serverį kaip ginklą. Užpuolikas siunčia į tavo (pažeidžiamą) WordPress užklausą:
<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param><value><string>http://AUKA.com/</string></value></param>
<param><value><string>https://tavo-svetaine.com/bet-koks-irasas/</string></value></param>
</params>
</methodCall>
Tavo WordPress paklusniai siunčia HTTP užklausą į aukos svetainę. Jei tokių WordPress svetainių yra tūkstantis — auka vienu metu gauna tūkstantį užklausų. Tavo serveris tampa DDoS atakos dalyviu, o tavo IP patenka į blokavimo sąrašus.
Būtent taip buvo atakuotas JAV Gynybos departamento (DoD) svetainių tinklas — per neuždarytus xmlrpc.php jų pačių WordPress ištekliuose.
3. Vidinių tinklų portų skenavimas (SSRF per pingback)
Pingback priima bet kokius URL, įskaitant vidinius adresus. Užpuolikas gali skeanuoti vidinę infrastruktūrą per tavo serverį:
<!-- bandymas pasiekti vidinius servisus -->
<value><string>http://192.168.1.1:8080/</string></value>
<value><string>http://10.0.0.1:3306/</string></value>
Pagal atsako laiko skirtumą ir klaidos kodą galima nustatyti, kurie portai atidaryti viduje.
KAIP UŽDARYTI: TRYS APSAUGOS LYGIAI
1 lygis: nginx — blokuoti serverio lygmeniu
Tai greičiausias ir efektyviausias būdas. Užklausa apskritai nepasiekia PHP.
Jei xmlrpc.php visai nereikalingas (dauguma atvejų):
# nginx konfigūracijos server{} bloke
location = /xmlrpc.php {
deny all;
access_log off;
log_not_found off;
}
Jei prieiga reikalinga tik iš konkrečių IP adresų (pvz., Jetpack ar WooCommerce mobilioji programėlė):
location = /xmlrpc.php {
allow 198.51.100.10; # tavo mobiliosios programėlės IP
allow 198.51.100.11; # Jetpack serverių IP
deny all;
access_log off;
}
Po pakeitimų — patikrinti konfigūraciją ir perkrauti:
nginx -t && systemctl reload nginx
Patikrinti, ar blokavimas veikia:
curl -I https://example.com/xmlrpc.php
# Laukiamas atsakymas: HTTP/1.1 403 Forbidden
2 lygis: fail2ban — drausti tuos, kurie vis tiek bando
Net ir blokuojant nginx lygmeniu — botai toliau beldžiasi, gauna 403 ir išeina. Bet beldžiasi jie nuolatos, užteršdami žurnalus. fail2ban prideda juos į juodąjį sąrašą nftables lygmeniu.
Sukurti filtrą:
cat > /etc/fail2ban/filter.d/nginx-xmlrpc.conf << 'EOF'
[Definition]
failregex = ^<HOST> - - \[.*\] "(GET|POST) /xmlrpc\.php HTTP/.*" (200|301|302|403|404) \d+ ".*" ".*"
ignoreregex =
EOF
Įsitikinti, kad fail2ban naudoja nftables kaip backend (šiuolaikinėse sistemose tai jau numatytoji reikšmė, bet verta patikrinti):
grep -r "banaction" /etc/fail2ban/jail.conf /etc/fail2ban/jail.local 2>/dev/null | grep -v "^#"
# Turėtų rodyti: banaction = nftables-multiport
# Jei rodo iptables — pridėk į /etc/fail2ban/jail.local:
# [DEFAULT]
# banaction = nftables-multiport
Sukurti jail:
cat > /etc/fail2ban/jail.d/nginx-xmlrpc.conf << 'EOF'
[nginx-xmlrpc]
enabled = true port = http,https filter = nginx-xmlrpc logpath = /var/log/nginx/access.log maxretry = 3 findtime = 60 bantime = 86400 action = nftables-multiport[name=xmlrpc, port=”80,443″, protocol=tcp] EOF
Perkrauti fail2ban:
systemctl restart fail2ban
fail2ban-client status nginx-xmlrpc
3 lygis: Cloudflare WAF (jei svetainė už Cloudflare)
Jei svetainė eina per Cloudflare, sukonfigūruoti Rate Limiting Rule valdymo skydelyje:
Security → WAF → Rate limiting rules → Create rule:
Field: URI Path
Operator: contains
Value: /xmlrpc.php
Action: Block
Duration: 1 hour
Threshold: 5 requests per 30 seconds per IP
Pro ir aukštesniuose planuose pasiekiama valdoma WAF taisyklė WP0018, kuri automatiškai aptinka system.multicall šablonus.
Svarbi pastaba: jei svetainė už Cloudflare, nginx access.log matai Cloudflare IP adresus, o ne tikruosius užpuolikų IP. Įsitikink, kad nginx sukonfigūruotas real_ip:
# nginx.conf, http{} bloke
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2a06:98c0::/29;
set_real_ip_from 2c0f:f248::/32;
real_ip_header CF-Connecting-IP;
JEI xmlrpc.php IŠ TIKRŲJŲ REIKALINGAS (Jetpack, WooCommerce Mobile)
Jetpack iki šiol naudoja XML-RPC ryšiui su WordPress.com. Visiškas blokavimas sulaužo Jetpack. Teisingas požiūris:
- Gauti aktualius Jetpack IP diapazonus ir leisti tik juos. IP adresai keičiasi — oficiali Jetpack dokumentacija aiškiai apie tai įspėja ir rekomenduoja automatizuoti atnaujinimus. Aktualus sąrašas visada pasiekiamas mašininio nuskaitymo formatu:
# Peržiūrėti aktualius Jetpack IP diapazonus
curl -s https://jetpack.com/ips-v4.json | python3 -m json.tool
# Konfigūracijos pavyzdys pagal aktualų sąrašą (jetpack.com/support/how-to-add-jetpack-ips-allowlist)
location = /xmlrpc.php {
# Aktualūs Jetpack IP diapazonai — tikrink adresu jetpack.com/ips-v4.json
allow 122.248.245.244/32;
allow 54.217.201.243/32;
allow 54.232.116.4/32;
allow 192.0.64.0/18;
allow 192.0.80.0/20;
allow 192.0.96.0/20;
allow 192.0.112.0/20;
allow 195.234.108.0/22;
deny all;
}
Svarbu: šis sąrašas keičiasi. Neįrašyk IP adresų vieną kartą ir nepamirštamiems — geriau automatizuok atnaujinimą per systemd timer.
- Pridėti greičio limitavimą net leistiniems IP:
limit_req_zone $binary_remote_addr zone=xmlrpc:10m rate=5r/m;
location = /xmlrpc.php {
limit_req zone=xmlrpc burst=3 nodelay;
allow 192.0.64.0/18;
# ... likę Jetpack diapazonai
deny all;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
DIAGNOSTIKA: KAIP SUPRASTI, KAD JAU ESI ATAKUOJAMAS
Patikrinti užklausų į xmlrpc.php skaičių per paskutinę valandą:
grep "xmlrpc.php" /var/log/nginx/access.log | \
awk '{print $1}' | \
sort | uniq -c | sort -rn | head -20
Peržiūrėti populiariausius atakuojančius IP adresus:
grep "POST /xmlrpc.php" /var/log/nginx/access.log | \
awk '{print $1}' | \
sort | uniq -c | sort -rn | head -20
Patikrinti PHP-FPM apkrovą:
systemctl status php8.3-fpm
# Arba tiesiogiai:
ps aux | grep php-fpm | wc -l
Jei matai dešimtis PHP darbuotojų esant minimaliam tikram srautui — xmlrpc.php greičiausiai jau apkrauna serverį.
DAŽNOS KLAIDOS
Klaida 1: „Įdiegiau papildinį Disable XML-RPC — dabar saugu”
Papildinys išjungia XML-RPC WordPress lygmeniu, tačiau užklausa vis tiek pasiekia PHP, PHP inicializuoja WordPress, įkelia visus papildinius ir tik tada grąžina tuščią atsakymą. Serveris vis tiek švaisto išteklius. Teisingas būdas — blokavimas nginx lygyje, kol PHP dar nepaleidžiamas.
Klaida 2: „Neturiu WordPress, tik PHP programą”
xmlrpc.php yra specifinis WordPress failas. Nėra WordPress — nėra problemos. Tačiau verta patikrinti: kartais WordPress įdiegiamas pakatalogje ir pamirštamas.
Klaida 3: „Blokuoju pagal IP — per daug užpuolikų, nespėju”
IP pagrindu veikiantis blokavimas neveikia prieš paskirstytuosius botnet tinklus — IP adresai nuolatos keičiasi. Teisingas požiūris: blokuoti endpoint visiškai (location = /xmlrpc.php { deny all; }), o ne vaikytis atskirus IP adresus.
Klaida 4: „fail2ban užblokavo, bet žurnaluose vis tiek atsiranda užklausos”
fail2ban veikia reaktyviai — pirmiausia užklausa apdorojama, tik tada IP užblokuojamas. Jei ataka paskirstyta (kiekvienas IP daro po 2 užklausas), fail2ban nespės. Sprendimas — derinys: nginx deny all endpointui + Cloudflare WAF viršutiniame lygyje.
PATIKRINK SAVO SVETAINES DABAR PAT
# Greitas patikrinimas — ar xmlrpc.php atviras?
curl -s -o /dev/null -w "%{http_code}" https://tavo-svetaine.com/xmlrpc.php
# 200 arba "XML-RPC server accepts POST requests only" = atviras, reikia uždaryti
# 403 = užblokuotas nginx
# 404 = failas nerastas (WordPress neįdiegtas arba kitas kelias)
# Patikrinti visas WordPress svetaines serveryje
for conf in /etc/nginx/sites-enabled/*; do
domain=$(grep server_name "$conf" | head -1 | awk '{print $2}' | tr -d ';')
code=$(curl -s -o /dev/null -w "%{http_code}" "https://$domain/xmlrpc.php" 2>/dev/null)
echo "$domain: $code"
done
IŠVADA
xmlrpc.php — tai palikimas iš 1998 metų, kurį WordPress tempia su savimi iki šiol. 2026 metais didžiajai daugumai svetainių jis nereikalingas. Tačiau jis įjungtas pagal nutylėjimą, pasiekiamas iš interneto, ir botai tiksliai žino, kur jo ieškoti.
Trys minutės nginx konfigūracijoje problemą uždaro visiškai:
location = /xmlrpc.php {
deny all;
access_log off;
log_not_found off;
}
Jei reikalingas Jetpack — baltasis sąrašas vietoj visiško blokavimo. Viršuje — fail2ban žurnalizavimui ir persistentinių beldėjų draudimui. Ir Cloudflare WAF, jei svetainė eina per tarpinį serverį.
Patikrink savo svetaines dabar pat. Didelė tikimybė, kad bent vienas xmlrpc.php yra visiškai atviras.
