Home / 

In this blog, we will discuss vulnerabilities found in Microsoft ASP.NET that are specific to Local File Inclusion and SQL Injection, and learn about how we can mitigate them.

The Local File Inclusion (LFI) vulnerability occurs in ASP.NET when a web application allows a user to read any file from the web server irrespective of its extension. It may lead to information disclosure vulnerability, allowing a malicious user to gain complete control of the web server when used in conjunction with other vulnerabilities, like remote execution.

SQL Injection vulnerability occurs when a user’s input is not sanitized and is sent as a parameter to SQL statements. Finding a chance, a malicious user may alter the data that can lead to session hijacking (account takeover) or injection of harmful scripts in the data to install malware (malicious software) on the end-user system when they visit the website. One of the major consequences of SQL Injection vulnerability in a web application is that the malicious user may take control of the entire web server or leak data to the public to bring down the business of an organization and hurt them financially.

Developers must be trained in coding standards so that they can securely write code. Code review procedures must be in place so that such vulnerabilities can be identified early during the development/testing phase. The testing phase must also include security checks to ensure that applications are secure from such vulnerabilities.

SQL Injection in ASP.NET Web Applications

xxxxxxxxxxxxxxxxxxxxxxxx ASP.NET Code Snippet start xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeFile="Login.aspx.cs" Inherits="Login" %> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" Runat="Server"> <form id="Form1" runat="server"> <table> <tr> <td>User:</td><td><asp:TextBox ID="user" runat="server"></asp:TextBox></td> </tr> <tr> <td>Password:</td><td><br /><asp:textbox TextMode="password" id="pass" runat="server" /></td> </tr> <tr> <td><asp:button Text="Login" runat="server" id="btn" onClick="submit"/></td> </tr> </table> </form> </asp:Content> //server side Submit button method protected void submit (object sender, EventArgs e) { string query1 = "Select username, password from admin where username <> 'admin' and password = '" + pass.Text.Trim() + "' "; try { DataSet dSet1 = new DataSet(); dSet1 = fetchWebDB(query1); } DataSet fetchWebDB(string query) { // connect to data source OleDbConnection myConn = new OleDbConnection("Provider=SQLOLEDB;Data Source=.;Initial Catalog=AMS;User ID=sa;Password=pass@123"); myConn.Open(); // initialize dataadapter with query OleDbDataAdapter myAdapter = new OleDbDataAdapter(query, myConn); // initalize and fill dataset with query results DataSet myData = new DataSet(); myAdapter.Fill(myData); myConn.Close(); // return dataset return myData; } } xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ASP.Net Code snippet end xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

The code mentioned above is vulnerable to SQL Injection attacks since the developer has not sanitized the user input before sending data to the SQL statement. Value of User and Pass variables are passed as it is to SQL statement, which should be avoided in all the cases. User input must and always be sanitized; it must escape all special characters that are not intended to be there. For example – a username will never have less than “<” or double quotes (“). User input must be encoded before it is saved in the database.

Secondly, the developer has used “system admin” user to connect with MS SQL database to fetch the data. In this case, all SQL statements will be executed under the context of “sa”(system admin) user. In MS SQL Server, “sa” user has the highest level of privileges. If a malicious user gains access to this application or somehow the password of “sa” user is known to him, he can perform certain actions that can help him gain system-level access on windows server.

Thirdly, it is not recommended to keep the connection string within an application page because the revelation of source code would also reveal the connection string. It is recommended to keep it in web.config under < connectionstring/ > key in an encrypted form. Just in case a malicious user gets access to the web.config file, he would not be able to decrypt the connection string.

How to encrypt the connection string in ASP.NET web application?

It is recommended that system administrators/developers should encrypt the connection strings and MachineKey in web.config before moving the application to production. Plain text passwords should always be avoided in configuration files since they become the primary targets during web attacks.

MachineKey itself must be encrypted since this key is used to encrypt/decrypt and validate ASP.NET Cookies and anti-forgery tokens.

MachineKey is as sensitive as connection strings because once this key is obtained by a malicious user, he can create an authenticated cookie that can allow him to log in as any user.

To encrypt the MachineKey element in web.config, run the following command:

aspnet_regiis -pe "system.web/machineKey" -app "[Your Application Name]"

aspnet_regiis.exe is located at following path on the web server:

c:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe

To encrypt connection strings using RSA Key Container, follow the approach mentioned below:

Use the existing RSA Key Container that is created by default when .NET is installed “NetFrameworkConfigurationKey“

First of all, to read the RSA key container, ASP.NET identity of the web application must be authorized:

Web App Authorization

To encrypt the sensitive data element “connectionstring” in web.config, run the following command:

aspnet_regiis -pe "connectionStrings" -app "[Your Application Name]"

Encrypting Sensitive Data Element

Below is the result of the above command when web.config is opened:

Web Configuration

Another way is - Adding “Trusted connection=true” in connection string:


<connectionStrings>
<add name=”DefaultConnection” connectionString=”Server=localhost;Database=mydb;Trusted_Connection=True”
</connectionStrings>



IIS Screenshot

The screenshot above is taken from Internet Information Services (IIS) by selecting the App Pool that is used to run the web application. A custom account (service account) can be configured to run for App Pool identity. The web application will now use this identity to connect to the database and run under the context of this service account. This account must have access to MSSQL Server so that it can run SQL queries and save data in the database.

If a malicious user collects different credentials, he may use them to brute force other services that are running at the web server level or at OS level, like FTP or SMB service. SQL Injection vulnerabilities are ‘input validation’ vulnerabilities and can be prevented by enforcing input validation on any user-controlled parameter.

Another important thing is to disable any anonymous access to the website and enable authentication so that the events that took place during their visit can be traced and tracked later on.

Also, directory listing should be disabled in IIS for the website:

  • Open IIS Manager
  • Select the website to disable the listing of files
  • Double-click the Directory Browsing icon in the IIS section

The admin should also restrict access to specific directories that has confidential/configuration-related data. Access to a specific file can be denied which is placed in a specific folder:


<location path="confidential">
     <system.webServer>
         <security>
             <authorization>
                 <remove users="*" roles="" verbs="" />
                 <add accessType="Allow" roles="Administrators" />
             </authorization>
         </security>
     </system.webServer>
 </location>>


Mitigation Steps:

Sometimes, the developers believe that they can save a web application from SQL Injection vulnerabilities by using stored procedures. However, a poorly written stored procedure can be the reason for exploiting SQL Injection vulnerability in the web application.

Consider the below-stored procedure:


ALTER PROCEDURE [dbo].[SearchLeaves]
  @searchleavetype VARCHAR(50) = ''
AS
BEGIN
DECLARE @query VARCHAR(100)
SET @query = 'SELECT * FROM LEAVES WHERE category LIKE ''%' + @searchleavetype + '%''';
EXEC(@query)
END


In the above-stored procedure, string concatenation is a problem and the stored procedure can be exploited to read data from any table in the database.

Parameterization can be the solution to the above-mentioned problem: Above-stored procedure can be rewritten to mitigate SQL Injection vulnerability.


ALTER PROCEDURE [dbo].[SearchLeaves]
  @searchleavetype NVARCHAR(50) = ''
AS
BEGIN
  DECLARE @query NVARCHAR(100)
  DECLARE @msearch NVARCHAR(55)
 
  SET @msearch = '%' + @searchleavetype + '%'
  SET @query = 'SELECT * FROM LEAVES WHERE category LIKE @search'
  EXEC sp_executesql @query, N'@search VARCHAR(55)', @msearch
END


Instead of concatenating strings, a new string can be created adding ‘%’ characters and then pass this new string as a parameter to SQL statement.

Another step to mitigate the SQL Injection vulnerability is to grant only limited permissions to the current user who is responsible to execute SQL statements.

Let’s assume, there is a web page that is supposed to display the leaves of the employee. A new SQL user can be created in the database. By mapping a custom role, the user will only be able to execute stored procedures. Only intended data will be re-tuned to the web page by executing stored procedures in this user’s context.

Consider the below role and user creation to mitigate SQL Injection:


CREATE ROLE CustomFetchDataRole
CREATE LOGIN TestUser WITH PASSWORD = '$Passw0rd@123##’
ALTER ROLE CustomFetchDataRole ADD MEMBER [TestUser]
IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N' TestUser ')
BEGIN
  CREATE USER [TestUser] FOR LOGIN [TestUser]
 
END;
GO

GRANT EXECUTE ON dbo.uspGetLeavesInfo TO TestUser
GRANT EXEC ON TYPE::DBO.MyTableType TO [CustomFetchDataRole]


Granting only specific and limited permissions to the database can go a long way in securing data. Malicious user will not be able to access any tables directly even if he is somehow able to log in on SQL Server. Now, the stored procedure (consider parameterization) will either return the intended data to the web page or will not return anything.

Another important thing to keep in mind is that the database administrators should disable “xp_cmdshell” stored procedure. Database users should not have permissions to enable this stored procedure. Developers should never use this stored procedure because a malicious user can execute the OS-level commands using this procedure.

DB admin should enable SQL Server audit logs to keep logs for Login auditing, SQL Server auditing, SQL Trace, DML, DDL, and Logon trigger events.

SQL Injection is the most dangerous attack considering the magnitude of the attack can compromise the entire database server and the web server. This attack can be stopped by parameterization of the stored procedures by granting only limited permissions to SQL DB users.

SQL DB admins should create different users, allowing them access to a specific database on the server. Limit the number of users granted access to a single database. Every user must have only limited permissions to access the database.

Sysadmin (SA) user should be disabled and a custom system admin user should be created instead with trimmed down privileges so that, if the malicious user somehow gets the access to the database, he should not be able to gain system-level privileges using “SA” user.

ASP.NET Local File Inclusion (LFI)

The LFI vulnerability allows a malicious user to access other files on the web server. This is one of the most critical vulnerabilities listed in OWASP 10 vulnerabilities list.

Below is a sample ASP.NET code, which allows certain documents to be downloaded.


http://abcd.com/<vuln.page>?<vuln query string>

Consider the below code:

public partial class Downloads_Download : System.Web.UI.Page
{
  string sBasePath="";
  protected void Page_Load(object sender, EventArgs e)
  {

    try
    {
      string filename = Request.QueryString["fname"];
      string sBasePath = System.Web.HttpContext.Current.Request.ServerVariables["APPL_PHYSICAL_PATH"];
      if (sBasePath.EndsWith("\\"))
        sBasePath = sBasePath.Substring(0, sBasePath.Length - 1);
      sBasePath = sBasePath + "\\" + ConfigurationManager.AppSettings["FilesPath"] + "\\" + filename; 
      Response.AddHeader("content-disposition", String.Format("attachment;filename={0}", filename));
      Response.WriteFile(sBasePath);
    }
    catch (Exception ex)
    {
Response.Redirect("~/Error.aspx?message=" + ex.Message.ToString() + " path=" + sBasePath);
    }
  }
}


Let us walk through the above code and find out what is wrong with it.

The “fname” parameter is accepted as it is without any validation or sanitization. This code may lead to Directory Traversal vulnerability and information disclosure vulnerability, which can reveal sensitive data that may lead to further attacks.

Code should be written in such a way that only specific file extensions are allowed to download. Content type: application/octet-stream is unsafe and should not be set as a default content-type defined in the switch case function.

Take a look at the code below - it is more secure than the earlier one:


protected void Page_Load(object sender, EventArgs e)
  {
    //string file = Server.MapPath(HttpUtility.UrlEncode(functions.RF("file")));
    string file = Server.MapPath(functions.RQ("file"));
    if (File.Exists(file))
    {
      Regex reg = new Regex(@"\.(\w+)$");
      string ext = reg.Match(file).Groups[1].Value;
      switch (ext)
      {
        case "xls":
          Response.ContentType = "application/vnd.ms-excel";
          break;
        case "xlsx":
          Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
          break;
        case "ppt":
          Response.ContentType = "application/vnd.ms-powerpoint";
          break;
        case "pptx":
          Response.ContentType = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
          break;
        default:
      Response.ContentType = " application/pdf ";
          break;
      }
      byte[] buffer = File.ReadAllBytes(file);
      Response.OutputStream.Write(buffer, 0, buffer.Length);
      Response.AddHeader("Content-Disposition", "attachment;filename=" + Path.GetFileName(file));
    }
    else
    {      
      Response.Write("This file Extension is not allowed");
    }
  }


In the above code, the user can download files only with these extensions: xls, xlsx, ppt, pptx, and pdf.

The developer has set a ‘content type’ for each file extension, which means for PDF file types, the content type is application/pdf and will only allow PDF files to download.

MIME (Multipurpose Internet Mail Extensions) content types describe the media type of the content/file which helps browsers to correctly process and display the content. The application must describe the ‘Content Type’ of the files while downloading or uploading the file, otherwise, the browser would have no idea about the intended content to be processed that is different from the reported MIME type.

Default content type should not be “application/octet-stream”. Developers should not use this content type in their code until it is intended to.

Apart from the issues mentioned above, there are many other ASP.NET vulnerabilities that are critical in nature. While the mitigation steps mentioned are not enough to secure Microsoft ASP.NET web applications from vulnerabilities as a whole, one can close at least basic vulnerabilities by following these steps. We will discuss the remaining high-risk vulnerabilities in our upcoming blogs.

To learn more about vulnerabilities in PHP web applications, you can have a look at my other blog “High-Risk Vulnerabilities in PHP Web Applications”.