User Input in Razor.Parse()
Passing user-controlled template strings directly to Razor.Parse() or Engine.Razor.RunCompile().
Server-Side Template Injection (SSTI) vulnerabilities occur when user-controlled input is passed to Razor.Parse() or similar template compilation methods. Razor templates can contain C# code that gets compiled and executed on the server, allowing attackers to execute arbitrary code, read sensitive files, access databases, or fully compromise the application.
using RazorEngine;
using Microsoft.AspNetCore.Mvc;
[ApiController]
public class TemplateController : ControllerBase
{
// VULNERABLE: user input passed to Razor.Parse
[HttpPost("api/template/render")]
public IActionResult RenderTemplate([FromBody] TemplateRequest request)
{
// DANGEROUS: compiles and executes user-controlled template
string result = Razor.Parse(request.Template, request.Model);
return Ok(new { result });
}
}
public class TemplateRequest
{
public string Template { get; set; }
public object Model { get; set; }
}
// Attack payload:
// @{ System.Diagnostics.Process.Start("calc.exe"); }
// @{ var content = System.IO.File.ReadAllText(@"C:\secrets\db.config"); }using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
[ApiController]
public class SecureTemplateController : ControllerBase
{
private readonly Dictionary<string, string> _templates = new()
{
["welcome"] = "Welcome, {{UserName}}! Your account is ready.",
["notification"] = "Hello {{UserName}}, you have {{Count}} messages."
};
// SECURE: use predefined templates only
[HttpPost("api/template/render")]
public IActionResult RenderTemplate([FromBody] SecureTemplateRequest request)
{
// Safe: only allow predefined template IDs
if (!_templates.TryGetValue(request.TemplateId, out string template))
{
return BadRequest(new { error = "Invalid template ID" });
}
// Safe: simple placeholder replacement
string result = ReplacePlaceholders(template, request.Data);
return Ok(new { result });
}
private string ReplacePlaceholders(string template, Dictionary<string, string> data)
{
string result = template;
foreach (var kvp in data)
{
result = result.Replace($"{{{{{kvp.Key}}}}}", SanitizeString(kvp.Value));
}
return result;
}
private string SanitizeString(string input)
{
return System.Web.HttpUtility.HtmlEncode(input ?? "");
}
}
public class SecureTemplateRequest
{
public string TemplateId { get; set; }
public Dictionary<string, string> Data { get; set; }
}The vulnerable code passes user-controlled template strings to Razor.Parse(), which compiles and executes C# code embedded in the template, allowing RCE. The secure version uses predefined templates selected by ID, with simple placeholder replacement that doesn't execute code.
Passing user-controlled template strings directly to Razor.Parse() or Engine.Razor.RunCompile().
Sourcery automatically identifies remote code execution via razor template injection and many other security issues in your codebase.