lundi 27 octobre 2008

Feel the power with TDI !

N'avez vous jamais pensé à avoir un support réseau pour votre merveilleux rootkit ?
Avoir la main sur cette bête même à distance ?

Je vais aujourd'hui réaliser votre rêve :)), plus sérieusement ce post traitera de l'une des interfaces proposées par microsoft pour nous permettre de faire du réseau ; et celle-ci porte le doux nom de TDI acronyme de "Transport Driver Interface".
Je cite microsoft techNet :

"The Transport Driver Interface (TDI) is a common interface for drivers (such as the Windows 2000 redirector and server) to use to communicate with the various network transport protocols. This allows services to remain independent of transport protocols."


Il faut déjà savoir qu'il existe plusieurs manières d'accéder aux ressources réseaux du kerneland

; c'est différent moyens sont implémentés ou pas selon la version de windows :

  • Avant les systèmes vista, nous avions à disposition TDI ainsi que NDIS acronyme de Network Driver Interface Specification.
  • Sous vista nous avons à disposition TDI, NDIS et les Kernels Sockets.
  • Et puis il est prévu de troquer TDI contre les Kernels Sockets pour les autres systèmes qui suivront vista ; autrement dit on ferra du reseau avec NDIS ou/et les Kernels Sockets.
J'ai donc choisis l'utilisation de TDI pour tout d'abord avoir une espèce de rétro-compatibilitée avec "l'avant vista" ; de plus l'utilisation de TDI est assez simple car les opérations à réaliser suivent un même schéma que je detaillerais plus bas.
L'utilisation de TDI est d'ailleurs très bien documentée ; non pas par la masse des écrits sur le sujet mais par la qualité des quelques papers trouvés.
Je veux bien sûr faire références à deux écrit :
  • Subverting The Windows Kernel , chapitre "Kernel TCP/IP Support for Your Rootkit Using TDI"
  • Audi-K - Nouvelles aventures en Kernel Land, paper parut dans le zine des blackclowns ; c'est un S.U.P.E.R.B.E article écrit par Tolwin ..pour couronner le tout c'est du français.Cette article est donc juste priceless, merci à lui.
Ces deux écrits permettent largement de se coder un petit Proof Of Concept quant à l'utilisation de TDI.
J'ai donc choisis de mettre en place une connexion à la manière d'un classique Reverse Shell ; autrement dit un serveur hébergé chez le h4ck3rz, le driver s'y connecte et propose des fonctions exécutées chez la victime tout cela sous forme d'un shell avec un jolie prompt toussa :D.
Seulement ce qui m'a intéressé dans ce code c'est l'implémentation de la partie reseau, vous comprendrez donc pourquoi je n'ai codé aucune fonction entrant dans le cadre du reverse shell ..c'est un travail on ne peux plus fastidieux ; de plus ce n'etait pas le but ! Au passage si quelques d'entre vous sortent leurs jolies IDEs avec comme objectif de rendre mon bout de code utilisable..ben qu'ils me previennent :D.


Entrons dans le vif du sujet ; chers passagers je vous prie de bien vouloir boucler vos ceintures, le voyage va bientôt commencer !

Comme je le disais un peu plus haut, TDI s'avère assez simple, peut-être un poil velus au départ mais au fur et à mesure que l'on code on se rend vite compte que les principales opérations sont redondantes.
Il faut aussi savoir que jouer avec TDI, c'est accepter de dealer avec le driver tcpip.sys.
Si on fouille dans les tools signé OSR, on peut tomber sur DeviceTree ; un outil assez pratique pour observer l'organisation des drivers, devices et autres :



Vous vous doutez bien que nous allons nous occuper du driver tcp, seulement lui !
On remarque aussi au passage que le device est en mode IO_DIRECT ; autrement dit le buffer d'entré et de sortie sera le même ; aucun gaspillage quant à la manipulation/recopie de ceux ci etc.
Le code va donc se résoudre à de multiples échanges entres le driver, et le notre par le biais d'IRP.
En effet, notre code se résout à , préparer une requête (l'IRP), l'envoyer au driver, bloquer pendant que le driver traite notre requête, et puis agir en fonction du code de retour.
Tout cela est simplifié mais dans l'idée c'est exactement ce qu'il faut faire ; microsoft nous propose alors un lot de macro tel que :
  • TdiBuildSend
  • TdiBuildReceive
  • TdiBuildConnect
  • TdiBuildInternalDeviceControlIrp
Entrons un peu plus dans les détails maintenant.
Les deux premières étapes consistent en la création de deux objets, un "Connection Object" ainsi qu'un "Transport Object" l'un servant à stocker les informations relatives a l'host et au port (le TransportObject donc) ; et un second objet gérant la connection (le Connection Objet).

Ces objets seront créés grâce à la fonction ZwCreateFile ; pour mener à bien la construction de ces objets ont doit passer des paramètres à la fonction qui va gérer la construction des objets contenu dans le driver bien sur.
Ceux-ci seront passé par encapsulation dans une structure du type FILE_FULL_EA_INFORMATION ; dans l'avant dernier argument de ZwCreateFile "EaBuffer".
Voici la définition de la structure :

typedef struct _FILE_FULL_EA_INFORMATION
{
ULONG NextEntryOffset;
UCHAR Flags;
UCHAR EaNameLength;
USHORT EaValueLength;
CHAR EaName[1];
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;

Je m'explique quant à cette histoire d'encapsulation ; le but est de pouvoir passé plusieurs structures au driver.

1. On alloue la mémoire total, c'est l'addition des tailles des structures à faire passé en argument plus la structure de type FILE_FULL_EA_INFORMATION.
2. On remplis les premiers champs de la structure : NextEntryOffset, Flags etc.
3. On écrit à la suite du dernier champs (EaBuffer) le contenus de nos structures.

Voilà le principe, la structure de type FILE_FULL_EA_INFORMATION englobe les autres, c'est le principe.

Je noterais EA la structure qui sert d'encapsulation.
Pour le Connection Object nous avons le schéma suivant :
  • [EA -> CONNECTION_CONTEXT]
Pour le Transport Object nous avons :
  • [EA -> TA_IP_ADDRESS]
Je ne sais pas si j'ai réussi à comprendre, mais j'ai fais de mon mieux, c'est pas facile :].

Bon voilà c'est beau, c'est magique, de la véritable poudre de perlinpinpin mais notre voyage est loin d'être terminé.

Il faut ensuite associer ces deux handles..à partir de cette étape le chemin restera toujours le même :
  1. Allocation de l'irp, grâce à la macro TdiBuildInternalDeviceControlIrp.
  2. Construction de l'irp par le biais de la macro associée à l'action désirée (TdiBuildSend, TdiBuildReceive, TdiBuildAssociateAddress etc).
  3. On transmet l'irp au driver en utilisant la fonction IoCallDriver.
  4. On bloque tant que le traitement n'est pas terminé avec la fonction KeWaitForSingleObject.
C'est en fait le schéma dont je vous parlais plus haut, c'est celui-ci que vous allez répéter pour chacune de vos actions ; vous comprendrez alors pourquoi je ne détaillerais pas le reste :)).

Concernant mon petit code, il va se connecter sur une ip sur un port donné, il envoit alors un prompt à la manière d'un shell tout simplement.
J'ai d'ailleurs tout mis en place pour faciliter l'implémentation de fonctions ..si il y a des courageux comme je le disais :)).
Un petit screenshot:




Sinan dans le genre priceless, mon s1th vient de nous dégotter un OllyDbg customisé on ne peux plus cool :)).
Il est blindé de scripts/plugins, et possède un gestionnaire de raccourcis pour placer tous ces tools préférés :



On voit aussi qu'il possède une barre de commande immunityDbg like :) ; enfin bon à avoir d'urgence !
Ensuite je voulais vous parlez d'un ami, Squallsurf et son nouveau outil H.a.r.P.E (une espèce de plateforme de manipulation/visualisation du PE ; projet prometteur :)).
N'hésitez donc pas à lui rendre visite, ou encore lui rapporter bugs et/ou amélioration quant à son joujou :).

Les codes de mon Proof Of Concept :
Esrever.c

En espérant avoir intéressé quelques uns :), cya.
PS : petit coucou à securfrog o/ ; merci à baboon !