I don’t think I’ll have room for this anywhere else, so I wanted to include a little bit of bonus material.
In my simple workbench website, I included two extra columns in my gridview: one in which I simply Eval(“Name”) and another in which I have a label whose Text property is set to Eval(“Name”). I wanted to show you that both of these are bad ideas, and you probably should not use them.
Setting Up An Attack
Let’s go back to our basic website, that you can see in Part 3. I’m going to perform SQL injection to insert an evil value. In this case, I’ll inject script code. Here is the code I would like to inject:
insert into Production.ProductSubcategory(ProductCategoryID, Name, rowguid, ModifiedDate) values(1, '// ', newid(), current_timestamp);--
So, trying it on the page gets me…an error:
Trying to run a script tag in a textbox returned this exception.
So ASP.NET protected the server from my malicious script insertion attempt. That’s pretty cool, and I like that. Unfortunately, there are ways around this… My favorite way is to convert the message to binary and have SQL Server do the decoding and running for me. Doing this is rather trivial. In a copy of SQL Server Management Studio, all I have to do is run the following statement:
This converts the query I want to run into varbinary. If I were to convert the varbinary back to text and execute that text, it would perform as if I had simply entered the text to begin with. The advantage (for an attacker) is that ASP.NET doesn’t understand what’s in that binary blob, so it doesn’t realize that there’s a script tag hanging around. Here’s the code we’ll use to attack:
declare @shmooi varchar(8000); set @shmooi = CAST(0x696E7365727420696E746F2050726F64756374696F6E2E50726F6475637453756263617465676F72792850726F6475637443617465676F727949442C204E616D652C20726F77677569642C204D6F64696669656444617465292076616C75657328312C20273C73637269707420747970653D22746578742F6A617661736372697074223E616C65727428224122293C2F7363726970743E272C206E6577696428292C2063757272656E745F74696D657374616D70293B2D2D as varchar(8000)); exec(@shmooi);--
That blob of binary is equivalent to the insert statement from above. But running it on the server presents us, as expected, with more favorable results.
Using a binary blob instead of text, we can circumvent ASP.NET's Request.Form protections.
We can go into SQL Server Management Studio and confirm that the evil subcategory has been inserted. Use the following query to find the record:
select * from Production.ProductSubcategory where Name like '%script%';
The next person who searches for anything on the page that matches this—like, say, the letter “A”—will be in for a rude surprise.
So why did this attack succeed? We can get a good idea of the problem by looking at the bottom of the page, where our new attack subcategory is:
The first two columns are empty. That is because they were evaluated and not simply displayed. By performing an evaluation, you also evaluate any script inside—regardless of whether you, as the developer, intended somebody to run your script. The third column, which is the default GridView column, simply displays the text in a label. It does not evaluate the code, and so even though somebody injected code into the database, it was not run.
Just like before, a SqlParameter can protect us against this attack. Here is what our parameterized, space-limited query looks like:
exec sp_executesql N'select Name, ProductSubcategoryID, ProductCategoryID from Production.ProductSubcategory where Name like ''%@Filter%'' order by ProductSubcategoryID;',N'@Filter nvarchar(50)',@Filter=N'boof''; declare @shmooi varchar(8000); set @shmooi '
Because there are only 50 characters allowed, the attack would fail. But more importantly, we made the query completely safe, so instead of looking for “boof” and then running the attack code, it tries to look for “boof'; declare @shmooi varchar(8000); set @schmooi ‘ as one of our product subcategories. No records are returned and the attack code, obviously, is not run.
Again, parameterize your queries. It will save you so much headache and hassle. In addition, I recommend not using the Eval functionality, and instead loading your labels and other form fields in the codebehind. That way, even if somebody does sneak something evil into your database, it won’t run the way the bad guy expected.