WordPress-kwetsbaarheden

Zo herkennen bots en detectietools WordPress, en welke sporen je weghaalt

Hoe bots en Wappalyzer een WordPress-site herkennen

Voordat een bot een kwetsbaarheid probeert, moet hij weten met wat voor site hij te maken heeft. Vaak is één pagina van je site genoeg om "WordPress" te concluderen, en daarna een paar verzoeken om een lijst van je plugins en hun versies op te stellen. Dit artikel beschrijft de precieze signalen die Wappalyzer en WPScan gebruiken, wat je netjes kunt weghalen en wat zichtbaar blijft, wat je ook doet.

Waarom identificatie aan de aanval voorafgaat

Geautomatiseerde aanvallen op WordPress zijn zelden op "jouw" site gericht. Ze richten zich op een specifieke pluginversie waarvan bekend is dat ze kwetsbaar is, op duizenden sites tegelijk. Om die sites te sorteren, beantwoordt een bot drie vragen in deze volgorde: is het WordPress? welke core-versie? welke plugins, en in welke versie?

WPScan illustreert deze logica goed. Als hij in agressieve modus naar plugins zoekt, test hij standaard niet alle bestaande plugins: hij probeert de lijst met plugins in zijn databank die bekend staan om een kwetsbaarheid. Een site die op deze vragen niets bruikbaars terugzegt, valt sneller uit de wachtrij; een site die alles aankondigt, belandt in het juiste mandje.

Passieve signalen: wat de homepage verraadt

Een "passieve" tool doet geen bijzondere verzoeken: hij leest de HTML, de headers en het JavaScript van de pagina die elke bezoeker ontvangt. Dat geldt voor de browserextensie Wappalyzer.

De generator-tag

De core koppelt standaard de functie wp_generator aan de hook wp_head. Die toont <meta name="generator" content="WordPress 6.9" /> met het exacte versienummer. Dezelfde informatie staat in de feeds: <generator> in RSS 2, het attribuut version in Atom. Haal je de tag uit de <head> zonder de feeds aan te pakken, dan blijft de versie dus te zien op /feed/.

De paden /wp-content/ en /wp-includes/

Stylesheets, scripts, afbeeldingen: bijna alle bronnen van een WordPress-site lopen via /wp-content/ (thema's, plugins, media) of /wp-includes/ (core-bibliotheken). Dit is het meest robuuste signaal, en het levert er de namen van de plugins bij: /wp-content/plugins/plugin-naam/ staat leesbaar in de broncode.

De parameter ver=

Scripts en stijlen van de core worden geregistreerd met een standaardversie die gelijk is aan de WordPress-versie (default_version = get_bloginfo( 'version' ) in script-loader.php). Niet allemaal gebruiken ze die (jQuery heeft een eigen nummer), maar een script dat zonder versie is geregistreerd, zoals wp-includes/js/comment-reply.min.js, wordt geserveerd met ?ver= gevolgd door de core-versie, ook als de generator-tag is verdwenen. Plugins geven in diezelfde parameter vaak hun eigen versienummer mee.

De HTTP-headers

  • Link: <https://voorbeeld.nl/wp-json/>; rel="https://api.w.org/": verstuurd door rest_output_link_header() op openbare pagina's om de REST API aan te kondigen. Wappalyzer herkent hem direct.
  • X-Pingback: sinds WordPress 4.4 wordt deze header alleen verstuurd op een afzonderlijk item (bericht, pagina) dat pings accepteert, en verwijst hij naar xmlrpc.php.
  • Korte links: wp_shortlink_header() voegt op content een Link van het type shortlink toe.

JavaScript en CSS-klassen

De blokeditor laat wp-block-...-klassen achter in de gepubliceerde content. Aan de JavaScript-kant zijn objecten als wp.ajax, een variabele wp_username of het script wp-embed.min.js genoeg voor Wappalyzer. WooCommerce voegt zijn eigen markers toe (woocommerce_params, wc_add_to_cart_params).

Overzicht van de signalen waarmee WordPress wordt herkend, in vier kolommen: HTML-code, HTTP-headers, JavaScript en actieve verzoeken
De eerste drie kolommen worden op een gewone pagina gelezen; de laatste vereist gerichte verzoeken.
SignaalWaar te zienGeeft de versie?Gelezen door
Generator-tag<head> van elke paginaJa, exactWappalyzer, WPScan
Generator in feeds/feed/, /feed/atom/Ja, exactWPScan
Paden wp-content, wp-includesBroncodeNee (wel pluginnamen)Wappalyzer, WPScan
Parameter ver=Adressen van scripts en stijlenVaakWPScan
Link-header api.w.orgHTTP-antwoordNeeWappalyzer
X-PingbackHTTP-antwoord (berichten)NeeWappalyzer
JavaScript-variabelenScripts van de paginaNeeWappalyzer
readme.txt van pluginsApart verzoekJa (Stable tag)WPScan
MD5-vingerafdrukkenVerzoeken naar statische bestandenJaWPScan
Belangrijkste signalen, volgens de WordPress-definitie van webappanalyzer en de code van WPScan.

Hoe Wappalyzer beslist

Wappalyzer vergelijkt de pagina met een databank van definities: per technologie een lijst reguliere expressies om te zoeken in de HTML, de meta-tags, de headers, de cookies, de scriptadressen en de JavaScript-variabelen. De oorspronkelijke repository werd in augustus 2023 privé; de databank wordt verder ontwikkeld in open projecten zoals webappanalyzer (Enthec), dat hetzelfde formaat aanhoudt.

De definitie "WordPress" in deze databank is kort en leerzaam:

  • meta generator: ^WordPress(?: ([\d.]+))?, waarbij het versienummer wordt vastgelegd;
  • headers: X-Pingback eindigend op /xmlrpc.php, en link met rel="https://api.w.org/";
  • scriptSrc en html: paden /wp-content/ of /wp-includes/, wp-embed.min.js;
  • js: wp.ajax, wp.receiveEmbedMessage, wp_username;
  • implies: PHP en MySQL. De WooCommerce-definitie vereist op haar beurt WordPress (requires).

Praktisch gevolg: één signaal is genoeg. De generator-tag weghalen verandert niets aan het oordeel zolang de paden /wp-content/ in de pagina staan. Omgekeerd wordt een site die de markers van andere software toont, via hetzelfde mechanisme in die andere categorie ingedeeld.

Hoe WPScan verder gaat

WPScan is een beveiligingsscanner speciaal voor WordPress, die zowel door auditors als door aanvallers wordt gebruikt. De broncode is openbaar, zodat je precies kunt nagaan waar hij naar zoekt:

  • Detectie van WordPress: de homepage en een 404-pagina worden onderzocht (paden, generator, HTML-commentaar waarin WordPress wordt genoemd, admin-ajax.php in een inline script). In gemengde of agressieve modus laadt hij ook wp-login.php en wp-admin/install.php.
  • Core-versie: generators in de RSS-, Atom- en RDF-feeds, readme.html (weinig betrouwbaar voor recente versies), daarna vingerafdrukken: hij vergelijkt de MD5 van statische bestanden met een databank van bekende checksums, met een betrouwbaarheid van 100%.
  • Plugins: adressen op de homepage en de 404-pagina, patronen in headers, commentaar en JavaScript-variabelen, daarna verzoeken naar /wp-content/plugins/slug/. Een antwoord 200, 401, 403 of 500 geldt als aanwezig.
  • Pluginversie: lezen van readme.txt, veld Stable tag (betrouwbaarheid 80) of de changelogsectie (betrouwbaarheid 50).
Schema in zes stappen van het verloop van een WPScan-scan, van de homepage tot de vergelijking met een kwetsbaarhedendatabank
De eerste stap is een gewoon bezoek; de oranje stappen genereren verzoeken die zichtbaar zijn in je toegangslogs.

De actieve stappen laten sporen na: tientallen verzoeken naar /wp-content/plugins/.../readme.txt met een 404 binnen enkele seconden wijzen op een opsomming. Daar kan een verdediging ook ingrijpen, door geen 200 of 403 terug te geven op bestanden die niet openbaar hoeven te zijn.

Demonstratie van WPScan door Secureaks: opsomming van plugins en versies op een testsite.

Wat je kunt beperken, en wat zichtbaar blijft

Met WordPress kun je verschillende aankondigingen weghalen met eenvoudige hooks, bijvoorbeeld in een eigen plugin:

remove_action( 'wp_head', 'wp_generator' );
add_filter( 'the_generator', '__return_empty_string' );   // inclusief feeds
remove_action( 'wp_head', 'rsd_link' );
remove_action( 'wp_head', 'wp_shortlink_wp_head', 10 );
remove_action( 'template_redirect', 'wp_shortlink_header', 11 );
remove_action( 'wp_head', 'rest_output_link_wp_head', 10 );
remove_action( 'template_redirect', 'rest_output_link_header', 11 );

Let op de prioriteit: remove_action() werkt alleen als je dezelfde prioriteit opgeeft als bij het toevoegen (11 voor de twee headers). Naar wlwmanifest_link hoef je daarentegen niet te zoeken: deze Windows Live Writer-link wordt sinds WordPress 6.3 niet meer door de core uitgegeven.

SignaalVerdedigingEffectBeperking
GeneratorFilter the_generatorVersie verwijderd uit pagina's en feedsThema's en plugins die hun eigen generator schrijven
Link api.w.orgDe twee REST-hooks verwijderenAankondiging van de API verdwenenDe API blijft bereikbaar op /wp-json/
ver=Parameter herschrijven of verwijderenCore-versie minder zichtbaarKan het verversen van de browsercache hinderen
Paden wp-contentAdresaliassen + serverregelsGeen /wp-content/ meer in de HTMLLastig: CSS, JS en JSON bevatten ook paden
readme.txt van pluginsBlokkeren op de server (404)Pluginversie moeilijker te lezenVingerafdrukken van statische bestanden blijven mogelijk
MD5-vingerafdrukkenGeen eenvoudige verdedigingAlleen bijwerken neemt de kwetsbaarheid weg
Het identificatieoppervlak verkleinen: effecten en beperkingen.

Houd de rangorde in gedachten: de versie verbergen verhelpt geen enkele kwetsbaarheid. Een bot die je site niet kan identificeren, kan de exploit nog steeds blind proberen, en sommige doen dat ook. Verbergen vermindert de ruis en haalt je site uit gesorteerde doelwitlijsten; updates, back-ups en sterke authenticatie blijven de basis.

WordPress verbergen of andere software nabootsen?

Signalen weghalen laat een site "zonder identiteit" achter, en dat is op zich al een aanwijzing. De andere aanpak is de markers van andere software te tonen. Dat is het principe van CMS-imitatie van WP Masquerade:

FunctieGratisPro
WordPress-generator verwijderd uit pagina's en feedsJaJa
Echte generator-tag van andere software5 profielen (Joomla!, Drupal, PrestaShop, TYPO3, MediaWiki)41 profielen
Markers die Wappalyzer herkent (headers, JavaScript-variabelen)NeeJa
RSD-, Windows Live Writer- en korte links verwijderdJaJa
Aankondigingen van de REST API en oEmbed verwijderdNeeJa
Aliassen voor wp-content, media, wp-includes, plugins, thema'sNeeJa
Readme- en vertaalbestanden van de plugin op 404 (Apache, LiteSpeed)JaJa
Wat WP Masquerade doet tegen identificatie (volgens de productdocumentatie).

De beperkingen zijn gedocumenteerd: de mapnamen van plugins en thema's blijven onder hun hoofdmap aanwezig, CSS- en JavaScript-bestanden worden niet volledig herschreven, en er is geen enkel gegarandeerd resultaat in een detector. Zie ook de technische links en de bestandsadressen. Een overzicht van wat de WordPress-beveiligingsplugin WP Masquerade verder doet, vind je op onze homepage.

Je eigen site testen in tien minuten

  1. Open een privévenster (uitgelogd) en bekijk de broncode: zoek naar generator, wp-content, wp-includes, ver=.
  2. Open /feed/ en zoek naar de tag <generator>.
  3. Lees de headers met curl -sI https://jouw-site.nl/: staat er een Link naar api.w.org?
  4. Start de Wappalyzer-extensie op de homepage en daarna op een bericht.
  5. Probeer /wp-content/plugins/een-plugin/readme.txt: een 200 geeft de versie aan iedereen.
  6. Leeg na elke instelling de paginacache: een oude kopie kan de oude signalen bewaren.
  7. Gebruik je WPScan, doe dat dan alleen op een site waarvoor je verantwoordelijk bent.
Het standpunt van de bezoeker: de eenvoudige manieren om een WordPress-site te herkennen.

Om te onthouden

  • Eén signaal is genoeg voor Wappalyzer: de generator-tag weghalen volstaat niet zolang /wp-content/ zichtbaar blijft.
  • De core-versie lekt ook via de feeds, de parameter ver= en de vingerafdrukken van bestanden.
  • WPScan leest de Stable tag in de readme.txt van plugins: blokkeer die bestanden als ze 200 teruggeven.
  • remove_action() vereist dezelfde prioriteit als bij het toevoegen (11 voor de Link-headers).
  • Verbergen beperkt geautomatiseerde targeting, maar alleen updates nemen een kwetsbaarheid weg.

Bronnen

Andere geraadpleegde bronnen: officiële documentatie en officiële pagina's van de leveranciers.

WordPress-beveiliging, zonder ruis

Eén e-mail wanneer het ertoe doet: nieuw waargenomen aanvallen, updates van de plugin en praktische gidsen. Geen spam, afmelden met één klik.

Begin gratis, stap over op Pro wanneer je eraan toe bent

De gratis versie verbergt WordPress al en verplaatst de inlogpagina. Pro voegt aliassen, tweestapsverificatie en bewaking toe.