xmlrpc.php Angriff auf WordPress Blog

Ein Webserver mit mehreren virtuellen Hosts reagierte sehr zäh bei Browser-Aufrufen und auch bei SSH-Login. Ein schneller Blick mit top zeigte eine ungewöhnlich hohe System-Last, und der Apache Server war auffällig häufig bei den Top-Prozessen.

Ein Blick auf die Apache access.log Dateien zeigte, dass ein bestimmter Vhost (auf dem WordPress läuft) mit POST Anfragen auf /xmlrpc.php bombardiert wurde und dies zu hoher Systemlast und langsamen Web-Server Antworten führte.

Als Sofortmaßnahme die IP des Angreifers an der vorgelagerten Firewall gesperrt, daraufhin ging die Systemlast auf normale Werte zurück. Dies ist die gründlichste Lösung dieses Problems: eine Abwehr mittels Web-Server Konfiguration oder mittels WordPress Plugin erfordert weitaus mehr Ressourcen (RAM, CPU), als ein IP-basiertes Blocken des Angreifers. Das Problem ist, eine gerade entdeckte IP dynamisch in das Firewall Blocken aufzunehmen. Klassischer Fall für IPS (Intrusion Protection Systems), hat aber nicht jeder (teuer und/oder komplex).

Das IP-Blocking verschaffte aber Zeit zu recherchieren, was genau das Problem ist und wie wir vorgehen sollten.

Was ist das Problem mit xmlrpc.php?

Es geht um “Password Guessing”, also den Versuch, einen Benutzernamen mit gültigem Passwort zu erraten, um mit diesen Rechten einen fremden Server für eigene Zwecke zu mißbrauchen. Kennt jeder Admin eines im Internet exponierten Servers, egal ob es um FTP, POP/IMAP, SMTP AUTH, Apache htaccess, PHPMyAdmin, WordPress usw geht.

XMLRPC steht für “Remote Procedure Calls over XML”. Das bedeutet, dass ein beliebiger Client im Internet remote eine Prozedur mit selbst definierten Parametern auf unserem WordPress ausführen kann, indem eine korrekt formatierte XML-Datei mit POST an unseren WordPress Server übergeben wird.

Falls die roten Lampen beim letzen Satz noch nicht angegangen sind: jeder beliebige Internet Nutzer kann ohne Authentifizierung die Prozedur “prüfe-password” etwa so aufrufen:

wp.getUsersBlogs("admin", "test")

und kann anhand der Antwort erkennen, ob das Passwort “test” korrekt ist oder nicht. Das ist optimal für “Password Guessing” und vermeidet sogar auffällige Aufrufe von wp-login.php.

Kollege Mike Kuketz hat eine lesenswerte Beschreibung des Problems und bringt das auf den Punkt:

Die XMLRPC-Schnittstelle stellt ein[e] … fragwürdige Design-Entscheidungen dar …. Aufgrund ihres Funktionsumfangs … ist die xmlrpc.php nicht nur eine nützliche Bereicherung im Blogger-Alltag, sondern stellt auch für Angreifer ein interessantes Angriffsziel dar.

Dieses Angriffsziel ist ein Problem, weil die angegriffenen Server signifikante Last- und Stabilitäts-Probleme zeigen. Diese Schnittstelle sollte deaktiviert werden.

Seit WordPress 3.5.1 ist diese Schnittstelle per Default aktiviert, um eine API für Fremdsoftware anzubieten (etwa zum Publizieren von Beiträgen aus MS Word oder von einem iPhone), oder für Erweiterungen wie JetPack. Manche WordPress Plugins könnten nicht mehr wie erwartet funktionieren, dies sollte jeder Admin entsprechend prüfen.

Wir empfehlen, den Zugriff auf /xmlrpc.php abzuschalten. Dies kann auf 3 Arten geschehen:

  • IP-Blocking per Firewall
  • Webserver Blocking (zB Apache/htaccess)
  • WordPress Blocking (Plugin Disable XML-RPC)

Diese Techniken können unabhängig oder im Kombination (buzzword multi-layer security) verwendet werden.

IP-Blocking per Firewall

Dies ist die effizienteste Variante, da eine unerwünschte Anfrage gar nicht erst den Webserver erreicht und dort Ressourcen verbraucht. Der Nachteil ist, dass man die IP-Adresse des Angreifers mühselig aus den Logs extrahieren und manuell in der Firewall eintragen muss, oder ein dynamisches Firewalling erforderlich ist, welches eigene Probleme haben könnte (Stichwort false-positives). Dynamisches Firewalling wird in einem späteren Artikel beschrieben.

Webserver Blocking

In einer Apache Konfigurationsdatei oder .htaccess Datei kann mit der folgenden Anweisung der Zugriff auf xmlrpc.php und andere sensitive Dateien unterbunden werden:

# Disallow access to important WordPress files
<FilesMatch "(^\.|wp-config\.php|xmlrpc\.php)">
  Order deny,allow
  Deny from all
</FilesMatch>

Ähnliche Lösungen gibt es für nginx und andere Webserver.

Apache mod_security bietet einen regelbasierten Ansatz, um unerwünschte Zugriffe zu sperren. Es ist allerdings recht komplex und muss gut verstanden werden, um false-positives zu vermeiden und damit legitime Anwender auszusperren.

WordPress Blocking

Als letzte Verteidigungslinie kann es sinnvoll sein, ein WordPress Plugin zu installieren, das den Zugriff auf xmlrpc.php unterbindet. Mit einer WordPress Plugin-Suche nach “disable xmlrpc” finden sich etliche Plugins, wir benutzen “Disable XMLRPC”.

Win7 Firefox Sucheinstellungen gekapert

Heute $SWEETHEART am Computer über die Schulter gegauckt, und bemerkt dass man ihr eine andere Suchmaschine (“sm.de”) im Firefox untergejubelt hat.

MalwareBytes sagt “alles gesund”, auch der installierte Avast Antivirus hat nicht angeschlagen. Sm.de ist nach meiner Einschätzung Schadsoftware, habe die Suchmaschinen-Einstellung manuell repariert.

Es macht mir Sorgen, dass es möglich ist, trotz Auto-Update von Windows und Firefox von fremder Seite persönliche Einstellungen zu ändern. Not feel safe, no-no.

Debian 7.9 network driver regression

So I thought I use this quiet weekend to install the pending Debian 7.9 updates to a couple of servers. Worked fine, except for the last and most important one. Of course.

We’re talking about 2 physical hosts in a remote datacenter. One of them running Xen and 5 VMs, the other one being a warm-spare backup for the first. All servers (2 hosts, 5 VMs) run Debian 7 “wheezy” which was installed at 7.0 and got upgraded all the way to 7.8 without problems.

I was already composing an “update done” email when I noticed that the console to that last server (the host running the VMs) was frozen. No pings to the system, and the VMs were not reachable either. Kicked out of the network, had to trigger Ctrl-Alt-Del remotely as remote access was cut off.

The machine came back, I could login and view logs. After a few minutes it happened again: dead, reboot, repeat. The smell of trouble. Quickly grabbed the logs before it froze once again. There (edited):

Jan  2 19:12:45 srv4 kernel: [  551.326415] NETDEV WATCHDOG: eth0 (r8169): transmit queue 0 timed out
Jan  2 19:12:45 srv4 kernel: [  551.327507] Pid: 0, comm: swapper/0 Not tainted 3.2.0-4-amd64 #1 Debian 3.2.73-2+deb7u1
Jan  2 19:12:45 srv4 kernel: [  551.327549] Call Trace:
Jan  2 19:12:45 srv4 kernel: [  551.327574]  <IRQ>  [<ffffffff81046ded>] ? warn_slowpath_common+0x78/0x8c
[...]

Kernel stack trace, don’t you love to see that on production servers.. At least that gave me something to google for, but the results were discouraging. Looks like an old kernel 2.6.x bug from around 2010-2012 that hit users of various Linux distributions. Basically an ethernet driver crash cutting off connectivity.

But: Debian 7.9 has kernel 3.2.0, just like 7.8 before which did not show this problem. How the hell did this issue resurface? Maybe because Xen is involved. And no one else got hit by that?

Anyway, as $CUSTOMER tends to get unhappy if his servers are not reachable anymore, I needed a quick solution or at least a workaround. Passing certain kernel boot parameters might be an option.

The workaround for now is to disable gigabit autonegotation for this interface and force it to 100Mb/s full-duplex using this command:

ethtool -s eth0 speed 100 duplex full autoneg off

This is a workaround, not a fix. But it DOES was claimed to prevent the machine from crashing after a few minutes. Need to investigate.

To me, this looks like a regression in Debian 7.9.

Update Jan 3 2016

Some more googling for NETDEV WATCHDOG: eth0 (r8169): transmit queue 0 timed out returned various bugtracker messages from Debian, Ubuntu, RedHat and others. Earliest from 2008, latest from 2015. Affected network cards were r8169, e1000 and bnx. Several hypotheses and suggested fixes were posted, including:

  • passing kernel boot options such as pcie_aspm=off (didn’t work for everyone)
  • try a newer kernel
  • try to compile driver from source
  • Jumbo Frames >1500 may be involved (not here)
  • Xen 4.1 may be involved (my theory)

Since this is a production server, I will certainly not be playing reboot games with kernel options that may or may not work. I won’t compile custom kernels or drivers either. I made the ethtool call described above permanent with these settings in /etc/network/interfaces:

auto  eth0
iface eth0 inet static
  address   x.x.x.x
  netmask   x.x.x.x
  gateway   x.x.x.x
  up sleep 5; ethtool -s eth0 speed 100 duplex full autoneg off