This is how I found a SQL Injection Hacker who was changing all the product titles in my custom shopping cart using a diabolical injection technique. First, I knew the problem was happening, because I saw HTML inserted into my product titles and product descriptions of my database and website. My application was written in “Classic ASP” and VBScript, before .NET came out; since I have hundreds of scripts, I never rewrote them for .NET.
Protecting my Data and Setting the Trap
First of all, I added a T-SQL trigger to disallow multiple record updates, and write to a log file when the code is triggered.
ALTER TRIGGER [dbo].[myTable_multiUpdatePrevention]
FOR UPDATE AS
DECLARE @Count int
DECLARE @MaxAllowedRowToChange int = 5
Select @Count = count(*) from Inserted
IF @Count > @MaxAllowedRowToChange
— Log the issue to the table called ‘Log’
declare @Message varchar(500)
set @Message = ‘Trigger: Possible Hack: Multiple update attempt: Table=Product rowCount=’ + convert(varchar(10),@Count)
insert into LogHacks (logmessage,logdatetime, logUserid) Values (@Message,getdate(), SYSTEM_USER)
RAISERROR(‘Cannot update more than x rows of Product table without disabling trigger ‘,16,1)
Install Auditing Software
I installed “Auto Audit” from Codeplex, see my prior blog on this great tool.
Look at IIS Log
Based on the above, I monitored the LogHacks table, and now can see when the attack is happening, and also now I’m protected and don’t have to restore my data anymore. I was able to capture the userid, which narrowed it down to one of my classic ASP pages.
I then went to my IIS log, and looked the pages retrieved or posted nearest to that time. I found this:
At first, I was being narrow minded, and though injection was only happening in web page forms.
I hadn’t thought about query strings on the URL itself!
Just to find out how this amazingly clever piece of code works, I did the following:
Captured SQL Generated fom my ASP Program
Below, you can see that they they took my “like” clause, and hijacked it, by closing it with a single quote, then adding their own statements.
Unscramble the Hex Code
To interpret the above mess, I wrote this little T-SQL Script (note, I copied the entire hex string, but below I put … so as not to repeat it again here:
declare @showvar varchar(8000)
set @showvar = cast(0x73...36f72 as varchar(8000))
Looking at the Hacker’s Actual Code
When I ran the above code, the actual SQL is shown (line breaks added to make it readable):
set ansi_warnings off
DECLARE @T VARCHAR(255),@C VARCHAR(255)
DECLARE Table_Cursor CURSOR FOR
select c.TABLE_NAME,c.COLUMN_NAME from INFORMATION_SCHEMA.columns c, INFORMATION_SCHEMA.tables t
where c.DATA_TYPE in ('nvarchar','varchar','ntext','text')
and c.CHARACTER_MAXIMUM_LENGTH>80 and t.table_name=c.table_name
and t.table_type='BASE TABLE'
FETCH NEXT FROM Table_Cursor INTO @T,@C
BEGIN EXEC(‘UPDATE [‘+@T+’]
FETCH NEXT FROM Table_Cursor INTO @T,@C
I must admit this is some clever code, and illustrates what hackers will do to sell generic drugs for “you know what”. When I went to their site, my Malware plug-i told me it wasn’t safe, so I didn’t actually load their pages.
The code is basically looking for all super-long text fields, anything over 80 characters in length. It does that by searching INFORMATION_SCHEMA.tables peaking at all my tables and columns. The goal of the attack is apparently to just append their code HTML code, and let your site keep working, so that for many days, they get the SEO impact of the extra link-backs, until you discover the issue. However, in some cases, the HTML was not closing properly, and made a mess of some of my web pages.
Finding my injection bug
I found that I was stringing together a complex where clause depending on various factors. So moral of the story is, never take anything directly form the URL! Request(“language”) is pulling a language variable from the URL So they basically set language to close off the like wild card, then put their own code after that…
optionalSelection = " and prod_title like '%" & Request("language") & "%'"
Fixing my injection bug
optionalSelection = " and prod_title like '%" & fixSqlInsertion(Request("language")) & "%'"
The above is not super-elegant; it results in a “500 Sever Error” to the caller, but I don’t care what the hacker sees really. If he sees the 500, maybe he will quit visiting my site. This was the fastest fix, not the most effective. The better technique would probably be to create a stored procedure, and pass parms to it. Another technique I could use is to limit the size of the URL to a maximum length, to avoid long chunks of code like the one above.
The code above just removes the characters that a SQL injection would need to do damage. To be valid T-SQL, you need to use @ sign for variables, and you need quote marks and equal signs. Doing a DELETE or a DROP doesn’t require all that punctuation, so I prohibit those words; and substitute some fake word that will cause the SQL to fail.
There are two other ideas I could make use of to improve my code more:
1) Parameterized queries
2) Just make sure a SQL select statement doesn’t contain the words “Update”, “Delete”, “Drop”, “Alter”, etc… A select should just be a select.