Prevent Command Injection In MVC .NET

Hi folks, i have recently started to look into vulnerabilities from development aspect and it’s quite interesting to write a vulnerable code and then fix it. In this post we are going to look at a simple command injection vulnerability and how to fix it. Before we start i would like to mention my test environment:-

Visual Studio 2017

Application Framework: .net 4.5

Source Code download link

So basically this the view code for a simple application that sends ping to the ip specified by the user.


@{
    ViewBag.Title = "Ping";
}

<h2>Ping</h2>


<h2>Ping</h2>
@using (Html.BeginForm("Exec",
                        "Home",
                        FormMethod.Post,
                        new { enctype = "plain/text" }))
{
    <label for="ip">IP to ping:</label>
    <input type="text" name="ip" id="ip" /><br><br>
    <input type="submit" value="Submit" />
    <br><br>

}

This is the controller code for performing ping

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;


namespace OS_CommandInjection.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult About()
        {
            ViewBag.Message = "Your application description page.";

            return View();
        }

        public ActionResult Contact()
        {
            ViewBag.Message = "Your contact page.";

            return View();
        }
        public ActionResult Ping()
        {
            return View();
        }
        public string Exec(FormCollection form)
        {
            string ip = Request.Form["ip"];
            
            ProcessStartInfo process = new ProcessStartInfo();
            process.FileName = "C:\\Windows\\System32\\cmd.exe";
            process.UseShellExecute = false;
            process.RedirectStandardOutput = true;
            process.Arguments = "/c ping " + ip;
            Process p = Process.Start(process);
            string strOutput = p.StandardOutput.ReadToEnd();
            //p.WaitForExit();

            //ViewBag.Message = "Ping Results" + strOutput;
            ViewBag.Message = strOutput;
            //return View();
            return strOutput;


        }

    }
}

What is wrong here ? the code is not sanitizing ip parameter passed to the ping controller, an attacker may input following to execute arbitrary commands:-

127.0.0.1 && whoami

Since the ip parameter is being passed as parameter all of the input will be executed by cmd. We can fix it by implementing a whitelist to allow only domain name and ip only to be passed as argument to ping by adding the following code in our controller

string whitelist = "[^a-zA-Z0-9.-]";
ip = Regex.Replace(ip, whitelist, string.Empty, RegexOptions.IgnoreCase);

After making changes to controller we can test again for command injection

Now when we reach the controller we can see that ip parameter contains ip and additional injected command

This screenshot shows that any special character except for – and . (dot) is remove thereby avoiding command injection.