The author of the Boring, see the company near a service agency site, ASP language, built in the IIS server. At first I did not notice that the site has SQL injection vulnerabilities, conveniently in a news.asp?id=52 URL entered a single quotation mark, then the server reported 500 errors, and gave the error of the SQL information (Khan, server-related error message must not leak out, But it seems that I did not look at the station did not notice the exception of the problem, the page to get the ID parameters should have mandatory int, so the current page constructs a few requests have not found the injection point, I defy the stubborn donkey strength immediately came out.
Bitter find no fruit, the author registered the site's members, and landed on, to see if there is no new injection port. The author after landing grab bag found in the clear text of the information stored in the member, such as the member login name, so the author changed the cookie, but also simple add a single quote, the result ... The server is also 500 and gives an error message. But this time the error message is different, the error message in the display program directly using cookies, did not do any filtering, like
SELECT * from xxx where username= ' $_cookie[' username '] '
So ... Then, the injection point appears.
So the author sent a message to the webmaster, told the website member system has SQL injection loophole, hope that the website in the service function before the official online repair it.
The author after work, Curiosity killed the cat, continue to see how broken.
(1) The author first guessed his backstage management system landing address for www.xxx.com/admin, sure enough, guessed right.
(2) The author again guessed his administrator login table called Admin, injected into the SQL ' exists (SELECT * from Admin) ', the results page 200, also guessed right.
(3) I guess his admin table admin should have a field name username, a field name password, the result has been 500, did not guess right
(4) So the author to see the next login page HTML source code, hoping to find clues from inside. The first step of the author is to see the landing form, found that the landing table Single-name for admin, login password table single-name for UserPassword, so the author based on the previous page of the error information to guess this program ape has the parameter name and database design mapping consistent habits, Then guess the admin table has a field is admin, there is a field name is UserPassword ... Sure enough, the author injected sql ' (select top 1 [admin] from Admin) >0′, also successful, the corresponding UserPassword also exist ... Well, this ...
(5) The author began to guess the length of the background login username and password, continue to inject SQL ' (select top 1 len (admin) =n '), so that N has been incremented, found its website first login username length is 5, password length is 16.
(6) Got the field name, length, next, I guess the two should be letters, numbers combination. The author sequentially injects SQL ' (select top 1 ASC (admin,n,1)) =m ', for user name n->[1,5],m->[0,128], password n->[1,16],m->[0,128], In turn, the user name and password each bit of the ASCII code ... The result is that the username is admin and the password is an encrypted 16-bit string
(7) I guess this is MD5 encryption password, so I found a website reverse decryption this string, get xxxxxxxxxx
(8) The author runs to the website landing page www.xxxx.com/admin, enters the crack user name and the password, the result ... Landed in their backstage!!!.
Walk through these steps, has basically completed the process of invasion, but I do not intend to go further down, directly shut down the machine to sleep.
Writing this article is not to teach you how to invade a website, but, through the intrusion of ideas, to summarize the site system security construction common pit, in the work can be appropriate to avoid.
(1) exception information, to remember try catch, and then the external shielding
(2) Information such as forms, do not have a strong association with the database field, preferably to do a mapping
(3) Do not believe any input from the client, including forms, URL parameters, cookies, when needed, must be safe to filter
(4) If you want to use cookies, do not rely on the important path of cookies, there is no way, please encrypt your cookies
(5) If the website has backstage, do not expose the entrance easily, do not use the commonly used name entrance name, do not expose too much information in the form
(6) Do not give users too detailed information even if they are not system-level error prompts.
(7) If there is a password, you can add other information in the encryption string, the reverse decryption plus interference ~
So how do you prevent injection?
First, using ASP to prevent injection
The method of completely sealing dead injection in ASP:
1. Digital type:
The code is as follows |
Copy Code |
Dim TMP TMP = Request ("Test") IF Len (TMP) > 9 Then Response.Write "Fuck you!" Response.End ElseIF isnumber (TMP) = False Then Response.Write "Fuck you!" Response.End ElseIF Instr (TMP, "+") > 0 Then Response.Write "Fuck you!" Response.End ElseIF Instr (TMP, "-") > 0 Then Response.Write "Fuck you!" Response.End ElseIF Instr (TMP, ".") > 0 Then Response.Write "Fuck you!" Response.End Else Response.Write "Ok!" End IF |
2, Character type:
The code is as follows |
Copy Code |
Dim TMP, I TMP = Request ("Test") TMP = Replace (tmp, "'", "'") TMP = Replace (tmp, "" "," "" "") TMP = REPLACE (tmp, CHR (10), "") TMP = REPLACE (TMP, CHR (13), "") Response.Write "Ok!" |
one, SQL parameterized query
Then we'll analyze why stitching SQL strings can lead to SQL injection risk?
First create a table of users:
The code is as follows |
Copy Code |
CREATE TABLE [dbo]. [Users] ( [Id] [uniqueidentifier] not NULL, [UserId] [INT] Not NULL, [UserName] [varchar] (m) NULL, [Password] [varchar] () not NULL, CONSTRAINT [pk_users] PRIMARY KEY CLUSTERED ( [Id] ASC With (Pad_index = off, Statistics_norecompute = off, Ignore_dup_key = off, Allow_row_locks = on, Allow_page_locks = O N) on [PRIMARY] ) on [PRIMARY] |
Insert some data:
The code is as follows |
Copy Code |
INSERT into [Test]. [dbo]. [Users] ([Id],[userid],[username],[password]) VALUES (NEWID (), 1, ' name1 ', ' pwd1 '); INSERT into [Test]. [dbo]. [Users] ([Id],[userid],[username],[password]) VALUES (NEWID (), 2, ' name2 ', ' pwd2 '); INSERT into [Test]. [dbo]. [Users] ([Id],[userid],[username],[password]) VALUES (NEWID (), 3, ' Name3 ', ' pwd3 '); INSERT into [Test]. [dbo]. [Users] ([Id],[userid],[username],[password]) VALUES (NEWID (), 4, ' name4 ', ' pwd4 '); INSERT into [Test]. [dbo]. [Users] ([Id],[userid],[username],[password]) VALUES (NEWID (), 5, ' name5 ', ' pwd5 '); |
Let's say we have a user login page with the following code:
Verify that the user logs on to SQL as follows:
The code is as follows |
Copy Code |
Select COUNT (*) from Users where Password = ' a ' and UserName = ' B ' |
This code returns the number of users matching password and username, if greater than 1, then represents the user.
This article does not discuss password policies in SQL or code specifications, mainly about why you can prevent SQL injection, and ask some students not to tangle with some code, or a topic unrelated to SQL injection.
You can see the results of the execution:
This is the SQL statement that SQL profile traces.
The injected code is as follows:
The code is as follows |
Copy Code |
Select COUNT (*) from Users where Password = ' a ' and UserName = ' B ' or 1=1-' there is someone who sets the UserName to "B" or 1=1–. |
The actual execution of SQL becomes the following:
It's obvious that the SQL injection was successful.
Many people know that parameterized queries can avoid the injection problems that appear above, such as the following code:
The code is as follows |
Copy Code |
Class Program { private static string connectionString = "Data source=.;i Nitial catalog=test;integrated security=true "; static void Main (string[] args) { Login ("B", "a"); Login ("B ' or 1=1--", "a"); } private static void Login (string userName, string password) { using (SqlConnection conn = new SqlConnection (connectionString)) { Conn. Open (); SqlCommand comm = new SqlCommand (); Comm. Connection = conn; Add a parameter to each piece of data Comm.commandtext = "Select COUNT (*) from the Users where Password = @Password and UserName = @UserName"; Comm. Parameters.addrange ( New sqlparameter[]{ New SqlParameter ("@Password", SqlDbType.VarChar) {Value = Password}, New SqlParameter ("@UserName", SqlDbType.VarChar) {Value = UserName}, }); Comm. ExecuteNonQuery (); } } } |
The actual execution of SQL is as follows:
The code is as follows |
Copy Code |
EXEC sp_executesql N ' select COUNT (*) from Users where Password = @Password and UserName = @UserName ', n ' @Password varchar (1 ), @UserName varchar (1) ', @Password = ' a ', @UserName = ' B ' exec sp_executesql N ' select COUNT (*) from Users where Password = @ Password and UserName = @UserName ', N ' @Password varchar (1), @UserName varchar ', @Password = ' a ', @UserName = ' B ' or 1=1-' |
You can see that parameterized queries do these things primarily:
1: Parameter filtering, you can see @UserName = ' B ' or 1=1-' 2: Execution plan reuse
Because the execution plan is reused, SQL injection can be prevented.
First, analyze the nature of SQL injection,
The user wrote a section of SQL used to indicate that the lookup password is a, and the user name is the number of all users of B.
By injecting SQL, this SQL now represents the lookup (the password is a and the username is B) or the number of all users 1=1.
You can see that the semantics of SQL has changed, why has it changed? Because the previous execution plan was not reused because the injected SQL statement was compiled again because the parsing of the syntax was performed again. So to make sure that the SQL semantics is the same, that I want to express SQL is what I want to express, not other injected meaning, should reuse the execution plan.
If the execution plan cannot be reused, there is a risk of SQL injection because the semantics of SQL may change and the query expressed may change.
Query execution plans in SQL Server can use the following script:
The code is as follows |
Copy Code |
DBCC Freeproccache Select Total_elapsed_time/execution_count Average time, total_logical_reads/execution_count logical Read, Usecounts Reuse times, SUBSTRING (D.text, (STATEMENT_START_OFFSET/2) + 1, (case statement_end_offset WHEN-1 THEN datalength (text) ELSE statement_end_offset End -Statement_start_offset)/2) + 1) statement execution from Sys.dm_exec_cached_plans A Cross apply sys.dm_exec_query_plan (A.plan_handle) c , sys.dm_exec_query_stats b Cross apply SYS.DM_EXEC_ Sql_text (b.sql_handle) d --where A.plan_handle=b.plan_handle and total_logical_reads/execution_count>4000 Order by Total_elapsed_time/execution_count DESC; |
Here's a quote from the author: "But there's no real difference between this type of writing and the direct execution of SQL."
Any concatenation of SQL is a risk of SQL injection, so if there is no substantive difference, then using exec dynamic execution of SQL does not prevent SQL injection.
For example, the following code:
The code is as follows |
Copy Code |
private static void TestMethod () { using (SqlConnection conn = new SqlConnection (connectionString)) { Conn. Open (); SqlCommand comm = new SqlCommand (); Comm. Connection = conn; Using exec to execute SQL dynamically The query plan actually executed is (@UserID varchar (max)) SELECT * from Users (NOLOCK) where UserID in (1,2,3,4) Not expected (@UserID varchar (max)) EXEC (' SELECT * from Users (NOLOCK) where UserID in (' + @UserID + ') ') Comm.commandtext = "Exec" (' SELECT * from Users (NOLOCK) where UserID in (' + @UserID + ') '); Comm. Parameters.Add (New SqlParameter ("@UserID", SqlDbType.VarChar,-1) {Value = "1,2,3,4"}); Comm. Parameters.Add (New SqlParameter ("@UserID", SqlDbType.VarChar,-1) {Value = "1,2,3,4"); Delete from users;--"}); Comm. ExecuteNonQuery (); } } |
The SQL executed is as follows:
The code is as follows |
Copy Code |
EXEC sp_executesql n ' EXEC (' select * from Users (NOLOCK) where UserID in (' + @UserID + ') ', n ' @UserID varchar (max) ', @Use Rid= ' 1,2,3,4 ' can see the SQL statement without parameterized queries. If you set the UserID to "1,2,3,4"; Delete from users;-- ", then execute SQL is the following: EXEC sp_executesql n ' exec (NOLOCK) where UserID in (' + @UserID + ') ') ', n ' @UserID varchar (max) ', @UserID = ' 1,2,3,4 '); Delete from users;--' |
Don't assume that adding a @userid represents the ability to prevent SQL injection, and the actual execution of SQL is as follows:
Any dynamic execution SQL has the risk of injecting, because dynamic means not reusing the execution plan, and if you don't reuse the execution plan, it's basically impossible to guarantee that the SQL you write is meant to mean what you're trying to say. This is like a childhood fill in the blanks, look for the password is (___) and user name is (___) user. Whatever value you fill in, that's what I'm saying. Finally, we conclude by saying that because parameterized queries can reuse execution plans, and if you reuse the execution plan, the semantics that SQL wants to express will not change, so you can prevent SQL injection, and if you can't reuse the execution plan, there's a possibility of SQL injection, and so is the stored procedure. Because the execution plan can be reused.