SQL Injection - Valid subqueries within T-SQL IN - sql-server

I am trying to learn some SQL Injection and wanted to ask if any other query other than select could be executed within a SQL IN Operator.
Normal IN operator syntax is this
select * from tableX where ID IN (select userId from tableY where colX='Y')
I would want to pass in a sql stmt via querystring to delete all users in a table.
Is this possible to do if this injected sql is executed within a IN operator.
This is what I want to achieve
select * from tableX where ID IN (delete tableY)
or
select * from tableX where ID IN (update tableX set ID=100 where 1=1)
or
select * from tableX where ID IN (exec(N'delete tableY'))
I keep getting weird syntax errors. May be this is not as per the sql spec. But in case anyone knows of valid subqueries which might help me achieve the goal, please post.
Edit: Should have also added this.
The system already has couple of defenses.
This value is passed to a SP as a parametrized value.
But one the whole there is one weakness
The only weak point I see which I am trying to exploit is the construction of the sql string in the SP using the passed in value.
set #where = #where + 'ID IN (' + #htmlEncodedParametrizedParam + ')
So all I have to break this code is send in only a pure sql string (no ; or ) which could be used within an IN operator.
Edit2: Coded up to verify #Dan's answer. No avail.
Create procedure HackTest
#qsVal nvarchar(200) = ''
As
Begin
declare #sql as nvarchar(1000)
set #sql = 'select * from AdventureWorks2008.Person.Person' + 'where PersontType IN (' + #qsVal + ')'
exec sp_executesql #sql
end
Below are the request and what MS Sql server profiler captured for these requests
--http://localhost:11727/SampleSite/Default.aspx?a=NULL);delete%20tableY;--
exec hackTest #qsVal='NULL);delete tableY;--'
--http://localhost:11727/SampleSite/Default.aspx?a=NULL);update%20tableX%20set%20ID=100;--
exec hackTest #qsVal='NULL);update tableX set ID=100;--'

No, the IN operator does only allow a subquery or a list of expressions:
test_expression [ NOT ] IN
( subquery | expression [ ,...n ]
)
So you can’t use a DELETE or an UPDATE statement there but only a SELECT statement.
However, since SQL Server support batches, you could simply append your own statement after the SELECT statement:
SELECT … ; DELETE …

you could simulate multiple queries, for example, this is your query, parameter will be replaced later by your program:
SELECT * FROM tableX WHERE ID IN (<somestring>)
if you were to replace with this text:
1); DROP TABLE tableY;--
you'll get executable query like this:
SELECT * FROM tableX WHERE ID IN (1); DROP TABLE tableY;--)

If the application constructs the SQL statement with string concatenation like:
selectQuery = "select * from tableX where ID IN (" + queryString + ")";
This particular SQL injection vulnerability can be exploited by adding "NULL);" followed by the SQL statement to be injected, and then a comment marker. Example query string values:
NULL);delete tableY;--
NULL);update tableX set ID=100;--
Of course, one should never pass a SQL statement via a query string. The best defense against SQL injection is simply to use parameterized queries rather than building statements with string concatenation from untrusted sources.

Related

Declaring Databasename in sql query

I am preparing a group of sql queries for a dashboard. I want to declare the database name at the beginning so that the queries will work on the database specified on top without making any changes in the underlying code
Original query:
SELECT *
FROM Check.dbo.Dates_table
The query I want:
DECLARE #Databasename VARCHAR(200)
SET #Databasename = 'Check.dbo'
SELECT * FROM #Databasename.Dates_table
You can use "USE" operator: https://msdn.microsoft.com/en-AU/library/ms188366.aspx
use Check
SELECT * FROM dbo.Dates_table

Insert statement fails on SQL Server 2005 but work well on 2008 R2 or later version

I have the following SQL insert statement inside the loop which iterate through a cursor:
SELECT #q_sql = 'INSERT INTO SYS_PARAMETER(parameter_uno, parameter_key, description, parameter_value, comments, created_date, created_user_id, created_user_name, last_modified_user, last_modified_date, module_uno, data_type)
VALUES ('+#sysparam_uno + ','''+#q_parameter_key+''','''','''+b.pairvalue+''','''',
getdate(),''setup'',''setup'',''setup'',getDate(),'''+#q_module_uno+''','''')'
from UTIL_pairkeys a
INNER JOIN UTIL_pairvalues b on a.pairkeyuno = b.pairkeyuno
and b.languno = 1
where a.pairkey=#q_parameter_key
EXEC sp_executesql #q_sql
Due to value coming to b.pairvalue parameter having a single quote, insert statement fails on SQL Server 2005, but work well on SQL Server 2008R2 and later versions. Any knows reason for this? I know that insert statement fails once parameter value has single quote in between varchar columns. But this something strange here.
Sample insert statement as follows;
INSERT INTO SYS_PARAMETER(parameter_uno,parameter_key,description,parameter_value,comments,created_date,created_user_id,created_user_name,last_modified_user,last_modified_date,module_uno,data_type)
values (269,'application.fs.company','','St John's Foods','',getdate(),'setup','setup','setup',getDate(),'1','')
If it is a problem of single quote only than you can replace it by two single quotes like this:
replace( b.pairvalue ,'''','''''')
I order to escape ', you need to replace it with '':
SELECT #q_sql = '
INSERT INTO SYS_PARAMETER(parameter_uno,parameter_key,description,parameter_value,comments,created_date,created_user_id,created_user_name,last_modified_user,last_modified_date,module_uno,data_type)
values ('+#sysparam_uno + ','''+#q_parameter_key+''','''','''+REPLACE(b.pairvalue, '''', '''''')+''','''',getdate(),''setup'',''setup'',''setup'',getDate(),'''+#q_module_uno+''','''')'
from UTIL_pairkeys a
INNER JOIN UTIL_pairvalues b on a.pairkeyuno = b.pairkeyuno
and b.languno = 1
where a.pairkey=#q_parameter_key
EXEC sp_executesql #q_sql
However, it's best if you could use parametrisation instead:
DECLARE #pair_value VARCHAR(100)
SELECT #pair_value = b.pair_value
from UTIL_pairkeys a
INNER JOIN UTIL_pairvalues b on a.pairkeyuno = b.pairkeyuno
and b.languno = 1
where a.pairkey=#q_parameter_k
SELECT #q_sql = '
INSERT INTO SYS_PARAMETER(parameter_uno,parameter_key,description,parameter_value,comments,created_date,created_user_id,created_user_name,last_modified_user,last_modified_date,module_uno,data_type)
VALUES( #parameter_uno_param,
#parameter_key_param,
'''',
#parameter_value_param,
'''',
getdate(),
''setup'',
''setup'',
getdate(),
''setup'',
#module_uno_param,
'''')'
EXEC sp_executesql
#q_sql,
N' #parameter_uno_param VARCHAR(100),
#parameter_key_param VARCHAR(100),
#parameter_value_param VARCHAR(100),
#module_uno_param VARCHAR(100)
',
#sysparam_uno,
#q_parameter_key,
#pair_value,
#q_module_uno
This assumes your select will only find one pair_value. If more, you need to consider looping through those.
Above code segment call by java program and since SQL Server 2005 thrown an exception to the calling java program it cause to break the java program itself. However, for SQL Server 2008, it look like database engine it self handle the exception without throwing exception to the calling application. To prove that I added try catch block to the error prone code segment and ran the java program against the SQL Server 2005. So, calling application didn't break. Did same against the SQL Server 2008 R2 and result was same.

SQL Server stored procedure parameter value as databasename

And to all experts here.. I'm a newbie to stored procedures.
I really need help, my problem is, is it possible to used a parameter as databasename
Below is my sample:
ALTER PROCEDURE [dbo].[SP_EventLogs]
(#HRTable as varchar(50))
AS
BEGIN
Set NOCOUNT ON
SELECT
a.*,LOG_ADDBY.ADDBY
FROM
EVENT_VIEWER a
INNER JOIN
(SELECT
PK, (Lastname + ', ' + Firstname + ' ' + ExtName) as ADDBY
FROM
#HRTable.[dbo].[EMP_RECORDS]) LOG_ADDBY ON a.PerformBy = LOG_ADDBY.PK
ORDER BY
a.PerformDate DESC
Set NOCOUNT OFF
END
#HRTable is my parameter.. and it returns an error.
Any suggestion would be greatly appreciated.
Thank you...
One alternative is to build a view that has the table name inside it and join to that
SELECT 't1'as tableName ,col1, col2, col3,... from t1
UNION ALL
SELECT 't2',col1, col2, col3,... from t2
where
.....
and tableName=#HRTable
But this is not really "the done thing". You can also use dynamic SQL & Exec if you must do this but maybe consider if you are doing whatever it is you are doing in the right way.
What's in the various tables that means you can't just put them into one?
As I understood #HRTable is your database name.This is not gonna to work. You should use dynamic query .
Declare Variable for query text and set it for your query then use Exec(#variable) to run the query text in variable.

How to create dynamic stored procedure in SQL Anywhere?

I'm having an issue with creating dynamic sql statement in SQL Anywhere.
CREATE PROCEDURE pplAnalysis
AS
BEGIN
DECLARE #Sql NVARCHAR(4000)
SELECT #Sql = "select * from cds.ppl"
EXECUTE(#Sql)
END
When I execute this procedure, I get an Column 'select * from cds.ppl' not found error.
Can you please tell me what am I doing wrong?
The issue had to do with syntax and the RESULT clause; after adding semicolons, RESULT clause, and used SET to initialize the Sql variable, here is what worked (tested in SQL Anywhere Network Server Version 12.0.1):
drop proc pplAnalysis;
CREATE PROCEDURE pplAnalysis()
RESULT (cnot CHAR(5), cnull CHAR(5), vnot VARCHAR(5), vnull VARCHAR(5), explanation VARCHAR(50))
BEGIN
DECLARE #Sql NVARCHAR(4000);
SET #Sql = 'select cnot, cnull, vnot, vnull, explanation from dbo.spaces';
EXECUTE ( #Sql );
END;
spaces is a table in the dbo schema and those columns are the same type specified in RESULT
Tested these two ways to execute the procedure and both returned result:
call pplAnalysis();
cnot cnull vnot vnull explanation
----- ----- ----- ----- --------------------------------------------------
Execution time: 0.027 seconds
Procedure completed
or
exec pplAnalysis;
cnot cnull vnot vnull explanation
----- ----- ----- ----- --------------------------------------------------
Execution time: 0.018 seconds
For more details:
Returning result sets from procedures
Create procedure statement
Try first saving the result of the query in a temporal table, and then do a SELECT from the temporal table:
SELECT #Sql = "select into #temp * from cds.ppl"
EXECUTE(#Sql)
SELECT * FROM #temp
Use single quotes.
SELECT #Sql = 'select * from cds.ppl'
After some research, I have edited my answer.
Regarding the EXECUTE ( string-expression ) statement, yes you have to use single quotes instead of double quotes for the string expression. This page mentions:
It lets you execute dynamically prepared statements, such as
statements that are constructed using the parameters passed in to a
procedure. Literal strings in the statement must be enclosed in single
quotes, and the statement must be on a single line.
Which will eliminate the column not found error but the procedure will return this other error:
Result set not permitted in '<batch statement>'
Same error returned when trying to execute this statement alone:
execute ('select * from sysusers')
With probable cause:
You attempted to execute a SELECT statement in a context where a
result set is not permitted.
See my most recent answer for the solution.
And regarding schemas, here's how to refer to objects:
It is always good practice to refer to database objects by a schema
name and the object name, separated by a period (.). For a complete example, to SELECT records from the Employee table in the HumanResources schema of the current database would look like:
SELECT * FROM HumanResources.Employee
To reference an object located in a remote database, the fully
qualified object name includes the server name and the database name.
For example, to SELECT records from the Employee table in the
HumanResources schema in the AdventureWorks database on MyServer would
look like:
SELECT * FROM MyServer.AdventureWorks.HumanResources.Employee
I tested that in SQL Anywhere 12 and it works the same. And even though I was not familiar with schemas, what I'm suggesting you below is actually using schemas, dbowner would be the schema name:
1) select * from dbname.dbowner.tablename
2) select * from dbowner.tablename
3) select * from dbname..tablename (assumes table exists in the dbo schema)
Bottom line.... In your select statement cds.ppl has to be a table named ppl created in the cds schema.
Or if cds is your database name and ppl your table name created in the dbo schema, you are missing a dot:
select * from cds..ppl

Query on multiple databases (SQL server)

I have multi databases with same structure its name like that "Client1234" the different in numbers beside "client" i have table called "Transactions" inside each database and i want to run query to get count all raws in "transactions" table in all databases.
also when i select database i need to check it has the client word and it has numbers beside the word.
Try to use sp_msforeachdb stored procedure like so:
create table #temp ([rows] int, [client] varchar(100))
exec sp_msforeachdb '
if ''?'' like ''Client%'' and exists(select * from ?.sys.tables t where t.name = ''Transactions'')
begin
insert into #temp select count(*), ''?'' from ?..Transactions
end
'
select * from #temp
drop table #temp
You can use dynamic SQL to create these queries:
select 'select count(*) from ' + name + '.dbo.transactions'
from master..sysdatabases
where name like 'Client%'
and isnumeric(substring(name,6,1))
This would return a result set with each row being a SQL query to count a specific database. It could be consumed by a programming language, used as a cursor, etc.. If you provide more detail I may be able to provide a better example.
When using Fosco's method, it is a good idea to put in brackets [] around the database name:
SELECT 'SELECT count(*) FROM ' + '[' + name + ']' + '.dbo.transactions'
FROM master..sysdatabases
WHERE name like 'Client%' and isnumeric(substring(name,6,1))
If the name and number of the databases you wish to query is not known beforehand then you can only do this by using a dynamic query. You'll need to generate a script like
SELECT COUNT(*) FROM Client1.dbo.Transactions
SELECT COUNT(*) FROM Client2.dbo.Transactions
...
Of course you need to have your appropriate permissions in place for each database.

Resources