************************************************************* White Paper sur le RPC Spoofing et la Construction de Paquets by JimJones [zsh] 2000 http://zsh.interniq.org ************************************************************* Translation by eberkut (eberkut@nexen.net) - http://www.chez.com/keep 1. Introduction --------------- Laissez-moi commencer en disant que je ne réclament nullement n'importe quelle étincelle d'originalité en écrivant cet article. Le RPC spoofing n'est pas vraiment une idée nouvelle, mais ce qui m'a fait écrire c'était le fait qu'elle est très rarement mis en application. Je remercie smiler puisqu'il avait fait ses exploits RPC avec des capacités de spoofing pendant un certain temps maintenant, notamment humpdee2, et étant la cause de mon intérêt concernant ceci. Maintenant que j'ai indiqué clairement que ce n'est pas révolutionnaire et que ça ne fera pas trembler al terre (juste survolé), je continuerai. 2. Vue d'ensemble basique ------------------------- Qu'est-ce que RPC ? J'espère que vous avez au moins une compréhension basique des remote procedure calls et comment ils fonctionnent, puisque ceci est une discution de niveau intermédiaire. étendons nous sur les TLI et les mécanismes de transport indépendant un moment, et nous trouvons fondamentalement implémenté sur les RPC 2 principaux protocoles : TCP et UDP. Cet article se concentrera seulement sur UDP, puisque celui semble logiquement la meilleure couche transport pour attaquer. Bien sûr, il est possible d'obtenir un outil de spoofing RPC basé sur TCP, mais ceci vaut-il vraiment la peine ? La plupart des services RPC écoutent aussi bien sur des ports UDP que TCP, et ceux qui écoutent plus particlulièrement sur l'un des 2 tendent souvent à être basé sur UDP. Prévoir les numéros de séquence est si encombrant comparé à forger simplement un paquet IP. La beauté du protocole RPC, depuis la perspective du hacker, est ce qu'il n'a aucune mesure inhérente d'intégrité. L'authentification DES et user-based sont toutes les 2 supportés dans les standards ONC, mais vous les voyez rarement utilisés. Les datagrames RCP n'ont pas de checksums ou de signatures ou de procédures handshake inhérente. Simplement l'envoi d'un paquet remplira le travail. Maintenant que j'en ai fini avec les bases, il est temps d'entrer dans les détails techniques. 3. Construction de paquets RPC de bas niveau -------------------------------------------- Les définitions que nous sommes sur le point d'observer peuvent également être trouvées dans Nous voyons la structure suivante : struct rpc_msg { u_int32_t rm_xid; enum msg_type rm_direction; union { struct call_body RM_cmb; struct reply_body RM_rmb; } ru; Le message RPC a une longueur statique (excepté pour TCP, où un composant longueur est ajouté au début au paquet), et est toujours généralement le même. Le xid est un identificateur de message unique 32 bits qui est employé pour distinguer les paquets et requêtes sur un serveur occupé RPC, comme un ticket. La valeur que vous assignez à cette variable est vraiment sans importance, parce que vous ne vous attendez pas à une réponse, ou que le xid non-0 soit rejetée. La direction sera toujours assignée au type CALL pour des raisons évidentes, rappelez vous d'utiliser toujours le réseau approprié ordonnant quand vous créer vos messages. Simplement les appels htons() et htonl() suffiront. Le format de message RPC emploie une union pour représenter un appel ou une réponse dans le même espace. Le prochain élément de cette structure de données est ru.RM_cmb, que nous pouvons référencer comme rm_call, comme c'est celui comme il est également défini. Maintenant jetons un oeil au corps de l'appel. struct call_body { u_int32_t cb_rpcvers; /* doit être égal à 2 */ u_int32_t cb_prog; u_int32_t cb_vers; u_int32_t cb_proc; struct opaque_auth cb_cred; struct opaque_auth cb_verf; /* protocole spécifique - fourni par le client */ }; Comme les fichiers include le suggèrent (et il est toujours sage de suivre leurs suggestions), cb_rpcvers doit toujours être mis à 2, puisque ceci est la version des communications RPC qui ont lieu. cb_prog est le numéro 32 bits du programme du service RPC que vous appelez, cb_vers est la version de ce service, et cb_proc est le numéro de procédure. Les procédures RPC définies par l'utilisateur commencent par 1 et se déplacent habituellement incrémentalement vers le haut, puisque 0 a été réservé sur tous les ports pour un essai de procédure nul. Maintenant, la prochaine partie concerne la portion d'authentification. Il n'y a pas un tas de services qui sont configurés pour l'authentification d'utilisateur, aussi elle ne sera pas discutée en détail. Mais dans tous les cas, il y a 3 types supportés universellement : NULL ou pas d'authentification, authentification basé sur l'utilisateur qui passe au serveur une UID, une GID, et un nom de machine, et l'authentification DES avec les horodateurs chiffrés. De toute façon, l'authentification null, comme son nom l'implique, se compose simplement des octets de null (0). Ah, une autre chose. Ces deux variables sont toutes les 2 de type "opaque_auth de struct". Les structures et les données opaques dans RPC sont fondamentalement ceux qui ne sont pas analysées par l'implémentation RPC et ne restent pas ouvertes pour l'examen par le serveur et le client. Ceci peut être comparé à une string binaire de forme libre. Pour des raisons simples, je ne veux suggèrer à personne de construire des paquets RPC DES_authenticated à la main. C'est réellement là l'essentiel d'un mécanisme de spoofing simple. Nous insérons simplement le corps du message RPC après l'en-tête (par corps du message, je veux dire les paramètres de l'appel de fonction RPC) et nous le tout envoyons au serveur. Maintenant, si vous êtes compétent en programmation RPC, vous savez employer XDR (eXternal Data Representation) pour formater des paramètres. Au lieu de passer vos paramètres vers une fonction créée par rpcgen, par exemple, vous les enchaînez simplement, l'un après l'autre, et les ajoutez à l'end de votre message RPC. Vous n'avez besoin de créer aucun autre objet de données. Le remote end décodera vos paramètres. Puisqu'il s'attend à certains paramètres, il lira seulement une quantité fixe d'octets du flux de données et les moulera alors au type approprié. Même les paramètres de longueur variable sont NULL-terminated ou ont un/des bit/s de longueur. 4. Il y a un truc ----------------- OK. Ainsi vous avez spoofé le paquet RPC et avez exécuté notre exploit ? Tout va bien, n'est-ce pas ? Enfin, d'une perspective naïve. Pour envoyer le paquet, vous naturellement avez dû savoir que le port distant UDP du service RPC était en écoute. Ainsi vous avez exécuté une commande comme rpcinfo, découvert la liste de services disponibles et tracé le service à son port correspondant. Les services RPC, naturellement, sont liés dynamiquement aux ports assignés. Ces ports changent constamment ; seuls les numéros de programme demeurent constants. Ainsi chaque fois que vous voulez envoyer un paquet vous devez découvrir quel port correspond. Quand vous exécutez rpcinfo sur un hôte avant de l'exploiter, vous contrez habituellement le but du RPC spoofing. rpcinfo exige d'un circuit virtuel d'être utilisé pour le transport, ainsi vous divulguez votre adresse quand vous faites une requête. Même si vous faites ceci par l'intermédiaire d'un proxy, un logger peut détecter l'exécution. Vous pourriez faire un scan UDP NULL (méthode halflife), mais ceci aussi pourrait déclencher des IDS quand ils identifient un intrus potentiel. Heureusement, il y a un truc pour coutourner ceci. Nous pouvons voir le code pour ceci dans . rpc.portmap a 5 procédures standard qui sont définies (à l'exception du NULL procedure test). La procédure qui nous intéresse là dedans est la #5. Les lignes suivantes du code la détaillent : #define PMAPPROC_CALLIT ((u_long)5) PMAPPROC_CALLIT(unsigned, unsigned, unsigned, string<>) encapsulatedresults = PMAPPROC_CALLIT(prog, vers, proc, encapsulatedargs); Si rpc.portmap écoute sur le port UDP 111 (ce qu'il fait probablement, puisque seul PMAPPROC_DUMP() exige le transport TCP), vous pouvez employer cette procédure pour passer vos paramètres vers les programme, version, et procédure spécifiées, indépendamment de votre connaissance du port. Et nous connaissons toujours ces paramètres en exécutant l'exploit. Maintenant nous spoofons complètement aveuglément, sans devoir jamais connaître le port que le service exécute. Rappelez vous des 3 zones que nous avons complétées d'information programme (cb_prog, cb_vers, cb_proc) ? Nous appelons notre service maintenant indirectement par le portmapper. Ainsi nous prenons ces 3 zones, et les passons vers PMAPPROC_CALLIT avant que nous appelions le string<>. Dans leur place, nous avons les defines suivant : #define PMAPPROG ((u_long)100000) #define PMAPVERS ((u_long)2) #define PMAPPROC_CALLIT ((u_long)5) cb_prog = 100000, cb_vers = 2, and cb_proc = 5 Note : PMAPPROC_CALLIT () fonctionnera seulement pour UDP de toute façon, ainsi ceci échouera autrement. En outre, tentez d'être créateur. Si vous spoofé afin de simuler l'existence d'un hôte de confiance, et pas simplement "rester hors de la vue", rappelez-vous de bein travailler vos paquets. Une box verrouillée aura probablement des règles de packet-filtring rigides en place. Dans ce cas, c'est probablement une bonne idée de placer votre port UDP source en 111, ou quelques autres services bien connus tels que SNMP (161) ou DNS (53), comme ADMFZap a fait pour éviter un logiciel mal configuré. Bonne chance et bon spoofing -) Autres matières et références ----------------------------- Toutes ces ressources ne sont pas appropriées au sujet de la programmation RPC de bas niveau, mais elles sont certainement utiles si vous souhaitez développer. Je pense me souvenir que UNIX Network Programming Volume 2 possède une section sur les paquets RPC mais je n'en suis pas tout à fait sur. Les suivantes sont des ressources que j'ai vérifiées. Power Programming with RPC by John Bloomer http://www.crc-tgr.edu.au/docs/dec/AA-Q0R5B-TET1_html/INDEX.html http://www.uccs.edu/~compsvcs/doc-cdrom/DOCS/HTML/AQ0R5BTE/DOCU_004.HTM