// der php hacker

// archiv

Das Experiment – WordPress Security & das Zend Framework

Geschrieben am 27. Apr 2009 von Cem Derin

laboratory

Herrlich, was für ein reisserischer Titel. Um ehrlich zu sein, mir fiel kein besserer ein. Denn in diesem Artikel werde ich gleich mehrere Themen behandeln. Intention war, dass ich mal wissen wollte, wer sein WordPress-Backend eigentlich zusätzlich per htaccess-Passwort schützt. Die Stichproben waren schon einmal ziemlich erschreckend. Ich wollte aber aussagekräftige(re) Daten. Also kam mir die Idee, dass ich mir ein Script schreiben könnte, dass die Blogs meines Feedreaders dahingehend prüft. Dann aber dachte ich mir: Warum das meinen Lesern vorenthalten. Da ich das Zend Framework einsetzen wollte, kann ich hier direkt ein paar Teilbereiche beleuchten und den Mehrwert erhöhen. Wir sehen also: heute habe ich einen wirklich multifunktionalen Beitrag für euch. :-)

Quelldaten sammeln

Wie eingangs erwähnt, will ich die Blogs aus meinem Feedreader einer genaueren Prüfung unterziehen. Da ich Google Reader verwende, gestaltet sich der Export sehr einfach: Der web-based Feedreader bietet einen OPML-Export. Prinzipiell bietet aber auch jeder andere halbwegs brauchbare Feedreader einen Export in das OPML-Format. Wenn nicht, würde ich die Applikation schnellstens wechseln, bevor sich mehr Feeds ansammeln, die man im Falle eines Falles von Hand nachtragen muss.

WordPress identifizieren

Der etwas schwierigere Teil ist, WordPress zu identifizieren. Klar, wenn das wp-admin Verzeichnis da ist, scheint WordPress installiert zu sein. Das kann man theoretisch aber auch umbenennen oder anderweitig verstecken. Die Suche nach Eastereggs verlief auch eher Mau (Zumindest, wenn sie öffentlich verfügbar sein müssen). Ein relativ sicheres Zeichen wird sein, wenn eine Ressource unter ‘wp-content’ aufgerufen wurd. Wir suchen also einfach nach diesem Fragment.

Feeds auslesen

Wie schon erwähnt, liegen uns die Feeds als XML-Datei vor. Mit der Zend_Config_Xml Klasse lassen sich XML-Daten bequem auslesen. Mit ein paar Zeilen Code kommt man schon an die Daten:

	$feed = new Zend_Config_Xml('feeds.xml');

	foreach($feed->body->outline as $outline) {
		echo $outline->htmlUrl. PHP_EOL;
	}

Hosts aufrufen

Um die Hosts anzusteuern verwende ich die Zend_Http_Client Klasse. Auch hier brauchen wir nicht viel machen. Mir ein paar Zeilen haben wir unser Client-Objekt:

	$client = new Zend_Http_Client(array(
		'timeout'	=>	10,
		'useragent'	=>	'Some experiments by unset - phphacker dot net'
	));

Ein Timeout von 10 Sekunden ist zwar ziemlich kritisch, aber wir wollen ja auch nicht ewig auf die Ergebnisse warten müssen. Außerdem setzen wir einen eigenen User Agent. Standardmäßig wird Zend_Http_Client mitgeschickt. Jeder, der das mal in seinen Statistiken gesehen hat, wird genervt von der Aussagelosigkeit gewesen sein. Dahingehend geben wir den Betreibern der Seite ein bisschen Information, wer da grade was macht – und wie man uns im Notfall kontaktieren kann.

Um den Host dann aufzurufen, reicht eine weitere Zeile:

		$client->request();

Es wird ein Response-Objekt zurückgegeben, dass wir dann auswerten können.

Prüfung

Basteln wir uns eine kleine Checkliste, wie wir vorgehen wollen.

  1. Prüfen, ob der Host erreichbar ist
  2. Prüfen, ob es sich um ein WordPress-Blog handelt
  3. Prüfen, ob dass Verzeichnis “wp-admin” zugänglich ist

Diese Checkliste wäre mit dem Code abgearbeitet – zusätzlich zu ein paar

	foreach($feed->body->outline as $outline) {
		$client->setUri($outline->htmlUrl);
		try {
			$result = $client->request();
			if(!empty($result) AND preg_match('/wp-content/i', $result->getBody())) {

				$client->setUri($outline->htmlUrl. '/wp-admin/');
				if($client->request()->getStatus() == '200') {
					// offen
				} else {
					// gesichert
				}
			} else {
				// offensichtlich nicht wordpress
			}
		} catch(Exception $e) {
			// nicht erreichbar
		}
	}

Dass ein Host nicht erreichbar ist merken wir daran, dass die Request-Methode eine Exception wirft. Diese müssen wir natürlich abfangen. Die Antwort des Servers werten wir aus, und suchen nach dem Textfragement »wp-content«, sofern dieser erreichbar ist. Finden wir dieses, prüfen wir, ob wir den Unterordner wp-admin aufrufen können und uns dieser einen Statuscode 200 liefert. Ist das der Fall, ist der Zugang zum Adminbereich nicht weiter abgeriegelt.

Fazit

Das Ergebnis der Aktion ist recht ernüchternd. Ach was sage ich: Erschreckend. Von 126 geprüfen Blogs erkannte das Script 84 als WordPress-Blogs. Gesichert waren nur 7. Wenn man bedenkt, dass zwei davon von mir sind, wird die Zahl noch trauriger. Bedenkt: WordPress bietet mit der Funktion Plugin-Ins und Updates direkt auf euren Server zu spielen zwar eine Menge Komfort und evtl. sorgt das auch dafür, dass Updates schneller gemacht werden. Aber diese vielen Rechte, die ihr WordPress einräumt sorgen auch dafür, dass finstere Zeitgenossen in euer System einbrechen wollen, denn man kann es nahezu vollständig kapern.

Dahingehend noch einmal mein Eindringlicher Rat: Sichert euren Admin-Bereich!

Appendix

Ich habe überlegt, ob ich das Script noch einmal mit der URL zu diesem Post als Referer laufen lassen soll, damit die Blogbetreiber darauf aufmerksam werden, habe mich aber dagegen entschieden. Nachher wird das noch als Referer-Spam gewertet. Da der User-Agent einen Verweis auf dieses Blog enthält, hoffe ich, dass das ausreicht.

Ich würde mich übrigens freuen, wenn ihr das kleine Script auch mal mit euren Feeds ausführt und eure Ergebnisse postet! Hier mal meine Ausgabe:

Gepruefte Blogs:        126
Nicht erreichte Blogs:  1
Wordpress-Blogs:        84
Andere Blogs:           41
Nicht gesichert:        77
Gesichert:              7 

Hier könnt ihr euch das Script runterladen. Beachtet, dass ihr das Zend Framework braucht. Das Script ist für die Konsole geschrieben. Ihr könnt es aber auch im Browser ausführen. Dazu würde ich ein echo ‘<pre>’; an den Anfang setzen.



#001
28. Apr 2009
unset

24 Stunden später sieht das Ergebnis so aus ;-)

Gepruefte Blogs:        126
Nicht erreichte Blogs:  1
Wordpress-Blogs:        84
Andere Blogs:           41
Nicht gesichert:        76
Gesichert:              8

#002
29. Apr 2009

Wobei man seit 2.5 den Ordner wp-content umbenennen und verschieben kann, rein durch Konstantendefinition. Daher hinkt die Prüfung vielleicht ein wenig, aber das Ergebnis wird sicher dadurch nicht sonderlich verändert. Ich denke, dass die Nutzer das Hindernis .htaccess nicht nutzen wollen, eine Hürde mehr und damit nutzerunfreundlich. Es gibt einige sehr gute Erweiterungen und Lösungen für WP, die Sicherheit erhöhen und trotzdem die Nutzerfreundlichkeit bestehen lassen.


#003
29. Apr 2009
unset

Stimmt, die Prüfung kann ein WordPress-Blog nicht zu 100% identifizieren. Aber wie das Ergebnis zeigt: Die “nicht”-Worpdpress-Blogs sind nu eine handvoll. Wenn ich meine Feeds veröffentlichen würde, würde man auch schnell sehen, dass es sich dabei tatsächlich nicht um WordPress handelt ;-)

Gleich mal sehen, ob es ein WP-HTTP-Auth Plugin gibt …

#004
05. Mai 2009

[...] Hier noch der 1024 Kommentar, ein wenig aus dem Kontext gerissen, aber ihr wolltet ihn ja haben. Ist übrigens von unset. Der ZF HTTP-Client bietet sich übrigens als toller Wrapper an, der verschiedene Implementationen als Adapter zulässt. Auch schön einfach. Hab ich hier neulich noch benutzt: http://phphacker.net/2009/04/27/das-experiment-wordpress-security-und-das-zend-framework/ [...]


#005
25. Apr 2011
Benjamin

Und wo liegt nun das Problem das /wp-admin erreichbar ist? Es ist noch lange keine Sicherheitslücke, nur in Indiz dafür das WordPress genutzt wird, und der Angreifer so von etwaige Sicherheitslücken Gebrauch machen kann. Das Problem kann allerdings überall auftreten, zum Beispiel bei persistentem XSS in Kommentaren.

// kommentieren

// senden
theme von mir, software von wordpress, grid von 960 grid system. funktioniert in allen browsern, aber der safari bekommt das mit der schrift am schönsten hin.