i built website that uses sql server and have this sql for deleting items by id that im getting from querystring:
DELETE FROM tablename WHERE GUID = 'param'
is it possible to inject sql that will return db_name() for example?
i know that i can inject sql only for select statements something like this
select name from tablename where 'parems'
union all
select db_name()
but what about delete statements
i know that i can drop table insert to table, but in this stage i need know if i can get kind of data, for ex.: db_name()
Sure. This value of param:
' OR ''='
will result in this statement:
DELETE FROM tablename WHERE GUID ='' OR '' = ''
which will delete all data in the table.
Yes, a query always returns a result, so if the database driver allows it you can simply add another query after the delete.
An input like this:
x'; select db_name() --
would give:
DELETE FROM tablename WHERE GUID = 'x'; select db_name() --'
You should read the wiki & MS docs on SQL Injection
I think your asking if its possible using purely delete statement. If so, its possible by adding the OUTPUT clause like so:
DELETE Sales.ShoppingCartItem
OUTPUT DELETED.*
WHERE ShoppingCartID = 20621;
This will output the deletes rows.
To avoid SQL injections, you should use SQL parameters
Related
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.
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.
I have a query in a stored procedure that needs to be executed on different servers and databases according to some parameters.
How can I do this without using neither exec, nor sp_executesql?
I'm using SQL Server 2008.
Thank you.
UPDATE
I've found some links
http://www.eggheadcafe.com/software/aspnet/29397800/dynamically-specify-serve.aspx
http://www.sommarskog.se/dynamic_sql.html
Is using SYNONYM possible solution? If yes, than how?
UPDATE 2
I forgot to mention that all this servers are linked to the server where stored procedure is stored.
UPDATE 3
OPENROWSET or OPENDATASOURCE are not accessible either. I need a solution without building query string concating server name, schema name, db name.
It surely can be done by using if or case in stored procedure, but if we have 37 variations, then it's not a good solution.
Any other suggestions?
Nobody wants to answer, so I'll do it myself, just to have accepted answer.
There's isn't any way to do this. You need to use one of specified suggestions, anyway the query must be generating by concatenating.
Does OPENROWSET or OPENDATASOURCE help?
EDIT: If it works, you can change the database at runtime & execute the query using the present connection. I cannot see any other way of executing query the way you want.
What is wrong with running query using string i.e dynamic query?
/* DO THIS FOR EACH TABLE IN THE PROCEDURE*/
--BEGIN TABLE_1
DECLARE #LINKEDSERVER AS VARCHAR(50)
SET #LINKEDSERVER = DBO.FN_RETURN_SERVER('SBROUBLES')
DROP SYNONYM MYLINKEDSERVER
EXEC (' CREATE SYNONYM MYLINKEDSERVER FOR ' + #LINKEDSERVER + 'ANYDB.DBO.ANYTABLE')
--- END TABLE_1
-- UTILIZATION
SELECT COUNT(*) FROM MYLINKEDSERVER
--AND FN_RETURN_SERVER COULD BE ANY SELECT CASE ON SQL AS WELL
I've created an ADO.NET connection manager, and a DataReader source with the following SQL Command:
select
'test' as testcol
INTO
#tmp
select * from #tmp
If I click the refresh button in the DataReader component, I get SqlException "Invalid object name #tmp". The SQL statment itself is clearly valid and executes properly in sql server management studio. I've also tried setting DelayValidation on the connection manager, to no avail.
is the error on the INSERT or the SELECT?
if you are issuing only one command that contains both the INSERT and SELECT, try putting a semicolon before the SELECT.
EDIT after OP comment
encapsulate all the logic within a stored procedure:
CREATE PROCEDURE YourProcedureName
AS
select
'test' as testcol
INTO
#tmp
select * from #tmp
GO
the have your application run this single SQL command:
exec YourProcedureName
EDIT after next OP comment
OP doesn't say which SQL Server version they are using, if 2005 or up, try a CTE:
;with CTEtemp as
(
select
'test' as testcol
)
select * from CTEtemp
Why couldn't this be replaced with a "SELECT 'test' as testcol"? The SSIS query parser may be having trouble with it because there's a temp table involved and it expects a single statement, not an actual SQL script. Or, if what you're sharing above is only an example for illustration, maybe something like this:
SELECT *
FROM (SELECT 'test' AS testcol)
Can you elaborate on what you're trying to accomplish here and, if it is, why the temp table is required?
Use sp_executesql
Your command would become
exec sp_executesql #statement=N'
select
''test'' as testcol
INTO
#tmp
select * from #tmp'
You must use nvarchar string (hence the N), and escape single quotes by doubling them.
I had the same problem as you and this is how I just fixed it.
I exported a table to a server but I can't find the table. Maybe I didn't put the right destination database. How can I find this table if my server has multiple databases, without opening each one of them?
I use MS Sql Server Management Studio 2008.
Rough and dirty, but it would do the job.
-- Instructions. Replace "table_name_here" with actual table name
sp_MSforeachdb 'USE ?
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''[table_name_here]'') AND OBJECTPROPERTY(id, N''IsUserTable'') = 1)
BEGIN
PRINT ''Found in db ?''
END'
One way
SELECT DISTINCT DB_NAME(database_id)
FROM [sys].[dm_db_index_operational_stats](NULL,NULL,NULL,NULL)
WHERE OBJECT_NAME(object_id,database_id) = 'table_name'
Or if you are reasonably confident it would be in the dbo schema in whichever database
SELECT name
FROM sys.databases
WHERE CASE
WHEN state_desc = 'ONLINE'
THEN OBJECT_ID(QUOTENAME(name) + '.[dbo].[table_name]', 'U')
END IS NOT NULL
Based off Martin Smith's answer above but generalised into a view to give a sort of cross-DB version of sys.tables -
CREATE VIEW ListTablesAllDBs
AS
SELECT
DB_NAME(database_id) as DBName,
OBJECT_SCHEMA_NAME(object_id,database_id) as SchemaName,
OBJECT_NAME(object_id,database_id) as TableName
FROM
[sys].[dm_db_index_operational_stats](NULL,NULL,NULL,NULL)
Now, if only I can work out a way to do the same for columns.......
EDIT - Ignore this, finding it sometimes misses tables altogether.
Minor clarification just to avoid headaches for those with 'SuperUsers' who don't know how to name DBs:
EXEC sp_MSForEachDB '
USE [?]
IF OBJECT_ID(''mytable'') IS NOT NULL AND
OBJECTPROPERTY(OBJECT_ID(''mytable''), ''IsTable'') = 1
PRINT ''Found here: ?'''
select 'select * from '+name+'.sys.tables where name=
''[yourtable]'';' from sys.databases
Instead of [yourtable], type the name of the missing table, and run the result again.
EXEC sp_MSForEachDB '
USE ?
IF OBJECT_ID(''mytable'') IS NOT NULL AND
OBJECTPROPERTY(OBJECT_ID(''mytable''), ''IsTable'') = 1
PRINT ''?''
'