Welcome to MSDN Blogs Sign in | Join | Help

I recently presented a TechNet Webcast on the topic “Configuring with Least Privilege in SQL Server 2008”.

The topics covered in the Webcast are:-

1. Configuring SQL Server service accounts with least privilege. Service isolation is also explained.

2. Configuring accounts connecting to SQL Server from a Web application (Principals) with least privilege.

3. Running xp_cmdshell with a proxy so that the account invoking xp_cmdshell need not be a sysadmin.

4. Running SQL Server jobs with least privilege.

The Webcast can be viewed here:-

http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?culture=en-US&EventID=1032415807&CountryCode=US

If you can find the security issue with this piece of code, write about it by adding a comment to this blog post.

This is the scenario:-

1. There is a Web site that allows end users to upload their pictures.

2. On the Web server, the Web site is physically located at C:\Inetpub\wwwroot\sampleapp, which is obviously a virtual directory.

3. The uploaded pictures are stored on the file system in the following directory C:\Uploads\{username}. This is not a virtual directory. {username} is the logged in user’s username and can contain only alphabets and numbers.

This is what the page looks like to the end user.

fileupload

This is the code that gets executed. The code for the UploadPicture.aspx.cs file is this.

   1: public partial class UploadPicture : System.Web.UI.Page
   2: {
   3:     protected void Page_Load(object sender, EventArgs e)
   4:     {
   5:         lblUserName.Text = "Welcome " + User.Identity.Name;
   6:     }
   7:     protected void btnSubmit_Click(object sender, EventArgs e)
   8:     {
   9:         new UploadHelper().UploadFile(fileUploadControl, User.Identity.Name);
  10:     }
  11: }

The code for the UploadHelper.cs file is this:-

   1: public class UploadHelper
   2: {
   3:     private string uploadFolder;
   4:     public UploadHelper()
   5:     {
   6:         uploadFolder = @"C:\Uploads\";
   7:     }
   8:     
   9:     private void SetUpUploadFolder(string userName)
  10:     {
  11:         uploadFolder += userName + @"\";
  12:         if (!System.IO.Directory.Exists(uploadFolder))
  13:             System.IO.Directory.CreateDirectory(uploadFolder);
  14:     }
  15:     
  16:     public string UploadFile(FileUpload fp, string userName)
  17:     {
  18:         string extension = fp.PostedFile.FileName.Substring(fp.PostedFile.FileName.LastIndexOf("."));
  19:         if (extension.ToLower() == ".jpg" || extension.ToLower() == ".gif")
  20:         {
  21:             string strFileName = fp.PostedFile.FileName.Substring(fp.PostedFile.FileName.LastIndexOf("\\") + 1);
  22:             SetUpUploadFolder(userName);
  23:             string strFullPath = uploadFolder + strFileName;
  24:             fp.SaveAs(strFullPath);
  25:             return strFileName;
  26:         }
  27:         return null;
  28:     }
  29: }

If I login to the Web site as “varun” and upload a picture with name mypic.gif, it gets uploaded to this folder on the Web server:-

C:\Uploads\varun\mypic.gif

 

Happy Hunting!

Microsoft Virtual TechDays is starting from the 18th February 09. In the security track, I will be presenting on the topic “Top 5 Web Application Security bugs in custom code”. As a security engineer in the ACE Team, I have been reviewing line-of-business applications for the past two years. In this presentation, I will talk about the most common security mistakes that developers make while writing code. Since developers from various geographical locations tend to make the same mistakes, the audience can take back a lot of practical knowledge and apply it to secure their applications.

In my last post, I showed input validation code that uses RegularExpressionValidators improperly. Thanks to Mathew Grabau and Marius Cristian CONSTANTIN for pointing out that the Page’s IsValid property has not been checked before using the input. As a result, effectively, the code performs only client side validation, which can easily be bypassed. As a matter of fact, there are three more vulnerabilities in the code, that, together lead to a bigger vulnerability.

1. “validateRequest” is set to false.

   1: <%@ Page Language="C#" ValidateRequest="false" 
   2: AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %>   

2. User supplied input is, without encoding, rendered back to the browser.

   1: protected void btnSubmit_Click(object sender, EventArgs e)   
   2: {            
   3:     Response.Write("Welcome " + Request["txtName"]);  
   4: }   

3. The Request object has been used ambiguously.

   1: Response.Write("Welcome " + Request["txtName"]);

What I mean by ambiguously is, that, a specific collection like Request.Forms or Request.QueryString has not been used to get the input parameter. If, Request[“txtName”] is used directly, the Web server searches the collections in the following order:

QueryString
Form
Cookies
ClientCertificate
ServerVariables

If “txtName” exists in more than one collection, the Request object returns the first instance that the object encounters [1]. As a result, if “txtName” is entered in the query string as well, it will be picked up from there and not from the form variable.

Therefore, an attacker can lure a victim into clicking a link like

http://server_name/firstwebsite/Default2.aspx?txtname=%3cscript%3ealert('xss')%3b%3c%2fscript%3e

If the victim clicks on this link, enters any name in the text box and clicks the submit button, script payload in the query string will execute in the victim’s browser, leading to cross-site scripting.

cth5

A much more secure way of writing the same code would be:-

   1: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %>
   2:      <html xmlns="http://www.w3.org/1999/xhtml" >
   3:      <body>
   4:         <form id="form1" runat="server">
   5:          <asp:TextBox ID="txtName" runat="server"></asp:TextBox>
   6:         <asp:Button ID="btnSubmit" runat="server" Text="Submit" OnClick="btnSubmit_Click" />
   7:         <asp:RegularExpressionValidator ID="regexpName" runat="server"     
   8:                                         ErrorMessage="This expression does not validate." 
   9:                                         ControlToValidate="txtName"     
  10:                                         ValidationExpression="^[a-zA-Z'.\s]{1,40}$" />
  11:         </form></body></html>
   1: public partial class Default2 : System.Web.UI.Page
   2: {
   3:     protected void Page_Load(object sender, EventArgs e)
   4:     {
   5:     }
   6:     protected void btnSubmit_Click(object sender, EventArgs e)
   7:     {
   8:         if (Page.IsValid)
   9:         {
  10:             Response.Write("Welcome " +
  11:                 HttpUtility.HtmlEncode(txtName.Text));
  12:         }
  13:     }
  14: }    

References:-
[1] Request Object
http://msdn.microsoft.com/en-us/library/ms524948.aspx

A lot of web applications use RegularExpressionValidators for performing input validation [1]. Sometimes these validators are not implemented properly, which can lead to potential flaws. See if you can catch the flaw here:-

Code for Default.aspx:-

   1: <%@ Page Language="C#" ValidateRequest="false" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %>
   2:  <html xmlns="http://www.w3.org/1999/xhtml" >
   3: <body>
   4:     <form id="form1" runat="server">
   5:  
   6:     <asp:TextBox ID="txtName" runat="server"></asp:TextBox>
   7:     <asp:Button ID="btnSubmit" runat="server" Text="Submit" OnClick="btnSubmit_Click" />
   8:     <asp:RegularExpressionValidator ID="regexpName" runat="server"     
   9:                                     ErrorMessage="This expression does not validate." 
  10:                                     ControlToValidate="txtName"     
  11:                                     ValidationExpression="^[a-zA-Z'.\s]{1,40}$" />
  12:  
  13:     </form>
  14: </body>
  15: </html>

Code behind Default.aspx.cs file:-

   1: public partial class Default2 : System.Web.UI.Page
   2: {
   3:     protected void Page_Load(object sender, EventArgs e)
   4:     {
   5:  
   6:     }
   7:     protected void btnSubmit_Click(object sender, EventArgs e)
   8:     {
   9:         Response.Write("Welcome " + Request["txtName"]);
  10:     }
  11: }

 

Reference:-

[1] How To: Use Regular Expressions to Constrain Input in ASP.NET
http://msdn.microsoft.com/en-us/library/ms998267.aspx

Identify as many security issues as you can with this piece of code:-

   1:     [WebMethod] 
   2:     public string GetEmpName(string empid) 
   3:     { 
   4:         SqlConnection con = new SqlConnection("server=.;database=test;uid=sa;pwd=PassW2rd12"); 
   5:         SqlCommand cmd = new SqlCommand("select username from users where id = " + empid, con); 
   6:         con.Open(); 
   7:         string empname = (string)cmd.ExecuteScalar(); 
   8:         con.Close(); 
   9:         return empname; 
  10:     }

How many did you get? Lets go over them one by one:-

1. The SQL connection string uses SQL Authentication to connect to the server. If possible, you should use Windows authentication instead. This eliminates the need to store credentials in the application.

2. If Windows Authentication is not possible, store the connection string in the web.config file and encrypt it using the aspnet_regiis tool.

3. The account used to connect to the SQL server is “sa”, which is a member of the all powerful sysadmin server role. Instead, the account used to connect to the SQL Server must have the least amount of permissions on the server as needed to do the job.

3. There is no validation for the “empid” parameter. If only integers are expected, try to parse it into an integer, otherwise use a regular expression for white list validation.

4. The “empid” parameters is being concatenated to form a SQL statement, which is then executed. This means that the application is vulnerable to SQL Injection. Instead of concatenating user input to create a SQL statement, use parameterized SQL.

5. There is no structured exception handling in this code. As a result, verbose error messages containing managed exceptions will be sent to the end user. Implement structured exception handling and close the connection in a finally block.

This is how the code looks after restructuring:-

   1:  
   2:     [WebMethod]
   3:     public string GetEmpName(string empid)
   4:     {
   5:         int id;
   6:         if (int.TryParse(empid, out id))
   7:         {
   8:             SqlConnection con = new SqlConnection(System.Web.Configuration.WebConfigurationManager.ConnectionStrings["Conn"].ConnectionString);
   9:             SqlCommand cmd = new SqlCommand("select username from users where id = @id", con);
  10:             cmd.Parameters.Add(new SqlParameter("@id", id));
  11:             try
  12:             {
  13:                 con.Open();
  14:                 return (string)cmd.ExecuteScalar();
  15:             }
  16:             catch (Exception exp)
  17:             {
  18:                 LogException(exp);
  19:                 throw new ApplicationException("An unexpected error occured");
  20:             }
  21:             finally
  22:             {
  23:                 if (con != null && con.State == ConnectionState.Open)
  24:                     con.Close();
  25:             }
  26:         }
  27:  
  28:         return null;
  29:     }

My colleague Sagar and I will be conducting an application security workshop at the NASSCOM – DSCI Information Security Summit 2008 on the 1st December in IIIT, Hyderabad, India.

More information can be found here:-

http://www.nasscom.in/Nasscom/Templates/CustomEvents.aspx?id=55164 

The agenda is here:-

http://www.nasscom.in/upload/59314/Agenda_Tutorial.pdf

1. Consider a Microsoft Office SharePoint Server 2007 site that will be used as a “Document Approval System”. Certain users will be “Editors” and they will be able to upload documents for approval. Another set of users will be “Approvers”. These users can either approve or reject the uploaded documents. The security requirement is that “Editors” should not be able to approve the documents and the “Approvers” should not be able to edit or delete the documents.

2. Create a document library where the documents will be uploaded.

clip_image002

In this document library, create an Out-of-the-box MOSS 2007 Approval Workflow. In the “Select a task list to use with this workflow”, select “New task list”.

clip_image004

3. In the text box for “Approvers”, add the windows group that will contain all the “Approver” users. Also so that an editor cannot change this “Approver” group at the time the workflow is being created, uncheck the “Allow changes to the participant list when this workflow is started” checkbox.

clip_image006

4. As you can see a new task list is created for this workflow.

clip_image008

5. Give Contribute permissions on the site to the windows group that will contain the “Editors”.

clip_image010

This group can now add, edit and delete items from lists.

6. Now login as an “Editor” and upload a document. clip_image012

Start the workflow on the uploaded document.

clip_image014

As you can see the approvers text box is disabled.

clip_image016

Once the workflow is started, a task is created in the task list specific to this workflow.

clip_image018

7. Let us see what happens if the “Editor” tries to approve the document herself.

clip_image020

We are trying to approve a workflow logged in as an editor.

clip_image022

The “Editor” will get an error message and the following will be added to the workflow history.

clip_image024

8. Now login as the “Administrator” and create a new permission level for the “Approver”. Give this permission level, edit items, view items, open items, view versions and view application pages permissions.

clip_image026

9. Create a new Share Point group for workflow approvers. Give it read permissions on the site.

clip_image028

clip_image030

Give the same group edit permissions on the workflow task list (that was specifically created for the approval workflow) using the “WorkflowApprovalPerm” permission just created.

clip_image032

clip_image034

The Share Point group “Workflow Approvers” now has the following effective permissions on the site. Effectively it has read-only permissions on the entire site, but “edit” permissions on the task list specific to the approval workflow.

clip_image036

10. Add the windows group containing all the approvers to this “Workflow Approvers” Share Point group.

clip_image038

11. Now login as an “Approver”. Go to the document library. See that the approver can neither edit nor delete the uploaded documents.

clip_image040

12. Go to the task created for approval. Try to approve it.

clip_image042

As you can see the “Approver” is able to approve the document.

clip_image044

Summary:-

This “How To” shows that it is possible using the out-of-the-box MOSS 2007 approval workflow to create a document approval system where

1. The “Editors” can only upload documents to be approved but cannot approve the documents themselves.

2. The “Approvers” can only approve or reject the documents but cannot edit or delete them.

Quite a few web applications encrypt query string values. This is generally done as an added measure to prevent unauthorized access. Since the end user cannot chose a value and then encrypt it, changing parameters becomes difficult. But encryption is not a panacea. See if you can spot this bug.

encrypt1

The code behind file looks like this:-

encrypt2

Implementation for the Encrypt and Decrypt methods is not shown. They are using the DES algorithm. There is no flaw in the usage or key management.

The end user can upload files and the screen look like this:-

encrypt3

On clicking Upload, the file gets uploaded and a message is shown. Note the query string values. The HTML source is also shown.

encrypt4

Do you think the code or design is flawed in any way? Can this be exploited?

Modern symmetric block encryption algorithms need to satisfy a number of properties to be considered strong. One such property is the property of “Confusion”.

What it means is that if an attacker is conducting an exhaustive key search, and if the key being tested is incorrect only in a few bits, the decrypted text should give no such indication. If the decrypted text does give such an indication, then the attacker can stop the brute force process, and simply change the incorrect bits in the key and get the actual key. This will take much less time relative to a full brute force attack.

To understand this better, I will demo it using the CrypTool, which is a great tool to learn about cryptography.

1. This will encrypt the text shown in the background using the Simple Substitution Cipher and the key “ONCEUPATIMXBDFGHJKLQRSVWYZ”.

crypt1

2. Text in the background has been encrypted. Using the tool, I will now perform an automatic analysis of the cipher text to try to get the key. Note: This could have been a brute force attack too.

crypt2

3. Although the correct key has not been found, since the decrypted text resembles text in English to a great extent, I may be “close” to the the actual key.

crypt3

 

4. I will now stop the brute force process and using manual analysis , one by one substitute only those characters in the key that seem to produce incorrect plaintext, thereby getting the actual key.

crypt4

 

Modern encryption algorithms like DES and AES have the Confusion property. Therefore if English text has been encrypted using DES or AES and during the brute force process, the key being tested differs from the actual key by only one bit, still the decrypted text does not resemble English text at all. 

In my previous “Catch the Security Flaw” post I wrote about a flawed CAPTCHA implementation. In this post I discuss what are the flaws in that implementation and how to prevent them. Before I go into the exact flaws, let us agree upon a standard notation to describe the flow of data.

C stands for Client. This is the end user. S stands for Server which is the web server in this case.

In the implementation of the flawed CAPTCHA, the data flow is:-

1) C ----> S; GET somepage.aspx

2) S ----> C; Somedata || CAPTCHA Picture || hash (CAPTCHA value)

3) C ----> S; Somedata’ || hash (CAPTCHA value) || CAPTCHA value

In the flawed implementation, hash (CAPTCHA value) is being maintained on the client side as a hidden field. CAPTCHA value is supposed to be entered by the end user in the text box.

If in the 3rd step, Server gets the ”right” CAPTCHA value, it takes the action. To check if the value is “right”, the server simply hashes the CAPTCHA value sent by the client and then compares it with the hash (CAPTCHA value) also sent by the client.

To discuss the flaws, we will look only at the 3rd step. The attacker, to run an automated script, will continually call step 3.

Attack 1:- The client can take any value, say A. Then hash it to get hash (A). It can then continually call step 3 as:-

3) C ----> S; Somedata’ || hash (A) || A

This value A is not the same as in the picture in step 2. It doesn't have to be. The server does not maintain state. Hence it doesn't know that this value A is not the same as the one it had sent in the picture in step 2.

Countermeasure of Attack 1:-

So to prevent this attack, instead of hashing the value, let us encrypt it. The flow now becomes:-

1) C ----> S; GET somepage.aspx

2) S ----> C; Somedata || CAPTCHA Picture || Encrypt (CAPTCHA value, Key)

3) C ----> S; Somedata’ || Encrypt (CAPTCHA value, Key) || CAPTCHA value

Key is the encryption key used to encrypt the CAPTCHA value. This key is known only to the server. Now if the Client takes any value, say A. It does not have the Key to encrypt it to create Encrypt (A, Key). Hence it cannot choose any A and then call step 3. Great!

Attack 2:-

Even if the above countermeasure is put in place, the Client can still call step 3 continually. The Client can once manually see the CAPTCHA picture. Say the value in it is B. It can then call step 3 continually with the same value.

3) C ----> S; Somedata’ || Encrypt (B, Key) || B

Client doesn't know how to calculate Encrypt (B, Key). It does not need to. The Server is not storing any state. Hence the server does not know if this value has been used before.

Therefore, never forget this principle for a CAPTHCA implementation:-

“The Server MUST maintain state in a secure CAPTCHA implementation”

Countermeasure for Attack 2:-

1) C ----> S; GET somepage.aspx

2) S ----> C; Somedata || CAPTCHA Picture =>Server maintains CAPTHCA value

3) C ----> S; Somedata’ || CAPTCHA value  =>Server verifies CAPTCHA value

In step 2, the Server creates a random CAPTCHA value and stores it on the server side. It links this value with a particular Client, say using a session ID. In step 3, the Server verifies if the value sent by the Client is the same as in its state. It then must nullify the CAPTCHA value for that client.

 

Reference:-

An ASP.NET Framework for Human Interactive Proofs

Consider a fictional web site that lets you create new accounts (as shown below).

 

This site implements CAPTCHA to prevent a malicious user from creating large number of false accounts by running an automated script.

The following code is used to implement the CAPTCHA. What do you think is the flaw here?

public partial class _Default : System.Web.UI.Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

        if (!IsPostBack)

        {

            int randomValue = GetRandomCaptchaValue();

            imgCaptcha.ImageUrl = GenerateImage(randomValue);

            // hdnCaptchaValue is a hidden variable.

            // <asp:HiddenField ID="hdnCaptchaValue" runat="server" />

hdnCaptchaValue.Value = GenerateHash(randomValue.ToString());

        }

 }

 

protected void btnSubmit_Click(object sender, EventArgs e)

    {

        if (GenerateHash(txtImageValue.Text).CompareTo(hdnCaptchaValue.Value) == 0)

        {

            // Code to create the account

 

        }

        else

        {

            lblCaptcha.Text = "The value entered is not correct.";

        }

    }

 

public static string GenerateHash(string text)

    {

        string hash = string.Empty;

        System.Text.UnicodeEncoding uEncode = new System.Text.UnicodeEncoding();

           

        System.Security.Cryptography.SHA512Managed sha = new System.Security.Cryptography.SHA512Managed();

        Byte[] hashBuffer = sha.ComputeHash(uEncode.GetBytes(text));

        return  Convert.ToBase64String(hashBuffer);

     }

 

private int GetRandomCaptchaValue()

    {

        Random random = new System.Random();

        return random.Next(100001, 999999);

    }

/// <summary>

    /// This method generates an image using Bitmap class and then saves it in webroot.

    /// It returns the URL of the image.

    /// </summary>

    /// <param name="captchaValue">URL of the image</param>

    /// <returns></returns>

    private string GenerateImage(int captchaValue)…

   

       

 

 

It is time to discuss the flawed code that I posted a couple of weeks back. The comments posted were good and in essence summarize the flaw.

The circled part is an example of an embedded code block. The query string parameter “id” will be inserted inside the <% %> block, creating HTML at the client that looks something like this:-

<!--<input type="text" value="123" />--> for a query string value id=123.

 This can lead to Cross Site Scripting (XSS).   

 

The countermeasure is simple. Treat user controlled input which is put inside an embedded code block with caution, even if there are comments around the HTML element. If you do not need the element, remove the embedded code block. If you need it, validate the input and encode the output.

Setting ValidateRequest to true (which is by default) would have prevented this bug from getting exploited. But would ValidateRequest prevent this from being exploited?

I will be from time to time, putting up flawed code as an open question on this blog. Those who can catch the flaw please do post about it in the comments section (preferably with the repro steps). After a few days, I will post the flaw and its countermeasure.

Here is the first one:-

I have seen quite a few web applications that rely on disabling controls for authorization. Consider this code:-

The scenario may be that the page has to be displayed in a read-only manner for certain roles, or after submission of some details but prior to approval (in a workflow). This reason depends on the business requirement. In this dummy app, the page looks like this:-

This authorization can easily be bypassed. Without using any special tool, an attacker can just enter script this way in the address bar and hit enter:-

Now the attacker enters some text and hits the submit button, completely bypassing the authorization control:-

Countermeasure:-

Check the condition in the event handler before taking action.

More Posts Next page »
 
Page view tracker