Server Side Template Injection in Aspnet (Csharp)
Server Side Template Injection in Aspnet with Csharp
Server Side Template Injection (SSTI) in ASP.NET occurs when an attacker can inject template directives that are processed by the server-side rendering engine. In C#-based ASP.NET applications, this commonly arises when using Razor views or other templating engines where user input is concatenated into template logic without proper validation or isolation. If a developer passes unsanitized input into a Razor template, an attacker can inject crafted payloads that cause the template engine to execute unintended code, access server-side objects, or read files.
For example, consider a scenario where an endpoint accepts a template parameter and renders it using Razor without isolating user input from the template syntax:
// Example of vulnerable code in an ASP.NET Core controller
public IActionResult Render(string template)
{
// WARNING: directly rendering user input in Razor is unsafe
return Content(RazorLightEngine.Render(template, model: null));
}
An attacker might submit a payload such as @{ typeof(System.IO.File).GetMethod("ReadAllText").Invoke(null, new[] {"/etc/passwd"}) }, which could cause the template engine to read sensitive files. Because Razor can instantiate types and invoke methods, SSTI can lead to remote code execution or sensitive data exposure. The risk is elevated when the application runs with elevated privileges or when the templating engine is configured to allow reflection.
In ASP.NET, SSTI is often tied to improper use of Microsoft.AspNetCore.Mvc.ViewFeatures or direct interaction with IRazorViewEngine. Attack techniques include leveraging built-in C# features like reflection, accessing configuration objects, or invoking static methods. Unlike simpler injection flaws, SSTI in templating engines can bypass input validation because the injected content is interpreted as code rather than data.
To detect this class of issue, scanners look for endpoints that accept template-like input and inspect whether user-controlled data reaches the rendering pipeline without sanitization. They also check whether the application uses strict type models or allows dynamic object graphs, which increase the attack surface.
Csharp-Specific Remediation in Aspnet
Remediation focuses on preventing user input from being interpreted as template code. The safest approach is to avoid rendering raw user input as Razor templates. If dynamic templates are required, use a sandboxed or isolated templating context that does not permit reflection or access to runtime objects.
1. Use strongly typed models and avoid dynamic template rendering:
public IActionResult RenderSafe(string name)
{
var model = new GreetingModel { Name = name };
return View(model);
}
public class GreetingModel
{
public string Name { get; set; }
}
In this pattern, the view receives a predefined model and cannot execute arbitrary code. The Razor markup should only reference model properties, not execute logic based on user input.
2. If you must process templates, use a dedicated library with strict sandboxing and disable reflection:
// Using a restricted template library instead of Razor for user templates
var config = new TemplateServiceConfiguration
{
DisableMethodSyntax = true,
AllowAnonymousAccess = false
};
var service = new TemplateService(config);
string result = service.ParseTemplate("Hello @Model.Name", model: new { Name = "Alice" });
Ensure that the library does not permit access to system types or methods. Validate and sanitize all inputs before they are merged into the template string.
3. Apply strict input validation and output encoding:
public IActionResult RenderWithValidation(string userText)
{
if (string.IsNullOrWhiteSpace(userText) || userText.Contains("@{") || userText.Contains("@"))
{
return BadRequest("Invalid input");
}
// Encode output to prevent injection
var encoded = System.Net.WebUtility.HtmlEncode(userText);
return Content(encoded);
}
Reject inputs that contain Razor-like syntax patterns and encode any dynamic content before inclusion in responses. This reduces the likelihood that attacker-controlled data is misinterpreted as executable template instructions.
4. Limit framework features when using Razor programmatically:
// Configure Razor to limit runtime type access (example for Razor Class Library)
var razorConfig = new RazorConfigurationBuilder()
.SetCompatibilityVersion(CompatibilityVersion.Latest)
.DisableTypeDiscovery()
.Build();
Disabling type discovery and reflection prevents the template engine from resolving arbitrary C# types, which is a common vector for SSTI exploitation.