SQL Injection, Part 3 of 8: Attacking Websites

I’ve shown you the basic structure of a SQL injection attack, as well as how to take advantage of dynamic SQL to do bad things.  Now, however, I want to move to the area where SQL injection really takes on a life of its own:  websites.  For my example, I’m going to use a very basic ASP.NET website.  The purpose is to display a grid of product subcategories in the AdventureWorks database.  I am doing some stupid things on purpose in order to show you how to attack a website and what a poorly-designed website looks like.  In Part 4, I will show you how you can make ASP.Net websites bulletproof.

The HTML

This is a simple GridView.  I turned AutoGenerateColumns on because this is a very simplistic exercise.  The two additional columns you’ll see the value in later. Note that this is a snippet and not the full code necessary. But if you create a project in Visual Studio, it should be easy to copy and paste this stuff in.

<div>
        <asp:TextBox ID="txtSearch" runat="server" Text="Enter some text here." />
        <asp:Button ID="btnClickMe" runat="server" Text="Click Me!" OnClick="btnClickMe_Click" />
    </div><br />
    <div>
        You searched for:  <asp:Label ID="lblSearchString" runat="server" />
    </div><br />
<div>
        <asp:GridView ID="gvGrid" runat="server" AutoGenerateColumns="true">
            <Columns>
                <asp:TemplateField>
                    <HeaderTemplate>Other Name</HeaderTemplate>
                    <ItemTemplate><%# Eval("Name") %></ItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField>
                    <HeaderTemplate>Other Name As Label</HeaderTemplate>
                    <ItemTemplate><asp:Label ID="lblName" runat="server"><%# Eval("Name") %></asp:Label></ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>
    </div>

The Codebehind

This codebehind is also purposefully simplistic. Its goal is to do the bare minimum necessary for this demonstration. Please don’t think that this is good code; it isn’t. That’s the whole point here: to show you how bad code has bad consequences.

protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                if (!String.IsNullOrWhiteSpace(Request.QueryString["search"]))
                    LoadData(Request.QueryString["search"]);
            }
        }

        protected void btnClickMe_Click(object sender, EventArgs e)
        {
            LoadData(txtSearch.Text);
        }

        protected void LoadData(string Filter)
        {
            if (String.IsNullOrWhiteSpace(Filter))
            {
                //This cleverly prevents anybody from seeing the whole list.  Mission Accomplished.
                Response.Write("You need a filter here, buddy!");
                gvGrid.Visible = false;
            }
            else
            {
                //I'm too sexy for my data objects.
                using (SqlConnection conn = new SqlConnection("server=localhost;database=AdventureWorks;trusted_connection=yes"))
                {
                    string sql = String.Empty;
                    sql = "select Name, ProductSubcategoryID, ProductCategoryID from Production.ProductSubcategory where Name like '%" + Filter + "%' order by ProductSubcategoryID;";
                    using (SqlCommand cmd = new SqlCommand(sql, conn))
                    {
                        cmd.CommandTimeout = 30;
                        conn.Open();

                        //We're using a reader, so there's no way that anybody could do anything bad, right?
                        SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                        gvGrid.DataSource = dr;
                        gvGrid.DataBind();
                        gvGrid.Visible = true;
                    }

                    lblSearchString.Visible = gvGrid.Visible;
                    lblSearchString.Text = Filter;
                }
            }
        }

Note that I’m using .NET 4, so if you’re on a 2.0-3.5 installation, you can change String.IsNullOrWhitespace to String.IsNullOrEmpty and life should be good.

The Attack

So, put that together and fire up your browser to see an ugly but functional page.  Note that the images here include some additional code.  I used this in my demonstration to my company’s developers (which prompted my starting this series), and will probably go into it in Part 4.

A simple page for displaying product subcategories.

A simple page for displaying product subcategories.

Using what we learned in Part 2 of this series, we can try some of the same tactics to attack this site.  First, we’ll use the “OR 1=1” technique.

bike' OR 1=1--

After entering this code snippet into our textbox, we can see that yes, it does work.

The entire list of product subcategories is displayed, proof of a SQL injection attack.

The entire list of product subcategories is displayed, proof of a SQL injection attack.

At this point, the world—or at least database—is your oyster. Let’s see some of the things we can do with it.

First, I want to see the tables on this database.

Displaying all of the tables in the current database.

Displaying all of the tables in the current database.

After that, I can see the ProductSubcategory table, which gives me an idea of what that table looks like.

Listing the product subcategory columns.

Listing the product subcategory columns.

From here, we can see one column which is probably a foreign key: ProductCategoryID. You could simply use another Information_schema call to make sure, but I’ll be lazy. Anyhow, let’s look at ProductCategory.

A listing of product categories.

A listing of product categories.

Now that we have the columns on the Product Category table, we can get the actual column values.

The listing of product categories.  Note that the column headers are "wrong."  The key is actually in ProductSubcategoryID, and ProductCategoryID is a filler field.

The listing of product categories. Note that the column headers are "wrong." The key is actually in ProductSubcategoryID, and ProductCategoryID is a filler field.

So, now that we have that information, we can strike.

A successful insertion of data using SQL injection.

A successful insertion of data using SQL injection.

Proof that our insertion was successful.

Proof that our insertion was successful.

Attacking Via Querystring

Another natural attack vector is querystring.  The only trick here is knowing ASCII codes.

A basic query might look like http://localhost:53953/QueryDriven.aspx?search=bike.  Note that the 53953 is because I am using the built-in web server rather than IIS.

We could change the querystring to include some unanticipated characters.  For example:  http://localhost:53953/QueryDriven.aspx?search=bike%27%20or%201%3D1%2D%2D.  I highlighted the ASCII codes in orange to make it a little easier to read.  In this case %27 is an apostrophe, %20 a space, %3D the equals sign, and %2D a hyphen.  Run that query and you end up with the same results as if we had put it into the form field.

Using the querystring to perform a SQL injection attack.

Using the querystring to perform a SQL injection attack.

The important thing to remember here is that the querystring and form fields are different methods to get to the codebehind.  But the security flaw is in the codebehind itself, not in the method of access.

HTTP Parameter Pollution

One of the most interesting techniques to perform SQL injection that I’ve seen is HTTP Parameter Pollution.  There are two very interesting aspects to this attack vector.  The first is that, as the presentation I linked to demonstrates, different environments have radically different reactions to this vector.  The second is that very few people even know about this attack vector, much less what it will do in each environment.  In my demonstration, none of the developers had even thought about this possibility, much less knew exactly what would happen in ASP.NET.  And remember:  just because a developer doesn’t know how to do something, doesn’t mean that thing can’t be done.

Anyhow, in ASP.NET, if you include a parameter multiple times in the querystring, it gets turned into a comma-delimited list with all of the various parameter values.

An example of HTTP Parameter Pollution in ASP.NET.

An example of HTTP Parameter Pollution in ASP.NET.

In the next article, I’ll go into how you can use this to bypass basic filtering, as well as how to lock down your web applications to prevent an attack.

One thought on “SQL Injection, Part 3 of 8: Attacking Websites

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s